added parsing of if-else statements and cleaned up the parser
This commit is contained in:
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
NODE_EXPR,
|
NODE_EXPR,
|
||||||
|
NODE_IFELSE,
|
||||||
} NodeKind;
|
} NodeKind;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@@ -70,4 +71,11 @@ typedef struct {
|
|||||||
int count;
|
int count;
|
||||||
} OperExprNode;
|
} OperExprNode;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Node base;
|
||||||
|
Node *condition;
|
||||||
|
Node *true_branch;
|
||||||
|
Node *false_branch;
|
||||||
|
} IfElseNode;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
+154
-39
@@ -3,15 +3,10 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
#include "../utils/defs.h"
|
||||||
#include "parse.h"
|
#include "parse.h"
|
||||||
#include "ASTi.h"
|
#include "ASTi.h"
|
||||||
|
|
||||||
#define UNREACHABLE assert(0)
|
|
||||||
|
|
||||||
#ifndef NULL
|
|
||||||
#define NULL ((void*) 0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
|
||||||
TPOS = '+',
|
TPOS = '+',
|
||||||
@@ -28,6 +23,9 @@ typedef enum {
|
|||||||
TFLOAT,
|
TFLOAT,
|
||||||
TSTRING,
|
TSTRING,
|
||||||
TIDENT,
|
TIDENT,
|
||||||
|
|
||||||
|
TKWIF,
|
||||||
|
TKWELSE,
|
||||||
} TokenKind;
|
} TokenKind;
|
||||||
|
|
||||||
typedef struct Token Token;
|
typedef struct Token Token;
|
||||||
@@ -47,6 +45,7 @@ typedef struct {
|
|||||||
static Node *parse_statement(Context *ctx);
|
static Node *parse_statement(Context *ctx);
|
||||||
static Node *parse_expression(Context *ctx);
|
static Node *parse_expression(Context *ctx);
|
||||||
static Node *parse_expression_statement(Context *ctx);
|
static Node *parse_expression_statement(Context *ctx);
|
||||||
|
static Node *parse_ifelse_statement(Context *ctx);
|
||||||
|
|
||||||
static inline _Bool isoper(char c)
|
static inline _Bool isoper(char c)
|
||||||
{
|
{
|
||||||
@@ -113,6 +112,20 @@ AST *parse(Source *src, BPAlloc *alloc, Error *error)
|
|||||||
i += 1;
|
i += 1;
|
||||||
|
|
||||||
tok->length = i - tok->offset;
|
tok->length = i - tok->offset;
|
||||||
|
|
||||||
|
#define matchstr(str, len, const_str) \
|
||||||
|
(len == sizeof(const_str)-1 && !strncmp(str, const_str, sizeof(const_str)-1))
|
||||||
|
|
||||||
|
if(matchstr(str + tok->offset, tok->length, "if"))
|
||||||
|
{
|
||||||
|
tok->kind = TKWIF;
|
||||||
|
}
|
||||||
|
else if(matchstr(str + tok->offset, tok->length, "else"))
|
||||||
|
{
|
||||||
|
tok->kind = TKWELSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef matchstr
|
||||||
}
|
}
|
||||||
else if(isdigit(str[i]))
|
else if(isdigit(str[i]))
|
||||||
{
|
{
|
||||||
@@ -261,11 +274,47 @@ AST *parse(Source *src, BPAlloc *alloc, Error *error)
|
|||||||
return ast;
|
return ast;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline Token *current_token(Context *ctx)
|
||||||
|
{
|
||||||
|
assert(ctx != NULL);
|
||||||
|
assert(ctx->token != NULL);
|
||||||
|
return ctx->token;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline TokenKind current(Context *ctx)
|
||||||
|
{
|
||||||
|
assert(ctx != NULL);
|
||||||
|
return current_token(ctx)->kind;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline TokenKind next(Context *ctx)
|
||||||
|
{
|
||||||
|
assert(ctx != NULL);
|
||||||
|
assert(ctx->token != NULL);
|
||||||
|
assert(ctx->token->kind != TDONE);
|
||||||
|
ctx->token = ctx->token->next;
|
||||||
|
return current(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline TokenKind prev(Context *ctx)
|
||||||
|
{
|
||||||
|
assert(ctx != NULL);
|
||||||
|
assert(ctx->token != NULL);
|
||||||
|
assert(ctx->token->prev != NULL);
|
||||||
|
ctx->token = ctx->token->prev;
|
||||||
|
return current(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline _Bool done(Context *ctx)
|
||||||
|
{
|
||||||
|
return current(ctx) == TDONE;
|
||||||
|
}
|
||||||
|
|
||||||
static Node *parse_statement(Context *ctx)
|
static Node *parse_statement(Context *ctx)
|
||||||
{
|
{
|
||||||
assert(ctx != NULL);
|
assert(ctx != NULL);
|
||||||
|
|
||||||
switch(ctx->token->kind)
|
switch(current(ctx))
|
||||||
{
|
{
|
||||||
case '*':
|
case '*':
|
||||||
case '/':
|
case '/':
|
||||||
@@ -282,6 +331,9 @@ static Node *parse_statement(Context *ctx)
|
|||||||
case TSTRING:
|
case TSTRING:
|
||||||
case TIDENT:
|
case TIDENT:
|
||||||
return parse_expression_statement(ctx);
|
return parse_expression_statement(ctx);
|
||||||
|
|
||||||
|
case TKWIF:
|
||||||
|
return parse_ifelse_statement(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
Error_Report(ctx->error, 0, "Got token \"%.*s\" where the start of a statement was expected",
|
Error_Report(ctx->error, 0, "Got token \"%.*s\" where the start of a statement was expected",
|
||||||
@@ -294,28 +346,21 @@ static Node *parse_expression_statement(Context *ctx)
|
|||||||
assert(ctx != NULL);
|
assert(ctx != NULL);
|
||||||
|
|
||||||
Node *expr = parse_expression(ctx);
|
Node *expr = parse_expression(ctx);
|
||||||
|
if(expr == NULL) return NULL;
|
||||||
if(expr == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
assert(ctx->token != NULL);
|
// The final statement doesn't need a ';'.
|
||||||
|
if(done(ctx)) return expr;
|
||||||
|
|
||||||
if(ctx->token->kind == TDONE)
|
if(current(ctx) != ';')
|
||||||
// If the source ended before
|
|
||||||
// the final ";", it's ok.
|
|
||||||
return expr;
|
|
||||||
|
|
||||||
if(ctx->token->kind != ';')
|
|
||||||
{
|
{
|
||||||
// ERROR: Got something other than
|
// ERROR: Got something other than a semicolon at the end
|
||||||
// a semicolon at the end of statement.
|
// of statement.
|
||||||
|
|
||||||
Error_Report(ctx->error, 0, "Got token \"%.*s\" where \";\" was expected", ctx->token->length, ctx->src + ctx->token->offset);
|
Error_Report(ctx->error, 0, "Got token \"%.*s\" where \";\" was expected", ctx->token->length, ctx->src + ctx->token->offset);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx->token = ctx->token->next;
|
next(ctx);
|
||||||
assert(ctx->token != NULL);
|
|
||||||
|
|
||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -324,6 +369,18 @@ static Node *parse_string_primary_expression(Context *ctx)
|
|||||||
assert(ctx != NULL);
|
assert(ctx != NULL);
|
||||||
assert(ctx->token->kind == TSTRING);
|
assert(ctx->token->kind == TSTRING);
|
||||||
|
|
||||||
|
if(current(ctx) == TDONE)
|
||||||
|
{
|
||||||
|
Error_Report(ctx->error, 0, "Source ended where a string literal was expected");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(current(ctx) != TSTRING)
|
||||||
|
{
|
||||||
|
Error_Report(ctx->error, 0, "Got token \"%.*s\" where a string literal was expected", ctx->token->length, ctx->src + ctx->token->offset);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
const char *src = ctx->src;
|
const char *src = ctx->src;
|
||||||
int len = ctx->token->offset + ctx->token->length - 1;
|
int len = ctx->token->offset + ctx->token->length - 1;
|
||||||
int i = ctx->token->offset + 1;
|
int i = ctx->token->offset + 1;
|
||||||
@@ -428,9 +485,7 @@ static Node *parse_string_primary_expression(Context *ctx)
|
|||||||
node->len = copyl;
|
node->len = copyl;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx->token = ctx->token->next;
|
next(ctx);
|
||||||
assert(ctx->token != NULL);
|
|
||||||
|
|
||||||
return (Node*) node;
|
return (Node*) node;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -438,19 +493,18 @@ static Node *parse_primary_expresion(Context *ctx)
|
|||||||
{
|
{
|
||||||
assert(ctx != NULL);
|
assert(ctx != NULL);
|
||||||
|
|
||||||
switch(ctx->token->kind)
|
switch(current(ctx))
|
||||||
{
|
{
|
||||||
case '+':
|
case '+':
|
||||||
case '-':
|
case '-':
|
||||||
{
|
{
|
||||||
Token *operator = ctx->token;
|
Token *unary_operator = current_token(ctx);
|
||||||
|
|
||||||
ctx->token = ctx->token->next;
|
next(ctx);
|
||||||
assert(ctx->token != NULL);
|
|
||||||
|
|
||||||
Node *operand = parse_primary_expresion(ctx);
|
Node *operand = parse_primary_expresion(ctx);
|
||||||
|
|
||||||
if(operand == NULL)
|
if(operand == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
OperExprNode *temp = BPAlloc_Malloc(ctx->alloc, sizeof(OperExprNode));
|
OperExprNode *temp = BPAlloc_Malloc(ctx->alloc, sizeof(OperExprNode));
|
||||||
@@ -460,9 +514,9 @@ static Node *parse_primary_expresion(Context *ctx)
|
|||||||
|
|
||||||
temp->base.base.kind = NODE_EXPR;
|
temp->base.base.kind = NODE_EXPR;
|
||||||
temp->base.base.next = NULL;
|
temp->base.base.next = NULL;
|
||||||
temp->base.base.offset = operator->offset;
|
temp->base.base.offset = unary_operator->offset;
|
||||||
temp->base.base.length = operand->offset + operand->length - operator->offset;
|
temp->base.base.length = operand->offset + operand->length - unary_operator->offset;
|
||||||
temp->base.kind = operator->kind == '+' ? EXPR_POS : EXPR_NEG;
|
temp->base.kind = unary_operator->kind == '+' ? EXPR_POS : EXPR_NEG;
|
||||||
temp->head = operand;
|
temp->head = operand;
|
||||||
temp->count = 1;
|
temp->count = 1;
|
||||||
}
|
}
|
||||||
@@ -471,22 +525,26 @@ static Node *parse_primary_expresion(Context *ctx)
|
|||||||
|
|
||||||
case '(':
|
case '(':
|
||||||
{
|
{
|
||||||
ctx->token = ctx->token->next;
|
next(ctx); // Consume the '('.
|
||||||
|
|
||||||
Node *node = parse_expression(ctx);
|
Node *node = parse_expression(ctx);
|
||||||
|
|
||||||
if(node == NULL)
|
if(node == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if(ctx->token->kind != ')')
|
if(current(ctx) == TDONE)
|
||||||
|
{
|
||||||
|
Error_Report(ctx->error, 0, "Source ended before \")\", after sub-expression");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(current(ctx) != ')')
|
||||||
{
|
{
|
||||||
Error_Report(ctx->error, 0, "Missing \")\", after sub-expression");
|
Error_Report(ctx->error, 0, "Missing \")\", after sub-expression");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx->token = ctx->token->next;
|
next(ctx); // Consume the ')'.
|
||||||
assert(ctx->token != NULL);
|
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -759,4 +817,61 @@ static Node *parse_expression(Context *ctx)
|
|||||||
return left_expr;
|
return left_expr;
|
||||||
|
|
||||||
return parse_expression_2(ctx, left_expr, -1000000000);
|
return parse_expression_2(ctx, left_expr, -1000000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Node *parse_ifelse_statement(Context *ctx)
|
||||||
|
{
|
||||||
|
assert(ctx != NULL);
|
||||||
|
assert(ctx->token != NULL);
|
||||||
|
assert(ctx->token->kind == TKWIF);
|
||||||
|
|
||||||
|
ctx->token = ctx->token->next;
|
||||||
|
|
||||||
|
if(ctx->token->kind == TDONE)
|
||||||
|
{
|
||||||
|
// ERROR: Source ended after if keyword.
|
||||||
|
// An expression was expected.
|
||||||
|
Error_Report(ctx->error, 0, "Source ended after if keyword. An expression was expected");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
Token *if_token = current_token(ctx);
|
||||||
|
|
||||||
|
Node *condition = parse_expression(ctx);
|
||||||
|
if(condition == NULL) return NULL;
|
||||||
|
|
||||||
|
Node *true_branch = parse_statement(ctx);
|
||||||
|
if(condition == NULL) return NULL;
|
||||||
|
|
||||||
|
Node *false_branch = NULL;
|
||||||
|
if(ctx->token->kind == TKWELSE)
|
||||||
|
{
|
||||||
|
// Consume the "else" token.
|
||||||
|
ctx->token = ctx->token->next;
|
||||||
|
|
||||||
|
false_branch = parse_statement(ctx);
|
||||||
|
if(false_branch == NULL) return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
IfElseNode *ifelse;
|
||||||
|
{
|
||||||
|
ifelse = BPAlloc_Malloc(ctx->alloc, sizeof(IfElseNode));
|
||||||
|
|
||||||
|
if(ifelse == NULL)
|
||||||
|
{
|
||||||
|
// ERROR: No memory.
|
||||||
|
Error_Report(ctx->error, 1, "No memory");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ifelse->base.kind = NODE_IFELSE;
|
||||||
|
ifelse->base.next = NULL;
|
||||||
|
ifelse->base.offset = if_token->offset;
|
||||||
|
ifelse->base.length = ctx->token->offset + ctx->token->length - if_token->offset;
|
||||||
|
ifelse->condition = condition;
|
||||||
|
ifelse->true_branch = true_branch;
|
||||||
|
ifelse->false_branch = false_branch;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (Node*) ifelse;
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user