/// <summary> /// Declares a method (a function in a class). /// Possible todo: merge this with FunctionDeclaration, as they share a lot of code. /// </summary> private void MethodDeclaration(string fnType, ELoxFunctionType fnType2) { Tokens.Consume(IDENTIFIER, $"Expect {fnType} name."); if (Tokens.Previous().Lexeme == "init") { fnType2 = ELoxFunctionType.TYPE_INITIALIZER; } string fnName = Tokens.Previous().Lexeme; int fnLine = LineOfLastToken; LoxCompiler fnCompiler = new LoxCompiler(Tokens, fnType2, fnName, this, _CurrentClass); fnCompiler.FunctionBody(); fnCompiler.EndCompiler(); EmitOpcode(fnLine, OP_LOAD_FUNCTION); EmitData(fnLine, (byte)fnCompiler.Arity); // EmitConstantIndex(MakeBitStrConstant(fnName), _FixupConstants); // has fixup AddFixup(fnLine, fnCompiler); EmitData(fnLine, (byte)fnCompiler._UpvalueCount); for (int i = 0; i < fnCompiler._UpvalueCount; i++) { EmitData(fnLine, (byte)(fnCompiler._UpvalueData[i].IsLocal ? 1 : 0)); EmitData(fnLine, (byte)(fnCompiler._UpvalueData[i].Index)); } EmitOpcode(fnLine, OP_METHOD); EmitConstantIndex(fnLine, MakeBitStrConstant(fnName), _FixupConstants); // has fixup }
private LoxCompiler(TokenList tokens, ELoxFunctionType type, string name, LoxCompiler enclosing, LoxCompilerClass enclosingClass) : base(tokens, name) { _FunctionType = type; Arity = 0; _Chunk = new GearsChunk(name); _Rules = new List <Rule>(); _EnclosingCompiler = enclosing; _CurrentClass = enclosingClass; // stack slot zero is used for 'this' reference in methods, and is empty for script/functions: if (type != ELoxFunctionType.TYPE_FUNCTION) { _LocalVarData[_LocalCount++] = new LoxCompilerLocal("this", 0); } else { _LocalVarData[_LocalCount++] = new LoxCompilerLocal(string.Empty, 0); } }
/// <summary> /// Attempts to compile the passed source, tokenizing first. /// If compilation is successful, compiler.Chunk will be set. /// If compilation fails, status will be the error message. /// </summary> public static bool TryCompileFromSource(string path, string source, out GearsChunk chunk, out string status) { try { TokenList tokens = new LoxTokenizer(path, source).ScanTokens(); LoxCompiler compiler = new LoxCompiler(tokens, ELoxFunctionType.TYPE_SCRIPT, path, null, null); if (compiler.Compile()) { chunk = compiler._Chunk; status = null; return(true); } status = $"LoxCompiler: uncaught error while compiling {path}."; chunk = null; return(false); } catch (CompilerException e) { chunk = null; status = $"LoxCompiler at {e.TargetSite.DeclaringType.Name}.{e.TargetSite.Name} {path} {e}"; return(false); } }
/// <summary> /// function → IDENTIFIER "(" parameters? ")" block ; /// parameters → IDENTIFIER( "," IDENTIFIER )* ; /// </summary> private void FunctionDeclaration(string fnType, ELoxFunctionType fnType2) { int global = ParseVariable($"Expect {fnType} name."); MarkInitialized(); string fnName = Tokens.Previous().Lexeme; int fnLine = LineOfLastToken; LoxCompiler fnCompiler = new LoxCompiler(Tokens, fnType2, fnName, this, _CurrentClass); fnCompiler.FunctionBody(); fnCompiler.EndCompiler(); EmitOpcode(fnLine, OP_LOAD_FUNCTION); EmitData(fnLine, (byte)fnCompiler.Arity); // EmitConstantIndex(MakeBitStrConstant(fnName), _FixupConstants); // has fixup AddFixup(fnLine, fnCompiler); // EmitOpcode(OP_CLOSURE); EmitData(fnLine, (byte)fnCompiler._UpvalueCount); for (int i = 0; i < fnCompiler._UpvalueCount; i++) { EmitData(fnLine, (byte)(fnCompiler._UpvalueData[i].IsLocal ? 1 : 0)); EmitData(fnLine, (byte)(fnCompiler._UpvalueData[i].Index)); } DefineVariable(fnLine, global); }
private void AddFixup(int line, LoxCompiler fn) { _FixupFns.Add(fn); fn._OriginAddress = _Chunk.SizeCode; EmitData(line, 0, 0); // fn jump - to fix up }