lox-d/src/clox/parser.d
2025-06-07 08:05:31 +00:00

166 lines
3.9 KiB
D

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;
}
}