166 lines
3.9 KiB
D
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;
|
|
}
|
|
}
|
|
|