forked from Mirror/wren
Start sketching out single-pass compiler.
This commit is contained in:
@ -1 +1 @@
|
||||
123(1, true, "a string".whatevs)
|
||||
2
|
||||
|
||||
373
src/compiler.c
Normal file
373
src/compiler.c
Normal file
@ -0,0 +1,373 @@
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include "compiler.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Buffer* source;
|
||||
Token* current;
|
||||
|
||||
// The block being compiled.
|
||||
Block* block;
|
||||
int numCodes;
|
||||
|
||||
// Non-zero if a compile error has occurred.
|
||||
int hasError;
|
||||
} Compiler;
|
||||
|
||||
typedef void (*CompileFn)(Compiler*, Token*);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CompileFn fn;
|
||||
int precedence;
|
||||
} InfixCompiler;
|
||||
|
||||
/*
|
||||
static void block(Compiler* compiler);
|
||||
*/
|
||||
static void statementLike(Compiler* compiler);
|
||||
static void expression(Compiler* compiler);
|
||||
static void compilePrecedence(Compiler* compiler, int precedence);
|
||||
static void prefixLiteral(Compiler* compiler, Token* token);
|
||||
static void infixCall(Compiler* compiler, Token* token);
|
||||
static void infixBinaryOp(Compiler* compiler, Token* token);
|
||||
static TokenType peek(Compiler* compiler);
|
||||
static Token* match(Compiler* compiler, TokenType expected);
|
||||
static Token* consume(Compiler* compiler, TokenType expected);
|
||||
static Token* advance(Compiler* compiler);
|
||||
static void error(Compiler* compiler, const char* format, ...);
|
||||
|
||||
enum
|
||||
{
|
||||
PREC_NONE,
|
||||
PREC_LOWEST,
|
||||
|
||||
PREC_EQUALITY, // == !=
|
||||
PREC_COMPARISON, // < > <= >=
|
||||
PREC_BITWISE, // | &
|
||||
PREC_TERM, // + -
|
||||
PREC_FACTOR, // * / %
|
||||
PREC_CALL // ()
|
||||
};
|
||||
|
||||
CompileFn prefixCompilers[] = {
|
||||
NULL, // TOKEN_LEFT_PAREN
|
||||
NULL, // TOKEN_RIGHT_PAREN
|
||||
NULL, // TOKEN_LEFT_BRACKET
|
||||
NULL, // TOKEN_RIGHT_BRACKET
|
||||
NULL, // TOKEN_LEFT_BRACE
|
||||
NULL, // TOKEN_RIGHT_BRACE
|
||||
NULL, // TOKEN_COLON
|
||||
NULL, // TOKEN_DOT
|
||||
NULL, // TOKEN_COMMA
|
||||
NULL, // TOKEN_STAR
|
||||
NULL, // TOKEN_SLASH
|
||||
NULL, // TOKEN_PERCENT
|
||||
NULL, // TOKEN_PLUS
|
||||
NULL, // TOKEN_MINUS
|
||||
NULL, // TOKEN_PIPE
|
||||
NULL, // TOKEN_AMP
|
||||
NULL, // TOKEN_BANG
|
||||
NULL, // TOKEN_EQ
|
||||
NULL, // TOKEN_LT
|
||||
NULL, // TOKEN_GT
|
||||
NULL, // TOKEN_LTEQ
|
||||
NULL, // TOKEN_GTEQ
|
||||
NULL, // TOKEN_EQEQ
|
||||
NULL, // TOKEN_BANGEQ
|
||||
NULL, // TOKEN_ELSE
|
||||
NULL, // TOKEN_IF
|
||||
NULL, // TOKEN_VAR
|
||||
NULL, // TOKEN_EMBEDDED
|
||||
prefixLiteral, // TOKEN_NAME
|
||||
prefixLiteral, // TOKEN_NUMBER
|
||||
prefixLiteral, // TOKEN_STRING
|
||||
NULL, // TOKEN_LINE
|
||||
NULL, // TOKEN_WHITESPACE
|
||||
NULL, // TOKEN_INDENT
|
||||
NULL, // TOKEN_OUTDENT
|
||||
NULL, // TOKEN_ERROR
|
||||
NULL // TOKEN_EOF
|
||||
};
|
||||
|
||||
// The indices in this array correspond to TOKEN enum values.
|
||||
InfixCompiler infixCompilers[] = {
|
||||
{ infixCall, PREC_CALL }, // TOKEN_LEFT_PAREN
|
||||
{ NULL, PREC_NONE }, // TOKEN_RIGHT_PAREN
|
||||
{ NULL, PREC_NONE }, // TOKEN_LEFT_BRACKET
|
||||
{ NULL, PREC_NONE }, // TOKEN_RIGHT_BRACKET
|
||||
{ NULL, PREC_NONE }, // TOKEN_LEFT_BRACE
|
||||
{ NULL, PREC_NONE }, // TOKEN_RIGHT_BRACE
|
||||
{ NULL, PREC_NONE }, // TOKEN_COLON
|
||||
{ NULL, PREC_NONE }, // TOKEN_DOT
|
||||
{ NULL, PREC_NONE }, // TOKEN_COMMA
|
||||
{ infixBinaryOp, PREC_FACTOR }, // TOKEN_STAR
|
||||
{ infixBinaryOp, PREC_FACTOR }, // TOKEN_SLASH
|
||||
{ infixBinaryOp, PREC_FACTOR }, // TOKEN_PERCENT
|
||||
{ infixBinaryOp, PREC_TERM }, // TOKEN_PLUS
|
||||
{ infixBinaryOp, PREC_TERM }, // TOKEN_MINUS
|
||||
{ infixBinaryOp, PREC_BITWISE }, // TOKEN_PIPE
|
||||
{ infixBinaryOp, PREC_BITWISE }, // TOKEN_AMP
|
||||
{ NULL, PREC_NONE }, // TOKEN_BANG
|
||||
{ NULL, PREC_NONE }, // TOKEN_EQ
|
||||
{ infixBinaryOp, PREC_COMPARISON }, // TOKEN_LT
|
||||
{ infixBinaryOp, PREC_COMPARISON }, // TOKEN_GT
|
||||
{ infixBinaryOp, PREC_COMPARISON }, // TOKEN_LTEQ
|
||||
{ infixBinaryOp, PREC_COMPARISON }, // TOKEN_GTEQ
|
||||
{ infixBinaryOp, PREC_EQUALITY }, // TOKEN_EQEQ
|
||||
{ infixBinaryOp, PREC_EQUALITY }, // TOKEN_BANGEQ
|
||||
{ NULL, PREC_NONE }, // TOKEN_ELSE
|
||||
{ NULL, PREC_NONE }, // TOKEN_IF
|
||||
{ NULL, PREC_NONE }, // TOKEN_VAR
|
||||
{ NULL, PREC_NONE }, // TOKEN_EMBEDDED
|
||||
{ NULL, PREC_NONE }, // TOKEN_NAME
|
||||
{ NULL, PREC_NONE }, // TOKEN_NUMBER
|
||||
{ NULL, PREC_NONE }, // TOKEN_STRING
|
||||
{ NULL, PREC_NONE }, // TOKEN_LINE
|
||||
{ NULL, PREC_NONE }, // TOKEN_WHITESPACE
|
||||
{ NULL, PREC_NONE }, // TOKEN_INDENT
|
||||
{ NULL, PREC_NONE }, // TOKEN_OUTDENT
|
||||
{ NULL, PREC_NONE }, // TOKEN_ERROR
|
||||
{ NULL, PREC_NONE } // TOKEN_EOF
|
||||
};
|
||||
|
||||
Block* compile(Buffer* source, Token* tokens)
|
||||
{
|
||||
Compiler compiler;
|
||||
compiler.source = source;
|
||||
compiler.current = tokens;
|
||||
compiler.hasError = 0;
|
||||
|
||||
compiler.block = malloc(sizeof(Block));
|
||||
// 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;
|
||||
|
||||
compiler.numCodes = 0;
|
||||
|
||||
// TODO(bob): Copied from block(). Unify.
|
||||
do
|
||||
{
|
||||
statementLike(&compiler);
|
||||
} while (!match(&compiler, TOKEN_EOF));
|
||||
|
||||
compiler.block->bytecode[compiler.numCodes++] = CODE_END;
|
||||
|
||||
return compiler.hasError ? NULL : compiler.block;
|
||||
}
|
||||
|
||||
/*
|
||||
static void block(Compiler* compiler)
|
||||
{
|
||||
consume(compiler, TOKEN_INDENT);
|
||||
|
||||
NodeSequence* sequence = malloc(sizeof(NodeSequence));
|
||||
sequence->node.type = NODE_SEQUENCE;
|
||||
sequence->nodes = NULL;
|
||||
|
||||
NodeList** nodes = &sequence->nodes;
|
||||
do
|
||||
{
|
||||
Node* node = statementLike(compiler);
|
||||
*nodes = malloc(sizeof(NodeList));
|
||||
(*nodes)->node = node;
|
||||
(*nodes)->next = NULL;
|
||||
nodes = &(*nodes)->next;
|
||||
|
||||
} while (!match(compiler, TOKEN_OUTDENT));
|
||||
|
||||
return (Node*)sequence;
|
||||
}
|
||||
*/
|
||||
|
||||
static void statementLike(Compiler* compiler)
|
||||
{
|
||||
/*
|
||||
if (match(compiler, TOKEN_IF))
|
||||
{
|
||||
// Compile the condition.
|
||||
expression(compiler);
|
||||
|
||||
consume(compiler, TOKEN_COLON);
|
||||
|
||||
// Compile the then arm.
|
||||
block(compiler);
|
||||
|
||||
// Compile the else arm.
|
||||
if (match(compiler, TOKEN_ELSE))
|
||||
{
|
||||
consume(compiler, TOKEN_COLON);
|
||||
block(parser);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (match(compiler, TOKEN_VAR))
|
||||
{
|
||||
Token* name = consume(compiler, TOKEN_NAME);
|
||||
Node* initializer = NULL;
|
||||
if (match(compiler, TOKEN_EQ))
|
||||
{
|
||||
initializer = expression(parser);
|
||||
}
|
||||
if (peek(parser) != TOKEN_OUTDENT) consume(compiler, TOKEN_LINE);
|
||||
|
||||
NodeVar* node = malloc(sizeof(NodeVar));
|
||||
node->node.type = NODE_VAR;
|
||||
node->name = name;
|
||||
node->initializer = initializer;
|
||||
return (Node*)node;
|
||||
}
|
||||
*/
|
||||
|
||||
// Statement expression.
|
||||
expression(compiler);
|
||||
consume(compiler, TOKEN_LINE);
|
||||
}
|
||||
|
||||
static void expression(Compiler* compiler)
|
||||
{
|
||||
compilePrecedence(compiler, PREC_LOWEST);
|
||||
}
|
||||
|
||||
void compilePrecedence(Compiler* compiler, int precedence)
|
||||
{
|
||||
Token* token = advance(compiler);
|
||||
CompileFn prefix = prefixCompilers[token->type];
|
||||
|
||||
if (prefix == NULL)
|
||||
{
|
||||
// TODO(bob): Handle error better.
|
||||
error(compiler, "No prefix parser.");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
prefix(compiler, token);
|
||||
|
||||
while (precedence <= infixCompilers[compiler->current->type].precedence)
|
||||
{
|
||||
token = advance(compiler);
|
||||
CompileFn infix = infixCompilers[token->type].fn;
|
||||
infix(compiler, token);
|
||||
}
|
||||
}
|
||||
|
||||
static void prefixLiteral(Compiler* compiler, Token* token)
|
||||
{
|
||||
// TODO(bob): Get actual value from token!
|
||||
// Define a constant for the literal.
|
||||
// TODO(bob): See if constant with same value already exists.
|
||||
Value constant = malloc(sizeof(Obj));
|
||||
constant->type = OBJ_INT;
|
||||
constant->flags = 0;
|
||||
constant->value = 234;
|
||||
|
||||
compiler->block->constants[compiler->block->numConstants++] = constant;
|
||||
|
||||
// Compile the code to load the constant.
|
||||
compiler->block->bytecode[compiler->numCodes++] = CODE_CONSTANT;
|
||||
compiler->block->bytecode[compiler->numCodes++] = compiler->block->numConstants - 1;
|
||||
}
|
||||
|
||||
static void infixCall(Compiler* compiler, Token* token)
|
||||
{
|
||||
printf("infix calls not implemented\n");
|
||||
exit(1);
|
||||
/*
|
||||
NodeList* args = NULL;
|
||||
if (match(compiler, TOKEN_RIGHT_PAREN) == NULL)
|
||||
{
|
||||
NodeList** arg = &args;
|
||||
do
|
||||
{
|
||||
*arg = malloc(sizeof(NodeList));
|
||||
(*arg)->node = expression(parser);
|
||||
(*arg)->next = NULL;
|
||||
arg = &(*arg)->next;
|
||||
}
|
||||
while (match(compiler, TOKEN_COMMA) != NULL);
|
||||
|
||||
consume(compiler, TOKEN_RIGHT_PAREN);
|
||||
}
|
||||
|
||||
NodeCall* node = malloc(sizeof(NodeCall));
|
||||
node->node.type = NODE_CALL;
|
||||
node->fn = left;
|
||||
node->args = args;
|
||||
|
||||
return (Node*)node;
|
||||
*/
|
||||
}
|
||||
|
||||
static void infixBinaryOp(Compiler* compiler, Token* token)
|
||||
{
|
||||
printf("infix binary ops not implemented\n");
|
||||
exit(1);
|
||||
/*
|
||||
// TODO(bob): Support right-associative infix. Needs to do precedence
|
||||
// - 1 here to be right-assoc.
|
||||
Node* right = parsePrecedence(parser,
|
||||
infixParsers[token->type].precedence);
|
||||
|
||||
NodeBinaryOp* node = malloc(sizeof(NodeBinaryOp));
|
||||
node->node.type = NODE_BINARY_OP;
|
||||
node->left = left;
|
||||
node->op = token;
|
||||
node->right = right;
|
||||
|
||||
return (Node*)node;
|
||||
*/
|
||||
}
|
||||
|
||||
static TokenType peek(Compiler* compiler)
|
||||
{
|
||||
return compiler->current->type;
|
||||
}
|
||||
|
||||
static Token* match(Compiler* compiler, TokenType expected)
|
||||
{
|
||||
if (peek(compiler) != expected) return NULL;
|
||||
|
||||
return advance(compiler);
|
||||
}
|
||||
|
||||
static Token* consume(Compiler* compiler, TokenType expected)
|
||||
{
|
||||
Token* token = advance(compiler);
|
||||
if (token->type != expected)
|
||||
{
|
||||
// TODO(bob): Better error.
|
||||
error(compiler, "Expected %d, got %d.\n", expected, token->type);
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
static Token* advance(Compiler* compiler)
|
||||
{
|
||||
// TODO(bob): Check for EOF.
|
||||
Token* token = compiler->current;
|
||||
compiler->current = compiler->current->next;
|
||||
return unlinkToken(token);
|
||||
}
|
||||
|
||||
static void error(Compiler* compiler, const char* format, ...)
|
||||
{
|
||||
compiler->hasError = 1;
|
||||
printf("Compile error on '");
|
||||
printToken(compiler->source, compiler->current);
|
||||
|
||||
printf("': ");
|
||||
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
vprintf(format, args);
|
||||
va_end(args);
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
9
src/compiler.h
Normal file
9
src/compiler.h
Normal file
@ -0,0 +1,9 @@
|
||||
#ifndef wren_parser_h
|
||||
#define wren_parser_h
|
||||
|
||||
#include "lexer.h"
|
||||
#include "vm.h"
|
||||
|
||||
Block* compile(Buffer* source, Token* tokens);
|
||||
|
||||
#endif
|
||||
14
src/main.c
14
src/main.c
@ -1,8 +1,9 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "compiler.h"
|
||||
#include "lexer.h"
|
||||
#include "parser.h"
|
||||
#include "vm.h"
|
||||
|
||||
#define MAX_FILE_SIZE 256 * 256
|
||||
|
||||
@ -32,14 +33,13 @@ int main(int argc, const char * argv[])
|
||||
//printf("Cleaned tokens:\n");
|
||||
dumpTokens(buffer, tokens);
|
||||
|
||||
/*Node* node =*/ parse(buffer, tokens);
|
||||
|
||||
/*
|
||||
if (defns) generate(buffer, defns);
|
||||
Block* block = compile(buffer, tokens);
|
||||
Fiber* fiber = newFiber();
|
||||
Value value = interpret(fiber, block);
|
||||
printValue(value);
|
||||
|
||||
// TODO(bob): Free tokens.
|
||||
// TODO(bob): Free ast.
|
||||
*/
|
||||
|
||||
freeBuffer(buffer);
|
||||
|
||||
@ -52,8 +52,6 @@ static void dumpTokens(Buffer* buffer, Token* token)
|
||||
{
|
||||
switch (token->type)
|
||||
{
|
||||
case TOKEN_INDENT: printf("(in)"); break;
|
||||
case TOKEN_OUTDENT: printf("(out)"); break;
|
||||
case TOKEN_LINE: printf("(line)"); break;
|
||||
case TOKEN_ERROR: printf("(error)"); break;
|
||||
case TOKEN_EOF: printf("(eof)"); break;
|
||||
|
||||
349
src/parser.c
349
src/parser.c
@ -1,349 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include "parser.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Buffer* source;
|
||||
Token* current;
|
||||
|
||||
// Non-zero if a parse error has occurred.
|
||||
int hasError;
|
||||
} Parser;
|
||||
|
||||
typedef Node* (*PrefixParserFn)(Parser*, Token*);
|
||||
typedef Node* (*InfixParserFn)(Parser*, Node*, Token*);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
InfixParserFn fn;
|
||||
int precedence;
|
||||
} InfixParser;
|
||||
|
||||
static Node* block(Parser* parser);
|
||||
static Node* statementLike(Parser* parser);
|
||||
static Node* expression(Parser* parser);
|
||||
static Node* parsePrecedence(Parser* parser, int precedence);
|
||||
static Node* prefixLiteral(Parser* parser, Token* token);
|
||||
static Node* infixCall(Parser* parser, Node* left, Token* token);
|
||||
static Node* infixBinaryOp(Parser* parser, Node* left, Token* token);
|
||||
static TokenType peek(Parser* parser);
|
||||
static Token* match(Parser* parser, TokenType expected);
|
||||
static Token* consume(Parser* parser, TokenType expected);
|
||||
static Token* advance(Parser* parser);
|
||||
static void error(Parser* parser, const char* format, ...);
|
||||
|
||||
enum
|
||||
{
|
||||
PREC_NONE,
|
||||
PREC_LOWEST,
|
||||
|
||||
PREC_EQUALITY, // == !=
|
||||
PREC_COMPARISON, // < > <= >=
|
||||
PREC_BITWISE, // | &
|
||||
PREC_TERM, // + -
|
||||
PREC_FACTOR, // * / %
|
||||
PREC_CALL // ()
|
||||
};
|
||||
|
||||
PrefixParserFn prefixParsers[] = {
|
||||
NULL, // TOKEN_LEFT_PAREN
|
||||
NULL, // TOKEN_RIGHT_PAREN
|
||||
NULL, // TOKEN_LEFT_BRACKET
|
||||
NULL, // TOKEN_RIGHT_BRACKET
|
||||
NULL, // TOKEN_LEFT_BRACE
|
||||
NULL, // TOKEN_RIGHT_BRACE
|
||||
NULL, // TOKEN_COLON
|
||||
NULL, // TOKEN_DOT
|
||||
NULL, // TOKEN_COMMA
|
||||
NULL, // TOKEN_STAR
|
||||
NULL, // TOKEN_SLASH
|
||||
NULL, // TOKEN_PERCENT
|
||||
NULL, // TOKEN_PLUS
|
||||
NULL, // TOKEN_MINUS
|
||||
NULL, // TOKEN_PIPE
|
||||
NULL, // TOKEN_AMP
|
||||
NULL, // TOKEN_BANG
|
||||
NULL, // TOKEN_EQ
|
||||
NULL, // TOKEN_LT
|
||||
NULL, // TOKEN_GT
|
||||
NULL, // TOKEN_LTEQ
|
||||
NULL, // TOKEN_GTEQ
|
||||
NULL, // TOKEN_EQEQ
|
||||
NULL, // TOKEN_BANGEQ
|
||||
NULL, // TOKEN_ELSE
|
||||
NULL, // TOKEN_IF
|
||||
NULL, // TOKEN_VAR
|
||||
NULL, // TOKEN_EMBEDDED
|
||||
prefixLiteral, // TOKEN_NAME
|
||||
prefixLiteral, // TOKEN_NUMBER
|
||||
prefixLiteral, // TOKEN_STRING
|
||||
NULL, // TOKEN_LINE
|
||||
NULL, // TOKEN_WHITESPACE
|
||||
NULL, // TOKEN_INDENT
|
||||
NULL, // TOKEN_OUTDENT
|
||||
NULL, // TOKEN_ERROR
|
||||
NULL // TOKEN_EOF
|
||||
};
|
||||
|
||||
// The indices in this array correspond to TOKEN enum values.
|
||||
InfixParser infixParsers[] = {
|
||||
{ infixCall, PREC_CALL }, // TOKEN_LEFT_PAREN
|
||||
{ NULL, PREC_NONE }, // TOKEN_RIGHT_PAREN
|
||||
{ NULL, PREC_NONE }, // TOKEN_LEFT_BRACKET
|
||||
{ NULL, PREC_NONE }, // TOKEN_RIGHT_BRACKET
|
||||
{ NULL, PREC_NONE }, // TOKEN_LEFT_BRACE
|
||||
{ NULL, PREC_NONE }, // TOKEN_RIGHT_BRACE
|
||||
{ NULL, PREC_NONE }, // TOKEN_COLON
|
||||
{ NULL, PREC_NONE }, // TOKEN_DOT
|
||||
{ NULL, PREC_NONE }, // TOKEN_COMMA
|
||||
{ infixBinaryOp, PREC_FACTOR }, // TOKEN_STAR
|
||||
{ infixBinaryOp, PREC_FACTOR }, // TOKEN_SLASH
|
||||
{ infixBinaryOp, PREC_FACTOR }, // TOKEN_PERCENT
|
||||
{ infixBinaryOp, PREC_TERM }, // TOKEN_PLUS
|
||||
{ infixBinaryOp, PREC_TERM }, // TOKEN_MINUS
|
||||
{ infixBinaryOp, PREC_BITWISE }, // TOKEN_PIPE
|
||||
{ infixBinaryOp, PREC_BITWISE }, // TOKEN_AMP
|
||||
{ NULL, PREC_NONE }, // TOKEN_BANG
|
||||
{ NULL, PREC_NONE }, // TOKEN_EQ
|
||||
{ infixBinaryOp, PREC_COMPARISON }, // TOKEN_LT
|
||||
{ infixBinaryOp, PREC_COMPARISON }, // TOKEN_GT
|
||||
{ infixBinaryOp, PREC_COMPARISON }, // TOKEN_LTEQ
|
||||
{ infixBinaryOp, PREC_COMPARISON }, // TOKEN_GTEQ
|
||||
{ infixBinaryOp, PREC_EQUALITY }, // TOKEN_EQEQ
|
||||
{ infixBinaryOp, PREC_EQUALITY }, // TOKEN_BANGEQ
|
||||
{ NULL, PREC_NONE }, // TOKEN_ELSE
|
||||
{ NULL, PREC_NONE }, // TOKEN_IF
|
||||
{ NULL, PREC_NONE }, // TOKEN_VAR
|
||||
{ NULL, PREC_NONE }, // TOKEN_EMBEDDED
|
||||
{ NULL, PREC_NONE }, // TOKEN_NAME
|
||||
{ NULL, PREC_NONE }, // TOKEN_NUMBER
|
||||
{ NULL, PREC_NONE }, // TOKEN_STRING
|
||||
{ NULL, PREC_NONE }, // TOKEN_LINE
|
||||
{ NULL, PREC_NONE }, // TOKEN_WHITESPACE
|
||||
{ NULL, PREC_NONE }, // TOKEN_INDENT
|
||||
{ NULL, PREC_NONE }, // TOKEN_OUTDENT
|
||||
{ NULL, PREC_NONE }, // TOKEN_ERROR
|
||||
{ NULL, PREC_NONE } // TOKEN_EOF
|
||||
};
|
||||
|
||||
Node* parse(Buffer* source, Token* tokens)
|
||||
{
|
||||
Parser parser;
|
||||
parser.source = source;
|
||||
parser.current = tokens;
|
||||
parser.hasError = 0;
|
||||
|
||||
NodeSequence* sequence = (NodeSequence*)malloc(sizeof(NodeSequence));
|
||||
sequence->node.type = NODE_SEQUENCE;
|
||||
sequence->nodes = NULL;
|
||||
|
||||
// TODO(bob): Copied from block(). Unify.
|
||||
NodeList** nodes = &sequence->nodes;
|
||||
do
|
||||
{
|
||||
Node* node = statementLike(&parser);
|
||||
*nodes = (NodeList*)malloc(sizeof(NodeList));
|
||||
(*nodes)->node = node;
|
||||
(*nodes)->next = NULL;
|
||||
nodes = &(*nodes)->next;
|
||||
|
||||
} while (!match(&parser, TOKEN_EOF));
|
||||
|
||||
return parser.hasError ? NULL : (Node*)sequence;
|
||||
}
|
||||
|
||||
static Node* block(Parser* parser)
|
||||
{
|
||||
consume(parser, TOKEN_INDENT);
|
||||
|
||||
NodeSequence* sequence = (NodeSequence*)malloc(sizeof(NodeSequence));
|
||||
sequence->node.type = NODE_SEQUENCE;
|
||||
sequence->nodes = NULL;
|
||||
|
||||
NodeList** nodes = &sequence->nodes;
|
||||
do
|
||||
{
|
||||
Node* node = statementLike(parser);
|
||||
*nodes = (NodeList*)malloc(sizeof(NodeList));
|
||||
(*nodes)->node = node;
|
||||
(*nodes)->next = NULL;
|
||||
nodes = &(*nodes)->next;
|
||||
|
||||
} while (!match(parser, TOKEN_OUTDENT));
|
||||
|
||||
return (Node*)sequence;
|
||||
}
|
||||
|
||||
static Node* statementLike(Parser* parser)
|
||||
{
|
||||
if (match(parser, TOKEN_IF))
|
||||
{
|
||||
Node* condition = expression(parser);
|
||||
consume(parser, TOKEN_COLON);
|
||||
Node* thenArm = block(parser);
|
||||
Node* elseArm = NULL;
|
||||
if (match(parser, TOKEN_ELSE))
|
||||
{
|
||||
consume(parser, TOKEN_COLON);
|
||||
elseArm = block(parser);
|
||||
}
|
||||
|
||||
NodeIf* expr = (NodeIf*)malloc(sizeof(NodeIf));
|
||||
expr->node.type = NODE_IF;
|
||||
expr->condition = condition;
|
||||
expr->thenArm = thenArm;
|
||||
expr->elseArm = elseArm;
|
||||
return (Node*)expr;
|
||||
}
|
||||
|
||||
if (match(parser, TOKEN_VAR))
|
||||
{
|
||||
Token* name = consume(parser, TOKEN_NAME);
|
||||
Node* initializer = NULL;
|
||||
if (match(parser, TOKEN_EQ))
|
||||
{
|
||||
initializer = expression(parser);
|
||||
}
|
||||
if (peek(parser) != TOKEN_OUTDENT) consume(parser, TOKEN_LINE);
|
||||
|
||||
NodeVar* node = (NodeVar*)malloc(sizeof(NodeVar));
|
||||
node->node.type = NODE_VAR;
|
||||
node->name = name;
|
||||
node->initializer = initializer;
|
||||
return (Node*)node;
|
||||
}
|
||||
|
||||
// Statement expression.
|
||||
Node* node = expression(parser);
|
||||
if (peek(parser) != TOKEN_OUTDENT) consume(parser, TOKEN_LINE);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static Node* expression(Parser* parser)
|
||||
{
|
||||
return parsePrecedence(parser, PREC_LOWEST);
|
||||
}
|
||||
|
||||
Node* parsePrecedence(Parser* parser, int precedence)
|
||||
{
|
||||
Token* token = advance(parser);
|
||||
PrefixParserFn prefix = prefixParsers[token->type];
|
||||
|
||||
if (prefix == NULL)
|
||||
{
|
||||
// TODO(bob): Handle error better.
|
||||
error(parser, "No prefix parser.");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
Node* left = prefix(parser, token);
|
||||
|
||||
while (precedence <= infixParsers[parser->current->type].precedence)
|
||||
{
|
||||
token = advance(parser);
|
||||
InfixParserFn infix = infixParsers[token->type].fn;
|
||||
left = infix(parser, left, token);
|
||||
}
|
||||
|
||||
return left;
|
||||
}
|
||||
|
||||
static Node* prefixLiteral(Parser* parser, Token* token)
|
||||
{
|
||||
NodeLiteral* node = (NodeLiteral*)malloc(sizeof(NodeLiteral));
|
||||
node->node.type = NODE_LITERAL;
|
||||
node->token = token;
|
||||
return (Node*)node;
|
||||
}
|
||||
|
||||
static Node* infixCall(Parser* parser, Node* left, Token* token)
|
||||
{
|
||||
NodeList* args = NULL;
|
||||
if (match(parser, TOKEN_RIGHT_PAREN) == NULL)
|
||||
{
|
||||
NodeList** arg = &args;
|
||||
do
|
||||
{
|
||||
*arg = (NodeList*)malloc(sizeof(NodeList));
|
||||
(*arg)->node = expression(parser);
|
||||
(*arg)->next = NULL;
|
||||
arg = &(*arg)->next;
|
||||
}
|
||||
while (match(parser, TOKEN_COMMA) != NULL);
|
||||
|
||||
consume(parser, TOKEN_RIGHT_PAREN);
|
||||
}
|
||||
|
||||
NodeCall* node = (NodeCall*)malloc(sizeof(NodeCall));
|
||||
node->node.type = NODE_CALL;
|
||||
node->fn = left;
|
||||
node->args = args;
|
||||
|
||||
return (Node*)node;
|
||||
}
|
||||
|
||||
static Node* infixBinaryOp(Parser* parser, Node* left, Token* token)
|
||||
{
|
||||
// TODO(bob): Support right-associative infix. Needs to do precedence
|
||||
// - 1 here to be right-assoc.
|
||||
Node* right = parsePrecedence(parser,
|
||||
infixParsers[token->type].precedence);
|
||||
|
||||
NodeBinaryOp* node = (NodeBinaryOp*)malloc(sizeof(NodeBinaryOp));
|
||||
node->node.type = NODE_BINARY_OP;
|
||||
node->left = left;
|
||||
node->op = token;
|
||||
node->right = right;
|
||||
|
||||
return (Node*)node;
|
||||
}
|
||||
|
||||
static TokenType peek(Parser* parser)
|
||||
{
|
||||
return parser->current->type;
|
||||
}
|
||||
|
||||
static Token* match(Parser* parser, TokenType expected)
|
||||
{
|
||||
if (peek(parser) != expected) return NULL;
|
||||
|
||||
return advance(parser);
|
||||
}
|
||||
|
||||
static Token* consume(Parser* parser, TokenType expected)
|
||||
{
|
||||
Token* token = advance(parser);
|
||||
if (token->type != expected)
|
||||
{
|
||||
// TODO(bob): Better error.
|
||||
error(parser, "Expected %d, got %d.\n", expected, token->type);
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
static Token* advance(Parser* parser)
|
||||
{
|
||||
// TODO(bob): Check for EOF.
|
||||
Token* token = parser->current;
|
||||
parser->current = parser->current->next;
|
||||
return unlinkToken(token);
|
||||
}
|
||||
|
||||
static void error(Parser* parser, const char* format, ...)
|
||||
{
|
||||
parser->hasError = 1;
|
||||
printf("Parse error on '");
|
||||
printToken(parser->source, parser->current);
|
||||
|
||||
printf("': ");
|
||||
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
vprintf(format, args);
|
||||
va_end(args);
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
90
src/parser.h
90
src/parser.h
@ -1,90 +0,0 @@
|
||||
#ifndef wren_parser_h
|
||||
#define wren_parser_h
|
||||
|
||||
#include "lexer.h"
|
||||
|
||||
// AST nodes.
|
||||
|
||||
typedef enum
|
||||
{
|
||||
NODE_LITERAL,
|
||||
NODE_SEQUENCE,
|
||||
NODE_CALL,
|
||||
NODE_BINARY_OP,
|
||||
NODE_IF,
|
||||
NODE_VAR,
|
||||
|
||||
NODE_MAX
|
||||
} NodeType;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
NodeType type;
|
||||
} Node;
|
||||
|
||||
typedef struct NodeList_s
|
||||
{
|
||||
Node* node;
|
||||
struct NodeList_s* next;
|
||||
} NodeList;
|
||||
|
||||
// Numbers, strings, variable names.
|
||||
typedef struct
|
||||
{
|
||||
Node node;
|
||||
Token* token;
|
||||
} NodeLiteral;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Node node;
|
||||
NodeList* nodes;
|
||||
} NodeSequence;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Node node;
|
||||
Node* fn;
|
||||
NodeList* args;
|
||||
} NodeCall;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Node node;
|
||||
Token* op;
|
||||
Node* left;
|
||||
Node* right;
|
||||
} NodeBinaryOp;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Node node;
|
||||
Node* condition;
|
||||
Node* thenArm;
|
||||
Node* elseArm;
|
||||
} NodeIf;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Node node;
|
||||
Token* name;
|
||||
Node* initializer;
|
||||
} NodeVar;
|
||||
|
||||
// Parameters.
|
||||
|
||||
// TODO(bob): Is this needed?
|
||||
typedef struct
|
||||
{
|
||||
Token* name;
|
||||
} Param;
|
||||
|
||||
typedef struct ParamList_s
|
||||
{
|
||||
Param* param;
|
||||
struct ParamList_s* next;
|
||||
} ParamList;
|
||||
|
||||
Node* parse(Buffer* source, Token* tokens);
|
||||
|
||||
#endif
|
||||
@ -50,8 +50,6 @@ typedef enum
|
||||
|
||||
TOKEN_LINE,
|
||||
TOKEN_WHITESPACE,
|
||||
TOKEN_INDENT,
|
||||
TOKEN_OUTDENT,
|
||||
|
||||
TOKEN_ERROR,
|
||||
TOKEN_EOF,
|
||||
|
||||
49
src/vm.c
Normal file
49
src/vm.c
Normal file
@ -0,0 +1,49 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "vm.h"
|
||||
|
||||
static Value pop(Fiber* fiber);
|
||||
|
||||
Fiber* newFiber()
|
||||
{
|
||||
Fiber* fiber = (Fiber*)malloc(sizeof(Fiber));
|
||||
fiber->stackSize = 0;
|
||||
|
||||
return fiber;
|
||||
}
|
||||
|
||||
Value interpret(Fiber* fiber, Block* block)
|
||||
{
|
||||
int ip = 0;
|
||||
for (;;)
|
||||
{
|
||||
switch (block->bytecode[ip++])
|
||||
{
|
||||
case CODE_CONSTANT:
|
||||
{
|
||||
Value value = block->constants[block->bytecode[ip++]];
|
||||
fiber->stack[fiber->stackSize++] = value;
|
||||
break;
|
||||
}
|
||||
|
||||
case CODE_END:
|
||||
return pop(fiber);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void printValue(Value value)
|
||||
{
|
||||
switch (value->type)
|
||||
{
|
||||
case OBJ_INT:
|
||||
printf("%d", value->value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Value pop(Fiber* fiber)
|
||||
{
|
||||
return fiber->stack[--fiber->stackSize];
|
||||
}
|
||||
57
src/vm.h
Normal file
57
src/vm.h
Normal file
@ -0,0 +1,57 @@
|
||||
#ifndef wren_vm_h
|
||||
#define wren_vm_h
|
||||
|
||||
// TODO(bob): Make this externally controllable.
|
||||
#define STACK_SIZE 1024
|
||||
|
||||
typedef enum {
|
||||
OBJ_INT
|
||||
} ObjType;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
// The object has been marked during the mark phase of GC.
|
||||
FLAG_MARKED = 0x01,
|
||||
} ObjFlags;
|
||||
|
||||
typedef struct sObj {
|
||||
ObjType type;
|
||||
ObjFlags flags;
|
||||
|
||||
union {
|
||||
/* OBJ_INT */
|
||||
int value;
|
||||
};
|
||||
} Obj;
|
||||
|
||||
typedef Obj* Value;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Value stack[STACK_SIZE];
|
||||
int stackSize;
|
||||
} Fiber;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CODE_CONSTANT,
|
||||
// Load the constant at index [arg].
|
||||
|
||||
CODE_END
|
||||
// The current block is done and should be exited.
|
||||
|
||||
} Code;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned char* bytecode;
|
||||
Value* constants;
|
||||
int numConstants;
|
||||
} Block;
|
||||
|
||||
Fiber* newFiber();
|
||||
Value interpret(Fiber* fiber, Block* block);
|
||||
|
||||
void printValue(Value value);
|
||||
|
||||
#endif
|
||||
@ -9,8 +9,9 @@
|
||||
/* Begin PBXBuildFile section */
|
||||
29AB1F281816E49C004B501E /* lexer.c in Sources */ = {isa = PBXBuildFile; fileRef = 29AB1F1E1816E49C004B501E /* lexer.c */; };
|
||||
29AB1F291816E49C004B501E /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = 29AB1F201816E49C004B501E /* main.c */; };
|
||||
29AB1F2A1816E49C004B501E /* parser.c in Sources */ = {isa = PBXBuildFile; fileRef = 29AB1F211816E49C004B501E /* parser.c */; };
|
||||
29AB1F2C1816E49C004B501E /* token.c in Sources */ = {isa = PBXBuildFile; fileRef = 29AB1F251816E49C004B501E /* token.c */; };
|
||||
29AB1F2F1816FA66004B501E /* vm.c in Sources */ = {isa = PBXBuildFile; fileRef = 29AB1F2E1816FA66004B501E /* vm.c */; };
|
||||
29AB1F3218170104004B501E /* compiler.c in Sources */ = {isa = PBXBuildFile; fileRef = 29AB1F3018170104004B501E /* compiler.c */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
@ -30,11 +31,13 @@
|
||||
29AB1F1E1816E49C004B501E /* lexer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lexer.c; path = src/lexer.c; sourceTree = SOURCE_ROOT; };
|
||||
29AB1F1F1816E49C004B501E /* lexer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = lexer.h; path = src/lexer.h; sourceTree = SOURCE_ROOT; };
|
||||
29AB1F201816E49C004B501E /* main.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = main.c; path = src/main.c; sourceTree = SOURCE_ROOT; };
|
||||
29AB1F211816E49C004B501E /* parser.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = parser.c; path = src/parser.c; sourceTree = SOURCE_ROOT; };
|
||||
29AB1F221816E49C004B501E /* parser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = parser.h; path = src/parser.h; sourceTree = SOURCE_ROOT; };
|
||||
29AB1F251816E49C004B501E /* token.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = token.c; path = src/token.c; sourceTree = SOURCE_ROOT; };
|
||||
29AB1F261816E49C004B501E /* token.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = token.h; path = src/token.h; sourceTree = SOURCE_ROOT; };
|
||||
29AB1F271816E49C004B501E /* wren.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; name = wren.1; path = src/wren.1; sourceTree = SOURCE_ROOT; };
|
||||
29AB1F2D1816FA5B004B501E /* vm.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = vm.h; path = src/vm.h; sourceTree = SOURCE_ROOT; };
|
||||
29AB1F2E1816FA66004B501E /* vm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = vm.c; path = src/vm.c; sourceTree = SOURCE_ROOT; };
|
||||
29AB1F3018170104004B501E /* compiler.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = compiler.c; path = src/compiler.c; sourceTree = SOURCE_ROOT; };
|
||||
29AB1F3118170104004B501E /* compiler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = compiler.h; path = src/compiler.h; sourceTree = SOURCE_ROOT; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@ -67,13 +70,15 @@
|
||||
29AB1F081816E3AD004B501E /* src */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
29AB1F3018170104004B501E /* compiler.c */,
|
||||
29AB1F3118170104004B501E /* compiler.h */,
|
||||
29AB1F1E1816E49C004B501E /* lexer.c */,
|
||||
29AB1F1F1816E49C004B501E /* lexer.h */,
|
||||
29AB1F201816E49C004B501E /* main.c */,
|
||||
29AB1F211816E49C004B501E /* parser.c */,
|
||||
29AB1F221816E49C004B501E /* parser.h */,
|
||||
29AB1F251816E49C004B501E /* token.c */,
|
||||
29AB1F261816E49C004B501E /* token.h */,
|
||||
29AB1F2E1816FA66004B501E /* vm.c */,
|
||||
29AB1F2D1816FA5B004B501E /* vm.h */,
|
||||
29AB1F271816E49C004B501E /* wren.1 */,
|
||||
);
|
||||
name = src;
|
||||
@ -132,7 +137,8 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
29AB1F291816E49C004B501E /* main.c in Sources */,
|
||||
29AB1F2A1816E49C004B501E /* parser.c in Sources */,
|
||||
29AB1F3218170104004B501E /* compiler.c in Sources */,
|
||||
29AB1F2F1816FA66004B501E /* vm.c in Sources */,
|
||||
29AB1F2C1816E49C004B501E /* token.c in Sources */,
|
||||
29AB1F281816E49C004B501E /* lexer.c in Sources */,
|
||||
);
|
||||
@ -212,6 +218,8 @@
|
||||
29AB1F101816E3AD004B501E /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
GCC_C_LANGUAGE_STANDARD = c89;
|
||||
GCC_WARN_PEDANTIC = NO;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Debug;
|
||||
@ -219,6 +227,8 @@
|
||||
29AB1F111816E3AD004B501E /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
GCC_C_LANGUAGE_STANDARD = c89;
|
||||
GCC_WARN_PEDANTIC = NO;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Release;
|
||||
|
||||
Reference in New Issue
Block a user