private CompilerState Emit(byte opcode, List <int> operands, CompilerState previousState) { var instruction = Bytecode.Create(opcode, operands); var position = previousState.CurrentScope.Instructions.Count; return(Factory.CompilerState() .Assign(previousState) .CurrentInstruction(new Instruction { Opcode = opcode, Position = position }) .PreviousInstruction(previousState.CurrentScope.CurrentInstruction) .Instructions(instruction) .Create()); }
private CompilerState CompileIfElseExpression(Expression expression, CompilerState previousState) { var ifElseExpression = (IfElseExpression)expression; var conditionState = CompileExpressionInner(ifElseExpression.Condition, previousState); // Create Opcode.JumpNotTruthy with transient offset, we are going // to change it to correct one later var jumpNotTruthyInstructionState = Emit((byte)Opcode.Name.JumpNotTruthy, new List <int> { 9999 }, conditionState); var jumpNotTruthyInstructionPosition = previousState.CurrentScope.CurrentInstruction.Position; var consequenceState = CompileStatements(ifElseExpression.Consequence.Statements, jumpNotTruthyInstructionState); // We want to leave last expression value on the stack, hence we // we need to check whether there is a Pop instruction after last // expression. If so, remove it if (previousState.CurrentScope.CurrentInstruction.Opcode == (byte)Opcode.Name.Pop) { consequenceState = RemoveLastPopInstruction(consequenceState); } // Create Opcode.Jump with transient offset, we are going // to change it to correct one later var jumpInstructionState = Emit((byte)Opcode.Name.Jump, new List <int> { 9999 }, consequenceState); var jumpInstructionPosition = previousState.CurrentScope.CurrentInstruction.Position; var afterJumpInstructionState = ReplaceInstruction( jumpNotTruthyInstructionPosition, Bytecode.Create((byte)Opcode.Name.JumpNotTruthy, new List <int> { previousState.CurrentScope.Instructions.Count }), jumpInstructionState ); CompilerState alternativeState; if (ifElseExpression.Alternative == default(BlockStatement)) { // Emit Opcode.Null as alternative branch alternativeState = Emit((byte)Opcode.Name.Null, new List <int>(), afterJumpInstructionState); } else { alternativeState = CompileStatements(ifElseExpression.Alternative.Statements, afterJumpInstructionState); if (previousState.CurrentScope.CurrentInstruction.Opcode == 3) { alternativeState = RemoveLastPopInstruction(alternativeState); } } // Set Opcode.Jump operand to correct offset and return compiled expression return(ReplaceInstruction( jumpInstructionPosition, Bytecode.Create((byte)Opcode.Name.Jump, new List <int> { previousState.CurrentScope.Instructions.Count }), alternativeState )); }
private CompilerState CompileFunctionExpression(Expression expression, CompilerState previousState) { var identifier = ((Statement)previousState.Node).Identifier; var functionExpression = (FunctionExpression)expression; var functionState = EnterScope(previousState); CompilerState afterFunctionState; if (identifier != default(Token)) { functionState.CurrentScope.SymbolTable.Define(identifier.Literal, SymbolScope.Function); } functionExpression.Parameters.ForEach(param => { functionState.CurrentScope.SymbolTable.Define(param.Literal); }); functionState = CompileStatements(functionExpression.Body.Statements, functionState); if (functionState.Errors.Count > 0) { return(functionState); } var instructions = functionState.CurrentScope.Instructions; var index = DetermineConstantIndex(expression, previousState); // Eg. fn () { } if (instructions.Count == 0) { afterFunctionState = LeaveScope(Emit((byte)Opcode.Name.Return, new List <int>(), functionState)); return(Factory.CompilerState() .Assign(Emit((byte)Opcode.Name.Closure, new List <int> { index, 0 }, afterFunctionState)) .Constant(index, Object.Create(ObjectKind.Function, instructions)) .Create()); } // Replace last Opcode.Pop instruction with implicit Opcode.ReturnValue, // since we want to keep last expression value on the stack if (functionState.CurrentScope.CurrentInstruction.Opcode == (byte)Opcode.Name.Pop) { functionState = ReplaceInstruction( instructions.Count - 1, Bytecode.Create((byte)Opcode.Name.ReturnValue, new List <int>()), functionState ); } // Add implicit Opcode.Return if there is no value to be returned if (functionState.CurrentScope.CurrentInstruction.Opcode != (byte)Opcode.Name.ReturnValue) { functionState = Emit((byte)Opcode.Name.Return, new List <int>(), functionState); } var frees = functionState.CurrentScope.SymbolTable.Frees; afterFunctionState = LeaveScope(functionState); foreach (var key in frees.Keys) { afterFunctionState = Emit(DetermineSymbolOpcode(frees[key]), new List <int> { frees[key].Index }, afterFunctionState); } return(Factory.CompilerState() .Assign(Emit((byte)Opcode.Name.Closure, new List <int> { index, frees.Count }, afterFunctionState)) .Constant(index, Object.Create(ObjectKind.Function, instructions)) .Create()); }