Documentation ¶
Overview ¶
Package listener exposes listener function implementations for ANTLR V4. Functions have to be public to be accessible for the ANTLR AST walker. Nevertheless, this is an internal package. For an external interface please refer to package pmmpost.
Interplay with ANTLR looks like this:
1. ANTLR V4 constructs an AST for us.
2. We use a listener to walk the AST and execute the statements.
We use AST-driven interpretation to execute the input program. Input is more or less a list of statements and function definitions. We will annotate the AST with scope-information, holding the symbols of dynamic scopes. Scopes stem from either:
- function definitions (macros in MetaFont: def and vardef)
- compound statements, i.e. groups (begingroup ... endgroup)
The interpreter relies on the scopes and definitions constructed earlier. It manages a memory frame stack to track the calling sequence of functions and groups.
Metafont, and therefore PMMPost, is a dynamically scoped language. This means, functions can access local variables from calling functions or groups. Nevertheless we will find the definition of all variables (which are explicitly defined) in a scope definition. This is mainly for type checking reasons and due to the complex structure of MetaFont variable identifiers.
BSD License ¶
Copyright (c) 2017, Norbert Pillmayer <norbert@pillmayer.com>
All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of Norbert Pillmayer nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Index ¶
- func T() tracing.Trace
- type PMMPostParseListener
- func (pl *PMMPostParseListener) EnterAssignment(ctx *grammar.AssignmentContext)
- func (pl *PMMPostParseListener) EnterCompound(ctx *grammar.CompoundContext)
- func (pl *PMMPostParseListener) EnterExprgroup(ctx *grammar.ExprgroupContext)
- func (pl *PMMPostParseListener) ExitAssignment(ctx *grammar.AssignmentContext)
- func (pl *PMMPostParseListener) ExitBeginfig(ctx *grammar.BeginfigContext)
- func (pl *PMMPostParseListener) ExitCompound(ctx *grammar.CompoundContext)
- func (pl *PMMPostParseListener) ExitDecimal(ctx *grammar.DecimalContext)
- func (pl *PMMPostParseListener) ExitDrawCmd(ctx *grammar.DrawCmdContext)
- func (pl *PMMPostParseListener) ExitEndfig(ctx *grammar.EndfigContext)
- func (pl *PMMPostParseListener) ExitEquation(ctx *grammar.EquationContext)
- func (pl *PMMPostParseListener) ExitExpression(ctx *grammar.ExpressionContext)
- func (pl *PMMPostParseListener) ExitExprgroup(ctx *grammar.ExprgroupContext)
- func (pl *PMMPostParseListener) ExitFactor(ctx *grammar.FactorContext)
- func (pl *PMMPostParseListener) ExitFillCmd(ctx *grammar.FillCmdContext)
- func (pl *PMMPostParseListener) ExitFuncatom(ctx *grammar.FuncatomContext)
- func (pl *PMMPostParseListener) ExitInterpolation(ctx *grammar.InterpolationContext)
- func (pl *PMMPostParseListener) ExitLiteralpair(ctx *grammar.LiteralpairContext)
- func (pl *PMMPostParseListener) ExitNumtokenatom(ctx *grammar.NumtokenatomContext)
- func (pl *PMMPostParseListener) ExitPairpart(ctx *grammar.PairpartContext)
- func (pl *PMMPostParseListener) ExitPath(ctx *grammar.PathContext)
- func (pl *PMMPostParseListener) ExitPickupCmd(ctx *grammar.PickupCmdContext)
- func (pl *PMMPostParseListener) ExitSaveStmt(ctx *grammar.SavecmdContext)
- func (pl *PMMPostParseListener) ExitScalaratom(ctx *grammar.ScalaratomContext)
- func (pl *PMMPostParseListener) ExitScalarmulop(ctx *grammar.ScalarmulopContext)
- func (pl *PMMPostParseListener) ExitShowcmd(ctx *grammar.ShowcmdContext)
- func (pl *PMMPostParseListener) ExitSubscript(ctx *grammar.SubscriptContext)
- func (pl *PMMPostParseListener) ExitTerm(ctx *grammar.TermContext)
- func (pl *PMMPostParseListener) ExitTransform(ctx *grammar.TransformContext)
- func (pl *PMMPostParseListener) ExitTypedecl(ctx *grammar.TypedeclContext)
- func (pl *PMMPostParseListener) ExitVardef(ctx *grammar.VardefContext)
- func (pl *PMMPostParseListener) ExitVariable(ctx *grammar.VariableContext)
- func (pl *PMMPostParseListener) LazyCreateParser(input antlr.CharStream) parseErrorListener
- func (pl *PMMPostParseListener) ParseStatements(input antlr.CharStream) []error
- func (pl *PMMPostParseListener) Summary()
- func (pl *PMMPostParseListener) VisitErrorNode(node antlr.ErrorNode)
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
Types ¶
type PMMPostParseListener ¶
type PMMPostParseListener struct { *grammar.BasePMMPostListener // build on top of ANTLR's base 'class' // contains filtered or unexported fields }
ANTLR will create an AST for us. We use a listener to attach to ANTLR's AST walker. The listener manages scopes (with declarations) and memory frames (with variable references and values).
func NewParseListener ¶
func NewParseListener(rt *runtime.Runtime, scr *corelang.Scripting, pictureCallback func(*gfx.Picture)) *PMMPostParseListener
Construct a new AST listener. Callers have to provide a runtime-environment and a scripting-environment. The callback is used to signal the caller that a picture has been completed and should be shipped out.
func (*PMMPostParseListener) EnterAssignment ¶
func (pl *PMMPostParseListener) EnterAssignment(ctx *grammar.AssignmentContext)
Annotate AST to expect an lvalue for assignments (which is a variable reference). We set a flag 'expectingLvalue' to suppress value substitution when the variable is put onto the expression stack.
assignment : variable ASSIGN expression
func (*PMMPostParseListener) EnterCompound ¶
func (pl *PMMPostParseListener) EnterCompound(ctx *grammar.CompoundContext)
Start a new scope / compound statement: "begingroup".
MetaFont uses dynamic scopes with the "begingroup ... endgroup" notation, but in a unique way: users have to "save" variables explicitly (The METAFONTBook, chapter 17).
We produce a new scope for the scope tree and a new memory frame onto the memory frame stack. The scope holds declarations for local variables. The memory frame holds variable references (pointing to the the decls in the scope).
We reserve a symbol in the group's scope on "save" statements only. Saved tags are of type tag/undefined initially.
Notice: It is a bit of an overkill to use dynamic scopes and memory frames in parallel, but I'll stick to this traditional approach for clarity. Furthermore, this approach comes handy for the runtime-handling of definitions (a.k.a. macros) and/or functions.
func (*PMMPostParseListener) EnterExprgroup ¶
func (pl *PMMPostParseListener) EnterExprgroup(ctx *grammar.ExprgroupContext)
Start a new scope within expressions.
atom : BEGINGROUP statementlist tertiary ENDGROUP # exprgroup
MetaFont allows "begingroup ... ; <expr> endgroup" within expressions, providing brackets around some statements and returning a (sub-) expression.
func (*PMMPostParseListener) ExitAssignment ¶
func (pl *PMMPostParseListener) ExitAssignment(ctx *grammar.AssignmentContext)
Variable assignment.
assignment : lvalue ASSIGN numtertiary
Both operands are on the expression stack: variable and expression
(1) Retract lvalue from the resolver's table (make a capsule)
(3) Unset the value of lvalue
(3) Re-incarnate lvalue (get a new ID for it)
(4) Create equation on expression stack
func (*PMMPostParseListener) ExitBeginfig ¶
func (pl *PMMPostParseListener) ExitBeginfig(ctx *grammar.BeginfigContext)
Start a figure. Name, width and height are given.
'beginfig' '(' LABEL ',' DECIMALTOKEN UNIT ',' DECIMALTOKEN UNIT ')' SEMIC
func (*PMMPostParseListener) ExitCompound ¶
func (pl *PMMPostParseListener) ExitCompound(ctx *grammar.CompoundContext)
End a scope: "endgroup". Restores all 'save'd variables and declarations. Restore of variable declarations is automatic due to popping the top scope, where the saved variable definitions live.
In MetaFont, variables declared inside expression-groups live on as "capsules", if they are still unknown and used in unsolved equations with global variables.
func (*PMMPostParseListener) ExitDecimal ¶
func (pl *PMMPostParseListener) ExitDecimal(ctx *grammar.DecimalContext)
Numeric expression primary: decimal constant, possibly including a unit.
Example: "3.14162mm".
atom : DECIMALTOKEN UNIT? # decimal
func (*PMMPostParseListener) ExitDrawCmd ¶
func (pl *PMMPostParseListener) ExitDrawCmd(ctx *grammar.DrawCmdContext)
Draw command: draw a path. Draws a path using current pen and current color.
drawCmd : 'draw' pathexpression
pathexpression is TOS of path builder stack.
func (*PMMPostParseListener) ExitEndfig ¶
func (pl *PMMPostParseListener) ExitEndfig(ctx *grammar.EndfigContext)
End a figure.
func (*PMMPostParseListener) ExitEquation ¶
func (pl *PMMPostParseListener) ExitEquation(ctx *grammar.EquationContext)
Read an equation and put it into the LEQ solver.
equation : expression ( EQUALS expression )+
Equations may be chained, i.e.
a=b=c
All operands are on the expression stack. Operates right-associative.
func (*PMMPostParseListener) ExitExpression ¶
func (pl *PMMPostParseListener) ExitExpression(ctx *grammar.ExpressionContext)
Top level expression.
expression : tertiary | expression PATHCLIPOP tertiary
Path clipping operations are
PATHCLIPOP : 'union' | 'intersection' | 'difference'
func (*PMMPostParseListener) ExitExprgroup ¶
func (pl *PMMPostParseListener) ExitExprgroup(ctx *grammar.ExprgroupContext)
See rule ExitCompound.
atom : BEGINGROUP statementlist tertiary ENDGROUP # exprgroup
Additionally leave the return expression on the stack.
func (*PMMPostParseListener) ExitFactor ¶
func (pl *PMMPostParseListener) ExitFactor(ctx *grammar.FactorContext)
Multiply/divide 2 factors. Factors may be of any kind. For pairs:
(1) Multiply or divide a pair by a number, or
(2) multiply a number by a pair.
secondary : primary # factor | secondary (TIMES|OVER) primary # factor
func (*PMMPostParseListener) ExitFillCmd ¶
func (pl *PMMPostParseListener) ExitFillCmd(ctx *grammar.FillCmdContext)
Fill command: fill a cloxed path. Fills a path using current color.
fillCmd : 'fill' pathexpression
pathexpression is TOS of path builder stack.
func (*PMMPostParseListener) ExitFuncatom ¶
func (pl *PMMPostParseListener) ExitFuncatom(ctx *grammar.FuncatomContext)
Apply a function to a known argument. Functions may be internal math functions (working on type numeric) or calls to the (Lua) scripting sub-system.
primary : MATHFUNC atom # funcatom MATHFUNC : 'floor' | 'ceil' | 'sqrt' | @func ;
func (*PMMPostParseListener) ExitInterpolation ¶
func (pl *PMMPostParseListener) ExitInterpolation(ctx *grammar.InterpolationContext)
Numeric interpolation, i.e. "n[a,b]".
primary | numtokenatom [ tertiary , tertiary ] # interpolation | atom [ tertiary , tertiary ] # interpolation
All three expressions will be on the expression stack. We will convert n[a,b] => a - na + nb.
func (*PMMPostParseListener) ExitLiteralpair ¶
func (pl *PMMPostParseListener) ExitLiteralpair(ctx *grammar.LiteralpairContext)
Literal pair, i.e. a point with 2 corrdinates (x-part, y-part).
atom : LPAREN tertiary COMMA tertiary RPAREN # literalpair
func (*PMMPostParseListener) ExitNumtokenatom ¶
func (pl *PMMPostParseListener) ExitNumtokenatom(ctx *grammar.NumtokenatomContext)
Numeric prefix for a variable, e.g., "3x", "1/2y.r", "0.25z".
numtokenatom : DECIMALTOKEN '/' DECIMALTOKEN | DECIMALTOKEN
func (*PMMPostParseListener) ExitPairpart ¶
func (pl *PMMPostParseListener) ExitPairpart(ctx *grammar.PairpartContext)
Put x-part or y-part of a pair variable on the expression stack. The variable may be known or unknown.
primary : PAIRPART primary # pairpart
func (*PMMPostParseListener) ExitPath ¶
func (pl *PMMPostParseListener) ExitPath(ctx *grammar.PathContext)
Create a path by joining path fragments.
path : secondary ( pathjoin secondary )+ cycle?
Each fragment is either a known pair or a sub-path. Fragment AST nodes are already labeled with a string marker. Pairs and sub-paths lie in reverse order on either the expression stack or the path stack.
The completed path is pushed onto the path stack.
func (*PMMPostParseListener) ExitPickupCmd ¶
func (pl *PMMPostParseListener) ExitPickupCmd(ctx *grammar.PickupCmdContext)
Pickup a pen. Example: "pickup pencircle scaled 3 withcolor #f080cc".
'pickup' PEN ( 'scaled' DECIMALTOKEN )? ( 'withcolor' COLOR )?
The pen is used for subsequent drawing and filling commands.
func (*PMMPostParseListener) ExitSaveStmt ¶
func (pl *PMMPostParseListener) ExitSaveStmt(ctx *grammar.SavecmdContext)
Save a tag within a group. The tag will be restored at the end of the group. Save-commands within global scope will be ignored.
SAVE TAG (COMMA TAG)* # savecmd
func (*PMMPostParseListener) ExitScalaratom ¶
func (pl *PMMPostParseListener) ExitScalaratom(ctx *grammar.ScalaratomContext)
Scale a (known or unknown) atom with a numeric coefficient. The numeric coefficient is on the stack and may be applied to atoms of different type.
primary : scalarmulop atom # scalarnumatom
TOS is a polynomial (known or unknown), 2OS is numeric constant. We just multiply them.
Note that this allows a decimal constant to be prefixed by a decimal constant: 1/3 12 yields 4. This is not allowed in MetaFont, but you can always accomplish the same effect with a group:
metafont> a=1/3 begingroup 12 endgroup; ## a=4
func (*PMMPostParseListener) ExitScalarmulop ¶
func (pl *PMMPostParseListener) ExitScalarmulop(ctx *grammar.ScalarmulopContext)
Attach a (signed) coefficient to a variable, e.g. +3x, -1/3y. We just have to leave a numeric constant on the stack.
scalarmulop : (PLUS|MINUS)? numtokenatom
We have to handle the MINUS case only, as the rule for numtokenatom already left a numeric constant on the expression stack.
func (*PMMPostParseListener) ExitShowcmd ¶
func (pl *PMMPostParseListener) ExitShowcmd(ctx *grammar.ShowcmdContext)
Tracing command: show <tag>.
SHOW TAG (COMMA TAG)* # showcmd
func (*PMMPostParseListener) ExitSubscript ¶
func (pl *PMMPostParseListener) ExitSubscript(ctx *grammar.SubscriptContext)
A variable subscript.
func (*PMMPostParseListener) ExitTerm ¶
func (pl *PMMPostParseListener) ExitTerm(ctx *grammar.TermContext)
Add/subtract 2 factors. Factors may be of type numeric or pair.
tertiary : secondary # term | tertiary (PLUS|MINUS) secondary # term
func (*PMMPostParseListener) ExitTransform ¶
func (pl *PMMPostParseListener) ExitTransform(ctx *grammar.TransformContext)
Apply a chain of affine transforms to a pair or a path.
secondary ( TRANSFORM primary )+ # transform
func (*PMMPostParseListener) ExitTypedecl ¶
func (pl *PMMPostParseListener) ExitTypedecl(ctx *grammar.TypedeclContext)
Finish a declaration statement. Example: "numeric a, b, c;" If var decl is new, insert a new symbol in global scope. If var decl already exists, erase all variables and re-enter var decl (MetaFont semantics). If var decl has been "saved" in the current or in an outer scope, make this tag a new undefined symbol.
TYPE TAG ( COMMA TAG )* # typedecl
func (*PMMPostParseListener) ExitVardef ¶
func (pl *PMMPostParseListener) ExitVardef(ctx *grammar.VardefContext)
Finish a vardef. Vardefs are primary tags, which call a Lua script.
vardef : VARDEF TAG ( COMMA TAG )*
func (*PMMPostParseListener) ExitVariable ¶
func (pl *PMMPostParseListener) ExitVariable(ctx *grammar.VariableContext)
Variable reference as an expression primary. Example: "x2.r". May be an lvalue (which must not be replaced by its value).
variable : MIXEDTAG ( subscript | anytag )* | TAG ( subscript | anytag )* | LAMBDAARG anytag : TAG | MIXEDTAG
TODO check for @#
func (*PMMPostParseListener) LazyCreateParser ¶
func (pl *PMMPostParseListener) LazyCreateParser(input antlr.CharStream) parseErrorListener
Create an ANTLR V4 parser. This function should cache the parser for re-use, but currently I do not understand how to do this in the Go version of ANTLR. According to a forum discussion, creating a new parser every time seems to be the accepted mode of operation and should not carry too much of a performance penalty.
To better support a REPL I'll implement some day a different antl.InputStream, which should be able to wait for the next chunk of text input in the middle of grammar productions.
func (*PMMPostParseListener) ParseStatements ¶
func (pl *PMMPostParseListener) ParseStatements(input antlr.CharStream) []error
We use ANTLR V4 for parsing the statement grammar.
func (*PMMPostParseListener) Summary ¶
func (pl *PMMPostParseListener) Summary()
Print out a summary of all the scopes and symbols collected up to now.
func (*PMMPostParseListener) VisitErrorNode ¶
func (pl *PMMPostParseListener) VisitErrorNode(node antlr.ErrorNode)
Print an error to the trace.