aboutsummaryrefslogtreecommitdiff
path: root/src/parse/containers.c
blob: 5c39eb5b1845eb2c9558f9762d87105f681f856f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// Logic for parsing container types (lists, sets, tables)

#include <stdarg.h>
#include <stdbool.h>
#include <string.h>

#include "../ast.h"
#include "../stdlib/util.h"
#include "context.h"
#include "errors.h"
#include "parse.h"
#include "utils.h"

ast_t *parse_list(parse_ctx_t *ctx, const char *pos) {
    const char *start = pos;
    if (!match(&pos, "[")) return NULL;

    whitespace(&pos);

    ast_list_t *items = NULL;
    for (;;) {
        ast_t *item = optional(ctx, &pos, parse_extended_expr);
        if (!item) break;
        ast_t *suffixed = parse_comprehension_suffix(ctx, item);
        while (suffixed) {
            item = suffixed;
            pos = suffixed->end;
            suffixed = parse_comprehension_suffix(ctx, item);
        }
        items = new (ast_list_t, .ast = item, .next = items);
        if (!match_separator(&pos)) break;
    }
    whitespace(&pos);
    expect_closing(ctx, &pos, "]", "I wasn't able to parse the rest of this list");

    REVERSE_LIST(items);
    return NewAST(ctx->file, start, pos, List, .items = items);
}

ast_t *parse_table(parse_ctx_t *ctx, const char *pos) {
    const char *start = pos;
    if (!match(&pos, "{")) return NULL;

    whitespace(&pos);

    ast_list_t *entries = NULL;
    for (;;) {
        const char *entry_start = pos;
        ast_t *key = optional(ctx, &pos, parse_extended_expr);
        if (!key) break;
        whitespace(&pos);
        if (!match(&pos, "=")) return NULL;
        ast_t *value = expect(ctx, pos - 1, &pos, parse_expr, "I couldn't parse the value for this table entry");
        ast_t *entry = NewAST(ctx->file, entry_start, pos, TableEntry, .key = key, .value = value);
        ast_t *suffixed = parse_comprehension_suffix(ctx, entry);
        while (suffixed) {
            entry = suffixed;
            pos = suffixed->end;
            suffixed = parse_comprehension_suffix(ctx, entry);
        }
        entries = new (ast_list_t, .ast = entry, .next = entries);
        if (!match_separator(&pos)) break;
    }

    REVERSE_LIST(entries);

    whitespace(&pos);

    ast_t *fallback = NULL, *default_value = NULL;
    if (match(&pos, ";")) {
        for (;;) {
            whitespace(&pos);
            const char *attr_start = pos;
            if (match_word(&pos, "fallback")) {
                whitespace(&pos);
                if (!match(&pos, "=")) parser_err(ctx, attr_start, pos, "I expected an '=' after 'fallback'");
                if (fallback) parser_err(ctx, attr_start, pos, "This table already has a fallback");
                fallback = expect(ctx, attr_start, &pos, parse_expr, "I expected a fallback table");
            } else if (match_word(&pos, "default")) {
                whitespace(&pos);
                if (!match(&pos, "=")) parser_err(ctx, attr_start, pos, "I expected an '=' after 'default'");
                if (default_value) parser_err(ctx, attr_start, pos, "This table already has a default");
                default_value = expect(ctx, attr_start, &pos, parse_expr, "I expected a default value");
            } else {
                break;
            }
            whitespace(&pos);
            if (!match(&pos, ",")) break;
        }
    }

    whitespace(&pos);
    expect_closing(ctx, &pos, "}", "I wasn't able to parse the rest of this table");

    return NewAST(ctx->file, start, pos, Table, .default_value = default_value, .entries = entries,
                  .fallback = fallback);
}

ast_t *parse_set(parse_ctx_t *ctx, const char *pos) {
    const char *start = pos;
    if (match(&pos, "||")) return NewAST(ctx->file, start, pos, Set);

    if (!match(&pos, "|")) return NULL;
    whitespace(&pos);

    ast_list_t *items = NULL;
    for (;;) {
        ast_t *item = optional(ctx, &pos, parse_extended_expr);
        if (!item) break;
        whitespace(&pos);
        ast_t *suffixed = parse_comprehension_suffix(ctx, item);
        while (suffixed) {
            item = suffixed;
            pos = suffixed->end;
            suffixed = parse_comprehension_suffix(ctx, item);
        }
        items = new (ast_list_t, .ast = item, .next = items);
        if (!match_separator(&pos)) break;
    }

    REVERSE_LIST(items);

    whitespace(&pos);
    expect_closing(ctx, &pos, "|", "I wasn't able to parse the rest of this set");

    return NewAST(ctx->file, start, pos, Set, .items = items);
}