151 lines
5.3 KiB
Markdown
151 lines
5.3 KiB
Markdown
|
# Proposed Extensions to FEA
|
||
|
|
||
|
This document describes a macro extension to FEA that will enable it to grow
|
||
|
and support more powerful OpenType descriptions. The proposal is presented as
|
||
|
various syntax extensions to the core FEA syntax.
|
||
|
|
||
|
## Functions
|
||
|
|
||
|
Currently FEA makes no use of parentheses. This may be a conscious decision to
|
||
|
reserve these for later ues. Such parentheses lend themselves perfectly to the
|
||
|
addition of macro functions to the FEA syntax:
|
||
|
|
||
|
```
|
||
|
function = funcname '(' (parameter (',' parameter)*)? ')'
|
||
|
|
||
|
funcname = /[A-Za-z_.][A-Za-z_0-9.]*/
|
||
|
parameter = glyph | glyphlist | classref | value_record | function
|
||
|
| ('"' string '"') | ("{" tokens "}")
|
||
|
tokens = noncurlytoken* | ("{" tokens "}")
|
||
|
glyphlist = '[' glyph* ']'
|
||
|
classref = '@' classname
|
||
|
value_record = number | '<' chars '>'
|
||
|
```
|
||
|
|
||
|
A function call consists of a function name and a parenthesised parameter list,
|
||
|
which may be empty.
|
||
|
|
||
|
|
||
|
and an optional following token list enclosed in braces. The
|
||
|
token list is just that, an unparsed sequence of lexical tokens. The result of
|
||
|
the function is also an unparsed sequence of lexical tokens that are then parsed
|
||
|
and processed as if the function were replaced by a textual representation of
|
||
|
the tokens.
|
||
|
|
||
|
The parameters are parsed, so for example a classref would expand to its
|
||
|
resulting list of glyphs. Likewise a function call result would be parsed to its
|
||
|
single semantic item, it is not parsed as a token list. A value_record is the
|
||
|
widest interpretation of a value record, including an anchor. Basically it is
|
||
|
either a number or anything between < and >.
|
||
|
|
||
|
A function statement is the use of a function result as a statement in the FEA
|
||
|
syntax.
|
||
|
|
||
|
The FEA syntax defines nothing more that functions exist and how they may be
|
||
|
referenced. It is up to a particular FEA processor to supply the functions and
|
||
|
to execute them to resolve them to a token list. It is also up to the particular
|
||
|
FEA processor to report an error or otherwise handle an unknown function
|
||
|
reference. As such this is similar to other programming languages where the
|
||
|
language itself says nothing about what functions exist or what they do. That is
|
||
|
for libraries.
|
||
|
|
||
|
There is one exception. The `include` statement in the core FEA syntax follows
|
||
|
the same syntax, apart from the missing quotation marks around the filename. As
|
||
|
such `include` is not available for use as a function name.
|
||
|
|
||
|
### Sample Implementation
|
||
|
|
||
|
In this section we give a sample implementation based on the FEA library in
|
||
|
fonttools.
|
||
|
|
||
|
Functions are kept in module style namespaces, much like a simplified python module
|
||
|
system. A function name then typically consists of a `modulename.funcname` The
|
||
|
top level module is reserved for the fea processor itself. The following
|
||
|
functions are defined in the top level module (i.e. no modulename.)
|
||
|
|
||
|
#### load
|
||
|
|
||
|
The `load` function takes a path to a file containing python definitions.
|
||
|
Whether this python code is preprocessed for security purposes or not is an open
|
||
|
question. It also takes a modulename as its second parameter.
|
||
|
|
||
|
```
|
||
|
load("path/to/pythonfile.py", "mymodule")
|
||
|
```
|
||
|
|
||
|
The function returns an empty token string but has the effect of loading all the
|
||
|
functions defined in the python file as those functions prefixed by the
|
||
|
modulename, as described above.
|
||
|
|
||
|
#### set
|
||
|
|
||
|
This sets a variable to a token list. Variables are described in a later syntax
|
||
|
extension. The first parameter is the name of a variable. The token list is then
|
||
|
used for the variable expansion.
|
||
|
|
||
|
```
|
||
|
set("distance") { 30 };
|
||
|
```
|
||
|
|
||
|
Other non top level module may be supplied with the core FEA processing module.
|
||
|
|
||
|
#### core.refilter
|
||
|
|
||
|
This function is passed a glyphlist (or via a classref) and a regular
|
||
|
expression. The result is a glyphlist consisting of all the glyphs whose name
|
||
|
matches the regular expression. For example:
|
||
|
|
||
|
```
|
||
|
@csc = core.refilter("\.sc$", @allglyphs)
|
||
|
```
|
||
|
|
||
|
#### core.pairup
|
||
|
|
||
|
This function is passed two classnames, a regular expression and a glyph list.
|
||
|
The result is two class definitions for the two classnames. One class is
|
||
|
of all the glyphs which match the regular expression. The other class is a
|
||
|
corresponding list of glyphs whose name is the same as the matching regular
|
||
|
expression with the matching regular expression text removed. If no such glyph
|
||
|
exists in the font, then neither the name or the glyph matching the regular
|
||
|
expression is included. The resulting classes may therefore be used in a simple
|
||
|
substitution. For example:
|
||
|
|
||
|
```
|
||
|
core.pairup("cnosc", "csc", "\.sc$", [a.sc b.sc fred.sc]);
|
||
|
lookup smallcap {
|
||
|
sub @cnosc by @csc;
|
||
|
} smallcap;
|
||
|
```
|
||
|
|
||
|
Assuming `fred.sc` exists but `fred` does not, this is equivalent to:
|
||
|
|
||
|
```
|
||
|
@cnosc = [a b];
|
||
|
@csc = [a.sc b.sc];
|
||
|
lookup smallcap {
|
||
|
sub @cnosc by @csc;
|
||
|
} smallcap;
|
||
|
```
|
||
|
|
||
|
## Variables
|
||
|
|
||
|
A further extension to the FEA syntax is to add a simple variable expansion. A
|
||
|
variable expands to a token list. Since variables may occur anywhere they need a
|
||
|
syntactic identifier. The proposed identifier is an initial `$`.
|
||
|
|
||
|
```
|
||
|
variable = '$' funcname
|
||
|
```
|
||
|
|
||
|
Variables are expanded at the point of expansion. Since expansion is recursive,
|
||
|
the variable may contain a function call which expands when the variable
|
||
|
expands.
|
||
|
|
||
|
There is no syntax for defining a variable. This is unnatural and may be
|
||
|
revisited if a suitable syntax can be found. Definition is therefore a processor
|
||
|
specific activity.
|
||
|
|
||
|
It is undecided whether undefined variables expand to an empty token list or an
|
||
|
error.
|
||
|
|