|
|
@@ -12,15 +12,24 @@ import (
|
|
|
"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{},
|
|
|
+ instructions: code.Instructions{},
|
|
|
+ constants: []object.Object{},
|
|
|
+ lastInstruction: EmittedInstruction{},
|
|
|
+ previousInstruction: EmittedInstruction{},
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -94,6 +103,52 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|
|
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()
|
|
|
+ }
|
|
|
+
|
|
|
+ if node.Alternative == nil {
|
|
|
+ afterConsequencePos := len(c.instructions)
|
|
|
+
|
|
|
+ c.changOperand(jumpNotTruthyPos, afterConsequencePos)
|
|
|
+ } else {
|
|
|
+ // Emit an `OpJump` with a bogus value
|
|
|
+ jumpPos := c.emit(code.OpJump, 9999)
|
|
|
+
|
|
|
+ afterConsequencePos := len(c.instructions)
|
|
|
+ c.changOperand(jumpNotTruthyPos, afterConsequencePos)
|
|
|
+
|
|
|
+ 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)
|
|
|
@@ -129,9 +184,41 @@ func (c *Compiler) addInstruction(ins []byte) 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,
|