forked from Mirror/wren
Block objects.
Also clean up newline handling in parser.
This commit is contained in:
@ -1,4 +1,6 @@
|
|||||||
class Foo {}
|
{
|
||||||
|
123
|
||||||
|
}
|
||||||
var a = 123
|
var a = 123
|
||||||
var b = 345
|
var b = 345
|
||||||
a
|
a
|
||||||
|
|||||||
403
src/compiler.c
403
src/compiler.c
@ -43,9 +43,7 @@ typedef enum
|
|||||||
TOKEN_LINE,
|
TOKEN_LINE,
|
||||||
|
|
||||||
TOKEN_ERROR,
|
TOKEN_ERROR,
|
||||||
TOKEN_EOF,
|
TOKEN_EOF
|
||||||
|
|
||||||
MAX_TOKEN
|
|
||||||
} TokenType;
|
} TokenType;
|
||||||
|
|
||||||
typedef struct Token_s
|
typedef struct Token_s
|
||||||
@ -74,76 +72,92 @@ typedef struct
|
|||||||
// The most recently consumed/advanced token.
|
// The most recently consumed/advanced token.
|
||||||
Token previous;
|
Token previous;
|
||||||
|
|
||||||
|
// Non-zero if subsequent newline tokens should be discarded.
|
||||||
|
int skipNewlines;
|
||||||
|
|
||||||
|
// Non-zero if a syntax or compile error has occurred.
|
||||||
|
int hasError;
|
||||||
|
} Parser;
|
||||||
|
|
||||||
|
typedef struct sCompiler
|
||||||
|
{
|
||||||
|
Parser* parser;
|
||||||
|
|
||||||
|
// The compiler for the block enclosing this one, or NULL if it's the
|
||||||
|
// top level.
|
||||||
|
struct sCompiler* parent;
|
||||||
|
|
||||||
// The block being compiled.
|
// The block being compiled.
|
||||||
ObjBlock* block;
|
ObjBlock* block;
|
||||||
int numCodes;
|
int numCodes;
|
||||||
|
|
||||||
// Symbol table for declared local variables in the current block.
|
// Symbol table for declared local variables in this block.
|
||||||
SymbolTable locals;
|
SymbolTable locals;
|
||||||
|
|
||||||
// Non-zero if a compile error has occurred.
|
|
||||||
int hasError;
|
|
||||||
} Compiler;
|
} Compiler;
|
||||||
|
|
||||||
|
static ObjBlock* compileBlock(Parser* parser, Compiler* parent,
|
||||||
|
TokenType endToken);
|
||||||
|
|
||||||
// Grammar:
|
// Grammar:
|
||||||
static void statement(Compiler* compiler);
|
static void statement(Compiler* compiler);
|
||||||
static void expression(Compiler* compiler);
|
static void expression(Compiler* compiler);
|
||||||
static void call(Compiler* compiler);
|
static void call(Compiler* compiler);
|
||||||
static void primary(Compiler* compiler);
|
static void primary(Compiler* compiler);
|
||||||
static void number(Compiler* compiler, Token* token);
|
static void number(Compiler* compiler);
|
||||||
static TokenType peek(Compiler* compiler);
|
static TokenType peek(Compiler* compiler);
|
||||||
static int match(Compiler* compiler, TokenType expected);
|
static int match(Compiler* compiler, TokenType expected);
|
||||||
static void consume(Compiler* compiler, TokenType expected);
|
static void consume(Compiler* compiler, TokenType expected);
|
||||||
static void advance(Compiler* compiler);
|
static void advance(Parser* parser);
|
||||||
|
|
||||||
// Tokens:
|
// Tokens:
|
||||||
static void readNextToken(Compiler* compiler);
|
static void readNextToken(Parser* parser);
|
||||||
static void readName(Compiler* compiler);
|
static void readRawToken(Parser* parser);
|
||||||
static void readNumber(Compiler* compiler);
|
static void readName(Parser* parser);
|
||||||
static void readString(Compiler* compiler);
|
static void readNumber(Parser* parser);
|
||||||
static void skipWhitespace(Compiler* compiler);
|
static void readString(Parser* parser);
|
||||||
static int isKeyword(Compiler* compiler, const char* keyword);
|
static void skipWhitespace(Parser* parser);
|
||||||
|
static int isKeyword(Parser* parser, const char* keyword);
|
||||||
static int isName(char c);
|
static int isName(char c);
|
||||||
static int isDigit(char c);
|
static int isDigit(char c);
|
||||||
static char advanceChar(Compiler* compiler);
|
static char advanceChar(Parser* parser);
|
||||||
static char peekChar(Compiler* compiler);
|
static char peekChar(Parser* parser);
|
||||||
static void makeToken(Compiler* compiler, TokenType type);
|
static void makeToken(Parser* parser, TokenType type);
|
||||||
|
|
||||||
// Utility:
|
// Utility:
|
||||||
|
static void initCompiler(Compiler* compiler, Parser* parser, Compiler* parent);
|
||||||
static void emit(Compiler* compiler, Code code);
|
static void emit(Compiler* compiler, Code code);
|
||||||
static void error(Compiler* compiler, const char* format, ...);
|
static void error(Compiler* compiler, const char* format, ...);
|
||||||
|
|
||||||
ObjBlock* compile(VM* vm, const char* source, size_t sourceLength)
|
ObjBlock* compile(VM* vm, const char* source, size_t sourceLength)
|
||||||
{
|
{
|
||||||
Compiler compiler;
|
Parser parser;
|
||||||
compiler.vm = vm;
|
parser.vm = vm;
|
||||||
compiler.source = source;
|
parser.source = source;
|
||||||
compiler.sourceLength = sourceLength;
|
parser.sourceLength = sourceLength;
|
||||||
compiler.hasError = 0;
|
parser.hasError = 0;
|
||||||
|
|
||||||
compiler.tokenStart = 0;
|
// Ignore leading newlines.
|
||||||
compiler.currentChar = 0;
|
parser.skipNewlines = 1;
|
||||||
|
|
||||||
initSymbolTable(&compiler.locals);
|
parser.tokenStart = 0;
|
||||||
|
parser.currentChar = 0;
|
||||||
|
|
||||||
// Zero-init the current token. This will get copied to previous when
|
// Zero-init the current token. This will get copied to previous when
|
||||||
// advance() is called below.
|
// advance() is called below.
|
||||||
compiler.current.type = TOKEN_EOF;
|
parser.current.type = TOKEN_EOF;
|
||||||
compiler.current.start = 0;
|
parser.current.start = 0;
|
||||||
compiler.current.end = 0;
|
parser.current.end = 0;
|
||||||
|
|
||||||
// Read the first token.
|
// Read the first token.
|
||||||
advance(&compiler);
|
advance(&parser);
|
||||||
|
|
||||||
compiler.block = makeBlock();
|
return compileBlock(&parser, NULL, TOKEN_EOF);
|
||||||
// TODO(bob): Hack! make variable sized.
|
}
|
||||||
compiler.block->bytecode = malloc(sizeof(Code) * 1024);
|
|
||||||
|
|
||||||
// TODO(bob): Hack! make variable sized.
|
ObjBlock* compileBlock(Parser* parser, Compiler* parent, TokenType endToken)
|
||||||
compiler.block->constants = malloc(sizeof(Value) * 256);
|
{
|
||||||
compiler.block->numConstants = 0;
|
Compiler compiler;
|
||||||
|
initCompiler(&compiler, parser, parent);
|
||||||
compiler.numCodes = 0;
|
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
@ -151,7 +165,7 @@ ObjBlock* compile(VM* vm, const char* source, size_t sourceLength)
|
|||||||
|
|
||||||
consume(&compiler, TOKEN_LINE);
|
consume(&compiler, TOKEN_LINE);
|
||||||
|
|
||||||
if (match(&compiler, TOKEN_EOF)) break;
|
if (match(&compiler, endToken)) break;
|
||||||
|
|
||||||
// Discard the result of the previous expression.
|
// Discard the result of the previous expression.
|
||||||
emit(&compiler, CODE_POP);
|
emit(&compiler, CODE_POP);
|
||||||
@ -161,7 +175,7 @@ ObjBlock* compile(VM* vm, const char* source, size_t sourceLength)
|
|||||||
|
|
||||||
compiler.block->numLocals = compiler.locals.count;
|
compiler.block->numLocals = compiler.locals.count;
|
||||||
|
|
||||||
return compiler.hasError ? NULL : compiler.block;
|
return parser->hasError ? NULL : compiler.block;
|
||||||
}
|
}
|
||||||
|
|
||||||
void statement(Compiler* compiler)
|
void statement(Compiler* compiler)
|
||||||
@ -172,8 +186,8 @@ void statement(Compiler* compiler)
|
|||||||
|
|
||||||
// TODO(bob): Copied from below. Unify.
|
// TODO(bob): Copied from below. Unify.
|
||||||
int local = addSymbol(&compiler->locals,
|
int local = addSymbol(&compiler->locals,
|
||||||
compiler->source + compiler->previous.start,
|
compiler->parser->source + compiler->parser->previous.start,
|
||||||
compiler->previous.end - compiler->previous.start);
|
compiler->parser->previous.end - compiler->parser->previous.start);
|
||||||
|
|
||||||
if (local == -1)
|
if (local == -1)
|
||||||
{
|
{
|
||||||
@ -190,9 +204,19 @@ void statement(Compiler* compiler)
|
|||||||
// Compile the method definitions.
|
// Compile the method definitions.
|
||||||
consume(compiler, TOKEN_LEFT_BRACE);
|
consume(compiler, TOKEN_LEFT_BRACE);
|
||||||
|
|
||||||
// TODO(bob): Definitions!
|
while (!match(compiler, TOKEN_RIGHT_BRACE))
|
||||||
|
{
|
||||||
|
// Method name.
|
||||||
|
consume(compiler, TOKEN_NAME);
|
||||||
|
//int symbol = internSymbol(compiler);
|
||||||
|
|
||||||
|
consume(compiler, TOKEN_LEFT_BRACE);
|
||||||
|
// TODO(bob): Parse body.
|
||||||
|
consume(compiler, TOKEN_RIGHT_BRACE);
|
||||||
|
|
||||||
|
consume(compiler, TOKEN_LINE);
|
||||||
|
}
|
||||||
|
|
||||||
consume(compiler, TOKEN_RIGHT_BRACE);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,8 +224,8 @@ void statement(Compiler* compiler)
|
|||||||
{
|
{
|
||||||
consume(compiler, TOKEN_NAME);
|
consume(compiler, TOKEN_NAME);
|
||||||
int local = addSymbol(&compiler->locals,
|
int local = addSymbol(&compiler->locals,
|
||||||
compiler->source + compiler->previous.start,
|
compiler->parser->source + compiler->parser->previous.start,
|
||||||
compiler->previous.end - compiler->previous.start);
|
compiler->parser->previous.end - compiler->parser->previous.start);
|
||||||
|
|
||||||
if (local == -1)
|
if (local == -1)
|
||||||
{
|
{
|
||||||
@ -240,9 +264,7 @@ void call(Compiler* compiler)
|
|||||||
if (match(compiler, TOKEN_DOT))
|
if (match(compiler, TOKEN_DOT))
|
||||||
{
|
{
|
||||||
consume(compiler, TOKEN_NAME);
|
consume(compiler, TOKEN_NAME);
|
||||||
int symbol = ensureSymbol(&compiler->vm->symbols,
|
int symbol = internSymbol(compiler);
|
||||||
compiler->source + compiler->previous.start,
|
|
||||||
compiler->previous.end - compiler->previous.start);
|
|
||||||
|
|
||||||
// Compile the method call.
|
// Compile the method call.
|
||||||
emit(compiler, CODE_CALL);
|
emit(compiler, CODE_CALL);
|
||||||
@ -252,11 +274,27 @@ void call(Compiler* compiler)
|
|||||||
|
|
||||||
void primary(Compiler* compiler)
|
void primary(Compiler* compiler)
|
||||||
{
|
{
|
||||||
|
// Block.
|
||||||
|
if (match(compiler, TOKEN_LEFT_BRACE))
|
||||||
|
{
|
||||||
|
ObjBlock* block = compileBlock(
|
||||||
|
compiler->parser, compiler, TOKEN_RIGHT_BRACE);
|
||||||
|
|
||||||
|
// Add the block to the constant table.
|
||||||
|
compiler->block->constants[compiler->block->numConstants++] = (Value)block;
|
||||||
|
|
||||||
|
// Compile the code to load it.
|
||||||
|
emit(compiler, CODE_CONSTANT);
|
||||||
|
emit(compiler, compiler->block->numConstants - 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Variable name.
|
||||||
if (match(compiler, TOKEN_NAME))
|
if (match(compiler, TOKEN_NAME))
|
||||||
{
|
{
|
||||||
int local = findSymbol(&compiler->locals,
|
int local = findSymbol(&compiler->locals,
|
||||||
compiler->source + compiler->previous.start,
|
compiler->parser->source + compiler->parser->previous.start,
|
||||||
compiler->previous.end - compiler->previous.start);
|
compiler->parser->previous.end - compiler->parser->previous.start);
|
||||||
if (local == -1)
|
if (local == -1)
|
||||||
{
|
{
|
||||||
// TODO(bob): Look for globals or names in outer scopes.
|
// TODO(bob): Look for globals or names in outer scopes.
|
||||||
@ -268,20 +306,22 @@ void primary(Compiler* compiler)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Number.
|
||||||
if (match(compiler, TOKEN_NUMBER))
|
if (match(compiler, TOKEN_NUMBER))
|
||||||
{
|
{
|
||||||
number(compiler, &compiler->previous);
|
number(compiler);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void number(Compiler* compiler, Token* token)
|
void number(Compiler* compiler)
|
||||||
{
|
{
|
||||||
|
Token* token = &compiler->parser->previous;
|
||||||
char* end;
|
char* end;
|
||||||
// TODO(bob): Parse actual double!
|
// TODO(bob): Parse actual double!
|
||||||
long value = strtol(compiler->source + token->start, &end, 10);
|
long value = strtol(compiler->parser->source + token->start, &end, 10);
|
||||||
// TODO(bob): Check errno == ERANGE here.
|
// TODO(bob): Check errno == ERANGE here.
|
||||||
if (end == compiler->source + token->start)
|
if (end == compiler->parser->source + token->start)
|
||||||
{
|
{
|
||||||
error(compiler, "Invalid number literal.");
|
error(compiler, "Invalid number literal.");
|
||||||
value = 0;
|
value = 0;
|
||||||
@ -300,7 +340,7 @@ void number(Compiler* compiler, Token* token)
|
|||||||
|
|
||||||
TokenType peek(Compiler* compiler)
|
TokenType peek(Compiler* compiler)
|
||||||
{
|
{
|
||||||
return compiler->current.type;
|
return compiler->parser->current.type;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(bob): Make a bool type?
|
// TODO(bob): Make a bool type?
|
||||||
@ -308,178 +348,237 @@ int match(Compiler* compiler, TokenType expected)
|
|||||||
{
|
{
|
||||||
if (peek(compiler) != expected) return 0;
|
if (peek(compiler) != expected) return 0;
|
||||||
|
|
||||||
advance(compiler);
|
advance(compiler->parser);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void consume(Compiler* compiler, TokenType expected)
|
void consume(Compiler* compiler, TokenType expected)
|
||||||
{
|
{
|
||||||
advance(compiler);
|
advance(compiler->parser);
|
||||||
if (compiler->previous.type != expected)
|
if (compiler->parser->previous.type != expected)
|
||||||
{
|
{
|
||||||
// TODO(bob): Better error.
|
// TODO(bob): Better error.
|
||||||
error(compiler, "Expected %d, got %d.\n", expected, compiler->previous.type);
|
error(compiler, "Expected %d, got %d.\n", expected,
|
||||||
|
compiler->parser->previous.type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void advance(Compiler* compiler)
|
void advance(Parser* parser)
|
||||||
{
|
{
|
||||||
// TODO(bob): Check for EOF.
|
// TODO(bob): Check for EOF.
|
||||||
compiler->previous = compiler->current;
|
parser->previous = parser->current;
|
||||||
readNextToken(compiler);
|
readNextToken(parser);
|
||||||
}
|
}
|
||||||
|
|
||||||
void readNextToken(Compiler* compiler)
|
void readNextToken(Parser* parser)
|
||||||
{
|
{
|
||||||
while (peekChar(compiler) != '\0')
|
for (;;)
|
||||||
{
|
{
|
||||||
compiler->tokenStart = compiler->currentChar;
|
readRawToken(parser);
|
||||||
|
switch (parser->current.type)
|
||||||
|
{
|
||||||
|
case TOKEN_LINE:
|
||||||
|
if (!parser->skipNewlines)
|
||||||
|
{
|
||||||
|
// Collapse multiple newlines into one.
|
||||||
|
parser->skipNewlines = 1;
|
||||||
|
|
||||||
char c = advanceChar(compiler);
|
// Emit this newline.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Discard newlines after tokens that cannot end an expression.
|
||||||
|
case TOKEN_LEFT_PAREN:
|
||||||
|
case TOKEN_LEFT_BRACKET:
|
||||||
|
case TOKEN_LEFT_BRACE:
|
||||||
|
case TOKEN_DOT:
|
||||||
|
case TOKEN_COMMA:
|
||||||
|
case TOKEN_STAR:
|
||||||
|
case TOKEN_SLASH:
|
||||||
|
case TOKEN_PERCENT:
|
||||||
|
case TOKEN_PLUS:
|
||||||
|
case TOKEN_MINUS:
|
||||||
|
case TOKEN_PIPE:
|
||||||
|
case TOKEN_AMP:
|
||||||
|
case TOKEN_BANG:
|
||||||
|
case TOKEN_EQ:
|
||||||
|
case TOKEN_LT:
|
||||||
|
case TOKEN_GT:
|
||||||
|
case TOKEN_LTEQ:
|
||||||
|
case TOKEN_GTEQ:
|
||||||
|
case TOKEN_EQEQ:
|
||||||
|
case TOKEN_BANGEQ:
|
||||||
|
case TOKEN_CLASS:
|
||||||
|
case TOKEN_META:
|
||||||
|
case TOKEN_VAR:
|
||||||
|
parser->skipNewlines = 1;
|
||||||
|
|
||||||
|
// Emit this token.
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Newlines are meaningful after other tokens.
|
||||||
|
default:
|
||||||
|
parser->skipNewlines = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void readRawToken(Parser* parser)
|
||||||
|
{
|
||||||
|
while (peekChar(parser) != '\0')
|
||||||
|
{
|
||||||
|
parser->tokenStart = parser->currentChar;
|
||||||
|
|
||||||
|
char c = advanceChar(parser);
|
||||||
switch (c)
|
switch (c)
|
||||||
{
|
{
|
||||||
case '(': makeToken(compiler, TOKEN_LEFT_PAREN); return;
|
case '(': makeToken(parser, TOKEN_LEFT_PAREN); return;
|
||||||
case ')': makeToken(compiler, TOKEN_RIGHT_PAREN); return;
|
case ')': makeToken(parser, TOKEN_RIGHT_PAREN); return;
|
||||||
case '[': makeToken(compiler, TOKEN_LEFT_BRACKET); return;
|
case '[': makeToken(parser, TOKEN_LEFT_BRACKET); return;
|
||||||
case ']': makeToken(compiler, TOKEN_RIGHT_BRACKET); return;
|
case ']': makeToken(parser, TOKEN_RIGHT_BRACKET); return;
|
||||||
case '{': makeToken(compiler, TOKEN_LEFT_BRACE); return;
|
case '{': makeToken(parser, TOKEN_LEFT_BRACE); return;
|
||||||
case '}': makeToken(compiler, TOKEN_RIGHT_BRACE); return;
|
case '}': makeToken(parser, TOKEN_RIGHT_BRACE); return;
|
||||||
case ':': makeToken(compiler, TOKEN_COLON); return;
|
case ':': makeToken(parser, TOKEN_COLON); return;
|
||||||
case '.': makeToken(compiler, TOKEN_DOT); return;
|
case '.': makeToken(parser, TOKEN_DOT); return;
|
||||||
case ',': makeToken(compiler, TOKEN_COMMA); return;
|
case ',': makeToken(parser, TOKEN_COMMA); return;
|
||||||
case '*': makeToken(compiler, TOKEN_STAR); return;
|
case '*': makeToken(parser, TOKEN_STAR); return;
|
||||||
case '/': makeToken(compiler, TOKEN_SLASH); return;
|
case '/': makeToken(parser, TOKEN_SLASH); return;
|
||||||
case '%': makeToken(compiler, TOKEN_PERCENT); return;
|
case '%': makeToken(parser, TOKEN_PERCENT); return;
|
||||||
case '+': makeToken(compiler, TOKEN_PLUS); return;
|
case '+': makeToken(parser, TOKEN_PLUS); return;
|
||||||
case '-':
|
case '-':
|
||||||
if (isDigit(peekChar(compiler)))
|
if (isDigit(peekChar(parser)))
|
||||||
{
|
{
|
||||||
readNumber(compiler);
|
readNumber(parser);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
makeToken(compiler, TOKEN_MINUS);
|
makeToken(parser, TOKEN_MINUS);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case '|': makeToken(compiler, TOKEN_PIPE); return;
|
case '|': makeToken(parser, TOKEN_PIPE); return;
|
||||||
case '&': makeToken(compiler, TOKEN_AMP); return;
|
case '&': makeToken(parser, TOKEN_AMP); return;
|
||||||
case '=':
|
case '=':
|
||||||
if (peekChar(compiler) == '=')
|
if (peekChar(parser) == '=')
|
||||||
{
|
{
|
||||||
advanceChar(compiler);
|
advanceChar(parser);
|
||||||
makeToken(compiler, TOKEN_EQEQ);
|
makeToken(parser, TOKEN_EQEQ);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
makeToken(compiler, TOKEN_EQ);
|
makeToken(parser, TOKEN_EQ);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case '<':
|
case '<':
|
||||||
if (peekChar(compiler) == '=')
|
if (peekChar(parser) == '=')
|
||||||
{
|
{
|
||||||
advanceChar(compiler);
|
advanceChar(parser);
|
||||||
makeToken(compiler, TOKEN_LTEQ);
|
makeToken(parser, TOKEN_LTEQ);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
makeToken(compiler, TOKEN_LT);
|
makeToken(parser, TOKEN_LT);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case '>':
|
case '>':
|
||||||
if (peekChar(compiler) == '=')
|
if (peekChar(parser) == '=')
|
||||||
{
|
{
|
||||||
advanceChar(compiler);
|
advanceChar(parser);
|
||||||
makeToken(compiler, TOKEN_GTEQ);
|
makeToken(parser, TOKEN_GTEQ);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
makeToken(compiler, TOKEN_GT);
|
makeToken(parser, TOKEN_GT);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case '!':
|
case '!':
|
||||||
if (peekChar(compiler) == '=')
|
if (peekChar(parser) == '=')
|
||||||
{
|
{
|
||||||
advanceChar(compiler);
|
advanceChar(parser);
|
||||||
makeToken(compiler, TOKEN_BANGEQ);
|
makeToken(parser, TOKEN_BANGEQ);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
makeToken(compiler, TOKEN_BANG);
|
makeToken(parser, TOKEN_BANG);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case '\n': makeToken(compiler, TOKEN_LINE); return;
|
case '\n': makeToken(parser, TOKEN_LINE); return;
|
||||||
|
|
||||||
case ' ': skipWhitespace(compiler); break;
|
case ' ': skipWhitespace(parser); break;
|
||||||
case '"': readString(compiler); return;
|
case '"': readString(parser); return;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (isName(c))
|
if (isName(c))
|
||||||
{
|
{
|
||||||
readName(compiler);
|
readName(parser);
|
||||||
}
|
}
|
||||||
else if (isDigit(c))
|
else if (isDigit(c))
|
||||||
{
|
{
|
||||||
readNumber(compiler);
|
readNumber(parser);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
makeToken(compiler, TOKEN_ERROR);
|
makeToken(parser, TOKEN_ERROR);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we get here, we're out of source, so just make EOF tokens.
|
// If we get here, we're out of source, so just make EOF tokens.
|
||||||
compiler->tokenStart = compiler->currentChar;
|
parser->tokenStart = parser->currentChar;
|
||||||
makeToken(compiler, TOKEN_EOF);
|
makeToken(parser, TOKEN_EOF);
|
||||||
}
|
}
|
||||||
|
|
||||||
void readName(Compiler* compiler)
|
void readName(Parser* parser)
|
||||||
{
|
{
|
||||||
// TODO(bob): Handle digits and EOF.
|
// TODO(bob): Handle EOF.
|
||||||
while (isName(peekChar(compiler)) || isDigit(peekChar(compiler))) advanceChar(compiler);
|
while (isName(peekChar(parser)) || isDigit(peekChar(parser)))
|
||||||
|
{
|
||||||
|
advanceChar(parser);
|
||||||
|
}
|
||||||
|
|
||||||
TokenType type = TOKEN_NAME;
|
TokenType type = TOKEN_NAME;
|
||||||
|
|
||||||
if (isKeyword(compiler, "class")) type = TOKEN_CLASS;
|
if (isKeyword(parser, "class")) type = TOKEN_CLASS;
|
||||||
if (isKeyword(compiler, "meta")) type = TOKEN_META;
|
if (isKeyword(parser, "meta")) type = TOKEN_META;
|
||||||
if (isKeyword(compiler, "var")) type = TOKEN_VAR;
|
if (isKeyword(parser, "var")) type = TOKEN_VAR;
|
||||||
|
|
||||||
makeToken(compiler, type);
|
makeToken(parser, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
int isKeyword(Compiler* compiler, const char* keyword)
|
int isKeyword(Parser* parser, const char* keyword)
|
||||||
{
|
{
|
||||||
size_t length = compiler->currentChar - compiler->tokenStart;
|
size_t length = parser->currentChar - parser->tokenStart;
|
||||||
size_t keywordLength = strlen(keyword);
|
size_t keywordLength = strlen(keyword);
|
||||||
return length == keywordLength &&
|
return length == keywordLength &&
|
||||||
strncmp(compiler->source + compiler->tokenStart, keyword, length) == 0;
|
strncmp(parser->source + parser->tokenStart, keyword, length) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void readNumber(Compiler* compiler)
|
void readNumber(Parser* parser)
|
||||||
{
|
{
|
||||||
// TODO(bob): Floating point, hex, scientific, etc.
|
// TODO(bob): Floating point, hex, scientific, etc.
|
||||||
while (isDigit(peekChar(compiler))) advanceChar(compiler);
|
while (isDigit(peekChar(parser))) advanceChar(parser);
|
||||||
|
|
||||||
makeToken(compiler, TOKEN_NUMBER);
|
makeToken(parser, TOKEN_NUMBER);
|
||||||
}
|
}
|
||||||
|
|
||||||
void readString(Compiler* compiler)
|
void readString(Parser* parser)
|
||||||
{
|
{
|
||||||
// TODO(bob): Escape sequences, EOL, EOF, etc.
|
// TODO(bob): Escape sequences, EOL, EOF, etc.
|
||||||
while (advanceChar(compiler) != '"');
|
while (advanceChar(parser) != '"');
|
||||||
|
|
||||||
makeToken(compiler, TOKEN_STRING);
|
makeToken(parser, TOKEN_STRING);
|
||||||
}
|
}
|
||||||
|
|
||||||
void skipWhitespace(Compiler* compiler)
|
void skipWhitespace(Parser* parser)
|
||||||
{
|
{
|
||||||
while (peekChar(compiler) == ' ') advanceChar(compiler);
|
while (peekChar(parser) == ' ') advanceChar(parser);
|
||||||
}
|
}
|
||||||
|
|
||||||
int isName(char c)
|
int isName(char c)
|
||||||
@ -492,23 +591,40 @@ int isDigit(char c)
|
|||||||
return c >= '0' && c <= '9';
|
return c >= '0' && c <= '9';
|
||||||
}
|
}
|
||||||
|
|
||||||
char advanceChar(Compiler* compiler)
|
char advanceChar(Parser* parser)
|
||||||
{
|
{
|
||||||
char c = peekChar(compiler);
|
char c = peekChar(parser);
|
||||||
compiler->currentChar++;
|
parser->currentChar++;
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
char peekChar(Compiler* compiler)
|
char peekChar(Parser* parser)
|
||||||
{
|
{
|
||||||
return compiler->source[compiler->currentChar];
|
return parser->source[parser->currentChar];
|
||||||
}
|
}
|
||||||
|
|
||||||
void makeToken(Compiler* compiler, TokenType type)
|
void makeToken(Parser* parser, TokenType type)
|
||||||
{
|
{
|
||||||
compiler->current.type = type;
|
parser->current.type = type;
|
||||||
compiler->current.start = compiler->tokenStart;
|
parser->current.start = parser->tokenStart;
|
||||||
compiler->current.end = compiler->currentChar;
|
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)
|
void emit(Compiler* compiler, Code code)
|
||||||
@ -516,14 +632,23 @@ void emit(Compiler* compiler, Code code)
|
|||||||
compiler->block->bytecode[compiler->numCodes++] = code;
|
compiler->block->bytecode[compiler->numCodes++] = code;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Adds the previous token's text to the symbol table and returns its index.
|
||||||
|
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, ...)
|
void error(Compiler* compiler, const char* format, ...)
|
||||||
{
|
{
|
||||||
compiler->hasError = 1;
|
compiler->parser->hasError = 1;
|
||||||
printf("Compile error on '");
|
printf("Compile error on '");
|
||||||
|
|
||||||
for (int i = compiler->previous.start; i < compiler->previous.end; i++)
|
for (int i = compiler->parser->previous.start;
|
||||||
|
i < compiler->parser->previous.end; i++)
|
||||||
{
|
{
|
||||||
putchar(compiler->source[i]);
|
putchar(compiler->parser->source[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("': ");
|
printf("': ");
|
||||||
|
|||||||
20
src/vm.c
20
src/vm.c
@ -26,7 +26,7 @@ typedef struct
|
|||||||
int numFrames;
|
int numFrames;
|
||||||
} Fiber;
|
} Fiber;
|
||||||
|
|
||||||
static void callBlock(Fiber* fiber, ObjBlock* block, int locals);
|
static void callBlock(Fiber* fiber, ObjBlock* block, int firstLocal);
|
||||||
static void push(Fiber* fiber, Value value);
|
static void push(Fiber* fiber, Value value);
|
||||||
static Value pop(Fiber* fiber);
|
static Value pop(Fiber* fiber);
|
||||||
static Value primitiveNumAbs(Value number);
|
static Value primitiveNumAbs(Value number);
|
||||||
@ -67,7 +67,7 @@ ObjClass* makeClass()
|
|||||||
ObjBlock* makeBlock()
|
ObjBlock* makeBlock()
|
||||||
{
|
{
|
||||||
ObjBlock* block = malloc(sizeof(ObjBlock));
|
ObjBlock* block = malloc(sizeof(ObjBlock));
|
||||||
block->obj.type = OBJ_NUM;
|
block->obj.type = OBJ_BLOCK;
|
||||||
block->obj.flags = 0;
|
block->obj.flags = 0;
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
@ -153,6 +153,9 @@ Value interpret(VM* vm, ObjBlock* block)
|
|||||||
int constant = frame->block->bytecode[frame->ip++];
|
int constant = frame->block->bytecode[frame->ip++];
|
||||||
Value value = frame->block->constants[constant];
|
Value value = frame->block->constants[constant];
|
||||||
fiber.stack[fiber.stackSize++] = value;
|
fiber.stack[fiber.stackSize++] = value;
|
||||||
|
printf("load constant ");
|
||||||
|
printValue(value);
|
||||||
|
printf(" to %d\n", fiber.stackSize - 1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,11 +254,20 @@ void printValue(Value value)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void callBlock(Fiber* fiber, ObjBlock* block, int locals)
|
void callBlock(Fiber* fiber, ObjBlock* block, int firstLocal)
|
||||||
{
|
{
|
||||||
fiber->frames[fiber->numFrames].block = block;
|
fiber->frames[fiber->numFrames].block = block;
|
||||||
fiber->frames[fiber->numFrames].ip = 0;
|
fiber->frames[fiber->numFrames].ip = 0;
|
||||||
fiber->frames[fiber->numFrames].locals = locals;
|
fiber->frames[fiber->numFrames].locals = firstLocal;
|
||||||
|
|
||||||
|
// Make empty slots for locals.
|
||||||
|
// TODO(bob): Don't push slots for params since the args are already there.
|
||||||
|
// TODO(bob): Should we push some real nil value here?
|
||||||
|
for (int i = 0; i < block->numLocals; i++)
|
||||||
|
{
|
||||||
|
push(fiber, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
fiber->numFrames++;
|
fiber->numFrames++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user