diff options
author | Carlos Maniero <carlos@maniero.me> | 2023-05-03 23:17:50 -0300 |
---|---|---|
committer | Johnny Richard <johnny@johnnyrichard.com> | 2023-05-04 21:47:51 +0200 |
commit | e2e0ed950bb147ebca3b9ac879268feeb185e20b (patch) | |
tree | d6888d4468d75a9d4498f20976b66a3d4bd8575a | |
parent | 8655b77d57ace45d4d040ef297cf172f3c12f4bd (diff) |
parser: Introduce statement keywords
This commit introduces a few changes in pipalang syntax. Now, both
functions and variables requires keywords to be defined.
before:
main(): i32 {
a: i32 = 2;
return a;
}
now:
fn main(): i32 {
let a: i32 = 2;
return a;
}
Signed-off-by: Carlos Maniero <carlos@maniero.me>
Reviewed-by: Johnny Richard <johnny@johnnyrichard.com>
-rw-r--r-- | examples/arithmetics.pipa | 2 | ||||
-rw-r--r-- | examples/main.pipa | 2 | ||||
-rw-r--r-- | examples/variables.pipa | 12 | ||||
-rw-r--r-- | src/lexer.c | 12 | ||||
-rw-r--r-- | src/lexer.h | 2 | ||||
-rw-r--r-- | src/parser.c | 21 | ||||
-rw-r--r-- | test/lexer_test.c | 17 | ||||
-rw-r--r-- | test/parser_test.c | 33 |
8 files changed, 67 insertions, 34 deletions
diff --git a/examples/arithmetics.pipa b/examples/arithmetics.pipa index e2b7645..f2449b8 100644 --- a/examples/arithmetics.pipa +++ b/examples/arithmetics.pipa @@ -1,3 +1,3 @@ -main(): i32 { +fn main(): i32 { return ((2 + 1) * (5 - 3) + 7) * 2 / 2; } diff --git a/examples/main.pipa b/examples/main.pipa index 6524bec..2da2231 100644 --- a/examples/main.pipa +++ b/examples/main.pipa @@ -1,3 +1,3 @@ -main(): i32 { +fn main(): i32 { return 69; } diff --git a/examples/variables.pipa b/examples/variables.pipa index 0723782..3eabc73 100644 --- a/examples/variables.pipa +++ b/examples/variables.pipa @@ -1,10 +1,10 @@ -main(): i32 { - a: i32 = 13; +fn main(): i32 { + let a: i32 = 13; a = a - 1; - b: i32 = 33; + let b: i32 = 33; b = 32; - c: i32 = 2 * (b + a); - d: i32 = (c - 33) + 1; - e: i32 = d; + let c: i32 = 2 * (b + a); + let d: i32 = (c - 33) + 1; + let e: i32 = d; return e / 2; } diff --git a/src/lexer.c b/src/lexer.c index 3f6948a..56e24af 100644 --- a/src/lexer.c +++ b/src/lexer.c @@ -97,10 +97,18 @@ lexer_tokenize_name(lexer_t *lexer, token_t *token) static void lexer_token_process_keyword(token_t *token) { + if (string_view_eq(string_view_from_str("let"), token->value)) { + token->kind = TOKEN_KEYWORD_LET; + return; + } if (string_view_eq(string_view_from_str("return"), token->value)) { token->kind = TOKEN_KEYWORD_RETURN; return; } + if (string_view_eq(string_view_from_str("fn"), token->value)) { + token->kind = TOKEN_KEYWORD_FN; + return; + } } void @@ -318,6 +326,10 @@ token_kind_to_str(token_kind_t kind) return "="; case TOKEN_KEYWORD_RETURN: return "return"; + case TOKEN_KEYWORD_FN: + return "fn"; + case TOKEN_KEYWORD_LET: + return "let"; case TOKEN_EOF: return "TOKEN_EOF"; case TOKEN_UNKNOWN: diff --git a/src/lexer.h b/src/lexer.h index 9aa8efe..6449a0a 100644 --- a/src/lexer.h +++ b/src/lexer.h @@ -38,6 +38,8 @@ typedef enum TOKEN_SLASH, TOKEN_EQUAL, TOKEN_KEYWORD_RETURN, + TOKEN_KEYWORD_FN, + TOKEN_KEYWORD_LET, TOKEN_EOF, TOKEN_UNKNOWN } token_kind_t; diff --git a/src/parser.c b/src/parser.c index 82d25f5..49803eb 100644 --- a/src/parser.c +++ b/src/parser.c @@ -296,9 +296,14 @@ parser_parse_variable_assignment(parser_t *parser) } static ast_node_t * -parser_parse_variable_definition(parser_t *parser) +parser_parse_variable_declaration(parser_t *parser) { token_t variable_name; + + if (!drop_expected_token(parser, TOKEN_KEYWORD_LET)) { + return NULL; + } + if (!expected_token(&variable_name, parser, TOKEN_NAME)) { return NULL; } @@ -340,13 +345,7 @@ is_next_statement_a_variable_declaration(parser_t *parser) token_t token; lexer_peek_next_token(parser->lexer, &token); - if (token.kind != TOKEN_NAME) { - return false; - } - - lexer_lookahead(parser->lexer, &token, 2); - - return token.kind == TOKEN_COLON; + return token.kind == TOKEN_KEYWORD_LET; } static bool @@ -440,7 +439,7 @@ parser_parse_block_declarations(parser_t *parser) } if (is_next_statement_a_variable_declaration(parser)) { - ast_node_t *variable_node = parser_parse_variable_definition(parser); + ast_node_t *variable_node = parser_parse_variable_declaration(parser); if (variable_node == NULL) { scope_leave(parser->scope); @@ -493,6 +492,10 @@ parser_parse_function_declaration(parser_t *parser) { token_t func_name_token; + if (!drop_expected_token(parser, TOKEN_KEYWORD_FN)) { + return NULL; + } + if (!expected_token(&func_name_token, parser, TOKEN_NAME)) { return NULL; } diff --git a/test/lexer_test.c b/test/lexer_test.c index 5326f5c..a3f644d 100644 --- a/test/lexer_test.c +++ b/test/lexer_test.c @@ -51,6 +51,22 @@ assert_token_at(char *source, int token_index, token_kind_t expected_kind, char assert_string_equal(expected, actual); } +void +assert_keyword(char *source, token_kind_t expected_kind) +{ + assert_token_at(source, 0, expected_kind, source); +} + +static MunitResult +test_tokenize_keywords(const MunitParameter params[], void *user_data_or_fixture) +{ + assert_keyword("return", TOKEN_KEYWORD_RETURN); + assert_keyword("fn", TOKEN_KEYWORD_FN); + assert_keyword("let", TOKEN_KEYWORD_LET); + + return MUNIT_OK; +} + static MunitResult test_tokenize_number(const MunitParameter params[], void *user_data_or_fixture) { @@ -106,6 +122,7 @@ test_peek_next_token(const MunitParameter params[], void *user_data_or_fixture) static MunitTest tests[] = { { "/test_tokenize_digit", test_tokenize_number, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL }, + { "/test_tokenize_keywords", test_tokenize_keywords, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL }, { "/test_tokenize_op", test_tokenize_op, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL }, { "/test_tokenize_unknown", test_tokenize_unknown, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL }, { "/test_peek_next_token", test_peek_next_token, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL }, diff --git a/test/parser_test.c b/test/parser_test.c index 2d749da..a1614b9 100644 --- a/test/parser_test.c +++ b/test/parser_test.c @@ -64,7 +64,7 @@ test_parse_function(const MunitParameter params[], void *user_data_or_fixture) lexer_t lexer; scope_t *scope = scope_new(); - make_lexer_from_static_src(&lexer, "main(): i32 { \nreturn 42;\n }"); + make_lexer_from_static_src(&lexer, "fn main(): i32 { \nreturn 42;\n }"); parser_init(&parser, &lexer, scope); ast_node_t *ast_function = parser_parse_function_declaration(&parser); @@ -98,7 +98,7 @@ test_parse_variable_definition(const MunitParameter params[], void *user_data_or lexer_t lexer; scope_t *scope = scope_new(); - make_lexer_from_static_src(&lexer, "main(): i32 { \nvariable : i32 = 42; \nreturn variable;\n }"); + make_lexer_from_static_src(&lexer, "fn main(): i32 { \nlet variable: i32 = 42; \nreturn variable;\n }"); parser_init(&parser, &lexer, scope); ast_node_t *ast_function = parser_parse_function_declaration(&parser); @@ -203,31 +203,30 @@ assert_string_view_equal(char *expected, string_view_t actual) static MunitResult test_parse_basic_syntax_errors(const MunitParameter params[], void *user_data_or_fixture) { - assert_parser_error("(): i32 { return 42; }", "expected 'TOKEN_NAME' but got '('"); - assert_parser_error("main): i32 { return 42; }", "expected '(' but got ')'"); - assert_parser_error("main(: i32 { return 42; }", "expected ')' but got ':'"); - assert_parser_error("main() i32 { return 42; }", "expected ':' but got 'TOKEN_NAME'"); - assert_parser_error("main(): { return 42; }", "expected 'TOKEN_NAME' but got '{'"); - assert_parser_error("main(): i32 return 42; }", "expected '{' but got 'return'"); - assert_parser_error("main(): i32 { 42; }", "unexpected token 'TOKEN_NUMBER' value='42'"); - assert_parser_error("main(): i32 { return; }", "unexpected '; (;)' token"); - assert_parser_error("main(): i32 { return 42;", "expected '}' but got end of file"); - assert_parser_error("main(): beff { return 42; }", "type 'beff' is not defined"); - assert_parser_error("main(): i32 { return b; }", "identifier 'b' not defined"); - assert_parser_error("main(): i32 { b = 1; return b; }", "trying to assign 'b' before defining it."); + assert_parser_error("main(): i32 { return 42; }", "expected 'fn' but got 'TOKEN_NAME'"); + assert_parser_error("fn (): i32 { return 42; }", "expected 'TOKEN_NAME' but got '('"); + assert_parser_error("fn main): i32 { return 42; }", "expected '(' but got ')'"); + assert_parser_error("fn main(: i32 { return 42; }", "expected ')' but got ':'"); + assert_parser_error("fn main() i32 { return 42; }", "expected ':' but got 'TOKEN_NAME'"); + assert_parser_error("fn main(): { return 42; }", "expected 'TOKEN_NAME' but got '{'"); + assert_parser_error("fn main(): i32 return 42; }", "expected '{' but got 'return'"); + assert_parser_error("fn main(): i32 { 42; }", "unexpected token 'TOKEN_NUMBER' value='42'"); + assert_parser_error("fn main(): i32 { return; }", "unexpected '; (;)' token"); + assert_parser_error("fn main(): i32 { return 42;", "expected '}' but got end of file"); + assert_parser_error("fn main(): beff { return 42; }", "type 'beff' is not defined"); + assert_parser_error("fn main(): i32 { return b; }", "identifier 'b' not defined"); + assert_parser_error("fn main(): i32 { b = 1; return b; }", "trying to assign 'b' before defining it."); // FIXME: once function calls are implemented, this error should inform that // neither a variable or function call was found. - assert_parser_error("main(): i32 { oxi 42; }", "unexpected token 'TOKEN_NAME' value='oxi'"); + assert_parser_error("fn main(): i32 { oxi 42; }", "unexpected token 'TOKEN_NAME' value='oxi'"); return MUNIT_OK; } static MunitTest tests[] = { { "/test_parse_function", test_parse_function, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL }, - { "/test_parse_function", test_parse_function, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL }, { "/test_parse_basic_syntax_errors", test_parse_basic_syntax_errors, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL }, { "/test_parse_arithmetic_expression", test_parse_arithmetic_expression, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL }, - { "/test_parse_arithmetic_expression", test_parse_arithmetic_expression, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL }, { "/test_parse_variable_definition", test_parse_variable_definition, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL }, { NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL } }; |