Documentation ¶
Overview ¶
Package corelang implements core commands for DSLs dealing with arithmetic expressions, pairs and paths. It borrows from MetaFont/MetaPost, as described in the accompanying ANTLR grammar file.
Language Features ¶
This package includes functions for numerous language features common to MetaFont-/MetaPost-derivated DSLs.
The implementation is tightly coupled to the ANTLR V4 parser generator. ANTLR is a great tool and I see no use in being independent from it.
Lua Scripting ¶
This package also includes the support for Lua scripting. The DSLs stemming from this language core are Lua-enabled by default.
For further information please refer to types Scripting and LuaVarRef.
BSD License ¶
Copyright (c) 2017–18, Norbert Pillmayer ¶
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 ¶
- Variables
- func AllocateVariableInMemory(vref *variables.PMMPVarRef, mf *runtime.DynamicMemoryFrame) *variables.PMMPVarRef
- func Assign(rt *runtime.Runtime, lvalue *variables.PMMPVarRef, e *runtime.ExprNode)
- func Begingroup(rt *runtime.Runtime, name string) (*runtime.Scope, *runtime.DynamicMemoryFrame)
- func CallFunc(val interface{}, fun string, scripting *Scripting) (*runtime.ExprNode, []*variables.PMMPVarRef)
- func CallVardef(vref *variables.PMMPVarRef, scripting *Scripting) (*runtime.ExprNode, []*variables.PMMPVarRef)
- func CollectVarRefParts(rt *runtime.Runtime, t string, children []antlr.Tree) string
- func Declare(rt *runtime.Runtime, tag string, tp variables.VariableType) *variables.PMMPVarDecl
- func EncapsulateVariable(rt *runtime.Runtime, v *variables.PMMPVarRef)
- func EncapsulateVarsInMemory(rt *runtime.Runtime, mf *runtime.DynamicMemoryFrame)
- func Endgroup(rt *runtime.Runtime)
- func FindVariableReferenceInMemory(rt *runtime.Runtime, vref *variables.PMMPVarRef, doAlloc bool) (*variables.PMMPVarRef, *runtime.DynamicMemoryFrame)
- func GetCtxText(ctx antlr.Tree) string
- func GetVariableFromExpression(rt *runtime.Runtime, e *runtime.ExprNode) *variables.PMMPVarRef
- func IsTerminal(node antlr.Tree) bool
- func LoadBuiltinSymbols(rt *runtime.Runtime, scripting *Scripting)
- func MakeCanonicalAndResolve(rt *runtime.Runtime, v string, create bool) (*variables.PMMPVarRef, error)
- func PopScopeAndMemory(rt *runtime.Runtime) *runtime.DynamicMemoryFrame
- func PushConstant(rt *runtime.Runtime, vref *variables.PMMPVarRef)
- func PushVariable(rt *runtime.Runtime, vref *variables.PMMPVarRef, asLValue bool)
- func S() tracing.Trace
- func Save(rt *runtime.Runtime, tag string)
- func ScaleDimension(dimen dec.Decimal, unit string) dec.Decimal
- func Showvariable(rt *runtime.Runtime, tag string) string
- func T() tracing.Trace
- func Unit2numeric(u string) dec.Decimal
- func Variable(rt *runtime.Runtime, decl *variables.PMMPVarDecl, value interface{}, ...) *variables.PMMPVarRef
- func Whatever(rt *runtime.Runtime) *variables.PMMPVarRef
- type DSLRuntimeEnv
- type LuaPair
- type LuaVarRef
- type Scripting
- func (lscript *Scripting) Call(table string, function string, arguments ...interface{}) (*ScriptingReturnValues, error)
- func (lscript *Scripting) CallHook(hook string, arguments ...interface{}) (*ScriptingReturnValues, error)
- func (lscript *Scripting) Eval(luacmd string, arguments ...interface{}) (*ScriptingReturnValues, error)
- func (lscript *Scripting) RegisterHook(name string, f lua.LGFunction)
- type ScriptingReturnValueIterator
- type ScriptingReturnValues
- type TracingErrorListener
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var WhateverDeclaration *variables.PMMPVarDecl
Declaration of 'whatever', used to instantiate anonymous whatever-variables.
Functions ¶
func AllocateVariableInMemory ¶
func AllocateVariableInMemory(vref *variables.PMMPVarRef, mf *runtime.DynamicMemoryFrame) *variables.PMMPVarRef
Allocate a variable in a memory frame. Existing variable references in this memory frame will be overwritten ! Clients should probably first call FindVariableReferenceInMemory(vref).
func Assign ¶
Variable assignment.
assignment : lvalue ASSIGN numtertiary
(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) If type is numeric or pair: Create equation on expression stack, else assign a path value to a path variable.
func Begingroup ¶
MetaFont begingroup command: push a new scope and memory frame. Clients may supply a name for the group, otherwise it will be set to "group".
func CallFunc ¶
func CallFunc(val interface{}, fun string, scripting *Scripting) (*runtime.ExprNode, []*variables.PMMPVarRef)
Apply a (math or scripting) function, given by name, to a known/constant argument. Internal math functions are floor(), ceil() and sqrt(). Other function names will be delegated to the scripting subsystem (Lua).
Lua functions will return just one value (of type numeric, pair or path).
func CallVardef ¶
func CallVardef(vref *variables.PMMPVarRef, scripting *Scripting) (*runtime.ExprNode, []*variables.PMMPVarRef)
Call the Lua script for a vardef variable. The parameters of the call will be the suffixes of the variable, i.e. all subscripts and tags after the base tag.
Return values are wrapped into an expression node. If variables are part of the returned expressions, they are packed into an array of variable references.
func CollectVarRefParts ¶
Construct a valid variable reference string from parts on the stack.
Collect fragments of a variable reference, e.g. "x[k+1]r". Subscripts should be found on the expression stack and inserted as numeric constants, i.e. resulting in "x[5]r" (if k=4).
Parameter t is the text of the variable ref literal, e.g. "x[k+1]r". It is split by the parser into:
. "x" -> TAG x . subscript { k+1 } . "r" -> TAG r
func Declare ¶
func Declare(rt *runtime.Runtime, tag string, tp variables.VariableType) *variables.PMMPVarDecl
Declare a tag to be of type tp.
If the tag is not declared, insert a new symbol in global scope. If a declaration already exists, erase all variables and re-enter a declaration (MetaFont semantics). If the tag has been "saved" in the current or in an outer scope, make this tag a new undefined symbol.
func EncapsulateVariable ¶
func EncapsulateVariable(rt *runtime.Runtime, v *variables.PMMPVarRef)
A variable which goes out of scope becomes a "capsule". We send a message to the expression stack to forget the Symbol(s) for the ID(s) of a variable. Variables are of type numeric or pair.
func EncapsulateVarsInMemory ¶
func EncapsulateVarsInMemory(rt *runtime.Runtime, mf *runtime.DynamicMemoryFrame)
Make all variables in a memory frame "capsules".
When a memory frame is popped from the stack, the local variables living in the frame have to be made "capsules". This is necessary, because they may still be relevant to the LEQ-solver. The LEQ will finally decide when to abondon the "zombie" variable.
func FindVariableReferenceInMemory ¶
func FindVariableReferenceInMemory(rt *runtime.Runtime, vref *variables.PMMPVarRef, doAlloc bool) ( *variables.PMMPVarRef, *runtime.DynamicMemoryFrame)
Given a variable reference, locate an incarnation in a memory frame. The frame is determined by the variable's declaring scope: search for the top frame linked to the scope.
Variable references live in memory frames. Memory frames correspond to scopes. To find a variable reference -- i.e. a living variable with a possible value -- we have to proceed as follows:
(1) find the variable declaration in a scope, beginning at the top
(2) find the most recent memory frame pointing to this scope
(3) find a variable reference with the correct name in the memory frame
(4) if no reference/incarnation exists, create one
Parameter doAlloc: should step (4) be performed ?
func GetCtxText ¶
I do not always understand ANTLR V4's Go runtime typing and tree semantics (rather poorly documented), so I introduce some helpers. Some of these are probably unnecessary for a better versed ANTLR Go user...
func GetVariableFromExpression ¶
The expression stack knows nothing about the interpreter's symbols, except the few properties of interface Symbol. The expression stack deals with polynomials and serial IDs of variables. To get back from IDs to variable references, we ask the expression stack for a Symbol (from an ID). If the variable is of type pair, the Symbol may be a pair part (x-part or y-part). Parts point to their parent symbol, thus giving us the variable reference.
func IsTerminal ¶
I do not always understand ANTLR V4's Go runtime typing and tree semantics (rather poorly documented), so I introduce some helpers. Some of these are probably unnecessary for a better versed ANTLR Go user...
func LoadBuiltinSymbols ¶
Load builtin symbols into a scope (usually the global scope). Additionally loads initial Lua definitions.
func MakeCanonicalAndResolve ¶
func MakeCanonicalAndResolve(rt *runtime.Runtime, v string, create bool) ( *variables.PMMPVarRef, error)
Get or create a variable reference. To get the canonical representation of the variable reference, we parse it and construct a small AST. This AST is fed into GetVarRefFromVarSyntax(). The resulting variable reference struct is used to find the memory location of the variable reference.
Example:
vref := MakeCanonicalAndResolve(rt, "a2r", true) // now vref.String() gives something like: // "<var a[2].r=<nil> w/ <decl a[].r/numeric>>"
If a variable has been undeclared and is now created, the top-most scope and memory-frame will hold the newly created variable.
func PopScopeAndMemory ¶
func PopScopeAndMemory(rt *runtime.Runtime) *runtime.DynamicMemoryFrame
Decrease grouping level. We pop the topmost scope and topmost memory frame. This happens after a group is left.
Returns the previously topmost memory frame.
func PushConstant ¶
func PushConstant(rt *runtime.Runtime, vref *variables.PMMPVarRef)
Push a constant (numeric or pair type) onto the expression stack.
func PushVariable ¶
func PushVariable(rt *runtime.Runtime, vref *variables.PMMPVarRef, asLValue bool)
The expression stack knows nothing about the interpreter's symbols, except the few properties of interface Symbol. The expression stack deals with polynomials and serial IDs of variables.
Push a variable (numeric or pair type) onto the expression stack.
func Save ¶
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. This method simply creates a var decl for the tag in the current scope.
func ScaleDimension ¶
Scale a numeric value by a unit.
func Showvariable ¶
Show all declarations and references for a tag.
func Unit2numeric ¶
TODO complete this. Return scaled points for high level units (cm, mm, pt, in, ...)
func Variable ¶
func Variable(rt *runtime.Runtime, decl *variables.PMMPVarDecl, value interface{}, subscripts []dec.Decimal, global bool) *variables.PMMPVarRef
Create a variable reference. Parameters are the declaration for the variable, a value and a flag, indicating if this variable should go to global memory. The subscripts parameter is a slice of array-subscripts, if the variable declaration is of array (complex) type.
Types ¶
type DSLRuntimeEnv ¶
type DSLRuntimeEnv struct {
// contains filtered or unexported fields
}
Lua UserData type for the DSL's interpreter runtime environment. The scripting sub-system has access to variables of the DSL (and therefore access to scopes and memory-frames of the runtime environment).
Example (Lua):
rt = runtime.current -- find the host-DSL runtime environment x = rt.connect_variable("x") -- create a varref (UserData) for tag 'x' print(x) -- print a representation for 'x' print(x:value()) -- print the value of 'x'
This will support other host-DSL commands in the future.
type LuaPair ¶
Lua UserData type for pairs.
Example (Lua):
p = pair.new{2, 5} print(p:x()) -- get x-part p:y(3.14) -- set y-part
type LuaVarRef ¶
type LuaVarRef struct {
// contains filtered or unexported fields
}
Lua UserData type for variables. Variables reference DSL-variables in the DSL's runtime environment (MetaFont-like variables of type numeric, pair, etc.) A variable may be known or unknown.
Example (Lua):
a = hostlang.numeric("a") -- connect to a tag of the host language a[2].r = 3.14 -- assign a numeric value to an 'a'-variable print(a[2].r:value()) -- prints "3.14"
Variable a[2].r (or short: a2r) is now set/known in the host-language (DSL):
DSL> show a; ## show a; tag=a a : numeric a[] : numeric a[].r : numeric ## a[2].r = 3.14
Lua's notation for (sub-)tables lends itself nicely for a congruency to MetaFont-style variable notations. However, it is not possible to use the DSL shorthand notation ("a2r") for variable names in Lua.
In Lua, there are two member-functions defined for type varref: value() and isknown(). value() is a getter/setter for the variable value. isknown() returns a boolean value.
Example (Lua):
a = hostlang.numeric("a") -- connect to a tag of the host language print(a:isknown()) -- prints "false" if not yet defined in the DSL a:value(3.14) -- must use this notation for 'a' base tag print(a:isknown()) -- prints "true"
Variables of this kind are 'live'-objects, i.e. they are always synchronous between the two languages.
type Scripting ¶
Type Scripting is an opaque data type to provide access to the scripting sub-system.
DSLs built on top of this language core may be scripted with Lua. Lua scripts may be called as hooks or as functions on primary level. Lua functions are preceded by an '@'.
Example:
a2r = 7 + @inlua(x0)
This will delegate to the Lua scripting subsystem, putting the value of x0 onto the Lua stack, and then call Lua-function inlua(...) on it.
Example (Hook) ¶
// Call hook from Go scripting := NewScripting(nil) scripting.RegisterHook("echo", func(L *lua.LState) int { // register closure lv := L.Get(-1) // get top of stack msg := fmt.Sprintf("echo: %s !", lua.LVAsString(lv)) // process L.Push(lua.LString(msg)) // push result return 1 // return value count }) r, _ := scripting.CallHook("echo", "hello world") // Lua: echo("hello world") fmt.Println(r)
Output: [echo: hello world !]
func NewScripting ¶
Create a new scripting subsystem. Scripting sub-systems are not thread safe.
func (*Scripting) Call ¶
func (lscript *Scripting) Call(table string, function string, arguments ...interface{}) ( *ScriptingReturnValues, error)
Call a Lua function, possibly qualified by a table prefix.
func (*Scripting) CallHook ¶
func (lscript *Scripting) CallHook(hook string, arguments ...interface{}) ( *ScriptingReturnValues, error)
Call a registered hook from Go. Arguments may be passed (Go data types) in a variable argument list. Return values are converted back from Lua types to Go types.
see RegisterHook()
func (*Scripting) Eval ¶
func (lscript *Scripting) Eval(luacmd string, arguments ...interface{}) (*ScriptingReturnValues, error)
Evaluate a Lua statement, given as string. Return arguments (from the Lua stack) are packed into an opaque data structure. The second return value is a possible error condition. The Lua command(s) must be syntactically correct and complete statements (no expressions etc. accepted).
Eval accepts a variable number of untyped arguments. These are put on the Lua stack before the statement is executed.
func (*Scripting) RegisterHook ¶
func (lscript *Scripting) RegisterHook(name string, f lua.LGFunction)
Register a hook function for a key given as string parameter. The hook function must accept a single argument: the Lua state, and return a single int: the number of return values on the Lua stack.
Hooks may be called from the Lua side by name, or from the Go side by CallHook(...).
Example:
scripting := NewScripting() scripting.RegisterHook("stars", func(L *lua.LState) int { L.Push(lua.LString("* * * * *")) // push result return 1 // return value count })
In Lua:
print(stars()) -- prints "* * * * *" to stdout
type ScriptingReturnValueIterator ¶
type ScriptingReturnValueIterator struct {
// contains filtered or unexported fields
}
Iterator type for scripting return values. Return values from Lua are wrapped into an opaque type ScriptingReturnValues and accessed using this iterator type.
see ScriptingReturnValues.Iterator()
func (*ScriptingReturnValueIterator) Next ¶
func (it *ScriptingReturnValueIterator) Next() bool
Is there a next scripting argument? Advances the iterator's cursor.
func (*ScriptingReturnValueIterator) Value ¶
func (it *ScriptingReturnValueIterator) Value() (interface{}, variables.VariableType)
Get the value of the scripting argument under the iterator's cursor. Returns the value and a type (see package 'variables' for the definition of variable types).
func (*ScriptingReturnValueIterator) ValueAsExprNode ¶
func (it *ScriptingReturnValueIterator) ValueAsExprNode() (*runtime.ExprNode, []*variables.PMMPVarRef)
Get the value of the scripting argument under the iterator's cursor. Returns the value wrapped in an expression node (or nil). If variables are part of the expression(s), they are returned in a separate array.
type ScriptingReturnValues ¶
type ScriptingReturnValues struct {
// contains filtered or unexported fields
}
Type to return values from Lua scripts. Single values are accessed with an iterator.
see ScriptingReturnValueIterator
func (*ScriptingReturnValues) Iterator ¶
func (r *ScriptingReturnValues) Iterator() *ScriptingReturnValueIterator
Create an iterator for scripting arguments / return values.
type TracingErrorListener ¶
type TracingErrorListener struct {
*antlr.DefaultErrorListener // use default as base class
}
We create our own type of error listener for the ANTLR parser
func (*TracingErrorListener) SyntaxErrorf ¶
func (c *TracingErrorListener) SyntaxErrorf(r antlr.Recognizer, sym interface{}, line, column int, msg string, e antlr.RecognitionException)
Our error listener prints an error to the trace.