module clox.parser; import core.stdc.stdio; import std.functional : ctEval; import clox.scanner; import clox.compiler; import clox.chunk; import clox.parserules; import clox.util; import clox.object; import clox.value; struct Parser{ Compiler* compiler; Token current, previous; bool hadError, panicMode; void initialise(Compiler* compiler){ this.compiler = compiler; } void advance(){ previous = current; while(true){ current = compiler.scanner.scanToken(); if(current.type != Token.Type.Error) break; errorAtCurrent(current.lexeme.ptr); } } void consume(Token.Type type, in char* message){ if(current.type == type){ advance(); return; } errorAtCurrent(message); } bool check(Token.Type type) => current.type == type; bool match(Token.Type type){ if(!check(type)) return false; advance(); return true; } void expression(){ parsePrecedence(Precedence.Assignment); } void expressionStatement(){ expression(); consume(Token.Type.Semicolon, "Expect ';' after expression."); compiler.emitter.emit(OpCode.Pop); } void varDeclaration(){ ubyte global = parseVariable("Expect variable name."); if(match(Token.Type.Equal)) expression(); else compiler.emitter.emit(OpCode.Nil); consume(Token.Type.Semicolon, "Expect ';' after variable declaration."); defineVariable(global); } void declaration(){ if(match(Token.Type.Var)) varDeclaration(); else statement(); if(compiler.parser.panicMode) synchronize(); } void statement(){ if(match(Token.Type.Print)) printStatement(); else expressionStatement(); } void printStatement(){ expression(); consume(Token.Type.Semicolon, "Expect ';' after value."); compiler.emitter.emit(OpCode.Print); } void defineVariable(ubyte global){ compiler.emitter.emit(OpCode.DefineGlobal, global); } ubyte parseVariable(const char* errorMessage){ consume(Token.Type.Identifier, errorMessage); return identifierConstant(&previous); } ubyte identifierConstant(Token* name){ Value nameVal = Value.from(Obj.String.copy(name.lexeme)); return cast(ubyte)compiler.emitter.makeConstant(nameVal); } void namedVariable(Token name, bool canAssign){ ubyte arg = compiler.parser.identifierConstant(&name); if(canAssign && match(Token.Type.Equal)){ expression(); compiler.emitter.emit(OpCode.SetGlobal, arg); } else { compiler.emitter.emit(OpCode.GetGlobal, arg); } } void parsePrecedence(Precedence precedence){ advance(); ParseFn prefixRule = ParseRule.get(previous.type).prefix; if(prefixRule == null){ error("Expect expression."); return; } bool canAssign = precedence <= Precedence.Assignment; prefixRule(compiler, canAssign); while(precedence <= ParseRule.get(current.type).precedence){ advance(); ParseFn infixRule = ParseRule.get(previous.type).infix; infixRule(compiler); } if(canAssign && match(Token.Type.Equal)) error("Invalid assignment target."); } void synchronize(){ panicMode = false; while(current.type != Token.Type.EOF){ if(previous.type == Token.Type.Semicolon) return; switch(current.type){ case Token.Type.Class: case Token.Type.Fun: case Token.Type.Var: case Token.Type.For: case Token.Type.If: case Token.Type.While: case Token.Type.Print: case Token.Type.Return: return; default: break; } advance(); } } void errorAtCurrent(in char* message){ errorAt(current, message); } void error(in char* message){ errorAt(previous, message); } void errorAt(in ref Token token, in char* message){ if(panicMode) return; panicMode = true; fprintf(stderr, ctEval!("[line %d] " ~ colour!("Error", Colour.Red)).ptr, token.line); if (token.type == Token.Type.EOF) { fprintf(stderr, " at end"); } else if (token.type == Token.Type.Error) { // Nothing. } else { fprintf(stderr, " at '%.*s'", cast(int)token.lexeme.length, token.lexeme.ptr); } fprintf(stderr, ": %s\n", message); hadError = true; } }