|
@@ -31,13 +31,17 @@ static ast_node_t *parse_call(token_t *ts);
|
|
|
static ast_node_t *parse_var(token_t *ts);
|
|
|
static ast_node_t *parse_if(token_t *ts);
|
|
|
static ast_node_t *parse_expr(token_t *ts, tokentype delimiter);
|
|
|
+static ast_node_t *rpnstack_to_ast(stack_t *rpn);
|
|
|
static void emit_node(ast_node_list_t *nl, ast_node_t *node);
|
|
|
static token_t *peek(token_t *ts);
|
|
|
static token_t *eat(token_t *ts);
|
|
|
|
|
|
bool PARSE_FAILED = false;
|
|
|
|
|
|
-static const match_type_t data_types[] = {
|
|
|
+static const struct {
|
|
|
+ char *name;
|
|
|
+ ast_type_t type;
|
|
|
+} data_types[] = {
|
|
|
{INT_KEYWORD , INT},
|
|
|
{I8_KEYWORD , I8},
|
|
|
{I16_KEYWORD , I16},
|
|
@@ -48,6 +52,7 @@ static const match_type_t data_types[] = {
|
|
|
{U32_KEYWORD , U32},
|
|
|
{U64_KEYWORD , U64},
|
|
|
{STRING_KEYWORD , STR},
|
|
|
+ {CHAR_KEYWORD , CHAR},
|
|
|
{BOOL_KEYWORD , BOOL},
|
|
|
{FLOAT_KEYWORD , FLOAT},
|
|
|
{F32_KEYWORD , F32},
|
|
@@ -82,17 +87,22 @@ static const ast_op_t operators[] = {
|
|
|
{TOKEN_LT_EQ , 7 , ASSOC_LEFT , false},
|
|
|
{TOKEN_GT_EQ , 7 , ASSOC_LEFT , false},
|
|
|
|
|
|
+ /* bitwise shifts */
|
|
|
+ {TOKEN_L_SHIFT , 7 , ASSOC_RIGHT , false},
|
|
|
+ {TOKEN_R_SHIFT , 7 , ASSOC_RIGHT , false},
|
|
|
+
|
|
|
/* mathematical operators */
|
|
|
- {TOKEN_PLUS , 8 , ASSOC_LEFT , false},
|
|
|
- {TOKEN_MIN , 8 , ASSOC_LEFT , false},
|
|
|
- {TOKEN_STAR , 9 , ASSOC_LEFT , false},
|
|
|
- {TOKEN_SLASH , 9 , ASSOC_LEFT , false},
|
|
|
- {TOKEN_MOD , 9 , ASSOC_LEFT , false},
|
|
|
- {TOKEN_STAR_STAR , 10 , ASSOC_RIGHT , false},
|
|
|
+ {TOKEN_PLUS , 9 , ASSOC_LEFT , false},
|
|
|
+ {TOKEN_MIN , 9 , ASSOC_LEFT , false},
|
|
|
+ {TOKEN_STAR , 10 , ASSOC_LEFT , false},
|
|
|
+ {TOKEN_SLASH , 10 , ASSOC_LEFT , false},
|
|
|
+ {TOKEN_MOD , 10 , ASSOC_LEFT , false},
|
|
|
+ {TOKEN_STAR_STAR , 11 , ASSOC_RIGHT , false},
|
|
|
|
|
|
/* unary operators */
|
|
|
- {TOKEN_BANG , 11 , ASSOC_RIGHT , true},
|
|
|
- {TOKEN_B_NOT , 11 , ASSOC_RIGHT , true},
|
|
|
+ {TOKEN_MIN , 12 , ASSOC_RIGHT , true},
|
|
|
+ {TOKEN_BANG , 12 , ASSOC_RIGHT , true},
|
|
|
+ {TOKEN_B_NOT , 12 , ASSOC_RIGHT , true},
|
|
|
|
|
|
/* misc */
|
|
|
{TOKEN_L_PAREN , 20 , ASSOC_NONE , false}
|
|
@@ -109,15 +119,15 @@ static bool is_operator(tokentype token)
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
-static ast_op_t get_operator(tokentype token)
|
|
|
+static const ast_op_t *get_operator(tokentype token)
|
|
|
{
|
|
|
size_t ops_len = sizeof(operators) / sizeof(operators[0]);
|
|
|
|
|
|
for (size_t i = 0; i < ops_len; ++i)
|
|
|
if (token == operators[i].symbol)
|
|
|
- return operators[i];
|
|
|
+ return &operators[i];
|
|
|
|
|
|
- return (ast_op_t) { TOKEN_ERROR, 0, ASSOC_NONE, false };
|
|
|
+ return NULL;
|
|
|
}
|
|
|
|
|
|
static ast_type_t get_type(char *name)
|
|
@@ -153,8 +163,7 @@ static ast_node_t *parse_top_level(token_t *ts)
|
|
|
case TOKEN_IMPORT:
|
|
|
/* TODO: parse import */
|
|
|
default:
|
|
|
- parser_file_error("unexpected '%s' on line %zu\n",
|
|
|
- ts->value, ts->line_n);
|
|
|
+ parser_file_error(ts->line_n, "unexpected '%s'\n", ts->value);
|
|
|
}
|
|
|
|
|
|
return NULL;
|
|
@@ -164,7 +173,11 @@ static ast_node_t *parse_top_level(token_t *ts)
|
|
|
* parse a block level declaration
|
|
|
*
|
|
|
* possible syntax:
|
|
|
- * alot
|
|
|
+ * mut Char var ...
|
|
|
+ * x += 3
|
|
|
+ * if x < 5 ... | unless x < 5 ...
|
|
|
+ * while x < 5 ...
|
|
|
+ * return x
|
|
|
*/
|
|
|
static ast_node_t *parse_block_level(token_t *ts)
|
|
|
{
|
|
@@ -181,11 +194,9 @@ static ast_node_t *parse_block_level(token_t *ts)
|
|
|
case TOKEN_RETURN:
|
|
|
/* TODO: parse return */
|
|
|
case TOKEN_EOF:
|
|
|
- file_fatal_error("unclosed block on line %zu\n",
|
|
|
- ts->line_n);
|
|
|
+ file_fatal_error(ts->line_n, "unclosed block\n");
|
|
|
default:
|
|
|
- parser_file_error("unexpected '%s' on line %zu\n",
|
|
|
- ts->value, ts->line_n);
|
|
|
+ parser_file_error(ts->line_n, "unexpected '%s'\n", ts->value);
|
|
|
}
|
|
|
|
|
|
return NULL;
|
|
@@ -198,8 +209,7 @@ static ast_node_list_t *parse_block(token_t *ts)
|
|
|
|
|
|
/* empty body */
|
|
|
if (ts->type == TOKEN_R_BRACE) {
|
|
|
- file_warning("empty block on line %zu\n",
|
|
|
- ts->line_n);
|
|
|
+ file_warning(ts->line_n, "empty block\n");
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
@@ -238,7 +248,7 @@ static ast_node_t *parse_fn(token_t *ts)
|
|
|
} else if (ts->type == TOKEN_L_BRACE) {
|
|
|
body = parse_block(ts);
|
|
|
} else {
|
|
|
- parser_file_error("expected semi-colon or block on line %zu\n", ts->line_n);
|
|
|
+ parser_file_error(ts->line_n, "expected semi-colon or block");
|
|
|
}
|
|
|
|
|
|
return create_fn(prototype, body);
|
|
@@ -261,6 +271,21 @@ static ast_node_t *parse_ident(token_t *ts)
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
+ * a token in an expression can be either an identifier or number. this creates
|
|
|
+ * the appropriate one
|
|
|
+ */
|
|
|
+static ast_node_t *parse_expr_val(token_t token)
|
|
|
+{
|
|
|
+ if (token.type == TOKEN_IDENT)
|
|
|
+ return create_var(token.value, UNKNOWN, NULL, NULL);
|
|
|
+
|
|
|
+ if (token.type == TOKEN_NUMBER)
|
|
|
+ return create_number(strtof(token.value, NULL));
|
|
|
+
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
* fun func(...) -> Int32 ...
|
|
|
* fun func(...) ...
|
|
|
* fun func ...
|
|
@@ -275,8 +300,7 @@ static ast_node_t *parse_fn_proto(token_t *ts)
|
|
|
eat(ts);
|
|
|
|
|
|
if (ts->type != TOKEN_IDENT)
|
|
|
- parser_file_error("illegal function name '%s' on line %zu\n",
|
|
|
- ts->value, ts->line_n);
|
|
|
+ parser_file_error(ts->line_n, "illegal function name '%s'", ts->value);
|
|
|
|
|
|
name = ts->value;
|
|
|
|
|
@@ -289,13 +313,12 @@ static ast_node_t *parse_fn_proto(token_t *ts)
|
|
|
if (peek(ts)->type == TOKEN_R_ARROW) {
|
|
|
eat(ts);
|
|
|
if ((data_type = get_type(eat(ts)->value)) == UNKNOWN)
|
|
|
- parser_file_error("unknown data type '%s' on line %zu in function '%s'\n",
|
|
|
- ts->value, ts->line_n, name);
|
|
|
+ parser_file_error(ts->line_n, "unknown data type '%s' function '%s'\n",
|
|
|
+ ts->value, name);
|
|
|
} else {
|
|
|
/* warn the user if a function besides main doesnt explicitly declare a return type */
|
|
|
if (strcmp(name, "main") != 0)
|
|
|
- file_warning("function '%s' has no return type set on line %zu\n",
|
|
|
- name, ts->line_n);
|
|
|
+ file_warning(ts->line_n, "function '%s' has no return type set\n", name);
|
|
|
}
|
|
|
|
|
|
/* eat the ) or type declaration token */
|
|
@@ -307,8 +330,7 @@ static ast_node_t *parse_fn_proto(token_t *ts)
|
|
|
static ast_node_list_t *parse_fn_params(token_t *ts)
|
|
|
{
|
|
|
if (ts->type == TOKEN_EOF)
|
|
|
- parser_file_error("unclosed function parameters on line %zu\n",
|
|
|
- ts->line_n);
|
|
|
+ parser_file_error(ts->line_n, "unclosed function parameters\n");
|
|
|
|
|
|
while (ts->type != TOKEN_R_PAREN)
|
|
|
eat(ts);
|
|
@@ -341,12 +363,10 @@ static ast_node_t *parse_var(token_t *ts)
|
|
|
eat(ts);
|
|
|
|
|
|
if ((type = get_type(ts->value)) == UNKNOWN)
|
|
|
- parser_file_error("unknown data type '%s' on line %zu\n",
|
|
|
- ts->value, ts->line_n);
|
|
|
+ parser_file_error(ts->line_n, "unknown data type '%s'\n", ts->value);
|
|
|
|
|
|
if (eat(ts)->type != TOKEN_IDENT)
|
|
|
- parser_file_error("illegal variable name '%s' on line %zu\n",
|
|
|
- ts->value, ts->line_n);
|
|
|
+ parser_file_error(ts->line_n, "illegal variable name '%s'\n", ts->value);
|
|
|
|
|
|
name = ts->value;
|
|
|
|
|
@@ -357,8 +377,7 @@ static ast_node_t *parse_var(token_t *ts)
|
|
|
else if (is_operator(ts->type))
|
|
|
return create_var(name, type, mutable, parse_expr(ts, TOKEN_SEMI_COLON));
|
|
|
|
|
|
- parser_file_error("expected semi-colon or assignment, got '%s' on line %zu\n",
|
|
|
- ts->value, ts->line_n);
|
|
|
+ parser_file_error(ts->line_n, "expected semi-colon or assignment, got '%s'\n", ts->value);
|
|
|
return create_var(name, type, mutable, NULL);
|
|
|
}
|
|
|
|
|
@@ -388,9 +407,8 @@ static ast_node_t *parse_if(token_t *ts)
|
|
|
|
|
|
static ast_node_t *parse_expr(token_t *ts, tokentype delimiter)
|
|
|
{
|
|
|
- /* eat assignment token */
|
|
|
- eat(ts);
|
|
|
-
|
|
|
+ /* shut up when an error gets printed so we don't spam the console */
|
|
|
+ bool shut_up = false;
|
|
|
size_t line_start = ts->line_n;
|
|
|
stack_t *operators = smalloc(sizeof(stack_t));
|
|
|
stack_t *output = smalloc(sizeof(stack_t));
|
|
@@ -400,13 +418,18 @@ static ast_node_t *parse_expr(token_t *ts, tokentype delimiter)
|
|
|
/* an identifier or number is expected after an
|
|
|
operator, except when the current operator is
|
|
|
anything but left-associated */
|
|
|
- if (is_operator(prev.type) && get_operator(ts->type).assoc == ASSOC_LEFT)
|
|
|
- parser_file_error("unexpected '%s' in expression on line %zu\n",
|
|
|
- ts->value, ts->line_n);
|
|
|
+ if (is_operator(prev.type) &&
|
|
|
+ get_operator(ts->type)->assoc == ASSOC_LEFT) {
|
|
|
+ if (!shut_up) {
|
|
|
+ parser_file_error(ts->line_n, "unexpected '%s' in expression\n",
|
|
|
+ ts->value);
|
|
|
+ shut_up = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
if (stack_size(operators) >= 2 &&
|
|
|
- get_operator(ts->type).assoc == ASSOC_LEFT &&
|
|
|
- get_operator(ts->type).precedence >=
|
|
|
- get_operator(stack_top(&operators).type).precedence) {
|
|
|
+ get_operator(ts->type)->assoc == ASSOC_LEFT &&
|
|
|
+ get_operator(ts->type)->precedence >=
|
|
|
+ get_operator(stack_top(&operators).type)->precedence) {
|
|
|
stack_push(&output, stack_pop(&operators));
|
|
|
}
|
|
|
|
|
@@ -414,10 +437,12 @@ static ast_node_t *parse_expr(token_t *ts, tokentype delimiter)
|
|
|
} else if (ts->type == TOKEN_IDENT || ts->type == TOKEN_NUMBER) {
|
|
|
/* an operator is expected after an identifier, number
|
|
|
or (. any other token is invalid */
|
|
|
- if (prev.type == TOKEN_IDENT || prev.type == TOKEN_NUMBER ||
|
|
|
- prev.type == TOKEN_R_PAREN)
|
|
|
- parser_file_error("unexpected '%s' in expression on line %zu\n",
|
|
|
- ts->value, ts->line_n);
|
|
|
+ if ((prev.type == TOKEN_IDENT || prev.type == TOKEN_NUMBER ||
|
|
|
+ prev.type == TOKEN_R_PAREN) && !shut_up) {
|
|
|
+ parser_file_error(ts->line_n, "unexpected '%s' in expression\n",
|
|
|
+ ts->value);
|
|
|
+ shut_up = true;
|
|
|
+ }
|
|
|
stack_push(&output, *ts);
|
|
|
} else if (ts->type == TOKEN_IF || ts->type == TOKEN_UNLESS) {
|
|
|
bool is_unless = ts->type == TOKEN_UNLESS;
|
|
@@ -429,9 +454,9 @@ static ast_node_t *parse_expr(token_t *ts, tokentype delimiter)
|
|
|
} else if (ts->type == TOKEN_R_PAREN) {
|
|
|
/* pop all operators to output stack until ( is found */
|
|
|
while (stack_top(&operators).type != TOKEN_L_PAREN) {
|
|
|
- if (!stack_top(&operators).type) {
|
|
|
- parser_file_error("unmatched ')' in expression on line %zu\n",
|
|
|
- ts->line_n);
|
|
|
+ if (!stack_top(&operators).type && !shut_up) {
|
|
|
+ parser_file_error(ts->line_n, "unmatched ')' in expression\n");
|
|
|
+ shut_up = true;
|
|
|
break;
|
|
|
}
|
|
|
|
|
@@ -442,11 +467,13 @@ static ast_node_t *parse_expr(token_t *ts, tokentype delimiter)
|
|
|
stack_pop(&operators);
|
|
|
} else {
|
|
|
if (ts->type == TOKEN_EOF)
|
|
|
- file_fatal_error("unterminated expression on line %zu\n",
|
|
|
- line_start);
|
|
|
+ file_fatal_error(line_start, "unterminated expression\n");
|
|
|
|
|
|
- parser_file_error("unexpected token '%s' in expression on line %zu\n",
|
|
|
- ts->value, ts->line_n);
|
|
|
+ if (!shut_up) {
|
|
|
+ parser_file_error(ts->line_n, "unexpected token '%s' in expression\n",
|
|
|
+ ts->value);
|
|
|
+ shut_up = true;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -456,9 +483,21 @@ static ast_node_t *parse_expr(token_t *ts, tokentype delimiter)
|
|
|
|
|
|
stack_dump(output);
|
|
|
|
|
|
- free(operators);
|
|
|
+ return rpnstack_to_ast(output);
|
|
|
+}
|
|
|
|
|
|
- return NULL;
|
|
|
+static ast_node_t *rpnstack_to_ast(stack_t *rpn)
|
|
|
+{
|
|
|
+ if (!rpn)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ ast_node_t *expression;
|
|
|
+
|
|
|
+ do {
|
|
|
+ printf("%s\n", stack_pop(&rpn).value);
|
|
|
+ } while (stack_size(rpn));
|
|
|
+
|
|
|
+ return expression;
|
|
|
}
|
|
|
|
|
|
static void emit_node(ast_node_list_t *nl, ast_node_t *node)
|