| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386 |
- // Author: simon
- // Author: ynwdlxm@163.com
- // Date: 2022/10/19 14:39
- // Desc: Compiler
- package compiler
- import (
- "fmt"
- "github/runnignwater/monkey/ast"
- "github/runnignwater/monkey/code"
- "github/runnignwater/monkey/object"
- "sort"
- )
- type EmittedInstruction struct {
- Opcode code.Opcode
- Position int
- }
- type Compiler struct {
- constants []object.Object // slice that serves as our constant pool
- symbolTable *SymbolTable
- scopes []CompilationScope
- scopeIndex int
- }
- type CompilationScope struct {
- instructions code.Instructions // hold the generated bytecode
- lastInstruction EmittedInstruction
- previousInstruction EmittedInstruction
- }
- func New() *Compiler {
- mainScope := CompilationScope{
- instructions: code.Instructions{},
- lastInstruction: EmittedInstruction{},
- previousInstruction: EmittedInstruction{},
- }
- return &Compiler{
- constants: []object.Object{},
- symbolTable: NewSymbolTable(),
- scopes: []CompilationScope{mainScope},
- scopeIndex: 0,
- }
- }
- func NewWithState(s *SymbolTable, constants []object.Object) *Compiler {
- compiler := New()
- compiler.symbolTable = s
- compiler.constants = constants
- return compiler
- }
- func (c *Compiler) Compile(node ast.Node) error {
- switch node := node.(type) {
- case *ast.Program:
- for _, s := range node.Statements {
- err := c.Compile(s)
- if err != nil {
- return err
- }
- }
- case *ast.ExpressionStatement:
- err := c.Compile(node.Expression)
- if err != nil {
- return err
- }
- c.emit(code.OpPop)
- case *ast.InfixExpression:
- if node.Operator == "<" {
- err := c.Compile(node.Right)
- if err != nil {
- return err
- }
- err = c.Compile(node.Left)
- if err != nil {
- return err
- }
- c.emit(code.OpGreaterThan)
- return nil
- }
- err := c.Compile(node.Left)
- if err != nil {
- return err
- }
- err = c.Compile(node.Right)
- if err != nil {
- return err
- }
- switch node.Operator {
- case "+":
- c.emit(code.OpAdd)
- case "-":
- c.emit(code.OpSub)
- case "*":
- c.emit(code.OpMul)
- case "/":
- c.emit(code.OpDiv)
- case ">":
- c.emit(code.OpGreaterThan)
- case "==":
- c.emit(code.OpEqual)
- case "!=":
- c.emit(code.OpNotEqual)
- default:
- return fmt.Errorf("unknown operator %s", node.Operator)
- }
- case *ast.PrefixExpression:
- err := c.Compile(node.Right)
- if err != nil {
- return err
- }
- switch node.Operator {
- case "!":
- c.emit(code.OpBang)
- case "-":
- c.emit(code.OpMinus)
- default:
- return fmt.Errorf("unknown operator %s", node.Operator)
- }
- case *ast.IfExpression:
- err := c.Compile(node.Condition)
- if err != nil {
- return err
- }
- // Emit an 'OpJumNotTruthy` with a bogus value
- jumpNotTruthyPos := c.emit(code.OpJumpNotTruthy, 9999)
- err = c.Compile(node.Consequence)
- if err != nil {
- return err
- }
- if c.lastInstructionIs(code.OpPop) {
- c.removeLastPop()
- }
- // Emit an `OpJump` with a bogus value
- jumpPos := c.emit(code.OpJump, 9999)
- afterConsequencePos := len(c.currentInstructions())
- c.changOperand(jumpNotTruthyPos, afterConsequencePos)
- if node.Alternative == nil {
- c.emit(code.OpNull)
- } else {
- err := c.Compile(node.Alternative)
- if err != nil {
- return err
- }
- if c.lastInstructionIs(code.OpPop) {
- c.removeLastPop()
- }
- }
- afterAlternativePos := len(c.currentInstructions())
- c.changOperand(jumpPos, afterAlternativePos)
- case *ast.BlockStatement:
- for _, s := range node.Statements {
- err := c.Compile(s)
- if err != nil {
- return err
- }
- }
- case *ast.LetStatement:
- err := c.Compile(node.Value)
- if err != nil {
- return err
- }
- symbol := c.symbolTable.Define(node.Name.Value)
- c.emit(code.OpSetGlobal, symbol.Index)
- case *ast.Identifier:
- symbol, ok := c.symbolTable.Resolve(node.Value)
- if !ok {
- return fmt.Errorf("undefined variable %s", node.Value)
- }
- c.emit(code.OpGetGlobal, symbol.Index)
- case *ast.Boolean:
- if node.Value {
- c.emit(code.OpTrue)
- } else {
- c.emit(code.OpFalse)
- }
- case *ast.IntegerLiteral:
- integer := &object.Integer{Value: node.Value}
- c.emit(code.OpConstant, c.addConstant(integer))
- case *ast.StringLiteral:
- str := &object.String{Value: node.Value}
- c.emit(code.OpConstant, c.addConstant(str))
- case *ast.ArrayLiteral:
- for _, el := range node.Element {
- err := c.Compile(el)
- if err != nil {
- return err
- }
- }
- c.emit(code.OpArray, len(node.Element))
- case *ast.HashLiteral:
- var keys []ast.Expression
- for k := range node.Pairs {
- keys = append(keys, k)
- }
- sort.Slice(keys, func(i, j int) bool {
- return keys[i].String() < keys[j].String()
- })
- for _, k := range keys {
- err := c.Compile(k)
- if err != nil {
- return err
- }
- err = c.Compile(node.Pairs[k])
- if err != nil {
- return err
- }
- }
- c.emit(code.OpHash, len(node.Pairs)*2)
- case *ast.IndexExpression:
- err := c.Compile(node.Left)
- if err != nil {
- return err
- }
- err = c.Compile(node.Index)
- if err != nil {
- return err
- }
- c.emit(code.OpIndex)
- case *ast.ReturnStatement:
- err := c.Compile(node.ReturnValue)
- if err != nil {
- return err
- }
- c.emit(code.OpReturnValue)
- case *ast.FunctionLiteral:
- c.enterScope()
- err := c.Compile(node.Body)
- if err != nil {
- return err
- }
- if c.lastInstructionIs(code.OpPop) {
- c.replaceLastPopWithReturn()
- }
- if !c.lastInstructionIs(code.OpReturnValue) {
- c.emit(code.OpReturn)
- }
- instructions := c.leaveScope()
- compiledFn := &object.CompileFunction{Instructions: instructions}
- c.emit(code.OpConstant, c.addConstant(compiledFn))
- case *ast.CallExpression:
- err := c.Compile(node.Function)
- if err != nil {
- return err
- }
- c.emit(code.OpCall)
- }
- return nil
- }
- func (c *Compiler) addConstant(obj object.Object) int {
- c.constants = append(c.constants, obj)
- return len(c.constants) - 1
- }
- func (c *Compiler) addInstruction(ins []byte) int {
- posNewInstruction := len(c.currentInstructions())
- updatedInstructions := append(c.currentInstructions(), ins...)
- c.scopes[c.scopeIndex].instructions = updatedInstructions
- return posNewInstruction
- }
- // emit generate an instruction and add it to the results,
- // either by printing it, writing it to a file or
- // by adding it to a collection in memory
- //
- // op code.Opcode
- //
- // operands [operand]int
- func (c *Compiler) emit(op code.Opcode, operands ...int) int {
- ins := code.Make(op, operands...)
- pos := c.addInstruction(ins)
- c.setLastInstruction(op, pos)
- return pos
- }
- func (c *Compiler) setLastInstruction(op code.Opcode, pos int) {
- previous := c.scopes[c.scopeIndex].lastInstruction
- last := EmittedInstruction{Opcode: op, Position: pos}
- c.scopes[c.scopeIndex].previousInstruction = previous
- c.scopes[c.scopeIndex].lastInstruction = last
- }
- func (c *Compiler) lastInstructionIsPop() bool {
- return c.scopes[c.scopeIndex].lastInstruction.Opcode == code.OpPop
- }
- func (c *Compiler) lastInstructionIs(op code.Opcode) bool {
- if len(c.currentInstructions()) == 0 {
- return false
- }
- return c.scopes[c.scopeIndex].lastInstruction.Opcode == op
- }
- func (c *Compiler) removeLastPop() {
- last := c.scopes[c.scopeIndex].lastInstruction
- previous := c.scopes[c.scopeIndex].previousInstruction
- oldIns := c.currentInstructions()
- newIns := oldIns[:last.Position]
- c.scopes[c.scopeIndex].instructions = newIns
- c.scopes[c.scopeIndex].lastInstruction = previous
- }
- func (c *Compiler) replaceInstruction(pos int, newInstruction []byte) {
- ins := c.currentInstructions()
- for i := 0; i < len(newInstruction); i++ {
- ins[pos+i] = newInstruction[i]
- }
- }
- func (c *Compiler) changOperand(opPos int, operand int) {
- op := code.Opcode(c.currentInstructions()[opPos])
- newInstruction := code.Make(op, operand)
- c.replaceInstruction(opPos, newInstruction)
- }
- func (c *Compiler) currentInstructions() code.Instructions {
- return c.scopes[c.scopeIndex].instructions
- }
- func (c *Compiler) enterScope() {
- scope := CompilationScope{
- instructions: code.Instructions{},
- lastInstruction: EmittedInstruction{},
- previousInstruction: EmittedInstruction{},
- }
- c.scopes = append(c.scopes, scope)
- c.scopeIndex++
- }
- func (c *Compiler) leaveScope() code.Instructions {
- ins := c.currentInstructions()
- c.scopes = c.scopes[:len(c.scopes)-1]
- c.scopeIndex--
- return ins
- }
- func (c *Compiler) replaceLastPopWithReturn() {
- lastPop := c.scopes[c.scopeIndex].lastInstruction.Position
- c.replaceInstruction(lastPop, code.Make(code.OpReturnValue))
- c.scopes[c.scopeIndex].lastInstruction.Opcode = code.OpReturnValue
- }
- func (c *Compiler) ByteCode() *ByteCode {
- return &ByteCode{
- Instructions: c.currentInstructions(),
- Constants: c.constants,
- }
- }
- type ByteCode struct {
- Instructions code.Instructions
- Constants []object.Object
- }
|