listener

package
v0.0.0-...-ae32867 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 12, 2020 License: BSD-3-Clause Imports: 19 Imported by: 0

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

Constants

This section is empty.

Variables

This section is empty.

Functions

func T

func T() tracing.Trace

We trace to the InterpreterTracer.

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL