mirror of
https://github.com/wren-lang/wren.git
synced 2026-01-11 22:28:45 +01:00
Reorganize compiler code a bit.
This commit is contained in:
302
src/compiler.c
302
src/compiler.c
@ -97,47 +97,13 @@ typedef struct sCompiler
|
||||
SymbolTable locals;
|
||||
} Compiler;
|
||||
|
||||
static void initCompiler(Compiler* compiler, Parser* parser, Compiler* parent);
|
||||
|
||||
static ObjBlock* compileBlock(Parser* parser, Compiler* parent,
|
||||
TokenType endToken);
|
||||
|
||||
static int addConstant(Compiler* compiler, Value constant);
|
||||
|
||||
// Grammar:
|
||||
static void statement(Compiler* compiler);
|
||||
static void expression(Compiler* compiler);
|
||||
static void call(Compiler* compiler);
|
||||
static void primary(Compiler* compiler);
|
||||
static void number(Compiler* compiler);
|
||||
static void string(Compiler* compiler);
|
||||
static TokenType peek(Compiler* compiler);
|
||||
static int match(Compiler* compiler, TokenType expected);
|
||||
static void consume(Compiler* compiler, TokenType expected);
|
||||
static void advance(Parser* parser);
|
||||
|
||||
// Tokens:
|
||||
|
||||
// Lex the next token in the source file and store it in parser.current. Omits
|
||||
// newlines that aren't meaningful.
|
||||
static void readNextToken(Parser* parser);
|
||||
|
||||
// Lex the next token and store it in parser.current. Does not do any newline
|
||||
// filtering.
|
||||
static void readRawToken(Parser* parser);
|
||||
|
||||
static void readName(Parser* parser);
|
||||
static void readNumber(Parser* parser);
|
||||
static void readString(Parser* parser);
|
||||
static void skipLineComment(Parser* parser);
|
||||
static void skipWhitespace(Parser* parser);
|
||||
static int isKeyword(Parser* parser, const char* keyword);
|
||||
static int isName(char c);
|
||||
static int isDigit(char c);
|
||||
static char advanceChar(Parser* parser);
|
||||
static char peekChar(Parser* parser);
|
||||
static void makeToken(Parser* parser, TokenType type);
|
||||
|
||||
// Utility:
|
||||
static void initCompiler(Compiler* compiler, Parser* parser, Compiler* parent);
|
||||
|
||||
// Parses a name token and defines a variable in the current scope with that
|
||||
// name. Returns its symbol.
|
||||
static int defineName(Compiler* compiler);
|
||||
@ -148,9 +114,70 @@ static void storeVariable(Compiler* compiler, int symbol);
|
||||
// Adds the previous token's text to the symbol table and returns its index.
|
||||
static int internSymbol(Compiler* compiler);
|
||||
|
||||
// Emits one bytecode instruction or argument.
|
||||
static void emit(Compiler* compiler, Code code);
|
||||
|
||||
// Outputs a compile or syntax error.
|
||||
static void error(Compiler* compiler, const char* format, ...);
|
||||
|
||||
// Parsing
|
||||
// -------
|
||||
|
||||
static void statement(Compiler* compiler);
|
||||
static void expression(Compiler* compiler);
|
||||
static void call(Compiler* compiler);
|
||||
static void primary(Compiler* compiler);
|
||||
static void number(Compiler* compiler);
|
||||
static void string(Compiler* compiler);
|
||||
static TokenType peek(Compiler* compiler);
|
||||
static int match(Compiler* compiler, TokenType expected);
|
||||
static void consume(Compiler* compiler, TokenType expected);
|
||||
|
||||
// Lexing
|
||||
// ------
|
||||
|
||||
// Lex the next token in the source file and store it in parser.current. Omits
|
||||
// newlines that aren't meaningful.
|
||||
static void nextToken(Parser* parser);
|
||||
|
||||
// Lex the next token and store it in parser.current. Does not do any newline
|
||||
// filtering.
|
||||
static void readRawToken(Parser* parser);
|
||||
|
||||
// Finishes lexing an identifier. Handles reserved words.
|
||||
static void readName(Parser* parser);
|
||||
|
||||
// Finishes lexing a number literal.
|
||||
static void readNumber(Parser* parser);
|
||||
|
||||
// Finishes lexing a string literal.
|
||||
static void readString(Parser* parser);
|
||||
|
||||
// Skips the rest of the current line.
|
||||
static void skipLineComment(Parser* parser);
|
||||
|
||||
// Skips forward until a non-whitespace character is reached.
|
||||
static void skipWhitespace(Parser* parser);
|
||||
|
||||
// Returns non-zero if the current token's text matches [keyword].
|
||||
static int isKeyword(Parser* parser, const char* keyword);
|
||||
|
||||
// Returns non-zero if [c] is a valid (non-initial) identifier character.
|
||||
static int isName(char c);
|
||||
|
||||
// Returns non-zero if [c] is a digit.
|
||||
static int isDigit(char c);
|
||||
|
||||
// Advances the parser forward one character.
|
||||
static char nextChar(Parser* parser);
|
||||
|
||||
// Returns the current character the parser is sitting on.
|
||||
static char peekChar(Parser* parser);
|
||||
|
||||
// Sets the parser's current token to the given [type] and current character
|
||||
// range.
|
||||
static void makeToken(Parser* parser, TokenType type);
|
||||
|
||||
ObjBlock* compile(VM* vm, const char* source, size_t sourceLength)
|
||||
{
|
||||
Parser parser;
|
||||
@ -172,11 +199,28 @@ ObjBlock* compile(VM* vm, const char* source, size_t sourceLength)
|
||||
parser.current.end = 0;
|
||||
|
||||
// Read the first token.
|
||||
advance(&parser);
|
||||
nextToken(&parser);
|
||||
|
||||
return compileBlock(&parser, NULL, TOKEN_EOF);
|
||||
}
|
||||
|
||||
void initCompiler(Compiler* compiler, Parser* parser,
|
||||
Compiler* parent)
|
||||
{
|
||||
compiler->parser = parser;
|
||||
compiler->parent = parent;
|
||||
compiler->numCodes = 0;
|
||||
initSymbolTable(&compiler->locals);
|
||||
|
||||
compiler->block = makeBlock();
|
||||
// TODO(bob): Hack! make variable sized.
|
||||
compiler->block->bytecode = malloc(sizeof(Code) * 1024);
|
||||
|
||||
// TODO(bob): Hack! make variable sized.
|
||||
compiler->block->constants = malloc(sizeof(Value) * 256);
|
||||
compiler->block->numConstants = 0;
|
||||
}
|
||||
|
||||
ObjBlock* compileBlock(Parser* parser, Compiler* parent, TokenType endToken)
|
||||
{
|
||||
Compiler compiler;
|
||||
@ -207,6 +251,73 @@ int addConstant(Compiler* compiler, Value constant)
|
||||
return compiler->block->numConstants - 1;
|
||||
}
|
||||
|
||||
int defineName(Compiler* compiler)
|
||||
{
|
||||
consume(compiler, TOKEN_NAME);
|
||||
|
||||
SymbolTable* symbols;
|
||||
if (compiler->parent)
|
||||
{
|
||||
// Nested block, so this is a local variable.
|
||||
symbols = &compiler->locals;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Top level global variable.
|
||||
symbols = &compiler->parser->vm->globalSymbols;
|
||||
}
|
||||
|
||||
int symbol = addSymbol(symbols,
|
||||
compiler->parser->source + compiler->parser->previous.start,
|
||||
compiler->parser->previous.end - compiler->parser->previous.start);
|
||||
|
||||
if (symbol == -1)
|
||||
{
|
||||
error(compiler, "Variable is already defined.");
|
||||
}
|
||||
|
||||
return symbol;
|
||||
}
|
||||
|
||||
void storeVariable(Compiler* compiler, int symbol)
|
||||
{
|
||||
emit(compiler, compiler->parent ? CODE_STORE_LOCAL : CODE_STORE_GLOBAL);
|
||||
emit(compiler, symbol);
|
||||
}
|
||||
|
||||
int internSymbol(Compiler* compiler)
|
||||
{
|
||||
return ensureSymbol(&compiler->parser->vm->symbols,
|
||||
compiler->parser->source + compiler->parser->previous.start,
|
||||
compiler->parser->previous.end - compiler->parser->previous.start);
|
||||
}
|
||||
|
||||
void emit(Compiler* compiler, Code code)
|
||||
{
|
||||
compiler->block->bytecode[compiler->numCodes++] = code;
|
||||
}
|
||||
|
||||
void error(Compiler* compiler, const char* format, ...)
|
||||
{
|
||||
compiler->parser->hasError = 1;
|
||||
printf("Compile error on '");
|
||||
|
||||
for (int i = compiler->parser->previous.start;
|
||||
i < compiler->parser->previous.end; i++)
|
||||
{
|
||||
putchar(compiler->parser->source[i]);
|
||||
}
|
||||
|
||||
printf("': ");
|
||||
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
vprintf(format, args);
|
||||
va_end(args);
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void statement(Compiler* compiler)
|
||||
{
|
||||
if (match(compiler, TOKEN_CLASS))
|
||||
@ -459,13 +570,13 @@ int match(Compiler* compiler, TokenType expected)
|
||||
{
|
||||
if (peek(compiler) != expected) return 0;
|
||||
|
||||
advance(compiler->parser);
|
||||
nextToken(compiler->parser);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void consume(Compiler* compiler, TokenType expected)
|
||||
{
|
||||
advance(compiler->parser);
|
||||
nextToken(compiler->parser);
|
||||
if (compiler->parser->previous.type != expected)
|
||||
{
|
||||
// TODO(bob): Better error.
|
||||
@ -474,15 +585,11 @@ void consume(Compiler* compiler, TokenType expected)
|
||||
}
|
||||
}
|
||||
|
||||
void advance(Parser* parser)
|
||||
void nextToken(Parser* parser)
|
||||
{
|
||||
// TODO(bob): Check for EOF.
|
||||
parser->previous = parser->current;
|
||||
readNextToken(parser);
|
||||
}
|
||||
|
||||
void readNextToken(Parser* parser)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
readRawToken(parser);
|
||||
@ -542,7 +649,7 @@ void readRawToken(Parser* parser)
|
||||
{
|
||||
parser->tokenStart = parser->currentChar;
|
||||
|
||||
char c = advanceChar(parser);
|
||||
char c = nextChar(parser);
|
||||
switch (c)
|
||||
{
|
||||
case '(': makeToken(parser, TOKEN_LEFT_PAREN); return;
|
||||
@ -582,7 +689,7 @@ void readRawToken(Parser* parser)
|
||||
case '=':
|
||||
if (peekChar(parser) == '=')
|
||||
{
|
||||
advanceChar(parser);
|
||||
nextChar(parser);
|
||||
makeToken(parser, TOKEN_EQEQ);
|
||||
}
|
||||
else
|
||||
@ -594,7 +701,7 @@ void readRawToken(Parser* parser)
|
||||
case '<':
|
||||
if (peekChar(parser) == '=')
|
||||
{
|
||||
advanceChar(parser);
|
||||
nextChar(parser);
|
||||
makeToken(parser, TOKEN_LTEQ);
|
||||
}
|
||||
else
|
||||
@ -606,7 +713,7 @@ void readRawToken(Parser* parser)
|
||||
case '>':
|
||||
if (peekChar(parser) == '=')
|
||||
{
|
||||
advanceChar(parser);
|
||||
nextChar(parser);
|
||||
makeToken(parser, TOKEN_GTEQ);
|
||||
}
|
||||
else
|
||||
@ -618,7 +725,7 @@ void readRawToken(Parser* parser)
|
||||
case '!':
|
||||
if (peekChar(parser) == '=')
|
||||
{
|
||||
advanceChar(parser);
|
||||
nextChar(parser);
|
||||
makeToken(parser, TOKEN_BANGEQ);
|
||||
}
|
||||
else
|
||||
@ -659,7 +766,7 @@ void readName(Parser* parser)
|
||||
// TODO(bob): Handle EOF.
|
||||
while (isName(peekChar(parser)) || isDigit(peekChar(parser)))
|
||||
{
|
||||
advanceChar(parser);
|
||||
nextChar(parser);
|
||||
}
|
||||
|
||||
TokenType type = TOKEN_NAME;
|
||||
@ -682,7 +789,7 @@ int isKeyword(Parser* parser, const char* keyword)
|
||||
void readNumber(Parser* parser)
|
||||
{
|
||||
// TODO(bob): Floating point, hex, scientific, etc.
|
||||
while (isDigit(peekChar(parser))) advanceChar(parser);
|
||||
while (isDigit(peekChar(parser))) nextChar(parser);
|
||||
|
||||
makeToken(parser, TOKEN_NUMBER);
|
||||
}
|
||||
@ -690,7 +797,7 @@ void readNumber(Parser* parser)
|
||||
void readString(Parser* parser)
|
||||
{
|
||||
// TODO(bob): Escape sequences, EOL, EOF, etc.
|
||||
while (advanceChar(parser) != '"');
|
||||
while (nextChar(parser) != '"');
|
||||
|
||||
makeToken(parser, TOKEN_STRING);
|
||||
}
|
||||
@ -699,13 +806,13 @@ void skipLineComment(Parser* parser)
|
||||
{
|
||||
while (peekChar(parser) != '\n' && peekChar(parser) != '\0')
|
||||
{
|
||||
advanceChar(parser);
|
||||
nextChar(parser);
|
||||
}
|
||||
}
|
||||
|
||||
void skipWhitespace(Parser* parser)
|
||||
{
|
||||
while (peekChar(parser) == ' ') advanceChar(parser);
|
||||
while (peekChar(parser) == ' ') nextChar(parser);
|
||||
}
|
||||
|
||||
int isName(char c)
|
||||
@ -718,7 +825,7 @@ int isDigit(char c)
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
char advanceChar(Parser* parser)
|
||||
char nextChar(Parser* parser)
|
||||
{
|
||||
char c = peekChar(parser);
|
||||
parser->currentChar++;
|
||||
@ -737,86 +844,3 @@ void makeToken(Parser* parser, TokenType type)
|
||||
parser->current.end = parser->currentChar;
|
||||
}
|
||||
|
||||
void initCompiler(Compiler* compiler, Parser* parser,
|
||||
Compiler* parent)
|
||||
{
|
||||
compiler->parser = parser;
|
||||
compiler->parent = parent;
|
||||
compiler->numCodes = 0;
|
||||
initSymbolTable(&compiler->locals);
|
||||
|
||||
compiler->block = makeBlock();
|
||||
// TODO(bob): Hack! make variable sized.
|
||||
compiler->block->bytecode = malloc(sizeof(Code) * 1024);
|
||||
|
||||
// TODO(bob): Hack! make variable sized.
|
||||
compiler->block->constants = malloc(sizeof(Value) * 256);
|
||||
compiler->block->numConstants = 0;
|
||||
}
|
||||
|
||||
void emit(Compiler* compiler, Code code)
|
||||
{
|
||||
compiler->block->bytecode[compiler->numCodes++] = code;
|
||||
}
|
||||
|
||||
int defineName(Compiler* compiler)
|
||||
{
|
||||
consume(compiler, TOKEN_NAME);
|
||||
|
||||
SymbolTable* symbols;
|
||||
if (compiler->parent)
|
||||
{
|
||||
// Nested block, so this is a local variable.
|
||||
symbols = &compiler->locals;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Top level global variable.
|
||||
symbols = &compiler->parser->vm->globalSymbols;
|
||||
}
|
||||
|
||||
int symbol = addSymbol(symbols,
|
||||
compiler->parser->source + compiler->parser->previous.start,
|
||||
compiler->parser->previous.end - compiler->parser->previous.start);
|
||||
|
||||
if (symbol == -1)
|
||||
{
|
||||
error(compiler, "Variable is already defined.");
|
||||
}
|
||||
|
||||
return symbol;
|
||||
}
|
||||
|
||||
void storeVariable(Compiler* compiler, int symbol)
|
||||
{
|
||||
emit(compiler, compiler->parent ? CODE_STORE_LOCAL : CODE_STORE_GLOBAL);
|
||||
emit(compiler, symbol);
|
||||
}
|
||||
|
||||
int internSymbol(Compiler* compiler)
|
||||
{
|
||||
return ensureSymbol(&compiler->parser->vm->symbols,
|
||||
compiler->parser->source + compiler->parser->previous.start,
|
||||
compiler->parser->previous.end - compiler->parser->previous.start);
|
||||
}
|
||||
|
||||
void error(Compiler* compiler, const char* format, ...)
|
||||
{
|
||||
compiler->parser->hasError = 1;
|
||||
printf("Compile error on '");
|
||||
|
||||
for (int i = compiler->parser->previous.start;
|
||||
i < compiler->parser->previous.end; i++)
|
||||
{
|
||||
putchar(compiler->parser->source[i]);
|
||||
}
|
||||
|
||||
printf("': ");
|
||||
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
vprintf(format, args);
|
||||
va_end(args);
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user