// 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" ) type EmittedInstruction struct { Opcode code.Opcode Position int } type Compiler struct { instructions code.Instructions // hold the generated bytecode constants []object.Object // slice that serves as our constant pool lastInstruction EmittedInstruction previousInstruction EmittedInstruction } func New() *Compiler { return &Compiler{ instructions: code.Instructions{}, constants: []object.Object{}, lastInstruction: EmittedInstruction{}, previousInstruction: EmittedInstruction{}, } } 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.lastInstructionIsPop() { c.removeLastPop() } // Emit an `OpJump` with a bogus value jumpPos := c.emit(code.OpJump, 9999) afterConsequencePos := len(c.instructions) 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.lastInstructionIsPop() { c.removeLastPop() } } afterAlternativePos := len(c.instructions) c.changOperand(jumpPos, afterAlternativePos) case *ast.BlockStatement: for _, s := range node.Statements { err := c.Compile(s) if err != nil { return err } } 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)) } 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.instructions) c.instructions = append(c.instructions, ins...) 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.lastInstruction last := EmittedInstruction{Opcode: op, Position: pos} c.previousInstruction = previous c.lastInstruction = last } func (c *Compiler) lastInstructionIsPop() bool { return c.lastInstruction.Opcode == code.OpPop } func (c *Compiler) removeLastPop() { c.instructions = c.instructions[:c.lastInstruction.Position] c.lastInstruction = c.previousInstruction } func (c *Compiler) replaceInstruction(pos int, newInstruction []byte) { for i := 0; i < len(newInstruction); i++ { c.instructions[pos+i] = newInstruction[i] } } func (c *Compiler) changOperand(opPos int, operand int) { op := code.Opcode(c.instructions[opPos]) newInstruction := code.Make(op, operand) c.replaceInstruction(opPos, newInstruction) } func (c *Compiler) ByteCode() *ByteCode { return &ByteCode{ Instructions: c.instructions, Constants: c.constants, } } type ByteCode struct { Instructions code.Instructions Constants []object.Object }