summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohnny Richard <johnny@johnnyrichard.com>2023-04-30 01:48:18 +0200
committerJohnny Richard <johnny@johnnyrichard.com>2023-04-30 01:55:29 +0200
commitef07fab261cce781ca750c1288574d4001f14bcf (patch)
tree9b4da44aee7cc64cec448adeee31b38e12d29e6d
parent88a08db927629032d6d4c662e00f0dce2c112ce4 (diff)
parser: Registry identifiers on scope
We are parsing variables/functions and checking if they are defined on scope. Otherwise we fail the parsing with a nice message. Signed-off-by: Johnny Richard <johnny@johnnyrichard.com> Co-authored-by: Carlos Maniero <carlosmaniero@gmail.com>
-rw-r--r--src/ast.c7
-rw-r--r--src/ast.h11
-rw-r--r--src/gas_assembly_generator.c3
-rw-r--r--src/parser.c27
-rw-r--r--src/parser.h4
-rw-r--r--src/pipac.c5
-rw-r--r--src/scope.c6
-rw-r--r--src/scope.h7
-rw-r--r--test/Makefile2
-rw-r--r--test/parser_test.c18
-rw-r--r--test/scope_test.c14
11 files changed, 80 insertions, 24 deletions
diff --git a/src/ast.c b/src/ast.c
index d3e9a28..49d772a 100644
--- a/src/ast.c
+++ b/src/ast.c
@@ -108,6 +108,13 @@ ast_node_init_variable_declaration(ast_node_t *node, string_view_t variable_name
}
void
+ast_node_init_variable(ast_node_t *node, ast_identifier_t *identifier)
+{
+ node->kind = AST_VARIABLE;
+ node->data = (ast_node_data_t){ .variable_ex = { .identifier = identifier } };
+}
+
+void
ast_node_init_identifier(ast_node_t *node, string_view_t name)
{
node->kind = AST_IDENTIFIER;
diff --git a/src/ast.h b/src/ast.h
index e25f1bb..4fd1d1d 100644
--- a/src/ast.h
+++ b/src/ast.h
@@ -37,6 +37,11 @@ typedef struct ast_identifier_t
string_view_t name;
} ast_identifier_t;
+typedef struct ast_variable_t
+{
+ ast_identifier_t *identifier;
+} ast_variable_t;
+
typedef struct ast_function_declaration_t
{
ast_identifier_t identifier;
@@ -91,6 +96,7 @@ typedef enum
AST_RETURN_STMT,
AST_UNKOWN_NODE,
AST_VARIABLE_DECLARATION,
+ AST_VARIABLE
} ast_node_kind_t;
typedef union
@@ -99,8 +105,9 @@ typedef union
ast_function_declaration_t function;
ast_literal_t literal;
ast_return_stmt_t return_stmt;
- ast_variable_declaration_t variable;
+ ast_variable_declaration_t variable; // FIXME: Rename to variable_declaration
ast_identifier_t identifier;
+ ast_variable_t variable_ex; // FIXME: Rename to variable
} ast_node_data_t;
typedef struct ast_node_t
@@ -127,5 +134,7 @@ ast_node_init_variable_declaration(ast_node_t *node, string_view_t variable_name
// FIXME: use the naming convention
void
ast_literal_integer_create(ast_node_t *node, uint32_t number);
+void
+ast_node_init_variable(ast_node_t *node, ast_identifier_t *identifier);
#endif /* AST_H */
diff --git a/src/gas_assembly_generator.c b/src/gas_assembly_generator.c
index ea76c5f..f09b60d 100644
--- a/src/gas_assembly_generator.c
+++ b/src/gas_assembly_generator.c
@@ -61,6 +61,9 @@ gas_assembly_generator_compile(gas_assembly_generator_t *gen, ast_node_t *ast)
case AST_VARIABLE_DECLARATION:
assert(false && "TODO: ast variable declaration not implemented yet");
break;
+ case AST_VARIABLE:
+ assert(false && "TODO: ast variable not implemented yet");
+ break;
case AST_UNKOWN_NODE:
assert(false && "unreachable");
}
diff --git a/src/parser.c b/src/parser.c
index 6b954c2..469fa99 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -24,14 +24,16 @@
#include "ast.h"
#include "lexer.h"
#include "parser.h"
+#include "scope.h"
#include "vector.h"
void
-parser_init(parser_t *parser, lexer_t *lexer)
+parser_init(parser_t *parser, lexer_t *lexer, scope_t *scope)
{
assert(parser && "parser must be defined");
assert(lexer && "lexer must be defined");
parser->lexer = lexer;
+ parser->scope = scope;
parser->errors_len = 0;
}
@@ -134,8 +136,16 @@ parser_parse_factor(parser_t *parser, ast_node_t *node)
return false;
return true;
case TOKEN_NAME:
- /// FIXME: Check if the identifier is defined
ast_node_init_identifier(node, token.value);
+ // TODO: Check node kind, today accepts only variables
+ if (scope_get(parser->scope, token.value) == NULL) {
+ parser_error_t error;
+ error.token = token;
+ sprintf(error.message, "identifier '" SVFMT "' not defined", SVARG(&token.value));
+ parser->errors[parser->errors_len++] = error;
+ return false;
+ }
+
return true;
default: {
parser_error_t error;
@@ -143,7 +153,7 @@ parser_parse_factor(parser_t *parser, ast_node_t *node)
sprintf(error.message, "unexpected '%s (" SVFMT ")' token", token_kind_to_str(token.kind), SVARG(&token.value));
parser->errors[parser->errors_len++] = error;
return false;
- }
+ }
}
}
@@ -247,6 +257,7 @@ parser_parse_variable_definition(parser_t *parser, string_view_t variable_name,
}
ast_node_init_variable_declaration(node, variable_name, type, expression);
+ scope_push(parser->scope, &node->data.variable.identifier, node);
return true;
}
@@ -333,11 +344,17 @@ parser_parse_function_declaration(parser_t *parser, ast_node_t *node)
return false;
vector_t *body = vector_new();
+ ast_node_init_function_declaration(node, func_name_token.value, return_type, body);
+ scope_push(parser->scope, &node->data.function.identifier, node);
+
+ scope_enter(parser->scope);
- if (!parser_parse_block_declarations(parser, body))
+ if (!parser_parse_block_declarations(parser, body)) {
+ scope_leave(parser->scope);
return false;
+ }
- ast_node_init_function_declaration(node, func_name_token.value, return_type, body);
+ scope_leave(parser->scope);
return true;
}
diff --git a/src/parser.h b/src/parser.h
index bdf0c5a..d3e1431 100644
--- a/src/parser.h
+++ b/src/parser.h
@@ -19,6 +19,7 @@
#include "ast.h"
#include "lexer.h"
+#include "scope.h"
#include "string_view.h"
typedef struct parser_error_t
@@ -30,13 +31,14 @@ typedef struct parser_error_t
typedef struct parser_t
{
lexer_t *lexer;
+ scope_t *scope;
int errors_len;
// FIXME: replace with vector
parser_error_t errors[1];
} parser_t;
void
-parser_init(parser_t *parser, lexer_t *lexer);
+parser_init(parser_t *parser, lexer_t *lexer, scope_t *scope);
bool
parser_parse_function_declaration(parser_t *parser, ast_node_t *node);
diff --git a/src/pipac.c b/src/pipac.c
index b31b8b5..2041218 100644
--- a/src/pipac.c
+++ b/src/pipac.c
@@ -22,6 +22,7 @@
#include "gas_assembly_generator.h"
#include "lexer.h"
#include "parser.h"
+#include "scope.h"
#include "string_view.h"
static void
@@ -62,8 +63,9 @@ main(int argc, char **argv)
lexer_t lexer;
lexer_init(&lexer, filepath);
+ scope_t *scope = scope_new();
parser_t parser;
- parser_init(&parser, &lexer);
+ parser_init(&parser, &lexer, scope);
ast_node_t *func = ast_node_new();
@@ -74,6 +76,7 @@ main(int argc, char **argv)
generate_gas_x86_64_linux(func);
+ scope_destroy(scope);
ast_node_destroy(func);
return EXIT_SUCCESS;
}
diff --git a/src/scope.c b/src/scope.c
index 5b696ad..662e59c 100644
--- a/src/scope.c
+++ b/src/scope.c
@@ -54,7 +54,7 @@ scope_new(void)
void
scope_destroy(scope_t *scope)
{
- if (scope->stack->size) {
+ if (scope->stack->size != 1) {
fprintf(stderr,
"Stack not cleaned before destroying. This may lead to memory leaks.\n"
"Please make sure to call the leave function before destroying it.");
@@ -97,7 +97,7 @@ scope_leave(scope_t *scope)
}
ast_node_t *
-scope_get(scope_t *scope, ast_identifier_t *identifier)
+scope_get(scope_t *scope, string_view_t name)
{
for (size_t i = scope->stack->size; i > 0; i--) {
vector_t *current_level = vector_at(scope->stack, i - 1);
@@ -105,7 +105,7 @@ scope_get(scope_t *scope, ast_identifier_t *identifier)
for (size_t j = 0; j < current_level->size; j++) {
scope_item_t *item = vector_at(current_level, j);
- if (string_view_eq(identifier->name, item->identifier->name)) {
+ if (string_view_eq(name, item->identifier->name)) {
return item->node;
}
}
diff --git a/src/scope.h b/src/scope.h
index c221703..2636990 100644
--- a/src/scope.h
+++ b/src/scope.h
@@ -14,6 +14,9 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
+#ifndef SCOPE_H
+#define SCOPE_H
+
#include "ast.h"
#include "string_view.h"
#include "vector.h"
@@ -41,6 +44,8 @@ void
scope_leave(scope_t *scope);
ast_node_t *
-scope_get(scope_t *scope, ast_identifier_t *identifier);
+scope_get(scope_t *scope, string_view_t name);
void
scope_push(scope_t *scope, ast_identifier_t *identifier, ast_node_t *node);
+
+#endif /* SCOPE_H */
diff --git a/test/Makefile b/test/Makefile
index 6d8bfb7..9572d0f 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -27,7 +27,7 @@ vector_test: munit.o ../build/vector.o vector_test.o
lexer_test: munit.o ../build/string_view.o ../build/lexer.o lexer_test.o
$(CC) $? $(CFLAGS) -o $@
-parser_test: munit.o ../build/string_view.o ../build/vector.o ../build/lexer.o ../build/ast.o ../build/parser.o parser_test.o
+parser_test: munit.o ../build/string_view.o ../build/scope.o ../build/vector.o ../build/lexer.o ../build/ast.o ../build/parser.o parser_test.o
$(CC) $? $(CFLAGS) -o $@
scope_test: munit.o ../build/string_view.o ../build/vector.o ../build/ast.o ../build/scope.o scope_test.o
diff --git a/test/parser_test.c b/test/parser_test.c
index 40c0198..0c54e50 100644
--- a/test/parser_test.c
+++ b/test/parser_test.c
@@ -19,6 +19,7 @@
#include "lexer.h"
#include "munit.h"
#include "parser.h"
+#include "scope.h"
#include "string.h"
#include "vector.h"
@@ -43,8 +44,9 @@ assert_parser_error(char *src, char *error_msg)
parser_t parser;
lexer_t lexer;
+ scope_t *scope = scope_new();
make_lexer_from_static_src(&lexer, src);
- parser_init(&parser, &lexer);
+ parser_init(&parser, &lexer, scope);
ast_node_t *ast_function = ast_node_new();
@@ -54,6 +56,7 @@ assert_parser_error(char *src, char *error_msg)
assert_string_equal(error_msg, parser.errors[0].message);
ast_node_destroy(ast_function);
+ scope_destroy(scope);
}
static MunitResult
@@ -61,9 +64,10 @@ test_parse_function(const MunitParameter params[], void *user_data_or_fixture)
{
parser_t parser;
lexer_t lexer;
+ scope_t *scope = scope_new();
make_lexer_from_static_src(&lexer, "main(): i32 { \nreturn 42;\n }");
- parser_init(&parser, &lexer);
+ parser_init(&parser, &lexer, scope);
ast_node_t *ast_function = ast_node_new();
bool parsed = parser_parse_function_declaration(&parser, ast_function);
@@ -85,6 +89,7 @@ test_parse_function(const MunitParameter params[], void *user_data_or_fixture)
assert_int(42, ==, ast_literal->data.literal.value.integer);
ast_node_destroy(ast_function);
+ scope_destroy(scope);
return MUNIT_OK;
}
@@ -94,9 +99,10 @@ test_parse_variable_definition(const MunitParameter params[], void *user_data_or
{
parser_t parser;
lexer_t lexer;
+ scope_t *scope = scope_new();
make_lexer_from_static_src(&lexer, "main(): i32 { \nvariable : i32 = 42; \nreturn variable;\n }");
- parser_init(&parser, &lexer);
+ parser_init(&parser, &lexer, scope);
ast_node_t *ast_function = ast_node_new();
bool parsed = parser_parse_function_declaration(&parser, ast_function);
@@ -124,6 +130,7 @@ test_parse_variable_definition(const MunitParameter params[], void *user_data_or
assert_string_view_equal("variable", ast_literal->data.identifier.name);
ast_node_destroy(ast_function);
+ scope_destroy(scope);
return MUNIT_OK;
}
@@ -133,9 +140,10 @@ test_parse_arithmetic_expression(const MunitParameter params[], void *user_data_
{
parser_t parser;
lexer_t lexer;
+ scope_t *scope = scope_new();
make_lexer_from_static_src(&lexer, "1 + 3 * 3 / 2 - 1");
- parser_init(&parser, &lexer);
+ parser_init(&parser, &lexer, scope);
ast_node_t *ast_expression = ast_node_new();
bool parsed = parser_parse_expression(&parser, ast_expression);
@@ -185,6 +193,7 @@ test_parse_arithmetic_expression(const MunitParameter params[], void *user_data_
}
ast_node_destroy(ast_expression);
+ scope_destroy(scope);
return MUNIT_OK;
}
@@ -210,6 +219,7 @@ test_parse_basic_syntax_errors(const MunitParameter params[], void *user_data_or
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");
// 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; }", "expected ':' but got 'TOKEN_NUMBER'");
diff --git a/test/scope_test.c b/test/scope_test.c
index b8ee30e..c71a38f 100644
--- a/test/scope_test.c
+++ b/test/scope_test.c
@@ -31,24 +31,24 @@ test_scope(const MunitParameter params[], void *user_data_or_fixture)
scope_t *scope = scope_new();
- assert_null(scope_get(scope, &first_id));
- assert_null(scope_get(scope, &second_id));
+ assert_null(scope_get(scope, first_id.name));
+ assert_null(scope_get(scope, second_id.name));
scope_push(scope, &first_id, &first_id_node_1);
scope_push(scope, &second_id, &second_id_node);
- assert_ptr_equal((void *)scope_get(scope, &first_id), (void *)&first_id_node_1);
- assert_ptr_equal((void *)scope_get(scope, &second_id), (void *)&second_id_node);
+ assert_ptr_equal((void *)scope_get(scope, first_id.name), (void *)&first_id_node_1);
+ assert_ptr_equal((void *)scope_get(scope, second_id.name), (void *)&second_id_node);
scope_enter(scope);
scope_push(scope, &first_id, &first_id_node_2);
- assert_ptr_equal((void *)scope_get(scope, &first_id), (void *)&first_id_node_2);
- assert_ptr_equal((void *)scope_get(scope, &second_id), (void *)&second_id_node);
+ assert_ptr_equal((void *)scope_get(scope, first_id.name), (void *)&first_id_node_2);
+ assert_ptr_equal((void *)scope_get(scope, second_id.name), (void *)&second_id_node);
scope_leave(scope);
- assert_ptr_equal((void *)scope_get(scope, &first_id), (void *)&first_id_node_1);
+ assert_ptr_equal((void *)scope_get(scope, first_id.name), (void *)&first_id_node_1);
scope_destroy(scope);