c-scripting/examples/expression_parser.c

346 lines
9.7 KiB
C

/*
** C Scripting - Expression Parser
*/
#define SY__MAIN 1
#include "symbol_set.h"
#include "symbol_set.init.h"
#include "examples.base.h"
#include "expression_parser.h"
#include "examples.base.c"
////////////////////////////////
// Operator Functions
static U32
exp_operator_id_from_string(String8 string){
// TODO(allen): bake accelerator ahead of time for this path
U32 operator_id = 0;
for (SyEachID(EXP_OPERATOR, id)){
EXP_Operator *op = SyAddressFromID(EXP_OPERATOR, id);
if (str8_match(op->token_str, string)){
operator_id = id;
break;
}
}
return(operator_id);
}
////////////////////////////////
// Lexer Functions
static EXP_TokenArray
exp_lex(Arena *arena, String8 text){
// Static Map of char -> TokenKind
static U8 token_kind_table[127] = {0};
static B8 token_kinds_initialized = 0;
if (!token_kinds_initialized){
token_kind_table[' '] = token_kind_table['\n'] =
token_kind_table['\t'] = token_kind_table['\r'] =
token_kind_table['\f'] = token_kind_table['\v'] = EXP_TokenKind_Whitespace;
token_kind_table['`'] = token_kind_table['~'] =
token_kind_table['!'] = token_kind_table['@'] =
token_kind_table['#'] = token_kind_table['$'] =
token_kind_table['%'] = token_kind_table['^'] =
token_kind_table['&'] = token_kind_table['*'] =
token_kind_table['='] = token_kind_table['-'] =
token_kind_table['+'] = token_kind_table['\\'] =
token_kind_table['|'] = token_kind_table[':'] =
token_kind_table[','] = token_kind_table['<'] =
token_kind_table['.'] = token_kind_table['>'] =
token_kind_table['/'] = token_kind_table['?'] = EXP_TokenKind_Operator;
for (U32 i = 'a'; i <= 'z'; i += 1){
token_kind_table[i] = EXP_TokenKind_IDNum;
}
for (U32 i = 'A'; i <= 'Z'; i += 1){
token_kind_table[i] = EXP_TokenKind_IDNum;
}
for (U32 i = '0'; i <= '9'; i += 1){
token_kind_table[i] = EXP_TokenKind_IDNum;
}
token_kind_table['('] = EXP_TokenKind_OpenParen|(1 << 7);
token_kind_table[')'] = EXP_TokenKind_CloseParen|(1 << 7);
token_kind_table['['] = EXP_TokenKind_OpenBracket|(1 << 7);
token_kind_table[']'] = EXP_TokenKind_CloseBracket|(1 << 7);
token_kind_table['{'] = EXP_TokenKind_OpenBrace|(1 << 7);
token_kind_table['}'] = EXP_TokenKind_CloseBrace|(1 << 7);
}
// over-allocate tokens
U32 token_cap = text.size + 1;
EXP_Token *tokens = push_array(arena, EXP_Token, token_cap);
// fill tokens array
EXP_Token *tp = tokens;
U8 *ptr = text.str;
U8 *opl = text.str + text.size;
for (;ptr < opl;){
U32 kind = EXP_TokenKind_IDNum;
if (*ptr < 127){
kind = token_kind_table[*ptr];
}
tp->pos = (U32)(ptr - text.str);
tp->kind = kind&~(1 << 7);
if (kind&(1 << 7)){
ptr += 1;
}
else{
for (ptr += 1;
ptr < opl && token_kind_table[*ptr] == kind;
ptr += 1);
}
tp += 1;
}
tp->pos = (U32)text.size;
tp->kind = EXP_TokenKind_EndOfFile;
tp += 1;
// put back extra memory
U32 token_count = (U32)(tp - tokens);
arena_pop_amount(arena, sizeof(*tokens)*(token_cap - token_count));
// arrange as result
EXP_TokenArray result = {0};
result.tokens = tokens;
result.count = token_count - 1;
return(result);
}
////////////////////////////////
// Parser Functions
EXP_PRECEDENCE(1, Multiplicative);
EXP_PRECEDENCE(2, Additive);
EXP_PRECEDENCE(3, Assignment, .right_to_left = 1);
EXP_OPERATOR(Multiply, "*", Multiplicative);
EXP_OPERATOR(Divide, "/", Multiplicative);
EXP_OPERATOR(Modulo, "%", Multiplicative);
EXP_OPERATOR(Add, "+", Additive);
EXP_OPERATOR(Subtract, "-", Additive);
EXP_OPERATOR(Assign, "=", Assignment);
EXP_OPERATOR(MulAssign, "*=", Assignment);
EXP_OPERATOR(DivAssign, "/=", Assignment);
EXP_OPERATOR(AddAssign, "+=", Assignment);
EXP_OPERATOR(SubAssign, "-=", Assignment);
static EXP_ParseNode*
exp_parse(Arena *arena, String8 text, EXP_TokenArray tokens){
EXP_ParseCtx ctx = {0};
ctx.text = text;
ctx.tokens = tokens;
ctx.token = tokens.tokens;
EXP_ParseNode *result = exp__parse_op(arena, &ctx);
return(result);
}
static EXP_ParseNode*
exp__parse_op_rec(Arena *arena, EXP_ParseCtx *ctx,
EXP_ParseNode *left, U32 max_precedence_level){
// read first operator
exp__parse_skip_whitespace(ctx);
EXP_OperatorRead peak_op = exp__parse_read_operator(ctx);
for (;;){
EXP_OperatorRead left_op = peak_op;
// end loop when precedence level increases (or no operator next)
if (left_op.operator_id == 0 ||
left_op.precedence_level > max_precedence_level){
break;
}
// consume left op
ctx->token += 1;
// read right leaf
exp__parse_skip_whitespace(ctx);
EXP_ParseNode *right = exp__parse_leaf(arena, ctx);
// apply stickier right hand operators
for (;;){
// read right operator
exp__parse_skip_whitespace(ctx);
peak_op = exp__parse_read_operator(ctx);
// check precedence
if (peak_op.operator_id == 0 ||
peak_op.precedence_level > left_op.precedence_level ||
(peak_op.precedence_level == left_op.precedence_level && !peak_op.right_to_left)){
break;
}
// recursively fold onto right
right = exp__parse_op_rec(arena, ctx, right, peak_op.precedence_level);
}
// combine left & right with operator
{
EXP_ParseNode *node = push_array(arena, EXP_ParseNode, 1);
node->kind = EXP_ParseNodeKind_Operator;
node->op.operator_id = left_op.operator_id;
node->op.child[0] = left;
node->op.child[1] = right;
left = node;
}
}
done:;
return(left);
}
static EXP_ParseNode*
exp__parse_op(Arena *arena, EXP_ParseCtx *ctx){
EXP_ParseNode *left = exp__parse_leaf(arena, ctx);
EXP_ParseNode *result = exp__parse_op_rec(arena, ctx, left, 3);
return(result);
}
static EXP_ParseNode*
exp__parse_leaf(Arena *arena, EXP_ParseCtx *ctx){
EXP_ParseNode *result = 0;
exp__parse_skip_whitespace(ctx);
// check next token
switch (ctx->token->kind){
case EXP_TokenKind_OpenParen: {
ctx->token += 1;
result = exp__parse_op(arena, ctx);
if (ctx->error) goto done;
if (ctx->token->kind == EXP_TokenKind_CloseParen){
ctx->token += 1;
}
else{
exp__parse_errorf(arena, ctx, "Expected ')'");
goto done;
}
}break;
case EXP_TokenKind_IDNum: {
result = push_array(arena, EXP_ParseNode, 1);
result->kind = EXP_ParseNodeKind_Leaf;
result->leaf.token_idx = (U32)(ctx->token - ctx->tokens.tokens);
ctx->token += 1;
}break;
}
done:;
return(result);
}
static void
exp__parse_errorf(Arena *arena, EXP_ParseCtx *ctx, char *fmt, ...){
if (ctx->error == 0){
va_list args;
va_start(args, fmt);
String8 string = str8_pushfv(arena, fmt, args);
va_end(args);
ctx->error = 1;
ctx->error_token_idx = (U32)(ctx->token - ctx->tokens.tokens);
ctx->error_message = string;
}
}
static void
exp__parse_skip_whitespace(EXP_ParseCtx *ctx){
if (ctx->token->kind == EXP_TokenKind_Whitespace){
ctx->token += 1;
}
}
static EXP_OperatorRead
exp__parse_read_operator(EXP_ParseCtx *ctx){
EXP_OperatorRead result = {0};
if (ctx->token->kind == EXP_TokenKind_Operator){
String8 operator_text = str8(ctx->text.str + ctx->token->pos,
(ctx->token + 1)->pos - ctx->token->pos);
result.operator_id = exp_operator_id_from_string(operator_text);
if (result.operator_id != 0){
// TODO(allen): [operator] -> precedence_level accelerator
EXP_Operator *op = SyAddressFromID(EXP_OPERATOR, result.operator_id);
EXP_Precedence *precedence = SyAddressFromID(EXP_PRECEDENCE, op->precedence_id);
result.precedence_level = precedence->order;
result.right_to_left = precedence->right_to_left;
}
}
return(result);
}
static void
exp_parse_node_print(String8 text, EXP_TokenArray tokens,
EXP_ParseNode *node, S32 indent){
printf("%.*s", indent, " ");
switch (node->kind){
case EXP_ParseNodeKind_Operator: {
EXP_Operator *op = SyAddressFromID(EXP_OPERATOR, node->op.operator_id);
printf("%.*s\n", (U32)op->token_str.size, op->token_str.str);
exp_parse_node_print(text, tokens, node->op.child[0], indent + 1);
exp_parse_node_print(text, tokens, node->op.child[1], indent + 1);
}break;
case EXP_ParseNodeKind_Leaf: {
EXP_Token *token = &tokens.tokens[node->leaf.token_idx];
String8 string = str8(text.str + token->pos, (token + 1)->pos - token->pos);
printf("%.*s\n", (U32)string.size, string.str);
}break;
}
}
////////////////////////////////
// Entrpy Point
int main(void){
sy__run_init();
Arena arena = arena_alloc((10 << 20));
#if 0
String8 text = str8_const("((FooBar + 100 ** 3 ^;;\r\n\t\t 17){{[[?/:::~`]]}})");
EXP_TokenArray tokens = exp_lex(&arena, text);
for (U32 i = 0; i < tokens.count; i += 1){
EXP_Token *token = &tokens.tokens[i];
char *token_kind_str = 0;
switch (token->kind){
#define X(N) case EXP_TokenKind_##N: token_kind_str = #N; break;
EXP_TokenKindXList(X)
#undef X
}
U32 token_size = (token + 1)->pos - token->pos;
printf("TOKEN %s: '%.*s'\n", token_kind_str, token_size, text.str + token->pos);
}
#endif
String8 text = str8_const("1 + 2 = a - b + c /= a + b * c = (a + b) / (x += c) = d");
EXP_TokenArray tokens = exp_lex(&arena, text);
EXP_ParseNode *root = exp_parse(&arena, text, tokens);
exp_parse_node_print(text, tokens, root, 0);
return(0);
}
/* TODO
** C-Scripted Interpretations
**
** C-Scripted Prefix Postfix Operators
**
** C-Scripted Bracket Operators
**
** C-Scripted Structure Builder
*/