コード例 #1
0
ファイル: Compiler.cs プロジェクト: robotii/sophielang
        // A list literal.
        private static void List(Compiler c, bool allowAssignment)
        {
            // Load the List class.
            int listClassSymbol = c._parser.Module.Variables.FindIndex(v => v.Name == "List");
            //ASSERT(listClassSymbol != -1, "Should have already defined 'List' variable.");
            c.EmitShortArg(Instruction.LoadModuleVar, listClassSymbol);

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

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

                    // 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.");
        }
コード例 #2
0
ファイル: Compiler.cs プロジェクト: robotii/sophielang
        // 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.Call0);
                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.LoadModuleVar);
        }
コード例 #3
0
ファイル: Compiler.cs プロジェクト: robotii/sophielang
        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.LoadFieldThis : Instruction.StoreFieldThis,
                            field);
            }
            else
            {
                c.LoadThis();
                c.EmitByteArg(isLoad ? Instruction.LoadField : Instruction.StoreField, field);
            }
        }
コード例 #4
0
ファイル: Compiler.cs プロジェクト: robotii/sophielang
        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.Name, Length = rule.Name.Length };
            c.CallSignature(Instruction.Call0, signature);
        }
コード例 #5
0
ファイル: Compiler.cs プロジェクト: robotii/sophielang
 private static void Boolean(Compiler c, bool allowAssignment)
 {
     c.Emit(c._parser.Previous.Type == TokenType.False ? Instruction.False : Instruction.True);
 }
コード例 #6
0
ファイル: Compiler.cs プロジェクト: robotii/sophielang
        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.JumpIf);

            // 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);
        }
コード例 #7
0
ファイル: Compiler.cs プロジェクト: robotii/sophielang
        private static void string_(Compiler c, bool allowAssignment)
        {
            int constant = c.StringConstant();

            // Compile the code to load the constant.
            c.EmitConstant(constant);
        }
コード例 #8
0
ファイル: Compiler.cs プロジェクト: robotii/sophielang
        public static ObjFn Compile(SophieVM 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 },
                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();
        }
コード例 #9
0
ファイル: Compiler.cs プロジェクト: robotii/sophielang
        private static void Number(Compiler c, bool allowAssignment)
        {
            if (c._parser.Number == 0.0)
            {
                c.Emit(Instruction.Zero);
                return;
            }
            if (c._parser.Number == 1.0)
            {
                c.Emit(Instruction.One);
                return;
            }
            int constant = c.AddConstant(new Obj(c._parser.Number));

            // Compile the code to load the constant.
            c.EmitConstant(constant);
        }
コード例 #10
0
ファイル: Compiler.cs プロジェクト: robotii/sophielang
        private static void StaticField(Compiler c, bool allowAssignment)
        {
            Instruction loadInstruction = Instruction.LoadLocal;
            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);
        }
コード例 #11
0
ファイル: Compiler.cs プロジェクト: robotii/sophielang
 private static void null_(Compiler c, bool allowAssignment)
 {
     c.Emit(Instruction.Null);
 }
コード例 #12
0
ファイル: Compiler.cs プロジェクト: robotii/sophielang
        private static void new_(Compiler c, bool allowAssignment)
        {
            // Allow a dotted name after 'new'.
            c.Consume(TokenType.Name, "Expect name after 'new'.");
            Name(c, false);
            while (c.Match(TokenType.Dot))
            {
                Call(c, false);
            }

            // The angle brackets in the name are to ensure users can't call it directly.
            c.CallMethod(0, "<instantiate>");

            // Invoke the constructor on the new instance.
            c.MethodCall(Instruction.Call0, "new", 3);
        }
コード例 #13
0
ファイル: Compiler.cs プロジェクト: robotii/sophielang
        // 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);
        }
コード例 #14
0
ファイル: Compiler.cs プロジェクト: robotii/sophielang
        // Compiles a method definition inside a class body. Returns the symbol in the
        // method table for the new method.
        private int Method(ClassCompiler classCompiler, bool isConstructor, SignatureFn signatureFn)
        {
            // Build the method signature.
            Signature signature = new Signature();
            SignatureFromToken(signature);

            classCompiler.MethodName = signature.Name;
            classCompiler.MethodLength = signature.Length;

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

            // Compile the method signature.
            signatureFn(methodCompiler, signature);

            // Include the full signature in debug messages in stack traces.

            Consume(TokenType.LeftBrace, "Expect '{' to begin method body.");
            methodCompiler.FinishBody(isConstructor);

            methodCompiler.EndCompiler();

            return SignatureSymbol(signature);
        }
コード例 #15
0
ファイル: Compiler.cs プロジェクト: robotii/sophielang
        // 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.Call0, signature);
        }
コード例 #16
0
ファイル: Compiler.cs プロジェクト: robotii/sophielang
        // Compiles an (optional) argument list and then calls it.
        private void MethodCall(Instruction instruction, string name, int length)
        {
            Signature signature = new Signature { Type = SignatureType.Getter, Arity = 0, Name = name, Length = length };

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

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

            // Parse the block argument, if any.
            if (Match(TokenType.LeftBrace))
            {
                // Include the block argument in the arity.
                signature.Type = SignatureType.Method;
                signature.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);

                // TODO: Use the name of the method the block is being provided to.
                fnCompiler.EndCompiler();
            }

            // TODO: Allow Grace-style mixfix methods?
            CallSignature(instruction, signature);
        }
コード例 #17
0
ファイル: Compiler.cs プロジェクト: robotii/sophielang
        // 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);
        }
コード例 #18
0
ファイル: Compiler.cs プロジェクト: robotii/sophielang
        private static void and_(Compiler c, bool allowAssignment)
        {
            c.IgnoreNewlines();

            // Skip the right argument if the left is false.
            int jump = c.EmitJump(Instruction.And);
            c.ParsePrecedence(false, Precedence.LogicalAnd);
            c.PatchJump(jump);
        }
コード例 #19
0
ファイル: Compiler.cs プロジェクト: robotii/sophielang
        private static void super_(Compiler c, bool allowAssignment)
        {
            ClassCompiler enclosingClass = c.GetEnclosingClass();

            if (enclosingClass == null)
            {
                c.Error("Cannot use 'super' outside of a method.");
            }
            else if (enclosingClass.IsStaticMethod)
            {
                c.Error("Cannot use 'super' in a static 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.Super0);
            }
            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.Super0, enclosingClass.MethodName, enclosingClass.MethodLength);
            }
        }
コード例 #20
0
ファイル: Compiler.cs プロジェクト: robotii/sophielang
 private static void Call(Compiler c, bool allowAssignment)
 {
     c.IgnoreNewlines();
     c.Consume(TokenType.Name, "Expect method name after '.'.");
     c.NamedCall(allowAssignment, Instruction.Call0);
 }
コード例 #21
0
ファイル: Compiler.cs プロジェクト: robotii/sophielang
        private static void this_(Compiler c, bool allowAssignment)
        {
            if (c.GetEnclosingClass() == null)
            {
                c.Error("Cannot use 'this' outside of a method.");
                return;
            }

            c.LoadThis();
        }
コード例 #22
0
ファイル: Compiler.cs プロジェクト: robotii/sophielang
        // Compiles a method signature for a constructor.
        private static void ConstructorSignature(Compiler c, Signature signature)
        {
            signature.Type = SignatureType.Getter;

            // Add the parameters, if there are any.
            c.ParameterList(signature);
        }
コード例 #23
0
ファイル: Compiler.cs プロジェクト: robotii/sophielang
        // 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.Name);
        }
コード例 #24
0
ファイル: Compiler.cs プロジェクト: robotii/sophielang
 // A parenthesized expression.
 private static void Grouping(Compiler c, bool allowAssignment)
 {
     c.Expression();
     c.Consume(TokenType.RightParen, "Expect ')' after expression.");
 }
コード例 #25
0
ファイル: Compiler.cs プロジェクト: robotii/sophielang
 // 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;
 }
コード例 #26
0
ファイル: Compiler.cs プロジェクト: robotii/sophielang
        // 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.");
        }
コード例 #27
0
ファイル: Compiler.cs プロジェクト: robotii/sophielang
        // 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>();
        }
コード例 #28
0
ファイル: Compiler.cs プロジェクト: robotii/sophielang
        // A map literal.
        private static void Map(Compiler c, bool allowAssignment)
        {
            // Load the Map class.
            int mapClassSymbol = c._parser.Module.Variables.FindIndex(v => v.Name == "Map");
            c.EmitShortArg(Instruction.LoadModuleVar, mapClassSymbol);

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

            // 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();

                    // 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.");

                    // 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.");
        }
コード例 #29
0
ファイル: Compiler.cs プロジェクト: robotii/sophielang
        // 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.");
            }
        }