script

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Mar 13, 2019 License: AGPL-3.0 Imports: 11 Imported by: 0

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

Examples

Constants

View Source
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

View Source
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")
)
View Source
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")
)
View Source
var LibCell = map[string]InterpreterFuncHandler{
	"cons": LibCellCons,
	"car":  LibCellCar,
	"cdr":  LibCellCdr,
}

LibCell contains functions to work with cons cells.

View Source
var LibClosure = map[string]InterpreterFuncHandler{
	"let": LibClosureLet,
}

LibClosure contains functions to work with closures.

View Source
var LibCond = map[string]InterpreterFuncHandler{
	"if":     LibCondIf,
	"unless": LibCondUnless,
}

LibCond contains functions for conditional statements.

View Source
var LibLambda = map[string]InterpreterFuncHandler{
	"lambda": LibLambdaLambda,
	"λ":      LibLambdaLambda,
}

LibLambda contains functions to work with lambda functions.

View Source
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
View Source
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.

View Source
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 CellID

func CellID() uint64

CellID generates a unique cell ID.

func Error

func Error(msg string, meta Meta, ident string) error

Error creates an error with S-Expression meta.

func InterpreterOptBuiltinLibs

func InterpreterOptBuiltinLibs(itr *Interpreter)

InterpreterOptBuiltinLibs adds all the builtin function libraries.

func IsList

func IsList(exp SExp) bool

IsList returns whether an S-Expression is a list.

  • nil is a list
  • other atoms are not a list
  • the cdr must be a list

func WrapError

func WrapError(err error, meta Meta, ident string) error

WrapError wraps an error with S-Expression meta.

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.

func (*Closure) Get

func (c *Closure) Get(key string) (SExp, bool)

Get returns a value.

It travels up the closures until it finds the key.

func (*Closure) Local

func (c *Closure) Local(key string) (SExp, bool)

Local returns a local value.

func (*Closure) Resolve

func (c *Closure) Resolve(sym SExp) (SExp, error)

Resolve resolves a symbol.

func (*Closure) Set

func (c *Closure) Set(key string, val SExp)

Set sets a value.

It travels up the closure until it finds the key. If it doesn't find one, it sets a local.

func (*Closure) SetLocal

func (c *Closure) SetLocal(key string, val SExp)

SetLocal sets a local value.

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

type LazyCall struct {
	FuncID uint64
	Args   SExpSlice
}

LazyCall contains information about a call to a lambda function. It is used for tail call optimization.

type Meta

type Meta struct {
	Line   int
	Offset int

	// UserData is custom external data.
	UserData interface{}
}

Meta contains metadata for an S-Expression.

type ParseError

type ParseError struct {
	Tok Token
}

ParseError represents an error from the parser.

func (ParseError) Error

func (err ParseError) Error() string

Error returns an error string.

type Parser

type Parser struct {
	// contains filtered or unexported fields
}

Parser produces an S-Expression list from scanner tokens.

func NewParser

func NewParser(scanner *Scanner) *Parser

NewParser creates a new parser.

func (*Parser) List

func (p *Parser) List(in string) (SExp, error)

List parses a single list.

func (*Parser) Parse

func (p *Parser) Parse(in string) (SExp, error)

Parse parses instructions in the given input.

It returns a list of S-Expressions which can be evaluated. It returns nil if there are not instructions.

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

type ResolveHandler func(closure *Closure, sym SExp) (SExp, error)

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 Bool

func Bool(val bool, meta Meta) SExp

Bool creates a new boolean atom.

func Cons

func Cons(car, cdr SExp, meta Meta) SExp

Cons creates a new cell.

func Int64

func Int64(val int64, meta Meta) SExp

Int64 creates a new 64-bit integer atom.

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 LibOpMul

func LibOpMul(ctx *InterpreterContext) (SExp, error)

LibOpMul multiplies 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 Nil

func Nil() SExp

Nil returns the nil value.

func ResolveName

func ResolveName(_ *Closure, sym SExp) (SExp, error)

ResolveName resolves symbols with their names.

func String

func String(val string, meta Meta) SExp

String creates a new string atom.

func Symbol

func Symbol(val string, meta Meta) SExp

Symbol creates a new symbol atom.

type SExpSlice

type SExpSlice []SExp

SExpSlice is a slice of S-Expressions.

func ToSlice

func ToSlice(exp SExp) SExpSlice

ToSlice converts an S-Expression to a slice.

The returned slice will contain the car values of all the expressions in the list. It returns nil if the expression is nil or an atom.

func (SExpSlice) Strings

func (s SExpSlice) Strings(quote bool) []string

Strings returns the strings value of every S-Expression in the slice.

If quote is true, strings are quoted.

type SExpType

type SExpType uint8

SExpType is an S-Expression type.

const (
	SExpInvalid SExpType = iota
	SExpString
	SExpInt64
	SExpBool
	SExpSymbol
	SExpCell
)

S-Expression types.

func (SExpType) String

func (t SExpType) String() string

String returns a string representation of the type.

type ScanError

type ScanError struct {
	Line   int
	Offset int
	Rune   rune
}

ScanError represents an error from the scanner.

func (ScanError) Error

func (err ScanError) Error() string

Error returns an error string.

type Scanner

type Scanner struct {
	// contains filtered or unexported fields
}

Scanner produces tokens from a string.

func NewScanner

func NewScanner(opts ...ScannerOpt) *Scanner

NewScanner creates a new scanner.

func (*Scanner) Emit

func (s *Scanner) Emit() Token

Emit emits the next token.

It returns TokEOF if all tokens have been read, and TokInvalid if no valid token was found.

func (*Scanner) SetInput

func (s *Scanner) SetInput(in string)

SetInput resets the scanner and sets its reader from an input string.

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 Token

type Token struct {
	Type   TokenType
	Value  string
	Line   int
	Offset int
}

Token represents a token.

func (Token) String

func (t Token) String() string

String returns a string representation of the token.

type TokenType

type TokenType uint8

TokenType is a script token.

const (
	TokInvalid TokenType = iota
	TokEOF
	TokLine
	TokLParen
	TokRParen
	TokLBrace
	TokRBrace
	TokQuote
	TokQuasiquote
	TokUnquote
	TokSymbol
	TokString
	TokInt
	TokTrue
	TokFalse
)

Available tokens.

func (TokenType) String

func (tok TokenType) String() string

String returns a string representation of a token type.

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.

Jump to

Keyboard shortcuts

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