| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231 |
- // 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
- }
|