diff options
Diffstat (limited to 'docs/fea2_proposal.md')
-rw-r--r-- | docs/fea2_proposal.md | 150 |
1 files changed, 150 insertions, 0 deletions
diff --git a/docs/fea2_proposal.md b/docs/fea2_proposal.md new file mode 100644 index 0000000..aa3ae99 --- /dev/null +++ b/docs/fea2_proposal.md @@ -0,0 +1,150 @@ +# 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. + |