about summary refs log tree commit diff
path: root/boot/parse.c
diff options
context:
space:
mode:
Diffstat (limited to 'boot/parse.c')
-rw-r--r--boot/parse.c104
1 files changed, 104 insertions, 0 deletions
diff --git a/boot/parse.c b/boot/parse.c
index d410476..7b40fb1 100644
--- a/boot/parse.c
+++ b/boot/parse.c
@@ -31,6 +31,7 @@ struct Parser_Error
         PARSER_ERROR_EXPECTED_STATEMENT_END,
         PARSER_ERROR_EXPECTED_PRIMARY_EXPRESSION,
         PARSER_ERROR_EXPECTED_TYPE,
+        PARSER_ERROR_EXPECTED_PRAGMA,
     } kind;
     // TODO: add span to error
 };
@@ -69,6 +70,8 @@ parser_error_to_string(const struct Parser_Error* error)
         return "expected primary expression";
     case PARSER_ERROR_EXPECTED_TYPE:
         return "expected type";
+    case PARSER_ERROR_EXPECTED_PRAGMA:
+        return "expected pragma";
     default:
         return "unknown error";
     }
@@ -596,6 +599,85 @@ parser_node_type(struct Parser* p, struct Parser_Error* error)
     return type;
 }
 
+struct Pragma_Node*
+parser_pragma_node(struct Parser* p, struct Parser_Error* error)
+{
+    // `| c_header "stdio.h"`
+    // `| clone always, printable
+    CHECK(parser_need(p, TOKEN_PIPE, error));
+
+    struct Pragma_Node *head = nil, *current = nil;
+    struct Token token = parser_peek(p);
+    while (!token_ends_statement(&token)) {
+        struct Token pragma_token = CHECK(parser_need(p, TOKEN_NAME, error));
+        enum Pragma_Type pragma_type = pragma_type_from_string(pragma_token.value.name);
+        if (!pragma_type) {
+            *error = parser_error(PARSER_ERROR_EXPECTED_PRAGMA);
+            return nil;
+        }
+
+        struct Span span = pragma_token.span;
+
+        // parse the arguments until either statement end or comma
+        // arguments can either be numbers, names or strings.
+        uint argument_index = 0;
+        struct Pragma_Argument arguments[PRAGMA_ARGUMENT_MAX] = { 0 };
+
+        token = parser_peek(p);
+        while (!token_ends_statement(&token)) {
+            check(argument_index < PRAGMA_ARGUMENT_MAX, "too many pragma arguments");
+            struct Pragma_Argument* argument = &arguments[argument_index];
+
+            union Token_Value* v = &token.value;
+            switch (token.kind) {
+            case TOKEN_LITERAL_INTEGER:
+                argument->type = PRAGMA_ARGUMENT_NUMBER;
+                argument->value.number = v->literal_integer;
+                break;
+            case TOKEN_LITERAL_FLOAT:
+                argument->type = PRAGMA_ARGUMENT_DECIMAL;
+                argument->value.decimal = v->literal_float;
+                break;
+            case TOKEN_LITERAL_STRING:
+                argument->type = PRAGMA_ARGUMENT_NAME_OR_STRING;
+                argument->value.name_or_string = v->literal_string;
+                break;
+            case TOKEN_NAME:
+                argument->type = PRAGMA_ARGUMENT_NAME_OR_STRING;
+                argument->value.name_or_string = v->name;
+                break;
+            default:
+                *error = parser_error(PARSER_ERROR_UNEXPECTED_TOKEN);
+                return nil;
+            }
+            argument_index++;
+            span = span_merge(span, token.span);
+            parser_next(p);
+
+            // comma separates pragmas on a single line.
+            token = parser_peek(p);
+            if (token_is(&token, TOKEN_COMMA)) {
+                parser_next(p);
+                break;
+            }
+            if (token_ends_statement(&token)) { break; }
+        }
+
+        struct Pragma_Node* pragma = pragma_node_new(pragma_type, span, pragma_token.location);
+        pragma->argument_count = argument_index;
+        memcpy(pragma->arguments, arguments, sizeof(arguments));
+
+        if (!head) {
+            head = pragma;
+        } else {
+            current->next = pragma;
+        }
+        current = pragma;
+    }
+
+    return head;
+}
+
 struct Expression*
 parser_expression_primary_name(struct Parser* p, struct Parser_Error* error)
 {
@@ -1152,6 +1234,26 @@ parser_statement_defer(struct Parser* p, struct Parser_Error* error)
 }
 
 struct Statement*
+parser_statement_pragma(struct Parser* p, struct Parser_Error* error)
+{
+    struct Token pipe_token = parser_peek(p);
+
+    struct Pragma_Node* pragma_node = parser_pragma_node(p, error);
+    if (!parser_error_is_none(error)) {
+        *error = parser_error(PARSER_ERROR_EXPECTED_PRAGMA);
+        return nil;
+    }
+
+    struct Span span = {};
+    FOR_EACH (struct Pragma_Node*, node, pragma_node) {
+        span = span_is_empty(span) ? node->span : span_merge(span, node->span);
+    }
+
+    union Statement_Value value = { .pragma.inner = pragma_node };
+    return statement_new(STATEMENT_PRAGMA, value, span, pipe_token.location);
+}
+
+struct Statement*
 parser_statement(struct Parser* p, struct Parser_Error* error)
 {
     struct Token token = parser_peek(p);
@@ -1182,6 +1284,8 @@ parser_statement(struct Parser* p, struct Parser_Error* error)
         return parser_statement_continue(p, error);
     case TOKEN_WORD_DEFER:
         return parser_statement_defer(p, error);
+    case TOKEN_PIPE:
+        return parser_statement_pragma(p, error);
     default:
         break;
     }