commit 2f6a6889f1b4a1ba86aeb169e7398704b1ee04c0 Author: Bob Nystrom Date: Tue Oct 22 11:22:22 2013 -0700 Initial commit. diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..9324dd90 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +# Output directory. +build/ + +# XCode user-specific stuff. +xcuserdata/ \ No newline at end of file diff --git a/example/hello.wren b/example/hello.wren new file mode 100644 index 00000000..c4b39389 --- /dev/null +++ b/example/hello.wren @@ -0,0 +1 @@ +123(1, true, "a string".whatevs) diff --git a/src/lexer.c b/src/lexer.c new file mode 100644 index 00000000..5a4f6586 --- /dev/null +++ b/src/lexer.c @@ -0,0 +1,233 @@ +#include +#include + +#include "lexer.h" + +typedef struct +{ + Buffer* source; + + int start; // The beginning of the current token. + int pos; + + Token* head; + Token* tail; + +} Lexer; + +static void readName(Lexer* lexer); +static void readNumber(Lexer* lexer); +static void readString(Lexer* lexer); +static void readEmbedded(Lexer* lexer); +static void readWhitespace(Lexer* lexer); +static int isKeyword(Lexer* lexer, const char* keyword); +static int isName(char c); +static int isDigit(char c); +static char advance(Lexer* lexer); +static char peek(Lexer* lexer); +static void emitToken(Lexer* lexer, TokenType type); + +Token* tokenize(Buffer* source) +{ + Lexer lexer; + lexer.source = source; + lexer.start = 0; + lexer.pos = 0; + lexer.head = NULL; + lexer.tail = NULL; + + while (peek(&lexer) != '\0') + { + lexer.start = lexer.pos; + + char c = advance(&lexer); + switch (c) + { + case '(': emitToken(&lexer, TOKEN_LEFT_PAREN); break; + case ')': emitToken(&lexer, TOKEN_RIGHT_PAREN); break; + case '[': emitToken(&lexer, TOKEN_LEFT_BRACKET); break; + case ']': emitToken(&lexer, TOKEN_RIGHT_BRACKET); break; + case '{': emitToken(&lexer, TOKEN_LEFT_BRACE); break; + case '}': emitToken(&lexer, TOKEN_RIGHT_BRACE); break; + case ':': emitToken(&lexer, TOKEN_COLON); break; + case '.': emitToken(&lexer, TOKEN_DOT); break; + case ',': emitToken(&lexer, TOKEN_COMMA); break; + case '*': emitToken(&lexer, TOKEN_STAR); break; + case '/': emitToken(&lexer, TOKEN_SLASH); break; + case '%': emitToken(&lexer, TOKEN_PERCENT); break; + case '+': emitToken(&lexer, TOKEN_PLUS); break; + case '-': emitToken(&lexer, TOKEN_MINUS); break; + case '|': emitToken(&lexer, TOKEN_PIPE); break; + case '&': emitToken(&lexer, TOKEN_AMP); break; + case '=': + if (peek(&lexer) == '=') + { + advance(&lexer); + emitToken(&lexer, TOKEN_EQEQ); + } + else + { + emitToken(&lexer, TOKEN_EQ); + } + break; + + case '<': + if (peek(&lexer) == '=') + { + advance(&lexer); + emitToken(&lexer, TOKEN_LTEQ); + } + else + { + emitToken(&lexer, TOKEN_LT); + } + break; + + case '>': + if (peek(&lexer) == '=') + { + advance(&lexer); + emitToken(&lexer, TOKEN_GTEQ); + } + else + { + emitToken(&lexer, TOKEN_GT); + } + break; + + case '!': + if (peek(&lexer) == '=') + { + advance(&lexer); + emitToken(&lexer, TOKEN_BANGEQ); + } + else + { + emitToken(&lexer, TOKEN_BANG); + } + break; + + case '\n': emitToken(&lexer, TOKEN_LINE); break; + + case ' ': readWhitespace(&lexer); break; + case '"': readString(&lexer); break; + case '`': readEmbedded(&lexer); break; + + default: + if (isName(c)) + { + readName(&lexer); + } + else if (isDigit(c)) + { + readNumber(&lexer); + } + else + { + emitToken(&lexer, TOKEN_ERROR); + } + break; + } + } + + lexer.start = lexer.pos; + emitToken(&lexer, TOKEN_EOF); + + return lexer.head; +} + +static void readName(Lexer* lexer) +{ + // TODO(bob): Handle digits and EOF. + while (isName(peek(lexer)) || isDigit(peek(lexer))) advance(lexer); + + TokenType type = TOKEN_NAME; + + if (isKeyword(lexer, "else")) type = TOKEN_ELSE; + else if (isKeyword(lexer, "if")) type = TOKEN_IF; + else if (isKeyword(lexer, "var")) type = TOKEN_VAR; + + emitToken(lexer, type); +} + +static int isKeyword(Lexer* lexer, const char* keyword) +{ + size_t length = lexer->pos - lexer->start; + size_t keywordLength = strlen(keyword); + return length == keywordLength && + strncmp(lexer->source->bytes + lexer->start, keyword, length) == 0; +} + +static void readNumber(Lexer* lexer) +{ + // TODO(bob): Floating point, hex, scientific, etc. + while (isDigit(peek(lexer))) advance(lexer); + + emitToken(lexer, TOKEN_NUMBER); +} + +static void readString(Lexer* lexer) +{ + // TODO(bob): Escape sequences, EOL, EOF, etc. + while (advance(lexer) != '"'); + + emitToken(lexer, TOKEN_STRING); +} + +static void readEmbedded(Lexer* lexer) +{ + // TODO(bob): EOF. + while (advance(lexer) != '`'); + + emitToken(lexer, TOKEN_EMBEDDED); +} + +static void readWhitespace(Lexer* lexer) +{ + while (peek(lexer) == ' ') advance(lexer); + + emitToken(lexer, TOKEN_WHITESPACE); +} + +static int isName(char c) +{ + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; +} + +static int isDigit(char c) +{ + return c >= '0' && c <= '9'; +} + +static char advance(Lexer* lexer) +{ + char c = peek(lexer); + lexer->pos++; + return c; +} + +static char peek(Lexer* lexer) +{ + return lexer->source->bytes[lexer->pos]; +} + +static void emitToken(Lexer* lexer, TokenType type) +{ + Token* token = newToken(type, lexer->start, lexer->pos); + + token->prev = lexer->tail; + token->next = NULL; + + if (lexer->tail == NULL) + { + // First token. + lexer->head = token; + } + else + { + // Not the first token, so add it to the end. + lexer->tail->next = token; + } + + lexer->tail = token; +} diff --git a/src/lexer.h b/src/lexer.h new file mode 100644 index 00000000..d13523a7 --- /dev/null +++ b/src/lexer.h @@ -0,0 +1,8 @@ +#ifndef wren_lexer_h +#define wren_lexer_h + +#include "token.h" + +Token* tokenize(Buffer* source); + +#endif diff --git a/src/main.c b/src/main.c new file mode 100644 index 00000000..adde06ea --- /dev/null +++ b/src/main.c @@ -0,0 +1,71 @@ +#include +#include + +#include "lexer.h" +#include "parser.h" + +#define MAX_FILE_SIZE 256 * 256 + +static void dumpTokens(Buffer* buffer, Token* token); + +Buffer* readFile(const char* path) +{ + FILE* file = fopen(path, "r"); + // TODO(bob): Handle error. + + Buffer* buffer = newBuffer(MAX_FILE_SIZE); + // TODO(bob): Hacky way to read a file! + size_t read = fread(buffer->bytes, sizeof(char), MAX_FILE_SIZE, file); + buffer->bytes[read] = '\0'; + + fclose(file); + return buffer; +} + +int main(int argc, const char * argv[]) +{ + // TODO(bob): Validate command line arguments. + Buffer* buffer = readFile(argv[1]); + Token* tokens = tokenize(buffer); + //printf("Raw tokens:\n"); + //dumpTokens(buffer, tokens); + //printf("Cleaned tokens:\n"); + dumpTokens(buffer, tokens); + + /*Node* node =*/ parse(buffer, tokens); + + /* + if (defns) generate(buffer, defns); + + // TODO(bob): Free tokens. + // TODO(bob): Free ast. + */ + + freeBuffer(buffer); + + return 0; +} + +static void dumpTokens(Buffer* buffer, Token* token) +{ + while (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; + default: + printf("⊏"); + for (int i = token->start; i < token->end; i++) + { + putchar(buffer->bytes[i]); + } + printf("⊐"); + } + token = token->next; + } + printf("\n"); +} \ No newline at end of file diff --git a/src/parser.c b/src/parser.c new file mode 100644 index 00000000..c976615d --- /dev/null +++ b/src/parser.c @@ -0,0 +1,349 @@ +#include +#include +#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"); +} diff --git a/src/parser.h b/src/parser.h new file mode 100644 index 00000000..e5d28bc1 --- /dev/null +++ b/src/parser.h @@ -0,0 +1,90 @@ +#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 diff --git a/src/token.c b/src/token.c new file mode 100644 index 00000000..14656bda --- /dev/null +++ b/src/token.c @@ -0,0 +1,49 @@ +#include +#include + +#include "token.h" + +Buffer* newBuffer(size_t size) +{ + Buffer* buffer = (Buffer*)malloc(sizeof(Buffer)); + buffer->bytes = (char*)malloc(size); + buffer->size = size; + + return buffer; +} + +void freeBuffer(Buffer* buffer) +{ + free(buffer->bytes); + free(buffer); +} + +Token* newToken(TokenType type, int start, int end) +{ + Token* token = (Token*)malloc(sizeof(Token)); + token->type = type; + token->start = start; + token->end = end; + token->prev = NULL; + token->next = NULL; + return token; +} + +void printToken(Buffer* buffer, Token* token) +{ + for (int i = token->start; i < token->end; i++) + { + putchar(buffer->bytes[i]); + } +} + +Token* unlinkToken(Token* token) +{ + if (token->next) token->next->prev = token->prev; + if (token->prev) token->prev->next = token->next; + + token->next = NULL; + token->prev = NULL; + + return token; +} diff --git a/src/token.h b/src/token.h new file mode 100644 index 00000000..4c6ba3c9 --- /dev/null +++ b/src/token.h @@ -0,0 +1,84 @@ +#ifndef wren_token_h +#define wren_token_h + +#include + +// TODO(bob): Move somewhere else? +typedef struct Buffer_s +{ + char* bytes; + size_t size; +} Buffer; + +// Note: if you add new token types, make sure to update the parser arrays in +// parser.c. +typedef enum +{ + TOKEN_LEFT_PAREN, + TOKEN_RIGHT_PAREN, + TOKEN_LEFT_BRACKET, + TOKEN_RIGHT_BRACKET, + TOKEN_LEFT_BRACE, + TOKEN_RIGHT_BRACE, + TOKEN_COLON, + TOKEN_DOT, + TOKEN_COMMA, + TOKEN_STAR, + TOKEN_SLASH, + TOKEN_PERCENT, + TOKEN_PLUS, + TOKEN_MINUS, + TOKEN_PIPE, + TOKEN_AMP, + TOKEN_BANG, + TOKEN_EQ, + TOKEN_LT, + TOKEN_GT, + TOKEN_LTEQ, + TOKEN_GTEQ, + TOKEN_EQEQ, + TOKEN_BANGEQ, + + TOKEN_ELSE, + TOKEN_IF, + TOKEN_VAR, + + TOKEN_EMBEDDED, + TOKEN_NAME, + TOKEN_NUMBER, + TOKEN_STRING, + + TOKEN_LINE, + TOKEN_WHITESPACE, + TOKEN_INDENT, + TOKEN_OUTDENT, + + TOKEN_ERROR, + TOKEN_EOF, + + MAX_TOKEN +} TokenType; + +typedef struct Token_s +{ + TokenType type; + int start; + int end; + + struct Token_s* prev; + struct Token_s* next; +} Token; + +Buffer* newBuffer(size_t size); +void freeBuffer(Buffer* buffer); + +// Creates a new unlinked token. +Token* newToken(TokenType type, int start, int end); + +// Prints the verbatim source text of the token. +void printToken(Buffer* buffer, Token* token); + +// Removes the token from the list containing it. Does not free it. +Token* unlinkToken(Token* token); + +#endif diff --git a/src/wren.1 b/src/wren.1 new file mode 100644 index 00000000..637c4f59 --- /dev/null +++ b/src/wren.1 @@ -0,0 +1,79 @@ +.\"Modified from man(1) of FreeBSD, the NetBSD mdoc.template, and mdoc.samples. +.\"See Also: +.\"man mdoc.samples for a complete listing of options +.\"man mdoc for the short list of editing options +.\"/usr/share/misc/mdoc.template +.Dd 10/22/13 \" DATE +.Dt wren 1 \" Program name and manual section number +.Os Darwin +.Sh NAME \" Section Header - required - don't modify +.Nm wren, +.\" The following lines are read in generating the apropos(man -k) database. Use only key +.\" words here as the database is built based on the words here and in the .ND line. +.Nm Other_name_for_same_program(), +.Nm Yet another name for the same program. +.\" Use .Nm macro to designate other names for the documented program. +.Nd This line parsed for whatis database. +.Sh SYNOPSIS \" Section Header - required - don't modify +.Nm +.Op Fl abcd \" [-abcd] +.Op Fl a Ar path \" [-a path] +.Op Ar file \" [file] +.Op Ar \" [file ...] +.Ar arg0 \" Underlined argument - use .Ar anywhere to underline +arg2 ... \" Arguments +.Sh DESCRIPTION \" Section Header - required - don't modify +Use the .Nm macro to refer to your program throughout the man page like such: +.Nm +Underlining is accomplished with the .Ar macro like this: +.Ar underlined text . +.Pp \" Inserts a space +A list of items with descriptions: +.Bl -tag -width -indent \" Begins a tagged list +.It item a \" Each item preceded by .It macro +Description of item a +.It item b +Description of item b +.El \" Ends the list +.Pp +A list of flags and their descriptions: +.Bl -tag -width -indent \" Differs from above in tag removed +.It Fl a \"-a flag as a list item +Description of -a flag +.It Fl b +Description of -b flag +.El \" Ends the list +.Pp +.\" .Sh ENVIRONMENT \" May not be needed +.\" .Bl -tag -width "ENV_VAR_1" -indent \" ENV_VAR_1 is width of the string ENV_VAR_1 +.\" .It Ev ENV_VAR_1 +.\" Description of ENV_VAR_1 +.\" .It Ev ENV_VAR_2 +.\" Description of ENV_VAR_2 +.\" .El +.Sh FILES \" File used or created by the topic of the man page +.Bl -tag -width "/Users/joeuser/Library/really_long_file_name" -compact +.It Pa /usr/share/file_name +FILE_1 description +.It Pa /Users/joeuser/Library/really_long_file_name +FILE_2 description +.El \" Ends the list +.\" .Sh DIAGNOSTICS \" May not be needed +.\" .Bl -diag +.\" .It Diagnostic Tag +.\" Diagnostic informtion here. +.\" .It Diagnostic Tag +.\" Diagnostic informtion here. +.\" .El +.Sh SEE ALSO +.\" List links in ascending order by section, alphabetically within a section. +.\" Please do not reference files that do not exist without filing a bug report +.Xr a 1 , +.Xr b 1 , +.Xr c 1 , +.Xr a 2 , +.Xr b 2 , +.Xr a 3 , +.Xr b 3 +.\" .Sh BUGS \" Document known, unremedied bugs +.\" .Sh HISTORY \" Document history if command behaves in a unique manner \ No newline at end of file diff --git a/wren.xcodeproj/project.pbxproj b/wren.xcodeproj/project.pbxproj new file mode 100644 index 00000000..cf527490 --- /dev/null +++ b/wren.xcodeproj/project.pbxproj @@ -0,0 +1,249 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* 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 */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 29AB1F041816E3AD004B501E /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 29AB1F061816E3AD004B501E /* wren */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = wren; sourceTree = BUILT_PRODUCTS_DIR; }; + 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; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 29AB1F031816E3AD004B501E /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 29AB1EFD1816E3AD004B501E = { + isa = PBXGroup; + children = ( + 29AB1F081816E3AD004B501E /* src */, + 29AB1F071816E3AD004B501E /* Products */, + ); + sourceTree = ""; + }; + 29AB1F071816E3AD004B501E /* Products */ = { + isa = PBXGroup; + children = ( + 29AB1F061816E3AD004B501E /* wren */, + ); + name = Products; + sourceTree = ""; + }; + 29AB1F081816E3AD004B501E /* src */ = { + isa = PBXGroup; + children = ( + 29AB1F1E1816E49C004B501E /* lexer.c */, + 29AB1F1F1816E49C004B501E /* lexer.h */, + 29AB1F201816E49C004B501E /* main.c */, + 29AB1F211816E49C004B501E /* parser.c */, + 29AB1F221816E49C004B501E /* parser.h */, + 29AB1F251816E49C004B501E /* token.c */, + 29AB1F261816E49C004B501E /* token.h */, + 29AB1F271816E49C004B501E /* wren.1 */, + ); + name = src; + path = wren; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 29AB1F051816E3AD004B501E /* wren */ = { + isa = PBXNativeTarget; + buildConfigurationList = 29AB1F0F1816E3AD004B501E /* Build configuration list for PBXNativeTarget "wren" */; + buildPhases = ( + 29AB1F021816E3AD004B501E /* Sources */, + 29AB1F031816E3AD004B501E /* Frameworks */, + 29AB1F041816E3AD004B501E /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = wren; + productName = wren; + productReference = 29AB1F061816E3AD004B501E /* wren */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 29AB1EFE1816E3AD004B501E /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0500; + ORGANIZATIONNAME = "Bob Nystrom"; + }; + buildConfigurationList = 29AB1F011816E3AD004B501E /* Build configuration list for PBXProject "wren" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 29AB1EFD1816E3AD004B501E; + productRefGroup = 29AB1F071816E3AD004B501E /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 29AB1F051816E3AD004B501E /* wren */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 29AB1F021816E3AD004B501E /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 29AB1F291816E49C004B501E /* main.c in Sources */, + 29AB1F2A1816E49C004B501E /* parser.c in Sources */, + 29AB1F2C1816E49C004B501E /* token.c in Sources */, + 29AB1F281816E49C004B501E /* lexer.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 29AB1F0D1816E3AD004B501E /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.8; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 29AB1F0E1816E3AD004B501E /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.8; + SDKROOT = macosx; + }; + name = Release; + }; + 29AB1F101816E3AD004B501E /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 29AB1F111816E3AD004B501E /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 29AB1F011816E3AD004B501E /* Build configuration list for PBXProject "wren" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 29AB1F0D1816E3AD004B501E /* Debug */, + 29AB1F0E1816E3AD004B501E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 29AB1F0F1816E3AD004B501E /* Build configuration list for PBXNativeTarget "wren" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 29AB1F101816E3AD004B501E /* Debug */, + 29AB1F111816E3AD004B501E /* Release */, + ); + defaultConfigurationIsVisible = 0; + }; +/* End XCConfigurationList section */ + }; + rootObject = 29AB1EFE1816E3AD004B501E /* Project object */; +} diff --git a/wren.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/wren.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..7225aa10 --- /dev/null +++ b/wren.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + +