summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlos Maniero <carlos@maniero.me>2023-05-03 23:17:50 -0300
committerJohnny Richard <johnny@johnnyrichard.com>2023-05-04 21:47:51 +0200
commite2e0ed950bb147ebca3b9ac879268feeb185e20b (patch)
treed6888d4468d75a9d4498f20976b66a3d4bd8575a
parent8655b77d57ace45d4d040ef297cf172f3c12f4bd (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.pipa2
-rw-r--r--examples/main.pipa2
-rw-r--r--examples/variables.pipa12
-rw-r--r--src/lexer.c12
-rw-r--r--src/lexer.h2
-rw-r--r--src/parser.c21
-rw-r--r--test/lexer_test.c17
-rw-r--r--test/parser_test.c33
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 }
};