コード例 #1
0
ファイル: Compiler.cs プロジェクト: thecocce/monkey-lang-vm
        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());
        }
コード例 #2
0
ファイル: Expression.cs プロジェクト: thecocce/monkey-lang-vm
        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
                       ));
        }
コード例 #3
0
ファイル: Expression.cs プロジェクト: thecocce/monkey-lang-vm
        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());
        }