Documentation ¶
Overview ¶
Package compiler implements the translation layer of lemur from the generate AST produced by the parser to a set of instructions that consist of byte code made up of operations which are define in the op package
Index ¶
- Variables
- type Array
- type Boolean
- type Builtin
- type BuiltinFunction
- type ByteCode
- type Closure
- type CompiledFunction
- type Compiler
- type Error
- type HashKey
- type Hashable
- type Integer
- type JumpTable
- type KeysGetter
- type Map
- type MapPair
- type Null
- type Object
- type ObjectType
- type String
- type Symbol
- type SymbolScope
- type SymbolTable
Constants ¶
This section is empty.
Variables ¶
var Builtins = []struct { Name string Builtin *Builtin }{ { "len", &Builtin{Fn: func(args ...Object) Object { if len(args) != 1 { return newError("wrong number of arguments. got=%d, want=1", len(args)) } switch arg := args[0].(type) { case *Array: return &Integer{Value: int64(len(arg.Elements))} case *String: return &Integer{ Value: int64(len(arg.Value)), } default: return newError("argument to `len` not supported, got %s", args[0].Type()) } }, }, }, { "puts", &Builtin{Fn: func(args ...Object) Object { for _, arg := range args { fmt.Println(arg.Inspect()) } return nil }, }, }, { "first", &Builtin{Fn: func(args ...Object) Object { if len(args) != 1 { return newError("wrong number of arguments. got=%d, want=1", len(args)) } if args[0].Type() != ARRAY_OBJ { return newError("argument to `first` must be ARRAY, got %s", args[0].Type()) } arr := args[0].(*Array) if len(arr.Elements) > 0 { return arr.Elements[0] } return nil }, }, }, { "last", &Builtin{Fn: func(args ...Object) Object { if len(args) != 1 { return newError("wrong number of arguments. got=%d, want=1", len(args)) } if args[0].Type() != ARRAY_OBJ { return newError("argument to `last` must be ARRAY, got %s", args[0].Type()) } arr := args[0].(*Array) length := len(arr.Elements) if length > 0 { return arr.Elements[length-1] } return nil }, }, }, { "rest", &Builtin{Fn: func(args ...Object) Object { if len(args) != 1 { return newError("wrong number of arguments. got=%d, want=1", len(args)) } if args[0].Type() != ARRAY_OBJ { return newError("argument to `rest` must be ARRAY, got %s", args[0].Type()) } arr := args[0].(*Array) length := len(arr.Elements) if length > 0 { newElements := make([]Object, length-1, length-1) copy(newElements, arr.Elements[1:length]) return &Array{Elements: newElements} } return nil }, }, }, { "push", &Builtin{Fn: func(args ...Object) Object { if len(args) != 2 { return newError("wrong number of arguments. got=%d, want=1", len(args)) } if args[0].Type() != ARRAY_OBJ { return newError("argument to `push` must be ARRAY, got %s", args[0].Type()) } arr := args[0].(*Array) length := len(arr.Elements) newElements := make([]Object, length+1, length+1) copy(newElements, arr.Elements) newElements[length] = args[1] return &Array{Elements: newElements} }, }, }, { "println", &Builtin{Fn: func(args ...Object) Object { if len(args) != 1 { return newError("wrong number of arguments. got=%d, want=1", len(args)) } fmt.Println(args[0].Inspect()) return &String{ Value: "", } }, }, }, }
Builtins provide the various built in function available to use in lemur source code
Functions ¶
This section is empty.
Types ¶
type Array ¶
Array represents collection of elements. If created during usual run time, each element is associated by a key integer incremented from 0. Unlike your typical array, the elements are not fixed types or memory size given the dynamic typing nature of Lemur
func (*Array) Type ¶
func (ao *Array) Type() ObjectType
type Boolean ¶
type Boolean struct {
Value bool
}
Boolean are designated for holding any lemur boolean value
func (*Boolean) Type ¶
func (b *Boolean) Type() ObjectType
type Builtin ¶
type Builtin struct {
Fn BuiltinFunction
}
Builtin represents an object which is a built in function provided by lemur
func (*Builtin) Type ¶
func (b *Builtin) Type() ObjectType
type BuiltinFunction ¶
BuiltinFunction is the key definition for what can make up a built in function provide by Lemur
type ByteCode ¶
type ByteCode struct { Instructions op.Instructions Constants []Object JumpTables []*JumpTable }
ByteCode represents the output from the AST being compiled. This output should be passed to the run time execution
type Closure ¶
type Closure struct { Fn *CompiledFunction Free []Object }
Closure represents an object for a given function on a stack that is about to be called
func (*Closure) Type ¶
func (c *Closure) Type() ObjectType
type CompiledFunction ¶
type CompiledFunction struct { Instructions op.Instructions NumLocals int NumParameters int }
CompiledFunction represents a way to hold an function as an object as a constant containing all it's instructions number of locals that it need to accommodate on the stack, and the number of parameters it takes in
func (*CompiledFunction) Inspect ¶
func (cf *CompiledFunction) Inspect() string
func (*CompiledFunction) Type ¶
func (cf *CompiledFunction) Type() ObjectType
type Compiler ¶
type Compiler struct {
// contains filtered or unexported fields
}
Compiler represents the main entry point to compilation of the AST. It's output ends up in the form of the ByteCode type where the compiled constants, instruction and any jump tables are handed off after compilation ends
func NewCompiler ¶
func NewCompiler() *Compiler
NewCompiler is a convenient constructor for Compiler
func NewCompilerWithState ¶
func NewCompilerWithState(s *SymbolTable, constants []Object) *Compiler
NewCompilerWithState is similar to NewCompiler but allows a symbol table and constants from a previous Compiler to be passed through and set to the new Compiler. This is useful for implementing something like a REPL
func (*Compiler) Compile ¶
Compile will take any node from the AST produced by the parser and compile it to byte code instructions based on the operation code found in the op package. Where applicable, it will also populate the symbolTable. Passing in a *ast.Program node (which the parser will output at the top of the AST), will result in a full compiled program. Accessing the compilation output is done via Compile.Bytecode
type Error ¶
type Error struct {
Message string
}
Error represent lemur errors
func (*Error) Type ¶
func (e *Error) Type() ObjectType
type HashKey ¶
type HashKey struct { Type ObjectType Value uint64 }
HashKey represents a hash value of an object and their the underlying object type
type Hashable ¶
type Hashable interface {
HashKey() HashKey
}
Hashable are any objects where their value can be hash to be utilised as a key in a data structure like a Map. This essentially aids in setting boundaries as to what primitives can be used as a key and be use to index into a something like a Map
type Integer ¶
type Integer struct {
Value int64
}
Integer are designated for holding any lemur integer value
func (*Integer) Type ¶
func (i *Integer) Type() ObjectType
type JumpTable ¶
type JumpTable struct {
// contains filtered or unexported fields
}
JumpTable represents and acts as your typical jump table compiler optimisation that is utilised in switch expressions
func (*JumpTable) DefaultJump ¶
DefaultJump returns the jump position of a default case in a switch statement
type KeysGetter ¶
type KeysGetter interface {
Keys() []Object
}
KeysGetter are any objects that can give a list of keys it contains for the underlying data structure it represents. These are usually Array or Map
type MapPair ¶
MapPair encapsulates both map key and value. This acts as the value to the underlying map within the the implementation as the key will be a HashKey of Key
type Null ¶
type Null struct{}
Null are designated for holding any lemur null value
func (*Null) Type ¶
func (i *Null) Type() ObjectType
type Object ¶
type Object interface { Type() ObjectType Inspect() string }
Object implementation are design to understand what the underlying type of the Object is (via Type()) and the value it represents (via Inspect())
type ObjectType ¶
type ObjectType string
ObjectType aliases strings of object types using the constants below. It's use during runtime to carry out things like binary operation against two objects where you need to check the two types are compatible together for said operation
const ( INTEGER_OBJ ObjectType = "INTEGER" BOOLEAN_OBJ ObjectType = "BOOLEAN" NULL_OBJ ObjectType = "NULL" ERROR_OBJ ObjectType = "ERROR" COMPILED_FUNCTION_OBJ ObjectType = "COMPILED_FUNCTION_OBJ" STRING_OBJ ObjectType = "STRING" BUILTIN_OBJ ObjectType = "BUILTIN" ARRAY_OBJ ObjectType = "ARRAY" MAP_OBJ ObjectType = "MAP" CLOSURE_OBJ ObjectType = "CLOSURE" )
type String ¶
type String struct {
Value string
}
String are designated for holding any lemur string value
func (*String) Type ¶
func (s *String) Type() ObjectType
type Symbol ¶
type Symbol struct { Name string Scope SymbolScope Index int Mutable bool }
Symbol represents a identifier and it's related definition such as it's name, scope, index in reference to where it'll be placed in a VM's globals or stack if part of a frame, and whether it is mutable
type SymbolScope ¶
type SymbolScope string
SymbolScope represents a string of what a symbol is scope to. The consts below define said scopes
const ( GlobalScope SymbolScope = "GLOBAL" LocalScope SymbolScope = "LOCAL" BuiltinScope SymbolScope = "BUILTIN" FreeScope SymbolScope = "FREE" FunctionScope SymbolScope = "FUNCTION" )
type SymbolTable ¶
type SymbolTable struct { Outer *SymbolTable FreeSymbols []Symbol // contains filtered or unexported fields }
SymbolTable holds the entries of symbols that have been defined during compilation. It also holds a reference to an out symbol table if the scope of the symbol is not global. A SymbolTable can also hold a list of FreeSymbols when resolving a non global scoped symbol does resolve as a local, global, builtin, or function scope
func NewEnclosedSymbolTable ¶
func NewEnclosedSymbolTable(outer *SymbolTable) *SymbolTable
NewEnclosedSymbolTable is constructor that aids in scope definition by creating a new SymbolTable with a reference to the another outside of the scope of the new one
func NewSymbolTable ¶
func NewSymbolTable() *SymbolTable
NewSymbolTable is a simple constructor for creating a new SymbolTable
func (*SymbolTable) Define ¶
func (s *SymbolTable) Define(name string, mutable bool) Symbol
Define provides a way to create a new Symbol and store said symbol inside the SymbolTable. If an outer SymbolTable exists, the scope is marked as local instead of global
func (*SymbolTable) DefineBuiltin ¶
func (s *SymbolTable) DefineBuiltin(index int, name string) Symbol
DefineBuiltin is similar to Define but for symbols that are built in functions
func (*SymbolTable) DefineFunctionName ¶
func (s *SymbolTable) DefineFunctionName(name string) Symbol
DefineFunctionName is similar to Define but for symbols that are functions