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(); }
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); } }
private static void this_(Compiler c, bool allowAssignment) { if (c.GetEnclosingClass() == null) { c.Error("Cannot use 'this' outside of a method."); return; } c.LoadThis(); }
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); } }
// 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); }
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); }
// 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); }