예제 #1
0
        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();
        }
예제 #2
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.LoadFieldThis : Instruction.StoreFieldThis,
                            field);
            }
            else
            {
                c.LoadThis();
                c.EmitByteArg(isLoad ? Instruction.LoadField : Instruction.StoreField, field);
            }
        }
예제 #3
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();
        }
예제 #4
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.");
            }
            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);
            }
        }
예제 #5
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.Call0, signature);
        }
예제 #6
0
        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);
        }
예제 #7
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.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);
        }