first commit
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
#ifndef AST_H
|
||||
#define AST_H
|
||||
typedef struct xAST AST;
|
||||
#endif
|
||||
@@ -0,0 +1,73 @@
|
||||
|
||||
#ifndef ASTi_H
|
||||
#define ASTi_H
|
||||
#include "../utils/source.h"
|
||||
#include "AST.h"
|
||||
|
||||
typedef enum {
|
||||
NODE_EXPR,
|
||||
} NodeKind;
|
||||
|
||||
typedef enum {
|
||||
EXPR_POS,
|
||||
EXPR_NEG,
|
||||
EXPR_ADD,
|
||||
EXPR_SUB,
|
||||
EXPR_MUL,
|
||||
EXPR_DIV,
|
||||
EXPR_INT,
|
||||
EXPR_FLOAT,
|
||||
EXPR_STRING,
|
||||
EXPR_IDENT,
|
||||
} ExprKind;
|
||||
|
||||
typedef struct Node Node;
|
||||
|
||||
struct xAST {
|
||||
Source *src;
|
||||
Node *root;
|
||||
};
|
||||
|
||||
struct Node {
|
||||
NodeKind kind;
|
||||
Node *next;
|
||||
int offset,
|
||||
length;
|
||||
char body[];
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
Node base;
|
||||
ExprKind kind;
|
||||
char body[];
|
||||
} ExprNode;
|
||||
|
||||
typedef struct {
|
||||
ExprNode base;
|
||||
long long int val;
|
||||
} IntExprNode;
|
||||
|
||||
typedef struct {
|
||||
ExprNode base;
|
||||
double val;
|
||||
} FloatExprNode;
|
||||
|
||||
typedef struct {
|
||||
ExprNode base;
|
||||
char *val;
|
||||
int len;
|
||||
} StringExprNode;
|
||||
|
||||
typedef struct {
|
||||
ExprNode base;
|
||||
char *val;
|
||||
int len;
|
||||
} IdentExprNode;
|
||||
|
||||
typedef struct {
|
||||
ExprNode base;
|
||||
Node *head;
|
||||
int count;
|
||||
} OperExprNode;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,123 @@
|
||||
#include <assert.h>
|
||||
#include <setjmp.h>
|
||||
#include <stdlib.h>
|
||||
#include "../utils/defs.h"
|
||||
#include "compile.h"
|
||||
#include "ASTi.h"
|
||||
|
||||
static Opcode exprkind_to_opcode(ExprKind kind)
|
||||
{
|
||||
switch(kind)
|
||||
{
|
||||
case EXPR_POS: return OPCODE_POS;
|
||||
case EXPR_NEG: return OPCODE_NEG;
|
||||
case EXPR_ADD: return OPCODE_ADD;
|
||||
case EXPR_SUB: return OPCODE_SUB;
|
||||
case EXPR_MUL: return OPCODE_MUL;
|
||||
case EXPR_DIV: return OPCODE_DIV;
|
||||
default:
|
||||
UNREACHABLE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static _Bool emit_instr_for_node(ExeBuilder *exeb, Node *node, Error *error)
|
||||
{
|
||||
assert(node != NULL);
|
||||
|
||||
switch(node->kind)
|
||||
{
|
||||
case NODE_EXPR:
|
||||
{
|
||||
ExprNode *expr = (ExprNode*) node;
|
||||
switch(expr->kind)
|
||||
{
|
||||
case EXPR_POS:
|
||||
case EXPR_NEG:
|
||||
case EXPR_ADD:
|
||||
case EXPR_SUB:
|
||||
case EXPR_MUL:
|
||||
case EXPR_DIV:
|
||||
{
|
||||
OperExprNode *oper = (OperExprNode*) expr;
|
||||
|
||||
for(Node *operand = oper->head; operand; operand = operand->next)
|
||||
emit_instr_for_node(exeb, operand, error);
|
||||
|
||||
return ExeBuilder_Append(exeb, error,
|
||||
exprkind_to_opcode(expr->kind),
|
||||
NULL, 0, node->offset, node->length);
|
||||
}
|
||||
|
||||
case EXPR_INT:
|
||||
{
|
||||
IntExprNode *p = (IntExprNode*) expr;
|
||||
Operand op = { .type = OPTP_INT, .as_int = p->val };
|
||||
return ExeBuilder_Append(exeb, error, OPCODE_PUSHI, &op, 1, node->offset, node->length);
|
||||
}
|
||||
|
||||
case EXPR_FLOAT:
|
||||
{
|
||||
FloatExprNode *p = (FloatExprNode*) expr;
|
||||
Operand op = { .type = OPTP_FLOAT, .as_float = p->val };
|
||||
return ExeBuilder_Append(exeb, error, OPCODE_PUSHF, &op, 1, node->offset, node->length);
|
||||
}
|
||||
|
||||
case EXPR_STRING:
|
||||
{
|
||||
StringExprNode *p = (StringExprNode*) expr;
|
||||
Operand op = { .type = OPTP_STRING, .as_string = p->val };
|
||||
return ExeBuilder_Append(exeb, error, OPCODE_PUSHS, &op, 1, node->offset, node->length);
|
||||
}
|
||||
|
||||
case EXPR_IDENT:
|
||||
{
|
||||
IdentExprNode *p = (IdentExprNode*) expr;
|
||||
Operand op = { .type = OPTP_STRING, .as_string = p->val };
|
||||
return ExeBuilder_Append(exeb, error, OPCODE_PUSHV, &op, 1, node->offset, node->length);
|
||||
}
|
||||
|
||||
default:
|
||||
UNREACHABLE;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
UNREACHABLE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Executable *compile(AST *ast, BPAlloc *alloc, Error *error)
|
||||
{
|
||||
assert(ast != NULL);
|
||||
assert(error != NULL);
|
||||
|
||||
BPAlloc *alloc2 = alloc;
|
||||
|
||||
if(alloc2 == NULL)
|
||||
{
|
||||
alloc2 = BPAlloc_Init(-1);
|
||||
|
||||
if(alloc2 == NULL)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Executable *exe = NULL;
|
||||
ExeBuilder *exeb = ExeBuilder_New(alloc2);
|
||||
|
||||
if(exeb)
|
||||
{
|
||||
if(!emit_instr_for_node(exeb, ast->root, error))
|
||||
return 0;
|
||||
|
||||
if(ExeBuilder_Append(exeb, error, OPCODE_RETURN, NULL, 0, Source_GetSize(ast->src), 0))
|
||||
exe = ExeBuilder_Finalize(exeb, error);
|
||||
}
|
||||
|
||||
if(alloc == NULL)
|
||||
BPAlloc_Free(alloc2);
|
||||
return exe;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#ifndef COMPILE_H
|
||||
#define COMPILE_H
|
||||
#include "../utils/error.h"
|
||||
#include "../utils/bpalloc.h"
|
||||
#include "../common/executable.h"
|
||||
#include "AST.h"
|
||||
Executable *compile(AST *ast, BPAlloc *alloc, Error *error);
|
||||
#endif
|
||||
@@ -0,0 +1,762 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
#include "parse.h"
|
||||
#include "ASTi.h"
|
||||
|
||||
#define UNREACHABLE assert(0)
|
||||
|
||||
#ifndef NULL
|
||||
#define NULL ((void*) 0)
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
|
||||
TPOS = '+',
|
||||
TNEG = '-',
|
||||
|
||||
TADD = '+',
|
||||
TSUB = '-',
|
||||
TMUL = '*',
|
||||
TDIV = '/',
|
||||
TASS = '=',
|
||||
|
||||
TDONE = 256,
|
||||
TINT,
|
||||
TFLOAT,
|
||||
TSTRING,
|
||||
TIDENT,
|
||||
} TokenKind;
|
||||
|
||||
typedef struct Token Token;
|
||||
struct Token {
|
||||
TokenKind kind;
|
||||
Token *prev, *next;
|
||||
int offset, length;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
const char *src;
|
||||
Token *token;
|
||||
BPAlloc *alloc;
|
||||
Error *error;
|
||||
} Context;
|
||||
|
||||
static Node *parse_statement(Context *ctx);
|
||||
static Node *parse_expression(Context *ctx);
|
||||
static Node *parse_expression_statement(Context *ctx);
|
||||
|
||||
static inline _Bool isoper(char c)
|
||||
{
|
||||
return c == '+' ||
|
||||
c == '-' ||
|
||||
c == '*' ||
|
||||
c == '/' ||
|
||||
c == '=';
|
||||
}
|
||||
|
||||
AST *parse(Source *src, BPAlloc *alloc, Error *error)
|
||||
{
|
||||
assert(src != NULL);
|
||||
assert(alloc != NULL);
|
||||
|
||||
const char *str = Source_GetBody(src);
|
||||
int len = Source_GetSize(src);
|
||||
assert(str != NULL);
|
||||
assert(len >= 0);
|
||||
|
||||
AST *ast = BPAlloc_Malloc(alloc, sizeof(AST));
|
||||
|
||||
if(ast == NULL)
|
||||
return NULL;
|
||||
|
||||
Token *head = NULL,
|
||||
*tail = NULL;
|
||||
int i = 0;
|
||||
while(1)
|
||||
{
|
||||
// Skip whitespace and comments.
|
||||
while(i < len && (isspace(str[i]) || str[i] == '#'))
|
||||
{
|
||||
while(i < len && isspace(str[i]))
|
||||
i += 1;
|
||||
|
||||
if(str[i] == '#')
|
||||
{
|
||||
i += 1;
|
||||
|
||||
while(i < len && str[i] != '\n')
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if(i == len)
|
||||
break; // No more tokens left.
|
||||
|
||||
Token *tok = BPAlloc_Malloc(alloc, sizeof(Token)); // Allocate a token.
|
||||
|
||||
if(tok == NULL)
|
||||
{
|
||||
// Error: No memory.
|
||||
Error_Report(error, 1, "No memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(isalpha(str[i]) || str[i] == '_')
|
||||
{
|
||||
tok->kind = TIDENT;
|
||||
tok->offset = i;
|
||||
|
||||
while(i < len && (isalpha(str[i]) || str[i] == '_'))
|
||||
i += 1;
|
||||
|
||||
tok->length = i - tok->offset;
|
||||
}
|
||||
else if(isdigit(str[i]))
|
||||
{
|
||||
tok->kind = TINT;
|
||||
tok->offset = i;
|
||||
|
||||
while(i < len && isdigit(str[i]))
|
||||
i += 1;
|
||||
|
||||
if(i+1 < len && str[i] == '.' && isdigit(str[i+1]))
|
||||
{
|
||||
i += 1; // Consume the dot.
|
||||
|
||||
tok->kind = TFLOAT;
|
||||
|
||||
while(i < len && isdigit(str[i]))
|
||||
i += 1;
|
||||
}
|
||||
|
||||
tok->length = i - tok->offset;
|
||||
}
|
||||
else if(str[i] == '\'' || str[i] == '"')
|
||||
{
|
||||
tok->kind = TSTRING;
|
||||
tok->offset = i;
|
||||
|
||||
char f = str[i];
|
||||
|
||||
i += 1; // Skip the starting quote.
|
||||
|
||||
while(i < len && str[i] != f)
|
||||
i += 1;
|
||||
|
||||
if(i == len)
|
||||
{
|
||||
Error_Report(error, 0, "Source ended inside string literal");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
i += 1; // Consume the ' or ".
|
||||
|
||||
tok->length = i - tok->offset;
|
||||
}
|
||||
else if(isoper(str[i]))
|
||||
{
|
||||
tok->offset = i;
|
||||
|
||||
while(i < len && isoper(str[i]))
|
||||
i += 1;
|
||||
|
||||
tok->length = i - tok->offset;
|
||||
|
||||
// Determine the token
|
||||
|
||||
#define matchop(str, len, const_str) \
|
||||
(len == sizeof(const_str)-1 && !strncmp(str, const_str, sizeof(const_str)-1))
|
||||
|
||||
if(matchop(str + tok->offset, tok->length, "+"))
|
||||
{
|
||||
tok->kind = TADD;
|
||||
}
|
||||
else if(matchop(str + tok->offset, tok->length, "-"))
|
||||
{
|
||||
tok->kind = TSUB;
|
||||
}
|
||||
else if(matchop(str + tok->offset, tok->length, "*"))
|
||||
{
|
||||
tok->kind = TMUL;
|
||||
}
|
||||
else if(matchop(str + tok->offset, tok->length, "/"))
|
||||
{
|
||||
tok->kind = TDIV;
|
||||
}
|
||||
else if(matchop(str + tok->offset, tok->length, "="))
|
||||
{
|
||||
tok->kind = TASS;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not a known operator.
|
||||
tok->kind = str[tok->offset];
|
||||
tok->length = 1;
|
||||
i = tok->offset + 1;
|
||||
}
|
||||
|
||||
#undef matchop
|
||||
}
|
||||
else
|
||||
{
|
||||
tok->kind = str[i];
|
||||
tok->offset = i;
|
||||
tok->length = 1;
|
||||
i += 1;
|
||||
}
|
||||
|
||||
// Append to the token list.
|
||||
if(head)
|
||||
tail->next = tok;
|
||||
else
|
||||
head = tok;
|
||||
tok->prev = tail;
|
||||
tok->next = NULL;
|
||||
tail = tok;
|
||||
}
|
||||
|
||||
{
|
||||
Token *tok = BPAlloc_Malloc(alloc, sizeof(Token)); // Allocate a token.
|
||||
|
||||
if(tok == NULL)
|
||||
{
|
||||
// Error: No memory.
|
||||
Error_Report(error, 1, "No memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tok->kind = TDONE;
|
||||
tok->offset = i;
|
||||
tok->length = 0;
|
||||
|
||||
if(head)
|
||||
tail->next = tok;
|
||||
else
|
||||
head = tok;
|
||||
tok->prev = tail;
|
||||
tok->next = NULL;
|
||||
tail = tok;
|
||||
}
|
||||
|
||||
Context ctx;
|
||||
ctx.src = str;
|
||||
ctx.token = head;
|
||||
ctx.alloc = alloc;
|
||||
ctx.error = error;
|
||||
|
||||
Node *root = parse_statement(&ctx);
|
||||
|
||||
if(root == NULL)
|
||||
return NULL;
|
||||
|
||||
ast->src = src; // Not copying! Be sure to not free the source before the AST!
|
||||
ast->root = root;
|
||||
|
||||
if(ast->src == NULL)
|
||||
return NULL;
|
||||
|
||||
return ast;
|
||||
}
|
||||
|
||||
static Node *parse_statement(Context *ctx)
|
||||
{
|
||||
assert(ctx != NULL);
|
||||
|
||||
switch(ctx->token->kind)
|
||||
{
|
||||
case '*':
|
||||
case '/':
|
||||
case TASS:
|
||||
case TDONE:
|
||||
UNREACHABLE;
|
||||
break;
|
||||
|
||||
case '(':
|
||||
case '+':
|
||||
case '-':
|
||||
case TINT:
|
||||
case TFLOAT:
|
||||
case TSTRING:
|
||||
case TIDENT:
|
||||
return parse_expression_statement(ctx);
|
||||
}
|
||||
|
||||
Error_Report(ctx->error, 0, "Got token \"%.*s\" where the start of a statement was expected",
|
||||
ctx->token->length, ctx->src + ctx->token->offset);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static Node *parse_expression_statement(Context *ctx)
|
||||
{
|
||||
assert(ctx != NULL);
|
||||
|
||||
Node *expr = parse_expression(ctx);
|
||||
|
||||
if(expr == NULL)
|
||||
return NULL;
|
||||
|
||||
assert(ctx->token != NULL);
|
||||
|
||||
if(ctx->token->kind == TDONE)
|
||||
// If the source ended before
|
||||
// the final ";", it's ok.
|
||||
return expr;
|
||||
|
||||
if(ctx->token->kind != ';')
|
||||
{
|
||||
// ERROR: Got something other than
|
||||
// a semicolon at the end of statement.
|
||||
Error_Report(ctx->error, 0, "Got token \"%.*s\" where \";\" was expected", ctx->token->length, ctx->src + ctx->token->offset);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ctx->token = ctx->token->next;
|
||||
assert(ctx->token != NULL);
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
static Node *parse_string_primary_expression(Context *ctx)
|
||||
{
|
||||
assert(ctx != NULL);
|
||||
assert(ctx->token->kind == TSTRING);
|
||||
|
||||
const char *src = ctx->src;
|
||||
int len = ctx->token->offset + ctx->token->length - 1;
|
||||
int i = ctx->token->offset + 1;
|
||||
|
||||
assert(src[i-1] == '"' || src[i-1] == '\'');
|
||||
assert(src[len] == '"' || src[len] == '\'');
|
||||
|
||||
char temp[4096];
|
||||
int temp_used = 0;
|
||||
|
||||
do
|
||||
{
|
||||
int segm_off, segm_len;
|
||||
{
|
||||
segm_off = i;
|
||||
|
||||
while(i < len && src[i] != '\\')
|
||||
i += 1;
|
||||
|
||||
segm_len = i - segm_off;
|
||||
}
|
||||
|
||||
if(temp_used + segm_len >= (int) sizeof(temp))
|
||||
{
|
||||
Error_Report(ctx->error, 1, "String is too big to be rendered inside the fixed size buffer");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(temp + temp_used, src + segm_off, segm_len);
|
||||
temp_used += segm_len;
|
||||
|
||||
if(src[i] == '\\')
|
||||
{
|
||||
i += 1; // Consume the \.
|
||||
|
||||
if(temp_used + 1 >= (int) sizeof(temp))
|
||||
{
|
||||
Error_Report(ctx->error, 1, "String is too big to be rendered inside the fixed size buffer");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(i == len)
|
||||
{
|
||||
// Append the \ as a normal char.
|
||||
temp[temp_used++] = '\\';
|
||||
}
|
||||
else
|
||||
{
|
||||
switch(src[i])
|
||||
{
|
||||
case 'n': temp[temp_used++] = '\n'; break;
|
||||
case 't': temp[temp_used++] = '\t'; break;
|
||||
case 'r': temp[temp_used++] = '\r'; break;
|
||||
|
||||
default:
|
||||
Error_Report(ctx->error, 0, "Invalid escape sequence \\%c", src[i]);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
i += 1; // Consume the char after the \.
|
||||
}
|
||||
}
|
||||
}
|
||||
while(i < len);
|
||||
|
||||
assert(temp_used < (int) sizeof(temp));
|
||||
|
||||
temp[temp_used] = '\0';
|
||||
|
||||
char *copy;
|
||||
int copyl;
|
||||
|
||||
{
|
||||
copy = BPAlloc_Malloc(ctx->alloc, temp_used + 1);
|
||||
|
||||
if(copy == NULL)
|
||||
{
|
||||
Error_Report(ctx->error, 1, "No memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
strcpy(copy, temp);
|
||||
copyl = temp_used;
|
||||
}
|
||||
|
||||
StringExprNode *node;
|
||||
{
|
||||
node = BPAlloc_Malloc(ctx->alloc, sizeof(StringExprNode));
|
||||
|
||||
if(node == NULL)
|
||||
{
|
||||
Error_Report(ctx->error, 1, "No memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
node->base.base.kind = NODE_EXPR;
|
||||
node->base.base.next = NULL;
|
||||
node->base.base.offset = ctx->token->offset;
|
||||
node->base.base.length = ctx->token->length;
|
||||
node->base.kind = EXPR_STRING;
|
||||
node->val = copy;
|
||||
node->len = copyl;
|
||||
}
|
||||
|
||||
ctx->token = ctx->token->next;
|
||||
assert(ctx->token != NULL);
|
||||
|
||||
return (Node*) node;
|
||||
}
|
||||
|
||||
static Node *parse_primary_expresion(Context *ctx)
|
||||
{
|
||||
assert(ctx != NULL);
|
||||
|
||||
switch(ctx->token->kind)
|
||||
{
|
||||
case '+':
|
||||
case '-':
|
||||
{
|
||||
Token *operator = ctx->token;
|
||||
|
||||
ctx->token = ctx->token->next;
|
||||
assert(ctx->token != NULL);
|
||||
|
||||
Node *operand = parse_primary_expresion(ctx);
|
||||
|
||||
if(operand == NULL)
|
||||
return NULL;
|
||||
|
||||
OperExprNode *temp = BPAlloc_Malloc(ctx->alloc, sizeof(OperExprNode));
|
||||
{
|
||||
if(temp == NULL)
|
||||
return NULL;
|
||||
|
||||
temp->base.base.kind = NODE_EXPR;
|
||||
temp->base.base.next = NULL;
|
||||
temp->base.base.offset = operator->offset;
|
||||
temp->base.base.length = operand->offset + operand->length - operator->offset;
|
||||
temp->base.kind = operator->kind == '+' ? EXPR_POS : EXPR_NEG;
|
||||
temp->head = operand;
|
||||
temp->count = 1;
|
||||
}
|
||||
return (Node*) temp;
|
||||
}
|
||||
|
||||
case '(':
|
||||
{
|
||||
ctx->token = ctx->token->next;
|
||||
|
||||
Node *node = parse_expression(ctx);
|
||||
|
||||
if(node == NULL)
|
||||
return NULL;
|
||||
|
||||
if(ctx->token->kind != ')')
|
||||
{
|
||||
Error_Report(ctx->error, 0, "Missing \")\", after sub-expression");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ctx->token = ctx->token->next;
|
||||
assert(ctx->token != NULL);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
case TINT:
|
||||
{
|
||||
char buffer[64];
|
||||
|
||||
if(ctx->token->length >= (int) sizeof(buffer))
|
||||
{
|
||||
Error_Report(ctx->error, 1, "Integer is too big");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(buffer, ctx->src + ctx->token->offset, ctx->token->length);
|
||||
buffer[ctx->token->length] = '\0';
|
||||
|
||||
errno = 0;
|
||||
|
||||
long long int val = strtoll(buffer, NULL, 10);
|
||||
|
||||
if(errno == ERANGE)
|
||||
{
|
||||
Error_Report(ctx->error, 1, "Integer is too big");
|
||||
return NULL;
|
||||
}
|
||||
else assert(errno == 0);
|
||||
|
||||
IntExprNode *node;
|
||||
{
|
||||
node = BPAlloc_Malloc(ctx->alloc, sizeof(IntExprNode));
|
||||
|
||||
if(node == NULL)
|
||||
{
|
||||
Error_Report(ctx->error, 1, "No memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
node->base.base.kind = NODE_EXPR;
|
||||
node->base.base.next = NULL;
|
||||
node->base.base.offset = ctx->token->offset;
|
||||
node->base.base.length = ctx->token->length;
|
||||
node->base.kind = EXPR_INT;
|
||||
node->val = val;
|
||||
}
|
||||
|
||||
ctx->token = ctx->token->next;
|
||||
assert(ctx->token != NULL);
|
||||
|
||||
return (Node*) node;
|
||||
}
|
||||
|
||||
case TFLOAT:
|
||||
{
|
||||
char buffer[64];
|
||||
|
||||
if(ctx->token->length >= (int) sizeof(buffer))
|
||||
{
|
||||
Error_Report(ctx->error, 1, "Floating is too big");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(buffer, ctx->src + ctx->token->offset, ctx->token->length);
|
||||
buffer[ctx->token->length] = '\0';
|
||||
|
||||
errno = 0;
|
||||
|
||||
double val = strtod(buffer, NULL);
|
||||
|
||||
if(errno == ERANGE)
|
||||
{
|
||||
Error_Report(ctx->error, 1, "Floating is too big");
|
||||
return NULL;
|
||||
}
|
||||
else assert(errno == 0);
|
||||
|
||||
FloatExprNode *node;
|
||||
{
|
||||
node = BPAlloc_Malloc(ctx->alloc, sizeof(FloatExprNode));
|
||||
|
||||
if(node == NULL)
|
||||
{
|
||||
Error_Report(ctx->error, 1, "No memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
node->base.base.kind = NODE_EXPR;
|
||||
node->base.base.next = NULL;
|
||||
node->base.base.offset = ctx->token->offset;
|
||||
node->base.base.length = ctx->token->length;
|
||||
node->base.kind = EXPR_FLOAT;
|
||||
node->val = val;
|
||||
}
|
||||
|
||||
ctx->token = ctx->token->next;
|
||||
assert(ctx->token != NULL);
|
||||
|
||||
return (Node*) node;
|
||||
}
|
||||
|
||||
case TSTRING:
|
||||
return parse_string_primary_expression(ctx);
|
||||
|
||||
case TIDENT:
|
||||
{
|
||||
char *copy;
|
||||
int copyl;
|
||||
|
||||
{
|
||||
copy = BPAlloc_Malloc(ctx->alloc, ctx->token->length + 1);
|
||||
|
||||
if(copy == NULL)
|
||||
{
|
||||
Error_Report(ctx->error, 1, "No memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
copyl = ctx->token->length;
|
||||
memcpy(copy, ctx->src + ctx->token->offset, copyl);
|
||||
copy[copyl] = '\0';
|
||||
|
||||
}
|
||||
|
||||
IdentExprNode *node;
|
||||
{
|
||||
node = BPAlloc_Malloc(ctx->alloc, sizeof(IdentExprNode));
|
||||
|
||||
if(node == NULL)
|
||||
{
|
||||
Error_Report(ctx->error, 1, "No memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
node->base.base.kind = NODE_EXPR;
|
||||
node->base.base.next = NULL;
|
||||
node->base.base.offset = ctx->token->offset;
|
||||
node->base.base.length = ctx->token->length;
|
||||
node->base.kind = EXPR_IDENT;
|
||||
node->val = copy;
|
||||
node->len = copyl;
|
||||
}
|
||||
|
||||
ctx->token = ctx->token->next;
|
||||
assert(ctx->token != NULL);
|
||||
|
||||
return (Node*) node;
|
||||
}
|
||||
|
||||
case TDONE:
|
||||
Error_Report(ctx->error, 1, "Unexpected end of source where a primary expression was expected");
|
||||
return NULL;
|
||||
|
||||
default:
|
||||
Error_Report(ctx->error, 1, "Unexpected token \"%.*s\" where a primary expression was expected", ctx->token->length, ctx->src + ctx->token->offset);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
UNREACHABLE;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline _Bool isbinop(Token *tok)
|
||||
{
|
||||
assert(tok != NULL);
|
||||
|
||||
return tok->kind == '+' ||
|
||||
tok->kind == '-' ||
|
||||
tok->kind == '*' ||
|
||||
tok->kind == '/';
|
||||
}
|
||||
|
||||
static inline _Bool isrightassoc(Token *tok)
|
||||
{
|
||||
assert(tok != NULL);
|
||||
|
||||
(void) tok;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int precedenceof(Token *tok)
|
||||
{
|
||||
assert(tok != NULL);
|
||||
|
||||
switch(tok->kind)
|
||||
{
|
||||
case '+':
|
||||
case '-':
|
||||
return 1;
|
||||
|
||||
case '*':
|
||||
case '/':
|
||||
return 2;
|
||||
|
||||
default:
|
||||
return -10000000;
|
||||
}
|
||||
|
||||
UNREACHABLE;
|
||||
return -100000000;
|
||||
}
|
||||
|
||||
static Node *parse_expression_2(Context *ctx, Node *left_expr, int min_prec)
|
||||
{
|
||||
while(isbinop(ctx->token) && precedenceof(ctx->token) >= min_prec)
|
||||
{
|
||||
Token *op = ctx->token;
|
||||
|
||||
ctx->token = ctx->token->next;
|
||||
assert(ctx->token != NULL);
|
||||
|
||||
Node *right_expr = parse_primary_expresion(ctx);
|
||||
|
||||
if(right_expr == NULL)
|
||||
return NULL;
|
||||
|
||||
while(isbinop(ctx->token) && (precedenceof(ctx->token) > precedenceof(op) || (precedenceof(ctx->token) == precedenceof(op) && isrightassoc(ctx->token))))
|
||||
{
|
||||
right_expr = parse_expression_2(ctx, right_expr, precedenceof(op) + 1);
|
||||
|
||||
if(right_expr == NULL)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
OperExprNode *temp;
|
||||
{
|
||||
temp = BPAlloc_Malloc(ctx->alloc, sizeof(OperExprNode));
|
||||
|
||||
if(temp == NULL)
|
||||
{
|
||||
Error_Report(ctx->error, 1, "No memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
temp->base.base.kind = NODE_EXPR;
|
||||
temp->base.base.next = NULL;
|
||||
temp->base.base.offset = left_expr->offset;
|
||||
temp->base.base.length = right_expr->offset + right_expr->length - left_expr->offset;
|
||||
|
||||
switch(op->kind)
|
||||
{
|
||||
case '+': temp->base.kind = EXPR_ADD; break;
|
||||
case '-': temp->base.kind = EXPR_SUB; break;
|
||||
case '*': temp->base.kind = EXPR_MUL; break;
|
||||
case '/': temp->base.kind = EXPR_DIV; break;
|
||||
|
||||
default:
|
||||
UNREACHABLE;
|
||||
break;
|
||||
}
|
||||
|
||||
temp->head = left_expr;
|
||||
temp->head->next = right_expr;
|
||||
temp->count = 2;
|
||||
assert(right_expr->next == NULL);
|
||||
}
|
||||
|
||||
left_expr = (Node*) temp;
|
||||
}
|
||||
|
||||
return left_expr;
|
||||
}
|
||||
|
||||
static Node *parse_expression(Context *ctx)
|
||||
{
|
||||
Node *left_expr = parse_primary_expresion(ctx);
|
||||
|
||||
if(left_expr == NULL)
|
||||
return NULL;
|
||||
|
||||
if(ctx->token->kind == TDONE)
|
||||
return left_expr;
|
||||
|
||||
return parse_expression_2(ctx, left_expr, -1000000000);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#ifndef PARSE_H
|
||||
#define PARSE_H
|
||||
#include "../utils/bpalloc.h"
|
||||
#include "../utils/source.h"
|
||||
#include "../utils/error.h"
|
||||
#include "AST.h"
|
||||
AST *parse(Source *src, BPAlloc *alloc, Error *error);
|
||||
#endif
|
||||
@@ -0,0 +1,161 @@
|
||||
#include <assert.h>
|
||||
#include <xjson.h>
|
||||
#include "serialize.h"
|
||||
#include "ASTi.h"
|
||||
|
||||
#define UNREACHABLE assert(0)
|
||||
|
||||
static xj_value *convert_node(Node *node, xj_error *error, xj_alloc **alloc);
|
||||
|
||||
char *serialize(AST *ast, int *len)
|
||||
{
|
||||
xj_alloc *alloc = NULL;
|
||||
xj_error error;
|
||||
|
||||
xj_value *value = convert_node(ast->root, &error, &alloc);
|
||||
|
||||
if(value == NULL)
|
||||
{
|
||||
xj_free(alloc);
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
char *serialized = xj_encode(value, len);
|
||||
xj_free(alloc);
|
||||
return serialized;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *expr_kind_name(ExprNode *expr)
|
||||
{
|
||||
switch(expr->kind)
|
||||
{
|
||||
case EXPR_POS: return "pos";
|
||||
case EXPR_NEG: return "neg";
|
||||
case EXPR_ADD: return "add";
|
||||
case EXPR_SUB: return "sub";
|
||||
case EXPR_MUL: return "mul";
|
||||
case EXPR_DIV: return "div";
|
||||
case EXPR_INT: return "int";
|
||||
case EXPR_FLOAT: return "float";
|
||||
case EXPR_STRING: return "string";
|
||||
case EXPR_IDENT: return "ident";
|
||||
|
||||
default:
|
||||
UNREACHABLE;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
UNREACHABLE;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static xj_value *convert_node_list(Node *head, xj_error *error, xj_alloc **alloc);
|
||||
|
||||
static xj_value *convert_node(Node *node, xj_error *error, xj_alloc **alloc)
|
||||
{
|
||||
switch(node->kind)
|
||||
{
|
||||
case NODE_EXPR:
|
||||
ExprNode *expr = (ExprNode*) node;
|
||||
switch(expr->kind)
|
||||
{
|
||||
case EXPR_POS:
|
||||
case EXPR_NEG:
|
||||
case EXPR_ADD:
|
||||
case EXPR_SUB:
|
||||
case EXPR_MUL:
|
||||
case EXPR_DIV:
|
||||
{
|
||||
OperExprNode *oper = (OperExprNode*) expr;
|
||||
|
||||
xj_value *operands = NULL;
|
||||
|
||||
if(oper->count > 0)
|
||||
{
|
||||
operands = convert_node_list((Node*) oper->head, error, alloc);
|
||||
|
||||
if(operands == NULL)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return xj_decodef(error, alloc,
|
||||
"{"
|
||||
"\t\"node-kind\": \"expr\", \n"
|
||||
"\t\"expr-kind\": %Q, \n"
|
||||
"\t\"operands\": %L\n"
|
||||
"}", expr_kind_name(expr), operands);
|
||||
}
|
||||
|
||||
case EXPR_INT:
|
||||
return xj_decodef(error, alloc,
|
||||
"{"
|
||||
"\t\"node-kind\": \"expr\", \n"
|
||||
"\t\"expr-kind\": %Q, \n"
|
||||
"\t\"value\": %d\n"
|
||||
"}", expr_kind_name(expr), ((IntExprNode*) expr)->val);
|
||||
|
||||
case EXPR_FLOAT:
|
||||
return xj_decodef(error, alloc,
|
||||
"{"
|
||||
"\t\"node-kind\": \"expr\", \n"
|
||||
"\t\"expr-kind\": %Q, \n"
|
||||
"\t\"value\": %f\n"
|
||||
"}", expr_kind_name(expr), ((FloatExprNode*) expr)->val);
|
||||
|
||||
case EXPR_STRING:
|
||||
return xj_decodef(error, alloc,
|
||||
"{"
|
||||
"\t\"node-kind\": \"expr\", \n"
|
||||
"\t\"expr-kind\": %Q, \n"
|
||||
"\t\"value\": %Q\n"
|
||||
"}", expr_kind_name(expr), ((StringExprNode*) expr)->val);
|
||||
|
||||
case EXPR_IDENT:
|
||||
return xj_decodef(error, alloc,
|
||||
"{"
|
||||
"\t\"node-kind\": \"expr\", \n"
|
||||
"\t\"expr-kind\": %Q, \n"
|
||||
"\t\"value\": %Q\n"
|
||||
"}", expr_kind_name(expr), ((IdentExprNode*) expr)->val);
|
||||
|
||||
default:
|
||||
UNREACHABLE;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
UNREACHABLE;
|
||||
break;
|
||||
}
|
||||
|
||||
UNREACHABLE;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static xj_value *convert_node_list(Node *head, xj_error *error, xj_alloc **alloc)
|
||||
{
|
||||
xj_value *json_head = NULL;
|
||||
xj_value **json_tail = &json_head;
|
||||
|
||||
Node *curs = head;
|
||||
while(curs)
|
||||
{
|
||||
xj_value *temp = convert_node(curs, error, alloc);
|
||||
|
||||
if(temp == NULL)
|
||||
return NULL;
|
||||
|
||||
temp->next = NULL;
|
||||
|
||||
*json_tail = temp;
|
||||
json_tail = &temp->next;
|
||||
|
||||
curs = curs->next;
|
||||
}
|
||||
|
||||
return json_head;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#ifndef SERIALIZE_H
|
||||
#define SERIALIZE_H
|
||||
#include "AST.h"
|
||||
char *serialize(AST *ast, int *len);
|
||||
#endif
|
||||
Reference in New Issue
Block a user