Esempio n. 1
0
        private void Function(FunctionType type)
        {
            current = new Instance {
                type      = type,
                enclosing = current
            };
            current.function.name = ObjString.CopyString(parser.previous.Lexeme);

            BeginScope();

            Consume(TokenType.LeftParen, "Expect '(' after function name.");
            if (!Check(TokenType.RightParen))
            {
                do
                {
                    current.function.arity += 1;
                    if (current.function.arity > 255)
                    {
                        ErrorAtCurrent("Can't have more than 255 parameters.");
                    }

                    byte paramConst = ParseVariable("Expect parameter name.");
                    DefineVariable(paramConst);
                } while (Match(TokenType.Comma));
            }
            Consume(TokenType.RightParen, "Expect ')' after parameters.");

            Consume(TokenType.LeftBrace, "Expect '{' before function body.");
            Block();

            ObjFunction fun = EndCompiler();

            EmitBytes(OpCode.Constant, MakeConstant(Value.Obj(fun)));
        }
Esempio n. 2
0
 private void DefineNative(string name, Func <int, Value[], Value> func)
 {
     Push(Value.Obj(ObjString.CopyString(name)));
     Push(Value.Obj(new ObjNative {
         Func = func
     }));
     globals[stack[0].AsString] = stack[1];
     Pop();
     Pop();
 }
Esempio n. 3
0
        private InterpretResult Run()
        {
            frame = frames[frameCount - 1];
            while (true)
            {
#if DEBUG
                writer.WriteLine("->[{0}]", string.Join(", ", stack.Take(stackTop).Select(v => v.ToString())));
                frame.function.chunk.DisassembleInstruction(writer, frame.ip);
#endif

                byte instruction = ReadByte();
                switch ((OpCode)instruction)
                {
                case OpCode.Constant:
                    Value constant = ReadConstant();
                    Push(constant);
                    break;

                case OpCode.Nil: Push(Value.Nil); break;

                case OpCode.True: Push(Value.Bool(true)); break;

                case OpCode.False: Push(Value.Bool(false)); break;

                case OpCode.Pop: Pop(); break;

                case OpCode.GetLocal: {
                    byte slot = ReadByte();
                    Push(frame.slots[slot]);
                    break;
                }

                case OpCode.SetLocal: {
                    byte slot = ReadByte();
                    frame.slots[slot] = Peek(0);
                    break;
                }

                case OpCode.GetGlobal: {
                    ObjString name = ReadString();
                    if (!globals.ContainsKey(name))
                    {
                        RuntimeError("Undefined variable '{0}'.", name.Chars);
                        return(InterpretResult.RuntimeError);
                    }
                    Value value = globals[name];
                    Push(value);
                    break;
                }

                case OpCode.DefineGlobal: {
                    ObjString name = ReadString();
                    globals[name] = Peek(0);
                    Pop();
                    break;
                }

                case OpCode.SetGlobal: {
                    ObjString name = ReadString();
                    if (!globals.ContainsKey(name))
                    {
                        RuntimeError("Undefined variable {0}.", name.Chars);
                        return(InterpretResult.RuntimeError);
                    }
                    globals[name] = Peek(0);
                    break;
                }

                case OpCode.Equal: {
                    Value b = Pop();
                    Value a = Pop();
                    Push(Value.Bool(a.IsEqual(b)));
                    break;
                }

                case OpCode.Greater: {
                    if (!Peek(0).IsNumber || !Peek(1).IsNumber)
                    {
                        RuntimeError("Operands must be numbers");
                        return(InterpretResult.RuntimeError);
                    }
                    double r = Pop().AsNumber;
                    double l = Pop().AsNumber;
                    Push(Value.Bool(l > r));
                    break;
                }

                case OpCode.Less: {
                    if (!Peek(0).IsNumber || !Peek(1).IsNumber)
                    {
                        RuntimeError("Operands must be numbers");
                        return(InterpretResult.RuntimeError);
                    }
                    double r = Pop().AsNumber;
                    double l = Pop().AsNumber;
                    Push(Value.Bool(l < r));
                    break;
                }

                case OpCode.Add: {
                    if (Peek(0).IsString&& Peek(1).IsString)
                    {
                        ObjString b = Pop().AsString;
                        ObjString a = Pop().AsString;
                        Push(Value.Obj(ObjString.CopyString(a.Chars + b.Chars)));
                    }
                    else if (Peek(0).IsNumber&& Peek(1).IsNumber)
                    {
                        double r = Pop().AsNumber;
                        double l = Pop().AsNumber;
                        Push(Value.Number(l + r));
                    }
                    else
                    {
                        RuntimeError("Operands must be two numbers or two strings");
                        return(InterpretResult.RuntimeError);
                    }
                    break;
                }

                case OpCode.Subtract: {
                    if (!Peek(0).IsNumber || !Peek(1).IsNumber)
                    {
                        RuntimeError("Operands must be numbers");
                        return(InterpretResult.RuntimeError);
                    }
                    double r = Pop().AsNumber;
                    double l = Pop().AsNumber;
                    Push(Value.Number(l - r));
                    break;
                }

                case OpCode.Multiply: {
                    if (!Peek(0).IsNumber || !Peek(1).IsNumber)
                    {
                        RuntimeError("Operands must be numbers");
                        return(InterpretResult.RuntimeError);
                    }
                    double r = Pop().AsNumber;
                    double l = Pop().AsNumber;
                    Push(Value.Number(l * r));
                    break;
                }

                case OpCode.Divide: {
                    if (!Peek(0).IsNumber || !Peek(1).IsNumber)
                    {
                        RuntimeError("Operands must be numbers");
                        return(InterpretResult.RuntimeError);
                    }
                    double r = Pop().AsNumber;
                    double l = Pop().AsNumber;
                    Push(Value.Number(l / r));
                    break;
                }

                case OpCode.Not:
                    Push(Value.Bool(IsFalsy(Pop())));
                    break;

                case OpCode.Negate:
                    if (!Peek(0).IsNumber)
                    {
                        RuntimeError("Operand must be a number");
                        return(InterpretResult.RuntimeError);
                    }
                    Push(Value.Number(-Pop().AsNumber));
                    break;

                case OpCode.Print: {
                    writer.WriteLine("{0}", Pop());
                    break;
                }

                case OpCode.Jump: {
                    ushort offset = ReadShort();
                    frame.ip += offset;
                    break;
                }

                case OpCode.JumpIfFalse: {
                    ushort offset = ReadShort();
                    if (IsFalsy(Peek(0)))
                    {
                        frame.ip += offset;
                    }
                    break;
                }

                case OpCode.Loop: {
                    ushort offset = ReadShort();
                    frame.ip -= offset;
                    break;
                }

                case OpCode.Call: {
                    int argCount = ReadByte();
                    if (!CallValue(Peek(argCount), argCount))
                    {
                        return(InterpretResult.RuntimeError);
                    }
                    frame = frames[frameCount - 1];
                    break;
                }

                case OpCode.Return: {
                    Value result = Pop();

                    frameCount -= 1;
                    if (frameCount == 0)
                    {
                        Pop();
                        return(InterpretResult.OK);
                    }

                    stackTop = frame.slots.Offset;
                    Push(result);

                    frame = frames[frameCount - 1];
                    break;
                }
                }
            }
        }
Esempio n. 4
0
 private byte IdentifierConstant(Token name)
 {
     return(MakeConstant(Value.Obj(ObjString.CopyString(name.Lexeme))));
 }