|
@@ -30,15 +30,15 @@ static ast_node_list_t *parse_fn_params(token_t *ts);
|
|
|
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);
|
|
|
+static ast_node_t *parse_expr(token_t *ts, tokentype delimiter);
|
|
|
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;
|
|
|
|
|
|
-/* string => ast_type_t "map" for detecting types */
|
|
|
-static match_type_t data_types[] = {
|
|
|
+static const match_type_t data_types[] = {
|
|
|
+ {INT_KEYWORD , INT},
|
|
|
{I8_KEYWORD , I8},
|
|
|
{I16_KEYWORD , I16},
|
|
|
{I32_KEYWORD , I32},
|
|
@@ -50,19 +50,52 @@ static match_type_t data_types[] = {
|
|
|
{STRING_KEYWORD , STR},
|
|
|
{BOOL_KEYWORD , BOOL},
|
|
|
{FLOAT_KEYWORD , FLOAT},
|
|
|
+ {F32_KEYWORD , F32},
|
|
|
+ {F64_KEYWORD , F64},
|
|
|
{NONE_KEYWORD , NONE}
|
|
|
};
|
|
|
|
|
|
-static ast_op_t operators[] = {
|
|
|
- {TOKEN_PLUS , 5 , ASSOC_LEFT , false},
|
|
|
- {TOKEN_MIN , 5 , ASSOC_LEFT , false},
|
|
|
- {TOKEN_STAR , 7 , ASSOC_LEFT , false},
|
|
|
- {TOKEN_SLASH , 7 , ASSOC_LEFT , false},
|
|
|
- {TOKEN_PLUS_EQ , 0 , ASSOC_NONE , false},
|
|
|
- {TOKEN_MIN_EQ , 0 , ASSOC_NONE , false},
|
|
|
- {TOKEN_SLASH_EQ , 0 , ASSOC_NONE , false},
|
|
|
- {TOKEN_SLASH_EQ , 0 , ASSOC_NONE , false},
|
|
|
- {TOKEN_EQ , 10 , ASSOC_NONE , false}
|
|
|
+static const ast_op_t operators[] = {
|
|
|
+ /* assignment operators */
|
|
|
+ {TOKEN_EQ , 0 , ASSOC_NONE , false},
|
|
|
+ {TOKEN_PLUS_EQ , 0 , ASSOC_NONE , false},
|
|
|
+ {TOKEN_MIN_EQ , 0 , ASSOC_NONE , false},
|
|
|
+ {TOKEN_STAR_EQ , 0 , ASSOC_NONE , false},
|
|
|
+ {TOKEN_SLASH_EQ , 0 , ASSOC_NONE , false},
|
|
|
+ {TOKEN_MOD_EQ , 0 , ASSOC_NONE , false},
|
|
|
+ {TOKEN_B_AND_EQ , 0 , ASSOC_NONE , false},
|
|
|
+ {TOKEN_B_OR_EQ , 0 , ASSOC_NONE , false},
|
|
|
+ {TOKEN_B_XOR_EQ , 0 , ASSOC_NONE , false},
|
|
|
+
|
|
|
+ /* (bitwise) logical operators */
|
|
|
+ {TOKEN_OR , 1 , ASSOC_RIGHT , false},
|
|
|
+ {TOKEN_AND , 2 , ASSOC_RIGHT , false},
|
|
|
+ {TOKEN_B_OR , 3 , ASSOC_RIGHT , false},
|
|
|
+ {TOKEN_B_XOR , 4 , ASSOC_RIGHT , false},
|
|
|
+ {TOKEN_B_AND , 5 , ASSOC_RIGHT , false},
|
|
|
+
|
|
|
+ /* comparison operators */
|
|
|
+ {TOKEN_EQ_EQ , 6 , ASSOC_LEFT , false},
|
|
|
+ {TOKEN_NOT_EQ , 6 , ASSOC_LEFT , false},
|
|
|
+ {TOKEN_LT , 7 , ASSOC_LEFT , false},
|
|
|
+ {TOKEN_GT , 7 , ASSOC_LEFT , false},
|
|
|
+ {TOKEN_LT_EQ , 7 , ASSOC_LEFT , false},
|
|
|
+ {TOKEN_GT_EQ , 7 , ASSOC_LEFT , 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},
|
|
|
+
|
|
|
+ /* unary operators */
|
|
|
+ {TOKEN_BANG , 11 , ASSOC_RIGHT , true},
|
|
|
+ {TOKEN_B_NOT , 11 , ASSOC_RIGHT , true},
|
|
|
+
|
|
|
+ /* misc */
|
|
|
+ {TOKEN_L_PAREN , 20 , ASSOC_NONE , false}
|
|
|
};
|
|
|
|
|
|
static bool is_operator(tokentype token)
|
|
@@ -76,6 +109,17 @@ static bool is_operator(tokentype token)
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
+static 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 (ast_op_t) { TOKEN_ERROR, 0, ASSOC_NONE, false };
|
|
|
+}
|
|
|
+
|
|
|
static ast_type_t get_type(char *name)
|
|
|
{
|
|
|
size_t types_len = sizeof(data_types) / sizeof(data_types[0]);
|
|
@@ -92,7 +136,7 @@ static ast_type_t get_type(char *name)
|
|
|
*
|
|
|
* possible syntax:
|
|
|
* fun function() ...
|
|
|
- * mut i32 var ...
|
|
|
+ * mut Int32 var ...
|
|
|
* use module ...
|
|
|
*
|
|
|
* any other declaration wil fail as these are the only declarations allowed at
|
|
@@ -136,6 +180,9 @@ static ast_node_t *parse_block_level(token_t *ts)
|
|
|
/* TODO: parse while */
|
|
|
case TOKEN_RETURN:
|
|
|
/* TODO: parse return */
|
|
|
+ case TOKEN_EOF:
|
|
|
+ file_fatal_error("unclosed block on line %zu\n",
|
|
|
+ ts->line_n);
|
|
|
default:
|
|
|
parser_file_error("unexpected '%s' on line %zu\n",
|
|
|
ts->value, ts->line_n);
|
|
@@ -176,7 +223,6 @@ ast_node_list_t *parse_token_stream(token_t *ts)
|
|
|
eat(ts);
|
|
|
} while (ts->type != TOKEN_EOF);
|
|
|
|
|
|
-
|
|
|
return nl;
|
|
|
}
|
|
|
|
|
@@ -200,7 +246,7 @@ static ast_node_t *parse_fn(token_t *ts)
|
|
|
|
|
|
/*
|
|
|
* func(...)
|
|
|
- * str var = ....
|
|
|
+ * Str var = ....
|
|
|
* var += 5;
|
|
|
*/
|
|
|
static ast_node_t *parse_ident(token_t *ts)
|
|
@@ -209,13 +255,13 @@ static ast_node_t *parse_ident(token_t *ts)
|
|
|
return parse_call(ts);
|
|
|
|
|
|
if (is_operator(peek(ts)->type))
|
|
|
- return parse_expr(ts);
|
|
|
+ return parse_expr(ts, TOKEN_SEMI_COLON);
|
|
|
|
|
|
return parse_var(ts);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
- * fun func(...) -> i32 ...
|
|
|
+ * fun func(...) -> Int32 ...
|
|
|
* fun func(...) ...
|
|
|
* fun func ...
|
|
|
*/
|
|
@@ -281,20 +327,18 @@ static ast_node_t *parse_call(token_t *ts)
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
- * i32 var;
|
|
|
- * i32 var = ...
|
|
|
- * mut i32 var = ...
|
|
|
+ * Int32 var;
|
|
|
+ * Int32 var = ...
|
|
|
+ * mut Int32 var = ...
|
|
|
*/
|
|
|
static ast_node_t *parse_var(token_t *ts)
|
|
|
{
|
|
|
char *name;
|
|
|
- bool mutable = false;
|
|
|
+ bool mutable = ts->type == TOKEN_MUTABLE;
|
|
|
ast_type_t type = UNKNOWN;
|
|
|
|
|
|
- if (ts->type == TOKEN_MUTABLE) {
|
|
|
- mutable = 1;
|
|
|
+ if (ts->type == TOKEN_MUTABLE)
|
|
|
eat(ts);
|
|
|
- }
|
|
|
|
|
|
if ((type = get_type(ts->value)) == UNKNOWN)
|
|
|
parser_file_error("unknown data type '%s' on line %zu\n",
|
|
@@ -308,7 +352,14 @@ static ast_node_t *parse_var(token_t *ts)
|
|
|
|
|
|
eat(ts);
|
|
|
|
|
|
- return create_var(name, type, mutable);
|
|
|
+ if (ts->type == TOKEN_SEMI_COLON)
|
|
|
+ return create_var(name, type, mutable, NULL);
|
|
|
+ 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);
|
|
|
+ return create_var(name, type, mutable, NULL);
|
|
|
}
|
|
|
|
|
|
static ast_node_t *parse_if(token_t *ts)
|
|
@@ -321,9 +372,13 @@ static ast_node_t *parse_if(token_t *ts)
|
|
|
ast_node_list_t *true_body = NULL;
|
|
|
ast_node_list_t *false_body = NULL;
|
|
|
|
|
|
- /* TODO: parse expression */
|
|
|
- while (ts->type != TOKEN_L_BRACE)
|
|
|
+ condition = parse_expr(ts, TOKEN_R_BRACE);
|
|
|
+ true_body = parse_block(ts);
|
|
|
+
|
|
|
+ if (ts->type == TOKEN_ELSE) {
|
|
|
eat(ts);
|
|
|
+ false_body = parse_block(ts);
|
|
|
+ }
|
|
|
|
|
|
if (swap)
|
|
|
swap_lists(true_body, false_body);
|
|
@@ -331,19 +386,88 @@ static ast_node_t *parse_if(token_t *ts)
|
|
|
return create_if(condition, true_body, false_body);
|
|
|
}
|
|
|
|
|
|
-static ast_node_t *parse_expr(token_t *ts)
|
|
|
+static ast_node_t *parse_expr(token_t *ts, tokentype delimiter)
|
|
|
{
|
|
|
- printf("one day i will be parsing expressions\n");
|
|
|
+ /* eat assignment token */
|
|
|
+ eat(ts);
|
|
|
+
|
|
|
+ size_t line_start = ts->line_n;
|
|
|
+ stack_t *operators = smalloc(sizeof(stack_t));
|
|
|
+ stack_t *output = smalloc(sizeof(stack_t));
|
|
|
+
|
|
|
+ for (token_t prev = NULL_TOKEN; ts->type != delimiter; prev = *ts, eat(ts)) {
|
|
|
+ if (is_operator(ts->type)) {
|
|
|
+ /* 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 (stack_size(operators) >= 2 &&
|
|
|
+ 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));
|
|
|
+ }
|
|
|
+
|
|
|
+ stack_push(&operators, *ts);
|
|
|
+ } 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);
|
|
|
+ stack_push(&output, *ts);
|
|
|
+ } else if (ts->type == TOKEN_IF || ts->type == TOKEN_UNLESS) {
|
|
|
+ bool is_unless = ts->type == TOKEN_UNLESS;
|
|
|
+
|
|
|
+ eat(ts);
|
|
|
+
|
|
|
+ /* TODO: shorthand if/unless */
|
|
|
+ return create_if(parse_expr(ts, TOKEN_SEMI_COLON), NULL, NULL);
|
|
|
+ } 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);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ stack_push(&output, stack_pop(&operators));
|
|
|
+ }
|
|
|
+
|
|
|
+ /* pop the remaining ( */
|
|
|
+ stack_pop(&operators);
|
|
|
+ } else {
|
|
|
+ if (ts->type == TOKEN_EOF)
|
|
|
+ file_fatal_error("unterminated expression on line %zu\n",
|
|
|
+ line_start);
|
|
|
+
|
|
|
+ parser_file_error("unexpected token '%s' in expression on line %zu\n",
|
|
|
+ ts->value, ts->line_n);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* any remaining operators get pushed to output */
|
|
|
+ while (stack_size(operators))
|
|
|
+ stack_push(&output, stack_pop(&operators));
|
|
|
+
|
|
|
+ stack_dump(output);
|
|
|
+
|
|
|
+ free(operators);
|
|
|
+
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
static void emit_node(ast_node_list_t *nl, ast_node_t *node)
|
|
|
{
|
|
|
/* node could be NULL in some context, such as import */
|
|
|
- if (node == NULL)
|
|
|
+ if (!node)
|
|
|
return;
|
|
|
|
|
|
- if (nl->node == NULL) {
|
|
|
+ if (!nl->node) {
|
|
|
nl->node = node;
|
|
|
return;
|
|
|
}
|