Documentation ¶
Overview ¶
Package script defines types to implement a simple script interpreter with Lisp-like syntax. It has a bit of syntactic sugar to make it easier to read.
It producces S-Expressions that can hold either symbols, strings, 64-bit integers or cons cells.
It comes with a few builtin functions and it's easy to add new functions.
It was designed with shell-like scripting in mind. A script is a list of instructions. Instructions are function calls. For convenience, top-level function calls don't need to be wrapped in parenthesis if they are one-liners. The is useful, especially in the context of a command line interface, but it introduces more complexity in the grammar because new lines are handled differently depending on context.
The lifecycle of a script is:
Source Code -> Scanner -> Tokens Tokens -> Parser -> S-Expression S-Expression -> Interpreter -> Output
At this point it's not designed for speed, but it does tail call optimizations (trampoline) to avoid blowing the stack with recursive calls.
The current focus is to "harden" the type system so that scripts can be statically type-checked. Once the language is usable, a faster bytecode compiler will be implemented.
Grammar ¶
The EBNF syntax is:
Script = [ InBody ] { NewLine } EOF InBody = [ Instr ] [ InBodyTail ] InBodyTail = { NewLine { NewLine } Instr } Instr = ( Call | InCall ) Call = "(" InCallInParen { NewLine } ")" InCallInParen = { NewLine } Symbol SExpListInParen InCall = Symbol SExpList SExpListInParen = { SExpInParen } SExpList = { SExp } SExpInParen = { NewLine } SExp SExp = QuotedSExp | Body | List | Atom QuotedSExp = "'" { NewLine } SExp QuasiquotedSExp = "`" { NewLine } SExp UnquotedSExp = "," { NewLine } SExp Body = "{" [ InBody ] { NewLine } "}" List = "(" SExpListInParen { NewLine } ")" Atom = symbol | string | int | true | false TODO: define tokens
Syntactic sugar ¶
Say you have a list of function calls like this:
((funcA a (cons b c)) (funcB d e f) (funcC h i))
You can use braces to open a "body" instead:
{ funcA a (cons b c) funcB d e f funcC hi }
Within a body the top-level instructions don't need to be surrounded by parenthesis. It can make code significantly easier to read. For example this code:
; Reverses a list recusively. let reverse (lambda (l) ( ; Define a nested recursive function with an accumulator. (let reverse-rec (lambda (l tail) ( (if (nil? l) tail (reverse-rec (cdr l) (cons (car l) tail)))))) ; Start the recursion (reverse-rec l ())))
Can be rewritten as:
; Reverses a list recusively with syntactic sugar. let reverse (lambda (l) { ; Define a nested recursive function with an accumulator. let reverse-rec (lamda (l tail) { if (nil? l) tail else { reverse-rec (cdr l) (cons (car l) tail) } }) ; Start the recursion reverse-rec l () })
This example also used the optional "else" symbol in the if statement for clarity.
The reason you don't need to use parenthesis at the top-level is because it uses the same rules as inside braces.
There are a few limitations to using braces -- currently all top-level instructions have to be function calls, otherwise the grammar would be ambiguous.
Example (CustomFunctions) ¶
package main import ( "context" "strings" "github.com/stratumn/go-node/script" ) func main() { // Script that will be evaluated. src := ` echo (title "hello world!") (echo (title "goodbye") (title "world!")) ` // Define custom functions for the interpreter. funcs := map[string]script.InterpreterFuncHandler{ "echo": func(ctx *script.InterpreterContext) (script.SExp, error) { // Evaluate the arguments to strings. args, err := ctx.EvalListToStrings(ctx, ctx.Args, false) if err != nil { return nil, err } // Join the argument strings. str := strings.Join(args, " ") // Return a string value. return script.String(str, ctx.Meta), nil }, "title": func(ctx *script.InterpreterContext) (script.SExp, error) { // Evaluate the arguments to strings. args, err := ctx.EvalListToStrings(ctx, ctx.Args, false) if err != nil { return nil, err } // Join the argument strings and convert it to a title. title := strings.Title(strings.Join(args, " ")) // Return a string value. return script.String(title, ctx.Meta), nil }, } // Initialize an interpreter with the custom functions. itr := script.NewInterpreter(script.InterpreterOptFuncHandlers(funcs)) // Evaluate the script. err := itr.EvalInput(context.Background(), src) if err != nil { panic(err) } }
Output: "Hello World!" "Goodbye World!"
Example (Fib) ¶
package main import ( "context" "github.com/stratumn/go-node/script" ) func main() { // Script that will be evaluated. src := ` ; Computes the nth element of the Fibonacci sequence. let fib (lambda (n) ( (let fib-rec (lambda (n f1 f2) ( (if (< n 1) f1 (fib-rec (- n 1) f2 (+ f1 f2)))))) (fib-rec n 0 1))) ; Test it out. fib 10 ` // Initialize an interpreter with the builtin libraries. itr := script.NewInterpreter(script.InterpreterOptBuiltinLibs) // Evaluate the script. err := itr.EvalInput(context.Background(), src) if err != nil { panic(err) } }
Output: (lambda (n) ((let fib-rec (lambda (n f1 f2) ((if (< n 1) f1 (fib-rec (- n 1) f2 (+ f1 f2)))))) (fib-rec n 0 1))) 55
Example (Recursion) ¶
package main import ( "context" "github.com/stratumn/go-node/script" ) func main() { // Script that will be evaluated. src := ` ; Reverses a list recusively. let reverse (lambda (l) ( ; Define a nested recursive function with an accumulator. (let reverse-rec (lambda (l tail) ( (if (nil? l) tail (reverse-rec (cdr l) (cons (car l) tail)))))) ; Start the recursion (reverse-rec l ()))) ; Test it out. reverse '(A L I C E) ` // Initialize an interpreter with the builtin libraries. itr := script.NewInterpreter(script.InterpreterOptBuiltinLibs) // Evaluate the script. err := itr.EvalInput(context.Background(), src) if err != nil { panic(err) } }
Output: (lambda (l) ((let reverse-rec (lambda (l tail) ((if (nil? l) tail (reverse-rec (cdr l) (cons (car l) tail)))))) (reverse-rec l ()))) (E C I L A)
Example (Recursion_braces) ¶
package main import ( "context" "github.com/stratumn/go-node/script" ) func main() { // Script that will be evaluated. src := ` ; Reverses a list recusively with syntactic sugar. let reverse (λ (l) { ; Define a nested recursive function with an accumulator. let reverse-rec (λ (l tail) { if (nil? l) tail else { reverse-rec (cdr l) (cons (car l) tail) } }) ; Start the recursion reverse-rec l () }) ; Test it out. reverse '(A L I C E) ` // Initialize an interpreter with the builtin libraries. itr := script.NewInterpreter(script.InterpreterOptBuiltinLibs) // Evaluate the script. err := itr.EvalInput(context.Background(), src) if err != nil { panic(err) } }
Output: (lambda (l) ((let reverse-rec (λ (l tail) ((if (nil? l) tail else ((reverse-rec (cdr l) (cons (car l) tail))))))) (reverse-rec l ()))) (E C I L A)
Example (Search_tree) ¶
package main import ( "context" "github.com/stratumn/go-node/script" ) func main() { // Script that will be evaluated. src := ` let search-tree (lambda (tree fn) ( (unless (nil? tree) (if (atom? tree) (if (fn tree) tree) ((let match (search-tree (car tree) fn)) (unless (nil? match) match (search-tree (cdr tree) fn))))))) (search-tree '(1 2 (3 (5 6) (7 8)) (9 10)) (lambda (leaf) (= (mod leaf 4) 0))) ` // Initialize an interpreter with the builtin libraries. itr := script.NewInterpreter(script.InterpreterOptBuiltinLibs) // Evaluate the script. err := itr.EvalInput(context.Background(), src) if err != nil { panic(err) } }
Output: (lambda (tree fn) ((unless (nil? tree) (if (atom? tree) (if (fn tree) tree) ((let match (search-tree (car tree) fn)) (unless (nil? match) match (search-tree (cdr tree) fn))))))) 8
Example (Simple) ¶
package main import ( "context" "github.com/stratumn/go-node/script" ) func main() { // Script that will be evaluated. src := ` ; You can choose not to evaluate a list by quoting it: let my-list '(A B C) ; You can do conditional expressions: if (list? my-list) "it's a list" "it's not a list" ; You can manipulate lists: car my-list cdr my-list car (cdr my-list) ; You can create functions: let cadr (lambda (l) (car (cdr l))) cadr my-list ; By the way can use parenthesis at the top level if you want: (cadr my-list) ` // Initialize an interpreter with the builtin libraries. itr := script.NewInterpreter(script.InterpreterOptBuiltinLibs) // Evaluate the script. err := itr.EvalInput(context.Background(), src) if err != nil { panic(err) } }
Output: (A B C) "it's a list" A (B C) B (lambda (l) (car (cdr l))) B B
Index ¶
- Constants
- Variables
- func CellID() uint64
- func Error(msg string, meta Meta, ident string) error
- func InterpreterOptBuiltinLibs(itr *Interpreter)
- func IsList(exp SExp) bool
- func WrapError(err error, meta Meta, ident string) error
- type Closure
- type ClosureOpt
- type FuncData
- type Interpreter
- type InterpreterContext
- type InterpreterFuncHandler
- type InterpreterOpt
- func InterpreterOptClosure(c *Closure) InterpreterOpt
- func InterpreterOptErrorHandler(h func(error)) InterpreterOpt
- func InterpreterOptFuncHandlers(m map[string]InterpreterFuncHandler) InterpreterOpt
- func InterpreterOptTailOptimizations(e bool) InterpreterOpt
- func InterpreterOptValueHandler(h func(SExp)) InterpreterOpt
- type LazyCall
- type Meta
- type ParseError
- type Parser
- type PrimitiveType
- type ResolveHandler
- type SExp
- func Bool(val bool, meta Meta) SExp
- func Cons(car, cdr SExp, meta Meta) SExp
- func Int64(val int64, meta Meta) SExp
- func LibCellCar(ctx *InterpreterContext) (SExp, error)
- func LibCellCdr(ctx *InterpreterContext) (SExp, error)
- func LibCellCons(ctx *InterpreterContext) (SExp, error)
- func LibClosureLet(ctx *InterpreterContext) (SExp, error)
- func LibCondIf(ctx *InterpreterContext) (SExp, error)
- func LibCondUnless(ctx *InterpreterContext) (SExp, error)
- func LibLambdaLambda(ctx *InterpreterContext) (SExp, error)
- func LibMetaEval(ctx *InterpreterContext) (SExp, error)
- func LibMetaQuasiquote(ctx *InterpreterContext) (SExp, error)
- func LibMetaQuote(ctx *InterpreterContext) (SExp, error)
- func LibMetaUnquote(ctx *InterpreterContext) (SExp, error)
- func LibOpAdd(ctx *InterpreterContext) (SExp, error)
- func LibOpAnd(ctx *InterpreterContext) (SExp, error)
- func LibOpDiv(ctx *InterpreterContext) (SExp, error)
- func LibOpEq(ctx *InterpreterContext) (SExp, error)
- func LibOpGt(ctx *InterpreterContext) (SExp, error)
- func LibOpGte(ctx *InterpreterContext) (SExp, error)
- func LibOpLt(ctx *InterpreterContext) (SExp, error)
- func LibOpLte(ctx *InterpreterContext) (SExp, error)
- func LibOpMod(ctx *InterpreterContext) (SExp, error)
- func LibOpMul(ctx *InterpreterContext) (SExp, error)
- func LibOpNot(ctx *InterpreterContext) (SExp, error)
- func LibOpOr(ctx *InterpreterContext) (SExp, error)
- func LibOpSub(ctx *InterpreterContext) (SExp, error)
- func LibTypeIsAtom(ctx *InterpreterContext) (SExp, error)
- func LibTypeIsBool(ctx *InterpreterContext) (SExp, error)
- func LibTypeIsInt64(ctx *InterpreterContext) (SExp, error)
- func LibTypeIsList(ctx *InterpreterContext) (SExp, error)
- func LibTypeIsNil(ctx *InterpreterContext) (SExp, error)
- func LibTypeIsString(ctx *InterpreterContext) (SExp, error)
- func LibTypeIsSym(ctx *InterpreterContext) (SExp, error)
- func Nil() SExp
- func ResolveName(_ *Closure, sym SExp) (SExp, error)
- func String(val string, meta Meta) SExp
- func Symbol(val string, meta Meta) SExp
- type SExpSlice
- type SExpType
- type ScanError
- type Scanner
- type ScannerOpt
- type Token
- type TokenType
- type TypeInfo
Examples ¶
Constants ¶
const ( // LambdaSymbol is the symbol used to declare lambda functions. LambdaSymbol = "lambda" // QuoteSymbol is the symbol used to quote expressions. QuoteSymbol = "quote" // QuasiquoteSymbol is the symbol used to quasiquote expressions. QuasiquoteSymbol = "quasiquote" // UnquoteSymbol is the symbol used to unquote expressions. UnquoteSymbol = "unquote" // ElseSymbol is the name of the optional "else" symbol in "if" and // "unless" statements. ElseSymbol = "else" )
Variables ¶
var ( // ErrInvalidUTF8 is returned when the input contains an invalid UTF8 // character. ErrInvalidUTF8 = errors.New("invalid UTF8 string") // ErrFuncName is returned when a function name is not a symbol. ErrFuncName = errors.New("function name is not a symbol") // ErrUnknownFunc is returned when a function is not found. ErrUnknownFunc = errors.New("unknown function") // ErrSymNotFound is returned when a symbol could not be resolved. ErrSymNotFound = errors.New("could not resolve symbol") // ErrNotFunc is returned when an S-Expression is not a function. ErrNotFunc = errors.New("the expression is not a function") // ErrInvalidCall is returned when an S-Expression is not a function // call. ErrInvalidCall = errors.New("invalid function call") // ErrDivByZero is returned when dividing by zero. ErrDivByZero = errors.New("division by zero") )
var ( // ErrTypeMissingArg is emitted when a function argument is missing. ErrTypeMissingArg = errors.New("missing function argument") // ErrTypeExtraArg is emitted when a function call has too many // arguments. ErrTypeExtraArg = errors.New("extra function argument") // ErrCarNil is emitted when the car of a cell is nil. ErrCarNil = errors.New("car cannot be nil") // ErrNotSymbol is emitted when a symbol is expected. ErrNotSymbol = errors.New("not a symbol") // ErrNotCell is emitted when a cell is expected. ErrNotCell = errors.New("not a cell") // ErrNotList is emitted when a list is expected. ErrNotList = errors.New("not a list") // ErrNil is emitted when a value cannot be nil. ErrNil = errors.New("value cannot be nil") // ErrCouldBeNil is emitted when a value could be nil. ErrCouldBeNil = errors.New("value could be nil") // ErrBound is emitted when a symbol is already locally bound. ErrBound = errors.New("symbol is already bound locally") // ErrNotBound is emitted when a symbol is not bound to a value. ErrNotBound = errors.New("symbol is not bound to a value") )
var LibCell = map[string]InterpreterFuncHandler{ "cons": LibCellCons, "car": LibCellCar, "cdr": LibCellCdr, }
LibCell contains functions to work with cons cells.
var LibClosure = map[string]InterpreterFuncHandler{ "let": LibClosureLet, }
LibClosure contains functions to work with closures.
var LibCond = map[string]InterpreterFuncHandler{ "if": LibCondIf, "unless": LibCondUnless, }
LibCond contains functions for conditional statements.
var LibLambda = map[string]InterpreterFuncHandler{ "lambda": LibLambdaLambda, "λ": LibLambdaLambda, }
LibLambda contains functions to work with lambda functions.
var LibMeta = map[string]InterpreterFuncHandler{ "quote": LibMetaQuote, "quasiquote": LibMetaQuasiquote, "unquote": LibMetaUnquote, "eval": LibMetaEval, }
LibMeta contains functions to quote and evaluate expressions.
Quasiquote and unquote nesting rules seem to differ amongst Lisp dialects. This implementation follows the rules of MIT Scheme:
https://www.gnu.org/software/mit-scheme/documentation/mit-scheme-ref/Quoting.html
var LibOp = map[string]InterpreterFuncHandler{ "=": LibOpEq, "+": LibOpAdd, "-": LibOpSub, "*": LibOpMul, "/": LibOpDiv, "mod": LibOpMod, "<": LibOpLt, ">": LibOpGt, "<=": LibOpLte, ">=": LibOpGte, "not": LibOpNot, "and": LibOpAnd, "or": LibOpOr, }
LibOp contains functions for primitive operations.
var LibType = map[string]InterpreterFuncHandler{ "nil?": LibTypeIsNil, "atom?": LibTypeIsAtom, "list?": LibTypeIsList, "sym?": LibTypeIsSym, "string?": LibTypeIsString, "int64?": LibTypeIsInt64, "bool?": LibTypeIsBool, }
LibType contains functions to work with types.
Functions ¶
func InterpreterOptBuiltinLibs ¶
func InterpreterOptBuiltinLibs(itr *Interpreter)
InterpreterOptBuiltinLibs adds all the builtin function libraries.
Types ¶
type Closure ¶
type Closure struct {
// contains filtered or unexported fields
}
Closure stores local values and a parent closure.
func NewClosure ¶
func NewClosure(opts ...ClosureOpt) *Closure
NewClosure creates a new closure with an optional parent.
type ClosureOpt ¶
type ClosureOpt func(*Closure)
ClosureOpt is a closure options.
func ClosureOptEnv ¶
func ClosureOptEnv(env []string) ClosureOpt
ClosureOptEnv sets values from environment variables of the form "key=value".
func ClosureOptParent ¶
func ClosureOptParent(parent *Closure) ClosureOpt
ClosureOptParent sets the parent closure.
func ClosureOptResolver ¶
func ClosureOptResolver(resolve ResolveHandler) ClosureOpt
ClosureOptResolver sets the resolver which is used if a symbol is not found.
type FuncData ¶
type FuncData struct { // ParentClosure is the closure the function was defined within. ParentClosure *Closure }
FuncData contains information about a lambda function.
type Interpreter ¶
type Interpreter struct {
// contains filtered or unexported fields
}
Interpreter evaluates S-Expressions.
func NewInterpreter ¶
func NewInterpreter(opts ...InterpreterOpt) *Interpreter
NewInterpreter creates a new interpreter.
func (*Interpreter) AddFuncHandler ¶
func (itr *Interpreter) AddFuncHandler(name string, handler InterpreterFuncHandler)
AddFuncHandler adds a function handler.
func (*Interpreter) DeleteFuncHandler ¶
func (itr *Interpreter) DeleteFuncHandler(name string)
DeleteFuncHandler removes a function handler.
func (*Interpreter) EvalInput ¶
func (itr *Interpreter) EvalInput(ctx context.Context, in string) error
EvalInput evaluates the given input.
All errors are sent the the error handler, but the last one is returned for convenience.
func (*Interpreter) EvalInstrs ¶
func (itr *Interpreter) EvalInstrs(ctx context.Context, instrs SExp) error
EvalInstrs evaluates a list of instructions.
type InterpreterContext ¶
type InterpreterContext struct { Ctx context.Context Name string Closure *Closure Args SExp Meta Meta QuasiquoteLevel int IsTail bool // These can be used to evaluate arguments. The last argument should // be set to IsTail if it is the value being returned. Eval func(*InterpreterContext, SExp, bool) (SExp, error) EvalList func(*InterpreterContext, SExp, bool) (SExp, error) EvalListToSlice func(*InterpreterContext, SExp, bool) (SExpSlice, error) EvalListToStrings func(*InterpreterContext, SExp, bool) ([]string, error) // This one evaluates a "body", such as a function body that may // contain multiple expressions. EvalBody func(*InterpreterContext, SExp, bool) (SExp, error) // contains filtered or unexported fields }
InterpreterContext is passed to a function handler.
type InterpreterFuncHandler ¶
type InterpreterFuncHandler func(*InterpreterContext) (SExp, error)
InterpreterFuncHandler handle a call to a function.
type InterpreterOpt ¶
type InterpreterOpt func(*Interpreter)
InterpreterOpt is an interpreter options.
func InterpreterOptClosure ¶
func InterpreterOptClosure(c *Closure) InterpreterOpt
InterpreterOptClosure sets the top closure.
func InterpreterOptErrorHandler ¶
func InterpreterOptErrorHandler(h func(error)) InterpreterOpt
InterpreterOptErrorHandler sets the error handler.
func InterpreterOptFuncHandlers ¶
func InterpreterOptFuncHandlers(m map[string]InterpreterFuncHandler) InterpreterOpt
InterpreterOptFuncHandlers adds function handlers.
func InterpreterOptTailOptimizations ¶
func InterpreterOptTailOptimizations(e bool) InterpreterOpt
InterpreterOptTailOptimizations sets whether to enable tail call optimizations (on by default).
func InterpreterOptValueHandler ¶
func InterpreterOptValueHandler(h func(SExp)) InterpreterOpt
InterpreterOptValueHandler sets the value handler.
The handler receives evaluated expressions.
type LazyCall ¶
LazyCall contains information about a call to a lambda function. It is used for tail call optimization.
type ParseError ¶
type ParseError struct {
Tok Token
}
ParseError represents an error from the parser.
type Parser ¶
type Parser struct {
// contains filtered or unexported fields
}
Parser produces an S-Expression list from scanner tokens.
type PrimitiveType ¶
type PrimitiveType uint8
PrimitiveType is a primitive type.
const ( PrimitiveInvalid PrimitiveType = iota PrimitiveString PrimitiveInt64 PrimitiveBool PrimitiveList )
Primitive types.
func (PrimitiveType) String ¶
func (t PrimitiveType) String() string
String returns a string representation of the primitive type.
type ResolveHandler ¶
ResolveHandler resolves symbols.
If it returns an error of the type ErrSymNotFound, the symbol will be resolved from its value in the closure. If it returns any other error, the symbol will not be resolved and an error will be produced.
It makes it possible to handle special cases such as if you want variables to have a prefix like a dollar sign.
type SExp ¶
type SExp interface { ID() uint64 String() string Meta() Meta UnderlyingType() SExpType IsNil() bool IsAtom() bool StringVal() (string, bool) Int64Val() (int64, bool) BoolVal() (bool, bool) SymbolVal() (string, bool) MustStringVal() string MustInt64Val() int64 MustSymbolVal() string MustBoolVal() bool Equals(SExp) bool Car() SExp Cdr() SExp }
SExp is a dynamic implementation of an S-Expression.
See:
https://en.wikipedia.org/wiki/S-expression
func LibCellCar ¶
func LibCellCar(ctx *InterpreterContext) (SExp, error)
LibCellCar returns the car of a cell.
func LibCellCdr ¶
func LibCellCdr(ctx *InterpreterContext) (SExp, error)
LibCellCdr returns the cdr of a cell.
func LibCellCons ¶
func LibCellCons(ctx *InterpreterContext) (SExp, error)
LibCellCons constructs a cell.
func LibClosureLet ¶
func LibClosureLet(ctx *InterpreterContext) (SExp, error)
LibClosureLet binds a value to a symbol in the current closure.
func LibCondIf ¶
func LibCondIf(ctx *InterpreterContext) (SExp, error)
LibCondIf evaluates cadr if car evaluates to a non-nil value, otherwise it evaluates caddr (if present).
func LibCondUnless ¶
func LibCondUnless(ctx *InterpreterContext) (SExp, error)
LibCondUnless evaluates cadr if car evaluates to nil, otherwise it evaluates caddr (if present).
func LibLambdaLambda ¶
func LibLambdaLambda(ctx *InterpreterContext) (SExp, error)
LibLambdaLambda creates a lambda function.
func LibMetaEval ¶
func LibMetaEval(ctx *InterpreterContext) (SExp, error)
LibMetaEval evaluates an expression.
func LibMetaQuasiquote ¶
func LibMetaQuasiquote(ctx *InterpreterContext) (SExp, error)
LibMetaQuasiquote returns an expression evaluating only its unquoted nested expressions.
func LibMetaQuote ¶
func LibMetaQuote(ctx *InterpreterContext) (SExp, error)
LibMetaQuote returns an expression without evaluating it.
func LibMetaUnquote ¶
func LibMetaUnquote(ctx *InterpreterContext) (SExp, error)
LibMetaUnquote evaluates unquoted expressions within a quasiquoted expression.
func LibOpAdd ¶
func LibOpAdd(ctx *InterpreterContext) (SExp, error)
LibOpAdd adds integers to the first expression.
func LibOpAnd ¶
func LibOpAnd(ctx *InterpreterContext) (SExp, error)
LibOpAnd returns true if all booleans are true.
func LibOpDiv ¶
func LibOpDiv(ctx *InterpreterContext) (SExp, error)
LibOpDiv divides the first expression by the remaining expressions.
func LibOpEq ¶
func LibOpEq(ctx *InterpreterContext) (SExp, error)
LibOpEq returns true if all arguments are equal.
func LibOpGt ¶
func LibOpGt(ctx *InterpreterContext) (SExp, error)
LibOpGt returns true if all integers are greater than the integer to their right.
func LibOpGte ¶
func LibOpGte(ctx *InterpreterContext) (SExp, error)
LibOpGte returns true if all integers are greater or equal to the integer to their right.
func LibOpLt ¶
func LibOpLt(ctx *InterpreterContext) (SExp, error)
LibOpLt returns true if all integers are less than the integer to their right.
func LibOpLte ¶
func LibOpLte(ctx *InterpreterContext) (SExp, error)
LibOpLte returns true if all integers are less or equal to the integer to their right.
func LibOpMod ¶
func LibOpMod(ctx *InterpreterContext) (SExp, error)
LibOpMod returns the modulo of integers.
func LibOpNot ¶
func LibOpNot(ctx *InterpreterContext) (SExp, error)
LibOpNot returns true if the expression is false and true if the expression is false.
func LibOpOr ¶
func LibOpOr(ctx *InterpreterContext) (SExp, error)
LibOpOr returns true if at least one boolean is true.
func LibOpSub ¶
func LibOpSub(ctx *InterpreterContext) (SExp, error)
LibOpSub substracts integers from the first expression.
func LibTypeIsAtom ¶
func LibTypeIsAtom(ctx *InterpreterContext) (SExp, error)
LibTypeIsAtom returns whether an expression is an atom. Nil is an atom.
func LibTypeIsBool ¶
func LibTypeIsBool(ctx *InterpreterContext) (SExp, error)
LibTypeIsBool returns whether an expression is of type bool.
func LibTypeIsInt64 ¶
func LibTypeIsInt64(ctx *InterpreterContext) (SExp, error)
LibTypeIsInt64 returns whether an expression is of type int64.
func LibTypeIsList ¶
func LibTypeIsList(ctx *InterpreterContext) (SExp, error)
LibTypeIsList returns whether an expression is a list. Nil is not a list.
func LibTypeIsNil ¶
func LibTypeIsNil(ctx *InterpreterContext) (SExp, error)
LibTypeIsNil returns whether an expression is nil.
func LibTypeIsString ¶
func LibTypeIsString(ctx *InterpreterContext) (SExp, error)
LibTypeIsString returns whether an expression is of type string.
func LibTypeIsSym ¶
func LibTypeIsSym(ctx *InterpreterContext) (SExp, error)
LibTypeIsSym returns whether an expression is of type symbol.
func ResolveName ¶
ResolveName resolves symbols with their names.
type SExpSlice ¶
type SExpSlice []SExp
SExpSlice is a slice of S-Expressions.
type Scanner ¶
type Scanner struct {
// contains filtered or unexported fields
}
Scanner produces tokens from a string.
func (*Scanner) Emit ¶
Emit emits the next token.
It returns TokEOF if all tokens have been read, and TokInvalid if no valid token was found.
func (*Scanner) SetReader ¶
func (s *Scanner) SetReader(reader io.RuneScanner)
SetReader resets the scanner ands sets its reader.
type ScannerOpt ¶
type ScannerOpt func(*Scanner)
ScannerOpt is a scanner option.
func ScannerOptErrorHandler ¶
func ScannerOptErrorHandler(h func(error)) ScannerOpt
ScannerOptErrorHandler sets the scanner's error handler.
type TokenType ¶
type TokenType uint8
TokenType is a script token.
type TypeInfo ¶
type TypeInfo struct { // Type is the primitive type, for instance bool, function, or list. Type PrimitiveType // Subtype is for compound types, such as list<int64> or // function()<bool>. Subtype *TypeInfo // Params is currently used for function arguments, for instance // function(<string>,<int64>). Params []*TypeInfo }
TypeInfo contains information about a type.