Documentation ¶
Overview ¶
Package bytecode contains a custom bytecode compiler and virtual machine.
Initially, an Evy program is turned into a sequence of tokens by the [lexer]. Then, the parser generates an Abstract Syntax Tree (AST) from the tokens. The Compiler of this package walks the AST and writes the instructions to custom bytecode. For more on the AST refer to the parser package documentation.
The VM can read the bytecode and execute the encoded instructions, providing a runtime similar to the [evaluator]. The virtual machine is a straight-forward stack implementation that does not use any additional registers.
Index ¶
- Constants
- Variables
- func Make(op Opcode, operands ...int) ([]byte, error)
- func ReadOperands(def *OpDefinition, ins Instructions) ([]int, int)
- func ReadUint16(ins Instructions) uint16
- type Bytecode
- type Compiler
- type Instructions
- type OpDefinition
- type Opcode
- type Symbol
- type SymbolScope
- type SymbolTable
- type VM
Constants ¶
const ( // StackSize defines an upper limit for the size of the stack. StackSize = 2048 // GlobalsSize is the total number of globals that can be specified // in an evy program. GlobalsSize = 65536 )
Variables ¶
var ( // ErrInternal and errors wrapping ErrInternal report internal // errors of the VM that should not occur during normal // program execution. ErrInternal = errors.New("internal error") // ErrPanic and errors wrapping ErrPanic report runtime errors, such // as an index out of bounds or a stack overflow. ErrPanic = errors.New("user error") // ErrUnknownOpcode is returned when an unknown opcode is encountered. ErrUnknownOpcode = fmt.Errorf("%w: unknown opcode", ErrInternal) )
var ( // ErrUndefinedVar is returned when a variable name cannot // be resolved in the symbol table. ErrUndefinedVar = fmt.Errorf("%w: undefined variable", ErrPanic) // ErrUnknownOperator is returned when an operator cannot // be resolved. ErrUnknownOperator = fmt.Errorf("%w: unknown operator", ErrInternal) )
var ( // ErrStackOverflow is returned when the stack exceeds its size limit. ErrStackOverflow = fmt.Errorf("%w: stack overflow", ErrPanic) // ErrDivideByZero is returned when a division by zero would // produce an invalid result. In Golang, floating point division // by zero produces +Inf, and modulo by zero produces NaN. ErrDivideByZero = fmt.Errorf("%w: division by zero", ErrPanic) )
Functions ¶
func Make ¶
Make assembles and returns a single bytecode instruction out of an Opcode and an optional list of operands. An error will be returned if there is no definition for the provided Opcode.
func ReadOperands ¶
func ReadOperands(def *OpDefinition, ins Instructions) ([]int, int)
ReadOperands will read from the provided Instructions based on the width of the provided OpDefinition. It returns the operands and the read offset inside the Instructions.
func ReadUint16 ¶
func ReadUint16(ins Instructions) uint16
ReadUint16 is exposed to allow reading without performing a Lookup to get an OpDefinition to pass to ReadOperands.
Types ¶
type Bytecode ¶
type Bytecode struct { Constants []value Instructions Instructions }
Bytecode represents raw evy bytecode.
type Compiler ¶
type Compiler struct {
// contains filtered or unexported fields
}
Compiler is responsible for turning a parsed evy program into bytecode.
type Instructions ¶
type Instructions []byte
Instructions represents raw bytecode, which is composed of opcodes and an optional number of operands after each opcode.
func (Instructions) String ¶
func (ins Instructions) String() string
String prints opcodes and their associated operands.
type OpDefinition ¶
OpDefinition defines a name and expected operand width for each OpCode.
func Lookup ¶
func Lookup(op Opcode) (*OpDefinition, error)
Lookup returns an OpDefinition for the value of an Opcode, any unknown value will result in an error.
type Opcode ¶
type Opcode byte
Opcode defines the type of operation to be performed when reading the bytecode.
const ( // OpConstant defines a constant that will be referred to by index // in the bytecode. OpConstant Opcode = iota // OpGetGlobal retrieves a symbol from the symbol table at the // specified index. OpGetGlobal // OpSetGlobal adds a symbol to the specified index in the symbol // table. OpSetGlobal // OpAdd instructs the virtual machine to perform an addition. OpAdd // OpSubtract instructs the virtual machine to perform a subtraction. OpSubtract // OpMultiply instructs the virtual machine to perform a multiplication. OpMultiply // OpDivide instructs the virtual machine to perform a division. OpDivide // OpModulo instructs the virtual machine to perform a modulo operation. // Modulo returns the remainder of dividing the left side of an expression // by the right. OpModulo )
type Symbol ¶
type Symbol struct { Name string Scope SymbolScope Index int }
Symbol is a variable inside an evy program.
type SymbolScope ¶
type SymbolScope string
SymbolScope defines a type of scope that a symbol can be defined inside.
const ( // GlobalScope is the top level scope of an evy program. GlobalScope SymbolScope = "GLOBAL" )
type SymbolTable ¶
type SymbolTable struct {
// contains filtered or unexported fields
}
SymbolTable is a mapping of string identifiers to symbols.
func (*SymbolTable) Define ¶
func (s *SymbolTable) Define(name string) Symbol
Define adds a symbol definition to the table or returns an already defined symbol with the same name.