compiler

package
v0.0.1 Latest Latest
Warning

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

Go to latest
Published: Apr 12, 2024 License: BSD-3-Clause Imports: 8 Imported by: 0

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

Constants

This section is empty.

Variables

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

type Array struct {
	Indexes  []Object
	Elements []Object
}

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) Inspect

func (ao *Array) Inspect() string

func (*Array) Keys

func (ao *Array) Keys() []Object

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) Inspect

func (b *Boolean) Inspect() string

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) Inspect

func (b *Builtin) Inspect() string

func (*Builtin) Type

func (b *Builtin) Type() ObjectType

type BuiltinFunction

type BuiltinFunction func(args ...Object) Object

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) Inspect

func (c *Closure) Inspect() string

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) Bytecode

func (c *Compiler) Bytecode() *ByteCode

Bytecode returns what is the output result of successful compilation

func (*Compiler) Compile

func (c *Compiler) Compile(node ast.Node) error

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) Inspect

func (e *Error) Inspect() string

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) HashKey

func (i *Integer) HashKey() HashKey

HashKey of an Integer is essentially it's value

func (*Integer) Inspect

func (i *Integer) Inspect() string

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

func (j *JumpTable) DefaultJump() int

DefaultJump returns the jump position of a default case in a switch statement

func (*JumpTable) JumpPosition

func (j *JumpTable) JumpPosition(index uint64) (int, bool)

JumpPosition returns a position to jump to from the table based on the index provided

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 Map

type Map struct {
	Indexes []Object
	Pairs   map[HashKey]MapPair
}

func (*Map) Inspect

func (m *Map) Inspect() string

func (*Map) Keys

func (m *Map) Keys() []Object

func (*Map) Type

func (m *Map) Type() ObjectType

type MapPair

type MapPair struct {
	Key   Object
	Value Object
}

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) Inspect

func (i *Null) Inspect() string

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) HashKey

func (s *String) HashKey() HashKey

func (*String) Inspect

func (s *String) Inspect() string

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

func (*SymbolTable) Resolve

func (s *SymbolTable) Resolve(name string) (Symbol, bool)

Resolve brings back a Symbol based on it's name it was defined with. The second return parameter will evaluated to false if there is no symbol by that name

Jump to

Keyboard shortcuts

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