Beispiel #1
0
        // Compiles an (optional) argument list and then calls it.
        private void MethodCall(Instruction instruction, Signature signature)
        {
            Signature called = new Signature { Type = SignatureType.Getter, Arity = 0, Name = signature.Name, Length = signature.Length };

            // Parse the argument list, if any.
            if (Match(TokenType.LeftParen))
            {
                called.Type = SignatureType.Method;

                // Allow empty an argument list.
                if (Peek() != TokenType.RightParen)
                {
                    FinishArgumentList(called);
                }
                Consume(TokenType.RightParen, "Expect ')' after arguments.");
            }

            // Parse the block argument, if any.
            if (Match(TokenType.LeftBrace))
            {
                // Include the block argument in the arity.
                called.Type = SignatureType.Method;
                called.Arity++;

                Compiler fnCompiler = new Compiler(_parser, this, true);

                // Make a dummy signature to track the arity.
                Signature fnSignature = new Signature { Arity = 0 };

                // Parse the parameter list, if any.
                if (Match(TokenType.Pipe))
                {
                    fnCompiler.FinishParameterList(fnSignature);
                    Consume(TokenType.Pipe, "Expect '|' after function parameters.");
                }

                fnCompiler._numParams = fnSignature.Arity;

                fnCompiler.FinishBody(false);

                String blockName = SignatureToString(called) + " block argument";
                fnCompiler.EndCompiler(blockName);
            }

            // TODO: Allow Grace-style mixfix methods?

            // If this is a super() call for an initializer, make sure we got an actual
            // argument list.
            if (signature.Type == SignatureType.Initializer)
            {
                if (called.Type != SignatureType.Method)
                {
                    Error("A superclass constructor must have an argument list.");
                }

                called.Type = SignatureType.Initializer;
            }

            CallSignature(instruction, called);
        }
Beispiel #2
0
        // Compiles a method definition inside a class body. Returns the symbol in the
        // method table for the new method.
        private bool Method(ClassCompiler classCompiler, int classSlot)
        {

            bool isForeign = Match(TokenType.Foreign);
            classCompiler.IsStaticMethod = Match(TokenType.Static);

            SignatureFn signatureFn = GetRule(_parser.Current.Type).Method;
            NextToken();

            if (signatureFn == null)
            {
                Error("Expect method definition.");
                return false;
            }

            // Build the method signature.
            Signature signature = SignatureFromToken(new Signature(), SignatureType.Getter);
            classCompiler.Signature = signature;

            Compiler methodCompiler = new Compiler(_parser, this, false);
            signatureFn(methodCompiler, signature);

            if (classCompiler.IsStaticMethod && signature.Type == SignatureType.Initializer)
            {
                Error("A constructor cannot be static.");
            }

            String fullSignature = SignatureToString(signature);

            if (isForeign)
            {
                int constant = AddConstant(Obj.MakeString(fullSignature));

                EmitShortArg(Instruction.CONSTANT, constant);
            }
            else
            {
                Consume(TokenType.LeftBrace, "Expect '{' to begin method body.");
                methodCompiler.FinishBody(signature.Type == SignatureType.Initializer);
                methodCompiler.EndCompiler(fullSignature);
            }

            // Define the method. For a constructor, this defines the instance
            // initializer method.
            int methodSymbol = SignatureSymbol(signature);
            DefineMethod(classSlot, classCompiler.IsStaticMethod, methodSymbol);

            if (signature.Type == SignatureType.Initializer)
            {
                signature.Type = SignatureType.Method;
                int constructorSymbol = SignatureSymbol(signature);
                CreateConstructor(signature, methodSymbol);
                DefineMethod(classSlot, true, constructorSymbol);
            }

            return true;
        }
Beispiel #3
0
        public static ObjFn Compile(WrenVM vm, ObjModule module, string sourcePath, string source, bool printErrors)
        {
            Parser parser = new Parser
            {
                vm = vm,
                Module = module,
                SourcePath = sourcePath,
                Source = source,
                TokenStart = 0,
                CurrentChar = 0,
                CurrentLine = 1,
                Current = { Type = TokenType.Error, Start = 0, Length = 0, Line = 0 },
                SkipNewlines = true,
                PrintErrors = printErrors,
                HasError = false,
                Raw = ""
            };

            Compiler compiler = new Compiler(parser, null, true);

            // Read the first token.
            compiler.NextToken();

            compiler.IgnoreNewlines();

            while (!compiler.Match(TokenType.Eof))
            {
                compiler.Definition();

                // If there is no newline, it must be the end of the block on the same line.
                if (!compiler.MatchLine())
                {
                    compiler.Consume(TokenType.Eof, "Expect end of file.");
                    break;
                }
            }

            compiler.Emit(Instruction.NULL);
            compiler.Emit(Instruction.RETURN);

            // See if there are any implicitly declared module-level variables that never
            // got an explicit definition.
            // TODO: It would be nice if the error was on the line where it was used.
            for (int i = 0; i < parser.Module.Variables.Count; i++)
            {
                ModuleVariable t = parser.Module.Variables[i];
                if (t.Container == Obj.Undefined)
                {
                    compiler.Error(string.Format("Variable '{0}' is used but not defined.", t.Name));
                }
            }

            return compiler.EndCompiler("(script)");
        }
Beispiel #4
0
        // Compiles a method signature for an operator that can either be unary or
        // infix (i.e. "-").
        private static void MixedSignature(Compiler c, Signature signature)
        {
            signature.Type = SignatureType.Getter;

            // If there is a parameter, it's an infix operator, otherwise it's unary.
            if (c.Match(TokenType.LeftParen))
            {
                // Add the RHS parameter.
                signature.Type = SignatureType.Method;
                signature.Arity = 1;

                // Parse the parameter name.
                c.DeclareNamedVariable();
                c.Consume(TokenType.RightParen, "Expect ')' after parameter name.");
            }
        }
Beispiel #5
0
        // Compiles a method signature for a named method or setter.
        private static void NamedSignature(Compiler c, Signature signature)
        {
            signature.Type = SignatureType.Getter;

            // If it's a setter, it can't also have a parameter list.
            if (c.MaybeSetter(signature)) return;

            // Regular named method with an optional parameter list.
            c.ParameterList(signature);
        }
Beispiel #6
0
        private static void Conditional(Compiler c, bool allowAssignment)
        {
            // Ignore newline after '?'.
            c.IgnoreNewlines();

            // Jump to the else branch if the condition is false.
            int ifJump = c.EmitJump(Instruction.JUMP_IF);

            // Compile the then branch.
            c.ParsePrecedence(allowAssignment, Precedence.Ternary);

            c.Consume(TokenType.Colon, "Expect ':' after then branch of conditional operator.");
            c.IgnoreNewlines();

            // Jump over the else branch when the if branch is taken.
            int elseJump = c.EmitJump(Instruction.JUMP);

            // Compile the else branch.
            c.PatchJump(ifJump);

            c.ParsePrecedence(allowAssignment, Precedence.Assignment);

            // Patch the jump over the else.
            c.PatchJump(elseJump);
        }
Beispiel #7
0
        // Compiles a method signature for an infix operator.
        static void InfixSignature(Compiler c, Signature signature)
        {
            // Add the RHS parameter.
            signature.Type = SignatureType.Method;
            signature.Arity = 1;

            // Parse the parameter name.
            c.Consume(TokenType.LeftParen, "Expect '(' after operator name.");
            c.DeclareNamedVariable();
            c.Consume(TokenType.RightParen, "Expect ')' after parameter name.");
        }
Beispiel #8
0
        private static void Field(Compiler c, bool allowAssignment)
        {
            // Initialize it with a fake value so we can keep parsing and minimize the
            // number of cascaded errors.
            int field = 255;

            ClassCompiler enclosingClass = c.GetEnclosingClass();

            if (enclosingClass == null)
            {
                c.Error("Cannot reference a field outside of a class definition.");
            }
            else if (enclosingClass.IsStaticMethod)
            {
                c.Error("Cannot use an instance field in a static method.");
            }
            else
            {
                // Look up the field, or implicitly define it.
                string fieldName = c._parser.Source.Substring(c._parser.Previous.Start, c._parser.Previous.Length);
                field = enclosingClass.Fields.IndexOf(fieldName);
                if (field < 0)
                {
                    enclosingClass.Fields.Add(fieldName);
                    field = enclosingClass.Fields.IndexOf(fieldName);
                }

                if (field >= MaxFields)
                {
                    c.Error(string.Format("A class can only have {0} fields.", MaxFields));
                }
            }

            // If there's an "=" after a field name, it's an assignment.
            bool isLoad = true;
            if (c.Match(TokenType.Eq))
            {
                if (!allowAssignment) c.Error("Invalid assignment.");

                // Compile the right-hand side.
                c.Expression();
                isLoad = false;
            }

            // If we're directly inside a method, use a more optimal instruction.
            if (c._parent != null && c._parent._enclosingClass == enclosingClass)
            {
                c.EmitByteArg(isLoad ? Instruction.LOAD_FIELD_THIS : Instruction.STORE_FIELD_THIS,
                            field);
            }
            else
            {
                c.LoadThis();
                c.EmitByteArg(isLoad ? Instruction.LOAD_FIELD : Instruction.STORE_FIELD, field);
            }
        }
Beispiel #9
0
        private static void StaticField(Compiler c, bool allowAssignment)
        {
            Instruction loadInstruction = Instruction.LOAD_LOCAL;
            int index = 255;

            Compiler classCompiler = c.GetEnclosingClassCompiler();
            if (classCompiler == null)
            {
                c.Error("Cannot use a static field outside of a class definition.");
            }
            else
            {
                // Look up the name in the scope chain.
                Token token = c._parser.Previous;

                // If this is the first time we've seen this static field, implicitly
                // define it as a variable in the scope surrounding the class definition.
                if (classCompiler.ResolveLocal(c._parser.Source.Substring(token.Start, token.Length), token.Length) == -1)
                {
                    int symbol = classCompiler.DeclareVariable(null);

                    // Implicitly initialize it to null.
                    classCompiler.Emit(Instruction.NULL);
                    classCompiler.DefineVariable(symbol);
                }

                // It definitely exists now, so resolve it properly. This is different from
                // the above resolveLocal() call because we may have already closed over it
                // as an upvalue.
                index = c.ResolveName(c._parser.Source.Substring(token.Start, token.Length), token.Length, out loadInstruction);
            }

            c.Variable(allowAssignment, index, loadInstruction);
        }
Beispiel #10
0
        // Unary operators like `-foo`.
        private static void UnaryOp(Compiler c, bool allowAssignment)
        {
            GrammarRule rule = c.GetRule(c._parser.Previous.Type);

            c.IgnoreNewlines();

            // Compile the argument.
            c.ParsePrecedence(false, Precedence.Unary + 1);

            // Call the operator method on the left-hand side.
            c.CallMethod(0, rule.Function);
        }
Beispiel #11
0
 private static void Boolean(Compiler c, bool allowAssignment)
 {
     c.Emit(c._parser.Previous.Type == TokenType.False ? Instruction.FALSE : Instruction.TRUE);
 }
Beispiel #12
0
        // A map literal.
        private static void Map(Compiler c, bool allowAssignment)
        {
            // Load the Map class.
            c.LoadCoreVariable("Map");

            // Instantiate a new map.
            c.CallMethod(0, "new()");

            // Compile the map elements. Each one is compiled to just invoke the
            // subscript setter on the map.
            if (c.Peek() != TokenType.RightBrace)
            {
                do
                {
                    c.IgnoreNewlines();

                    if (c.Peek() == TokenType.RightBrace)
                        break;

                    // Push a copy of the map since the subscript call will consume it.
                    c.Emit(Instruction.DUP);

                    // The key.
                    c.ParsePrecedence(false, Precedence.Primary);
                    c.Consume(TokenType.Colon, "Expect ':' after map key.");
                    c.IgnoreNewlines();

                    // The value.
                    c.Expression();
                    c.CallMethod(2, "[_]=(_)");

                    // Discard the result of the setter call.
                    c.Emit(Instruction.POP);
                } while (c.Match(TokenType.Comma));
            }

            // Allow newlines before the closing '}'.
            c.IgnoreNewlines();
            c.Consume(TokenType.RightBrace, "Expect '}' after map entries.");
        }
Beispiel #13
0
        // A list literal.
        private static void List(Compiler c, bool allowAssignment)
        {
            // Load the List class.
            c.LoadCoreVariable("List");

            // Instantiate a new list.
            c.CallMethod(0, "new()");

            // Compile the list elements. Each one compiles to a ".add()" call.
            if (c.Peek() != TokenType.RightBracket)
            {
                do
                {
                    c.IgnoreNewlines();

                    if (c.Peek() == TokenType.RightBracket)
                        break;

                    // Push a copy of the list since the add() call will consume it.
                    c.Emit(Instruction.DUP);

                    // The element.
                    c.Expression();
                    c.CallMethod(1, "add(_)");

                    // Discard the result of the add() call.
                    c.Emit(Instruction.POP);
                } while (c.Match(TokenType.Comma));
            }

            // Allow newlines before the closing ']'.
            c.IgnoreNewlines();
            c.Consume(TokenType.RightBracket, "Expect ']' after list elements.");
        }
Beispiel #14
0
 // A parenthesized expression.
 private static void Grouping(Compiler c, bool allowAssignment)
 {
     c.Expression();
     c.Consume(TokenType.RightParen, "Expect ')' after expression.");
 }
Beispiel #15
0
 private static void Call(Compiler c, bool allowAssignment)
 {
     c.IgnoreNewlines();
     c.Consume(TokenType.Name, "Expect method name after '.'.");
     c.NamedCall(allowAssignment, Instruction.CALL_0);
 }
Beispiel #16
0
        // Compiles a variable name or method call with an implicit receiver.
        private static void Name(Compiler c, bool allowAssignment)
        {
            // Look for the name in the scope chain up to the nearest enclosing method.
            Token token = c._parser.Previous;

            Instruction loadInstruction;
            string varName = c._parser.Source.Substring(token.Start, token.Length);
            int index = c.ResolveNonmodule(varName, token.Length, out loadInstruction);
            if (index != -1)
            {
                c.Variable(allowAssignment, index, loadInstruction);
                return;
            }

            // TODO: The fact that we return above here if the variable is known and parse
            // an optional argument list below if not means that the grammar is not
            // context-free. A line of code in a method like "someName(foo)" is a parse
            // error if "someName" is a defined variable in the surrounding scope and not
            // if it isn't. Fix this. One option is to have "someName(foo)" always
            // resolve to a self-call if there is an argument list, but that makes
            // getters a little confusing.

            // If we're inside a method and the name is lowercase, treat it as a method
            // on this.
            if (IsLocalName(varName) && c.GetEnclosingClass() != null)
            {
                c.LoadThis();
                c.NamedCall(allowAssignment, Instruction.CALL_0);
                return;
            }

            // Otherwise, look for a module-level variable with the name.
            int module = c._parser.Module.Variables.FindIndex(v => v.Name == varName);
            if (module == -1)
            {
                if (IsLocalName(varName))
                {
                    c.Error("Undefined variable.");
                    return;
                }

                // If it's a nonlocal name, implicitly define a module-level variable in
                // the hopes that we get a real definition later.
                module = c._parser.vm.DeclareVariable(c._parser.Module, varName);

                if (module == -2)
                {
                    c.Error("Too many module variables defined.");
                }
            }

            c.Variable(allowAssignment, module, Instruction.LOAD_MODULE_VAR);
        }
Beispiel #17
0
        static void or_(Compiler c, bool allowAssignment)
        {
            c.IgnoreNewlines();

            // Skip the right argument if the left is true.
            int jump = c.EmitJump(Instruction.OR);
            c.ParsePrecedence(false, Precedence.LogicalOr);
            c.PatchJump(jump);
        }
Beispiel #18
0
 private static void null_(Compiler c, bool allowAssignment)
 {
     c.Emit(Instruction.NULL);
 }
Beispiel #19
0
        private static void InfixOp(Compiler c, bool allowAssignment)
        {
            GrammarRule rule = c.GetRule(c._parser.Previous.Type);

            // An infix operator cannot end an expression.
            c.IgnoreNewlines();

            // Compile the right-hand side.
            c.ParsePrecedence(false, rule.Precedence + 1);

            // Call the operator method on the left-hand side.
            Signature signature = new Signature { Type = SignatureType.Method, Arity = 1, Name = rule.Function, Length = rule.Function.Length };
            c.CallSignature(Instruction.CALL_0, signature);
        }
Beispiel #20
0
        private static void Number(Compiler c, bool allowAssignment)
        {
            int constant = c.AddConstant(new Obj(c._parser.Number));

            // Compile the code to load the constant.
            c.EmitShortArg(Instruction.CONSTANT, constant);
        }
Beispiel #21
0
 // Compiles a method signature for an unary operator (i.e. "!").
 private static void UnarySignature(Compiler c, Signature signature)
 {
     // Do nothing. The name is already complete.
     signature.Type = SignatureType.Getter;
 }
Beispiel #22
0
        private static void string_(Compiler c, bool allowAssignment)
        {
            int constant = c.StringConstant();

            // Compile the code to load the constant.
            c.EmitShortArg(Instruction.CONSTANT, constant);
        }
Beispiel #23
0
        // Compiles a method signature for a subscript operator.
        private static void SubscriptSignature(Compiler c, Signature signature)
        {
            signature.Type = SignatureType.Subscript;

            // The signature currently has "[" as its name since that was the token that
            // matched it. Clear that out.
            signature.Length = 0;
            signature.Name = "";

            // Parse the parameters inside the subscript.
            c.FinishParameterList(signature);
            c.Consume(TokenType.RightBracket, "Expect ']' after parameters.");

            c.MaybeSetter(signature);
        }
Beispiel #24
0
        private static void super_(Compiler c, bool allowAssignment)
        {
            ClassCompiler enclosingClass = c.GetEnclosingClass();

            if (enclosingClass == null)
            {
                c.Error("Cannot use 'super' outside of a method.");
            }

            c.LoadThis();

            // TODO: Super operator calls.

            // See if it's a named super call, or an unnamed one.
            if (c.Match(TokenType.Dot))
            {
                // Compile the superclass call.
                c.Consume(TokenType.Name, "Expect method name after 'super.'.");
                c.NamedCall(allowAssignment, Instruction.SUPER_0);
            }
            else if (enclosingClass != null)
            {
                // No explicit name, so use the name of the enclosing method. Make sure we
                // check that enclosingClass isn't null first. We've already reported the
                // error, but we don't want to crash here.
                c.MethodCall(Instruction.SUPER_0, enclosingClass.Signature);
            }
        }
Beispiel #25
0
        // Compiles a method signature for a constructor.
        private static void ConstructorSignature(Compiler c, Signature signature)
        {
            c.Consume(TokenType.Name, "Expect constructor name after 'construct'.");

            // Capture the name.
            signature = c.SignatureFromToken(signature, SignatureType.Initializer);

            if (c.Match(TokenType.Eq))
            {
                c.Error("A constructor cannot be a setter.");
            }

            if (!c.Match(TokenType.LeftParen))
            {
                c.Error("A constructor cannot be a getter.");
                return;
            }

            if (c.Match(TokenType.RightParen)) return;

            c.FinishParameterList(signature);
            c.Consume(TokenType.RightParen, "Expect ')' after parameters.");
        }
Beispiel #26
0
        private static void this_(Compiler c, bool allowAssignment)
        {
            if (c.GetEnclosingClass() == null)
            {
                c.Error("Cannot use 'this' outside of a method.");
                return;
            }

            c.LoadThis();
        }
Beispiel #27
0
        private void CreateConstructor(Signature signature, int initializerSymbol)
        {
            Compiler methodCompiler = new Compiler(_parser, this, false);

            methodCompiler.Emit(_enclosingClass.IsForeign ? Instruction.FOREIGN_CONSTRUCT : Instruction.CONSTRUCT);
            methodCompiler.EmitShortArg(Instruction.CALL_0 + signature.Arity, initializerSymbol);
            methodCompiler.Emit(Instruction.RETURN);

            methodCompiler.EndCompiler("");
        }
Beispiel #28
0
        // Subscript or "array indexing" operator like `foo[bar]`.
        private static void Subscript(Compiler c, bool allowAssignment)
        {
            Signature signature = new Signature { Name = "", Length = 0, Type = SignatureType.Subscript, Arity = 0 };

            // Parse the argument list.
            c.FinishArgumentList(signature);
            c.Consume(TokenType.RightBracket, "Expect ']' after arguments.");

            if (c.Match(TokenType.Eq))
            {
                if (!allowAssignment) c.Error("Invalid assignment.");

                signature.Type = SignatureType.SubscriptSetter;

                // Compile the assigned value.
                c.ValidateNumParameters(++signature.Arity);
                c.Expression();
            }

            c.CallSignature(Instruction.CALL_0, signature);
        }
Beispiel #29
0
        // Initializes [compiler].
        public Compiler(Parser parser, Compiler parent, bool isFunction)
        {
            _parser = parser;
            _parent = parent;

            // Initialize this to null before allocating in case a GC gets triggered in
            // the middle of initializing the compiler.
            _constants = new List<Obj>();

            _numUpValues = 0;
            _numParams = 0;
            _loop = null;
            _enclosingClass = null;

            parser.vm.Compiler = this;

            if (parent == null)
            {
                _numLocals = 0;

                // Compiling top-level code, so the initial scope is module-level.
                _scopeDepth = -1;
            }
            else
            {
                // Declare a fake local variable for the receiver so that it's slot in the
                // stack is taken. For methods, we call this "this", so that we can resolve
                // references to that like a normal variable. For functions, they have no
                // explicit "this". So we pick a bogus name. That way references to "this"
                // inside a function will try to walk up the parent chain to find a method
                // enclosing the function whose "this" we can close over.
                _numLocals = 1;
                if (isFunction)
                {
                    _locals[0].Name = null;
                    _locals[0].Length = 0;
                }
                else
                {
                    _locals[0].Name = "this";
                    _locals[0].Length = 4;
                }
                _locals[0].Depth = -1;
                _locals[0].IsUpvalue = false;

                // The initial scope for function or method is a local scope.
                _scopeDepth = 0;
            }

            _bytecode = new List<byte>();
            _debugSourceLines = new int[16000];
        }
Beispiel #30
0
        // Compiles a method definition inside a class body. Returns the symbol in the
        // method table for the new method.
        private bool Method(ClassCompiler classCompiler, int classSlot, out bool hasConstructor)
        {
            hasConstructor = false;

            bool isForeign = Match(TokenType.TOKEN_FOREIGN);
            classCompiler.isStaticMethod = Match(TokenType.TOKEN_STATIC);

            SignatureFn signatureFn = GetRule(parser.current.type).method;
            NextToken();

            if (signatureFn == null)
            {
                Error("Expect method definition.");
                return false;
            }

            // Build the method signature.
            Signature signature = SignatureFromToken(SignatureType.SIG_GETTER);
            classCompiler.signature = signature;

            Compiler methodCompiler = new Compiler(parser, this, false);
            signatureFn(methodCompiler, signature);

            if (classCompiler.isStaticMethod && signature.Type == SignatureType.SIG_INITIALIZER)
            {
                Error("A constructor cannot be static.");
            }

            String fullSignature = SignatureToString(signature);

            if (isForeign)
            {
                int constant = AddConstant(new Value(fullSignature));

                EmitShortArg(Instruction.CONSTANT, constant);
            }
            else
            {
                Consume(TokenType.TOKEN_LEFT_BRACE, "Expect '{' to begin method body.");
                methodCompiler.FinishBody(signature.Type == SignatureType.SIG_INITIALIZER);
                methodCompiler.EndCompiler(fullSignature);
            }

            // Define the method. For a constructor, this defines the instance
            // initializer method.
            int methodSymbol = SignatureSymbol(signature);
            DefineMethod(classSlot, classCompiler.isStaticMethod, methodSymbol);

            if (signature.Type == SignatureType.SIG_INITIALIZER)
            {
                signature.Type = SignatureType.SIG_METHOD;
                int constructorSymbol = SignatureSymbol(signature);
                CreateConstructor(signature, methodSymbol);
                DefineMethod(classSlot, true, constructorSymbol);

                hasConstructor = true;
            }

            return true;
        }