diff options
Diffstat (limited to 'src/parse.c')
| -rw-r--r-- | src/parse.c | 1020 |
1 files changed, 445 insertions, 575 deletions
diff --git a/src/parse.c b/src/parse.c index 06184b97..9f6f1581 100644 --- a/src/parse.c +++ b/src/parse.c @@ -14,9 +14,9 @@ #include <unistr.h> #endif +#include <signal.h> #include <unictype.h> #include <uniname.h> -#include <signal.h> #include "ast.h" #include "stdlib/print.h" @@ -32,7 +32,7 @@ #endif static const double RADIANS_PER_DEGREE = 0.0174532925199432957692369076848861271344287188854172545609719144; -static const char closing[128] = {['(']=')', ['[']=']', ['<']='>', ['{']='}'}; +static const char closing[128] = {['('] = ')', ['['] = ']', ['<'] = '>', ['{'] = '}'}; typedef struct { file_t *file; @@ -45,26 +45,40 @@ typedef struct { #define PARSER(name) ast_t *name(parse_ctx_t *ctx, const char *pos) int op_tightness[] = { - [Power]=9, - [Multiply]=8, [Divide]=8, [Mod]=8, [Mod1]=8, - [Plus]=7, [Minus]=7, - [Concat]=6, - [LeftShift]=5, [RightShift]=5, [UnsignedLeftShift]=5, [UnsignedRightShift]=5, - [Min]=4, [Max]=4, - [Equals]=3, [NotEquals]=3, - [LessThan]=2, [LessThanOrEquals]=2, [GreaterThan]=2, [GreaterThanOrEquals]=2, - [Compare]=2, - [And]=1, [Or]=1, [Xor]=1, + [Power] = 9, + [Multiply] = 8, + [Divide] = 8, + [Mod] = 8, + [Mod1] = 8, + [Plus] = 7, + [Minus] = 7, + [Concat] = 6, + [LeftShift] = 5, + [RightShift] = 5, + [UnsignedLeftShift] = 5, + [UnsignedRightShift] = 5, + [Min] = 4, + [Max] = 4, + [Equals] = 3, + [NotEquals] = 3, + [LessThan] = 2, + [LessThanOrEquals] = 2, + [GreaterThan] = 2, + [GreaterThanOrEquals] = 2, + [Compare] = 2, + [And] = 1, + [Or] = 1, + [Xor] = 1, }; static const char *keywords[] = { - "C_code", "_max_", "_min_", "and", "assert", "break", "continue", "defer", "deserialize", "do", "else", - "enum", "extend", "extern", "for", "func", "if", "in", "lang", "mod", "mod1", "no", "none", - "not", "or", "pass", "return", "skip", "skip", "stop", "struct", "then", "unless", "use", "when", - "while", "xor", "yes", + "C_code", "_max_", "_min_", "and", "assert", "break", "continue", "defer", "deserialize", "do", + "else", "enum", "extend", "extern", "for", "func", "if", "in", "lang", "mod", + "mod1", "no", "none", "not", "or", "pass", "return", "skip", "skip", "stop", + "struct", "then", "unless", "use", "when", "while", "xor", "yes", }; -enum {NORMAL_FUNCTION=0, EXTERN_FUNCTION=1}; +enum { NORMAL_FUNCTION = 0, EXTERN_FUNCTION = 1 }; static INLINE size_t some_of(const char **pos, const char *allow); static INLINE size_t some_not(const char **pos, const char *forbid); @@ -72,8 +86,8 @@ static INLINE size_t spaces(const char **pos); static INLINE void whitespace(const char **pos); static INLINE size_t match(const char **pos, const char *target); static INLINE size_t match_word(const char **pos, const char *word); -static INLINE const char* get_word(const char **pos); -static INLINE const char* get_id(const char **pos); +static INLINE const char *get_word(const char **pos); +static INLINE const char *get_id(const char **pos); static INLINE bool comment(const char **pos); static INLINE bool indent(parse_ctx_t *ctx, const char **pos); static INLINE ast_e match_binary_operator(const char **pos); @@ -145,83 +159,83 @@ static PARSER(parse_var); static PARSER(parse_when); static PARSER(parse_while); static PARSER(parse_deserialize); -static ast_list_t *_parse_text_helper(parse_ctx_t *ctx, const char **out_pos, char open_quote, char close_quote, char open_interp, bool allow_escapes); +static ast_list_t *_parse_text_helper(parse_ctx_t *ctx, const char **out_pos, char open_quote, char close_quote, + char open_interp, bool allow_escapes); // // Print a parse error and exit (or use the on_err longjmp) // -#define parser_err(ctx, start, end, ...) ({ \ - if (USE_COLOR) \ - fputs("\x1b[31;1;7m", stderr); \ - fprint_inline(stderr, (ctx)->file->relative_filename, ":", get_line_number((ctx)->file, (start)), \ - ".", get_line_column((ctx)->file, (start)), ": ", __VA_ARGS__); \ - if (USE_COLOR) \ - fputs(" \x1b[m", stderr); \ - fputs("\n\n", stderr); \ - highlight_error((ctx)->file, (start), (end), "\x1b[31;1;7m", 2, USE_COLOR); \ - fputs("\n", stderr); \ - if (getenv("TOMO_STACKTRACE")) \ - print_stacktrace(stderr, 1); \ - if ((ctx)->on_err) \ - longjmp(*((ctx)->on_err), 1); \ - raise(SIGABRT); \ - exit(1); \ -}) +#define parser_err(ctx, start, end, ...) \ + ({ \ + if (USE_COLOR) fputs("\x1b[31;1;7m", stderr); \ + fprint_inline(stderr, (ctx)->file->relative_filename, ":", get_line_number((ctx)->file, (start)), ".", \ + get_line_column((ctx)->file, (start)), ": ", __VA_ARGS__); \ + if (USE_COLOR) fputs(" \x1b[m", stderr); \ + fputs("\n\n", stderr); \ + highlight_error((ctx)->file, (start), (end), "\x1b[31;1;7m", 2, USE_COLOR); \ + fputs("\n", stderr); \ + if (getenv("TOMO_STACKTRACE")) print_stacktrace(stderr, 1); \ + if ((ctx)->on_err) longjmp(*((ctx)->on_err), 1); \ + raise(SIGABRT); \ + exit(1); \ + }) // // Expect a string (potentially after whitespace) and emit a parser error if it's not there // -#define expect_str(ctx, start, pos, target, ...) ({ \ - spaces(pos); \ - if (!match(pos, target)) { \ - if (USE_COLOR) \ - fputs("\x1b[31;1;7m", stderr); \ - parser_err(ctx, start, *pos, __VA_ARGS__); \ - } \ - char _lastchar = target[strlen(target)-1]; \ - if (isalpha(_lastchar) || isdigit(_lastchar) || _lastchar == '_') { \ - if (is_xid_continue_next(*pos)) { \ - if (USE_COLOR) \ - fputs("\x1b[31;1;7m", stderr); \ - parser_err(ctx, start, *pos, __VA_ARGS__); \ - } \ - } \ -}) +#define expect_str(ctx, start, pos, target, ...) \ + ({ \ + spaces(pos); \ + if (!match(pos, target)) { \ + if (USE_COLOR) fputs("\x1b[31;1;7m", stderr); \ + parser_err(ctx, start, *pos, __VA_ARGS__); \ + } \ + char _lastchar = target[strlen(target) - 1]; \ + if (isalpha(_lastchar) || isdigit(_lastchar) || _lastchar == '_') { \ + if (is_xid_continue_next(*pos)) { \ + if (USE_COLOR) fputs("\x1b[31;1;7m", stderr); \ + parser_err(ctx, start, *pos, __VA_ARGS__); \ + } \ + } \ + }) // // Helper for matching closing parens with good error messages // -#define expect_closing(ctx, pos, close_str, ...) ({ \ - const char *_start = *pos; \ - spaces(pos); \ - if (!match(pos, (close_str))) { \ - const char *_eol = strchr(*pos, '\n'); \ - const char *_next = strstr(*pos, (close_str)); \ - const char *_end = _eol < _next ? _eol : _next; \ - if (USE_COLOR) \ - fputs("\x1b[31;1;7m", stderr); \ - parser_err(ctx, _start, _end, __VA_ARGS__); \ - } \ -}) - -#define expect(ctx, start, pos, parser, ...) ({ \ - const char **_pos = pos; \ - spaces(_pos); \ - __typeof(parser(ctx, *_pos)) _result = parser(ctx, *_pos); \ - if (!_result) { \ - if (USE_COLOR) \ - fputs("\x1b[31;1;7m", stderr); \ - parser_err(ctx, start, *_pos, __VA_ARGS__); \ - } \ - *_pos = _result->end; \ - _result; }) - -#define optional(ctx, pos, parser) ({ \ - const char **_pos = pos; \ - spaces(_pos); \ - __typeof(parser(ctx, *_pos)) _result = parser(ctx, *_pos); \ - if (_result) *_pos = _result->end; \ - _result; }) +#define expect_closing(ctx, pos, close_str, ...) \ + ({ \ + const char *_start = *pos; \ + spaces(pos); \ + if (!match(pos, (close_str))) { \ + const char *_eol = strchr(*pos, '\n'); \ + const char *_next = strstr(*pos, (close_str)); \ + const char *_end = _eol < _next ? _eol : _next; \ + if (USE_COLOR) fputs("\x1b[31;1;7m", stderr); \ + parser_err(ctx, _start, _end, __VA_ARGS__); \ + } \ + }) + +#define expect(ctx, start, pos, parser, ...) \ + ({ \ + const char **_pos = pos; \ + spaces(_pos); \ + __typeof(parser(ctx, *_pos)) _result = parser(ctx, *_pos); \ + if (!_result) { \ + if (USE_COLOR) fputs("\x1b[31;1;7m", stderr); \ + parser_err(ctx, start, *_pos, __VA_ARGS__); \ + } \ + *_pos = _result->end; \ + _result; \ + }) + +#define optional(ctx, pos, parser) \ + ({ \ + const char **_pos = pos; \ + spaces(_pos); \ + __typeof(parser(ctx, *_pos)) _result = parser(ctx, *_pos); \ + if (_result) *_pos = _result->end; \ + _result; \ + }) // // Convert an escape sequence like \n to a string @@ -233,7 +247,8 @@ static ast_list_t *_parse_text_helper(parse_ctx_t *ctx, const char **out_pos, ch static const char *unescape(parse_ctx_t *ctx, const char **out) { const char **endpos = out; const char *escape = *out; - static const char *unescapes[256] = {['a']="\a",['b']="\b",['e']="\x1b",['f']="\f",['n']="\n",['r']="\r",['t']="\t",['v']="\v",['_']=" "}; + static const char *unescapes[256] = {['a'] = "\a", ['b'] = "\b", ['e'] = "\x1b", ['f'] = "\f", ['n'] = "\n", + ['r'] = "\r", ['t'] = "\t", ['v'] = "\v", ['_'] = " "}; assert(*escape == '\\'); if (unescapes[(int)escape[1]]) { *endpos = escape + 2; @@ -241,16 +256,14 @@ static const char *unescape(parse_ctx_t *ctx, const char **out) { } else if (escape[1] == '[') { // ANSI Control Sequence Indicator: \033 [ ... m size_t len = strcspn(&escape[2], "\r\n]"); - if (escape[2+len] != ']') - parser_err(ctx, escape, escape + 2 + len, "Missing closing ']'"); + if (escape[2 + len] != ']') parser_err(ctx, escape, escape + 2 + len, "Missing closing ']'"); *endpos = escape + 3 + len; return String("\033[", string_slice(&escape[2], len), "m"); } else if (escape[1] == '{') { // Unicode codepoints by name size_t len = strcspn(&escape[2], "\r\n}"); - if (escape[2+len] != '}') - parser_err(ctx, escape, escape + 2 + len, "Missing closing '}'"); - char name[len+1]; + if (escape[2 + len] != '}') parser_err(ctx, escape, escape + 2 + len, "Missing closing '}'"); + char name[len + 1]; memcpy(name, &escape[2], len); name[len] = '\0'; @@ -260,25 +273,24 @@ static const char *unescape(parse_ctx_t *ctx, const char **out) { } // Unicode codepoints by hex char *endptr = NULL; - long codepoint = strtol(name+1, &endptr, 16); + long codepoint = strtol(name + 1, &endptr, 16); uint32_t ustr[2] = {codepoint, 0}; size_t bufsize = 8; uint8_t buf[bufsize]; (void)u32_to_u8(ustr, bufsize, buf, &bufsize); *endpos = escape + 3 + len; - return GC_strndup((char*)buf, bufsize); + return GC_strndup((char *)buf, bufsize); } - look_up_unicode_name:; + look_up_unicode_name:; uint32_t codepoint = unicode_name_character(name); if (codepoint == UNINAME_INVALID) - parser_err(ctx, escape, escape + 3 + len, - "Invalid unicode codepoint name: ", quoted(name)); + parser_err(ctx, escape, escape + 3 + len, "Invalid unicode codepoint name: ", quoted(name)); *endpos = escape + 3 + len; char *str = GC_MALLOC_ATOMIC(16); size_t u8_len = 16; - (void)u32_to_u8(&codepoint, 1, (uint8_t*)str, &u8_len); + (void)u32_to_u8(&codepoint, 1, (uint8_t *)str, &u8_len); str[u8_len] = '\0'; return str; } else if (escape[1] == 'x' && escape[2] && escape[3]) { @@ -287,14 +299,15 @@ static const char *unescape(parse_ctx_t *ctx, const char **out) { char c = (char)strtol(buf, NULL, 16); *endpos = escape + 4; return GC_strndup(&c, 1); - } else if ('0' <= escape[1] && escape[1] <= '7' && '0' <= escape[2] && escape[2] <= '7' && '0' <= escape[3] && escape[3] <= '7') { + } else if ('0' <= escape[1] && escape[1] <= '7' && '0' <= escape[2] && escape[2] <= '7' && '0' <= escape[3] + && escape[3] <= '7') { char buf[] = {escape[1], escape[2], escape[3], 0}; char c = (char)strtol(buf, NULL, 8); *endpos = escape + 4; return GC_strndup(&c, 1); } else { *endpos = escape + 2; - return GC_strndup(escape+1, 1); + return GC_strndup(escape + 1, 1); } } #ifdef __GNUC__ @@ -302,8 +315,7 @@ static const char *unescape(parse_ctx_t *ctx, const char **out) { #endif // Indent is in number of spaces (assuming that \t is 4 spaces) -PUREFUNC static INLINE int64_t get_indent(parse_ctx_t *ctx, const char *pos) -{ +PUREFUNC static INLINE int64_t get_indent(parse_ctx_t *ctx, const char *pos) { int64_t line_num = get_line_number(ctx->file, pos); const char *line = get_line(ctx->file, line_num); if (line == NULL) { @@ -311,12 +323,14 @@ PUREFUNC static INLINE int64_t get_indent(parse_ctx_t *ctx, const char *pos) } else if (*line == ' ') { int64_t spaces = (int64_t)strspn(line, " "); if (line[spaces] == '\t') - parser_err(ctx, line + spaces, line + spaces + 1, "This is a tab following spaces, and you can't mix tabs and spaces"); + parser_err(ctx, line + spaces, line + spaces + 1, + "This is a tab following spaces, and you can't mix tabs and spaces"); return spaces; } else if (*line == '\t') { int64_t indent = (int64_t)strspn(line, "\t"); if (line[indent] == ' ') - parser_err(ctx, line + indent, line + indent + 1, "This is a space following tabs, and you can't mix tabs and spaces"); + parser_err(ctx, line + indent, line + indent + 1, + "This is a space following tabs, and you can't mix tabs and spaces"); return indent * SPACES_PER_INDENT; } else { return 0; @@ -338,9 +352,7 @@ size_t some_not(const char **pos, const char *forbid) { return len; } -size_t spaces(const char **pos) { - return some_of(pos, " \t"); -} +size_t spaces(const char **pos) { return some_of(pos, " \t"); } void whitespace(const char **pos) { while (some_of(pos, " \t\r\n") || comment(pos)) @@ -349,23 +361,21 @@ void whitespace(const char **pos) { size_t match(const char **pos, const char *target) { size_t len = strlen(target); - if (strncmp(*pos, target, len) != 0) - return 0; + if (strncmp(*pos, target, len) != 0) return 0; *pos += len; return len; } static INLINE bool is_xid_continue_next(const char *pos) { ucs4_t point = 0; - u8_next(&point, (const uint8_t*)pos); + u8_next(&point, (const uint8_t *)pos); return uc_is_property_xid_continue(point); } size_t match_word(const char **out, const char *word) { const char *pos = *out; spaces(&pos); - if (!match(&pos, word) || is_xid_continue_next(pos)) - return 0; + if (!match(&pos, word) || is_xid_continue_next(pos)) return 0; *out = pos; return strlen(word); @@ -374,31 +384,26 @@ size_t match_word(const char **out, const char *word) { const char *get_word(const char **inout) { const char *word = *inout; spaces(&word); - const uint8_t *pos = (const uint8_t*)word; + const uint8_t *pos = (const uint8_t *)word; ucs4_t point; pos = u8_next(&point, pos); - if (!uc_is_property_xid_start(point) && point != '_') - return NULL; + if (!uc_is_property_xid_start(point) && point != '_') return NULL; for (const uint8_t *next; (next = u8_next(&point, pos)); pos = next) { - if (!uc_is_property_xid_continue(point)) - break; + if (!uc_is_property_xid_continue(point)) break; } - *inout = (const char*)pos; - return GC_strndup(word, (size_t)((const char*)pos - word)); + *inout = (const char *)pos; + return GC_strndup(word, (size_t)((const char *)pos - word)); } static CONSTFUNC bool is_keyword(const char *word) { - int64_t lo = 0, hi = sizeof(keywords)/sizeof(keywords[0])-1; + int64_t lo = 0, hi = sizeof(keywords) / sizeof(keywords[0]) - 1; while (lo <= hi) { int64_t mid = (lo + hi) / 2; int32_t cmp = strcmp(word, keywords[mid]); - if (cmp == 0) - return true; - else if (cmp > 0) - lo = mid + 1; - else if (cmp < 0) - hi = mid - 1; + if (cmp == 0) return true; + else if (cmp > 0) lo = mid + 1; + else if (cmp < 0) hi = mid - 1; } return false; } @@ -406,15 +411,12 @@ static CONSTFUNC bool is_keyword(const char *word) { const char *get_id(const char **inout) { const char *pos = *inout; const char *word = get_word(&pos); - if (!word || is_keyword(word)) - return NULL; + if (!word || is_keyword(word)) return NULL; *inout = pos; return word; } -static const char *eol(const char *str) { - return str + strcspn(str, "\r\n"); -} +static const char *eol(const char *str) { return str + strcspn(str, "\r\n"); } bool comment(const char **pos) { if ((*pos)[0] == '#') { @@ -430,11 +432,9 @@ bool indent(parse_ctx_t *ctx, const char **out) { int64_t starting_indent = get_indent(ctx, pos); whitespace(&pos); const char *next_line = get_line(ctx->file, get_line_number(ctx->file, pos)); - if (next_line <= *out) - return false; + if (next_line <= *out) return false; - if (get_indent(ctx, next_line) != starting_indent + SPACES_PER_INDENT) - return false; + if (get_indent(ctx, next_line) != starting_indent + SPACES_PER_INDENT) return false; *out = next_line + strspn(next_line, " \t"); return true; @@ -486,8 +486,7 @@ PARSER(parse_parens) { expect_closing(ctx, &pos, ")", "I wasn't able to parse the rest of this expression"); // Update the span to include the parens: - return new(ast_t, .file=(ctx)->file, .start=start, .end=pos, - .tag=expr->tag, .__data=expr->__data); + return new (ast_t, .file = (ctx)->file, .start = start, .end = pos, .tag = expr->tag, .__data = expr->__data); } PARSER(parse_int) { @@ -505,7 +504,7 @@ PARSER(parse_int) { } char *str = GC_MALLOC_ATOMIC((size_t)(pos - start) + 1); memset(str, 0, (size_t)(pos - start) + 1); - for (char *src = (char*)start, *dest = str; src < pos; ++src) { + for (char *src = (char *)start, *dest = str; src < pos; ++src) { if (*src != '_') *(dest++) = *src; } @@ -514,13 +513,13 @@ PARSER(parse_int) { if (match(&pos, "%")) { double n = strtod(str, NULL) / 100.; - return NewAST(ctx->file, start, pos, Num, .n=n); + return NewAST(ctx->file, start, pos, Num, .n = n); } else if (match(&pos, "deg")) { double n = strtod(str, NULL) * RADIANS_PER_DEGREE; - return NewAST(ctx->file, start, pos, Num, .n=n); + return NewAST(ctx->file, start, pos, Num, .n = n); } - return NewAST(ctx->file, start, pos, Int, .str=str); + return NewAST(ctx->file, start, pos, Int, .str = str); } type_ast_t *parse_table_type(parse_ctx_t *ctx, const char *pos) { @@ -541,11 +540,13 @@ type_ast_t *parse_table_type(parse_ctx_t *ctx, const char *pos) { ast_t *default_value = NULL; if (match(&pos, ";") && match_word(&pos, "default")) { expect_str(ctx, pos, &pos, "=", "I expected an '=' here"); - default_value = expect(ctx, start, &pos, parse_extended_expr, "I couldn't parse the default value for this table"); + default_value = + expect(ctx, start, &pos, parse_extended_expr, "I couldn't parse the default value for this table"); } whitespace(&pos); expect_closing(ctx, &pos, "}", "I wasn't able to parse the rest of this table type"); - return NewTypeAST(ctx->file, start, pos, TableTypeAST, .key=key_type, .value=value_type, .default_value=default_value); + return NewTypeAST(ctx->file, start, pos, TableTypeAST, .key = key_type, .value = value_type, + .default_value = default_value); } type_ast_t *parse_set_type(parse_ctx_t *ctx, const char *pos) { @@ -557,7 +558,7 @@ type_ast_t *parse_set_type(parse_ctx_t *ctx, const char *pos) { pos = item_type->end; whitespace(&pos); expect_closing(ctx, &pos, "|", "I wasn't able to parse the rest of this set type"); - return NewTypeAST(ctx->file, start, pos, SetTypeAST, .item=item_type); + return NewTypeAST(ctx->file, start, pos, SetTypeAST, .item = item_type); } type_ast_t *parse_func_type(parse_ctx_t *ctx, const char *pos) { @@ -569,35 +570,31 @@ type_ast_t *parse_func_type(parse_ctx_t *ctx, const char *pos) { spaces(&pos); type_ast_t *ret = match(&pos, "->") ? optional(ctx, &pos, parse_type) : NULL; expect_closing(ctx, &pos, ")", "I wasn't able to parse the rest of this function type"); - return NewTypeAST(ctx->file, start, pos, FunctionTypeAST, .args=args, .ret=ret); + return NewTypeAST(ctx->file, start, pos, FunctionTypeAST, .args = args, .ret = ret); } type_ast_t *parse_list_type(parse_ctx_t *ctx, const char *pos) { const char *start = pos; if (!match(&pos, "[")) return NULL; - type_ast_t *type = expect(ctx, start, &pos, parse_type, - "I couldn't parse a list item type after this point"); + type_ast_t *type = expect(ctx, start, &pos, parse_type, "I couldn't parse a list item type after this point"); expect_closing(ctx, &pos, "]", "I wasn't able to parse the rest of this list type"); - return NewTypeAST(ctx->file, start, pos, ListTypeAST, .item=type); + return NewTypeAST(ctx->file, start, pos, ListTypeAST, .item = type); } type_ast_t *parse_pointer_type(parse_ctx_t *ctx, const char *pos) { const char *start = pos; bool is_stack; - if (match(&pos, "@")) - is_stack = false; - else if (match(&pos, "&")) - is_stack = true; - else - return NULL; + if (match(&pos, "@")) is_stack = false; + else if (match(&pos, "&")) is_stack = true; + else return NULL; spaces(&pos); - type_ast_t *type = expect(ctx, start, &pos, parse_non_optional_type, - "I couldn't parse a pointer type after this point"); - type_ast_t *ptr_type = NewTypeAST(ctx->file, start, pos, PointerTypeAST, .pointed=type, .is_stack=is_stack); + type_ast_t *type = + expect(ctx, start, &pos, parse_non_optional_type, "I couldn't parse a pointer type after this point"); + type_ast_t *ptr_type = NewTypeAST(ctx->file, start, pos, PointerTypeAST, .pointed = type, .is_stack = is_stack); spaces(&pos); while (match(&pos, "?")) - ptr_type = NewTypeAST(ctx->file, start, pos, OptionalTypeAST, .type=ptr_type); + ptr_type = NewTypeAST(ctx->file, start, pos, OptionalTypeAST, .type = ptr_type); return ptr_type; } @@ -614,20 +611,15 @@ type_ast_t *parse_type_name(parse_ctx_t *ctx, const char *pos) { id = String(id, ".", next_id); pos = next; } - return NewTypeAST(ctx->file, start, pos, VarTypeAST, .name=id); + return NewTypeAST(ctx->file, start, pos, VarTypeAST, .name = id); } type_ast_t *parse_non_optional_type(parse_ctx_t *ctx, const char *pos) { const char *start = pos; type_ast_t *type = NULL; - bool success = (false - || (type=parse_pointer_type(ctx, pos)) - || (type=parse_list_type(ctx, pos)) - || (type=parse_table_type(ctx, pos)) - || (type=parse_set_type(ctx, pos)) - || (type=parse_type_name(ctx, pos)) - || (type=parse_func_type(ctx, pos)) - ); + bool success = (false || (type = parse_pointer_type(ctx, pos)) || (type = parse_list_type(ctx, pos)) + || (type = parse_table_type(ctx, pos)) || (type = parse_set_type(ctx, pos)) + || (type = parse_type_name(ctx, pos)) || (type = parse_func_type(ctx, pos))); if (!success && match(&pos, "(")) { whitespace(&pos); type = optional(ctx, &pos, parse_type); @@ -648,7 +640,7 @@ type_ast_t *parse_type(parse_ctx_t *ctx, const char *pos) { pos = type->end; spaces(&pos); while (match(&pos, "?")) - type = NewTypeAST(ctx->file, start, pos, OptionalTypeAST, .type=type); + type = NewTypeAST(ctx->file, start, pos, OptionalTypeAST, .type = type); return type; } @@ -659,21 +651,17 @@ PARSER(parse_num) { else if (*pos == '.' && !isdigit(pos[1])) return NULL; size_t len = strspn(pos, "0123456789_"); - if (strncmp(pos+len, "..", 2) == 0) - return NULL; - else if (pos[len] == '.') - len += 1 + strspn(pos + len + 1, "0123456789"); - else if (pos[len] != 'e' && pos[len] != 'f' && pos[len] != '%') - return NULL; + if (strncmp(pos + len, "..", 2) == 0) return NULL; + else if (pos[len] == '.') len += 1 + strspn(pos + len + 1, "0123456789"); + else if (pos[len] != 'e' && pos[len] != 'f' && pos[len] != '%') return NULL; if (pos[len] == 'e') { len += 1; - if (pos[len] == '-') - len += 1; + if (pos[len] == '-') len += 1; len += strspn(pos + len, "0123456789_"); } - char *buf = GC_MALLOC_ATOMIC(len+1); - memset(buf, 0, len+1); - for (char *src = (char*)pos, *dest = buf; src < pos+len; ++src) { + char *buf = GC_MALLOC_ATOMIC(len + 1); + memset(buf, 0, len + 1); + for (char *src = (char *)pos, *dest = buf; src < pos + len; ++src) { if (*src != '_') *(dest++) = *src; } double d = strtod(buf, NULL); @@ -681,22 +669,18 @@ PARSER(parse_num) { if (negative) d *= -1; - if (match(&pos, "%")) - d /= 100.; - else if (match(&pos, "deg")) - d *= RADIANS_PER_DEGREE; + if (match(&pos, "%")) d /= 100.; + else if (match(&pos, "deg")) d *= RADIANS_PER_DEGREE; - return NewAST(ctx->file, start, pos, Num, .n=d); + return NewAST(ctx->file, start, pos, Num, .n = d); } static INLINE bool match_separator(const char **pos) { // Either comma or newline const char *p = *pos; int separators = 0; for (;;) { - if (some_of(&p, "\r\n,")) - ++separators; - else if (!comment(&p) && !some_of(&p, " \t")) - break; + if (some_of(&p, "\r\n,")) ++separators; + else if (!comment(&p) && !some_of(&p, " \t")) break; } if (separators > 0) { *pos = p; @@ -722,15 +706,14 @@ PARSER(parse_list) { pos = suffixed->end; suffixed = parse_comprehension_suffix(ctx, item); } - items = new(ast_list_t, .ast=item, .next=items); - if (!match_separator(&pos)) - break; + 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); + return NewAST(ctx->file, start, pos, List, .items = items); } PARSER(parse_table) { @@ -746,17 +729,16 @@ PARSER(parse_table) { 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 *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; + entries = new (ast_list_t, .ast = entry, .next = entries); + if (!match_separator(&pos)) break; } REVERSE_LIST(entries); @@ -771,14 +753,12 @@ PARSER(parse_table) { 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"); + 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"); + 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; @@ -791,13 +771,13 @@ PARSER(parse_table) { 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); + return NewAST(ctx->file, start, pos, Table, .default_value = default_value, .entries = entries, + .fallback = fallback); } PARSER(parse_set) { const char *start = pos; - if (match(&pos, "||")) - return NewAST(ctx->file, start, pos, Set); + if (match(&pos, "||")) return NewAST(ctx->file, start, pos, Set); if (!match(&pos, "|")) return NULL; whitespace(&pos); @@ -813,9 +793,8 @@ PARSER(parse_set) { pos = suffixed->end; suffixed = parse_comprehension_suffix(ctx, item); } - items = new(ast_list_t, .ast=item, .next=items); - if (!match_separator(&pos)) - break; + items = new (ast_list_t, .ast = item, .next = items); + if (!match_separator(&pos)) break; } REVERSE_LIST(items); @@ -823,7 +802,7 @@ PARSER(parse_set) { 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); + return NewAST(ctx->file, start, pos, Set, .items = items); } ast_t *parse_field_suffix(parse_ctx_t *ctx, ast_t *lhs) { @@ -834,49 +813,41 @@ ast_t *parse_field_suffix(parse_ctx_t *ctx, ast_t *lhs) { if (*pos == '.') return NULL; whitespace(&pos); bool dollar = match(&pos, "$"); - const char* field = get_id(&pos); + const char *field = get_id(&pos); if (!field) return NULL; if (dollar) field = String("$", field); - return NewAST(ctx->file, lhs->start, pos, FieldAccess, .fielded=lhs, .field=field); + return NewAST(ctx->file, lhs->start, pos, FieldAccess, .fielded = lhs, .field = field); } ast_t *parse_optional_suffix(parse_ctx_t *ctx, ast_t *lhs) { if (!lhs) return NULL; const char *pos = lhs->end; - if (match(&pos, "?")) - return NewAST(ctx->file, lhs->start, pos, Optional, .value=lhs); - else - return NULL; + if (match(&pos, "?")) return NewAST(ctx->file, lhs->start, pos, Optional, .value = lhs); + else return NULL; } ast_t *parse_non_optional_suffix(parse_ctx_t *ctx, ast_t *lhs) { if (!lhs) return NULL; const char *pos = lhs->end; - if (match(&pos, "!")) - return NewAST(ctx->file, lhs->start, pos, NonOptional, .value=lhs); - else - return NULL; + if (match(&pos, "!")) return NewAST(ctx->file, lhs->start, pos, NonOptional, .value = lhs); + else return NULL; } PARSER(parse_reduction) { const char *start = pos; if (!match(&pos, "(")) return NULL; - + whitespace(&pos); ast_e op = match_binary_operator(&pos); if (op == Unknown) return NULL; - ast_t *key = NewAST(ctx->file, pos, pos, Var, .name="$"); - for (bool progress = true; progress; ) { + ast_t *key = NewAST(ctx->file, pos, pos, Var, .name = "$"); + for (bool progress = true; progress;) { ast_t *new_term; - progress = (false - || (new_term=parse_index_suffix(ctx, key)) - || (new_term=parse_method_call_suffix(ctx, key)) - || (new_term=parse_field_suffix(ctx, key)) - || (new_term=parse_fncall_suffix(ctx, key)) - || (new_term=parse_optional_suffix(ctx, key)) - || (new_term=parse_non_optional_suffix(ctx, key)) - ); + progress = + (false || (new_term = parse_index_suffix(ctx, key)) || (new_term = parse_method_call_suffix(ctx, key)) + || (new_term = parse_field_suffix(ctx, key)) || (new_term = parse_fncall_suffix(ctx, key)) + || (new_term = parse_optional_suffix(ctx, key)) || (new_term = parse_non_optional_suffix(ctx, key))); if (progress) key = new_term; } if (key && key->tag == Var) key = NULL; @@ -897,7 +868,7 @@ PARSER(parse_reduction) { whitespace(&pos); expect_closing(ctx, &pos, ")", "I wasn't able to parse the rest of this reduction"); - return NewAST(ctx->file, start, pos, Reduction, .iter=iter, .op=op, .key=key); + return NewAST(ctx->file, start, pos, Reduction, .iter = iter, .op = op, .key = key); } ast_t *parse_index_suffix(parse_ctx_t *ctx, ast_t *lhs) { @@ -910,7 +881,7 @@ ast_t *parse_index_suffix(parse_ctx_t *ctx, ast_t *lhs) { whitespace(&pos); bool unchecked = match(&pos, ";") && (spaces(&pos), match_word(&pos, "unchecked") != 0); expect_closing(ctx, &pos, "]", "I wasn't able to parse the rest of this index"); - return NewAST(ctx->file, start, pos, Index, .indexed=lhs, .index=index, .unchecked=unchecked); + return NewAST(ctx->file, start, pos, Index, .indexed = lhs, .index = index, .unchecked = unchecked); } ast_t *parse_comprehension_suffix(parse_ctx_t *ctx, ast_t *expr) { @@ -924,12 +895,10 @@ ast_t *parse_comprehension_suffix(parse_ctx_t *ctx, ast_t *expr) { ast_list_t *vars = NULL; for (;;) { ast_t *var = optional(ctx, &pos, parse_var); - if (var) - vars = new(ast_list_t, .ast=var, .next=vars); + if (var) vars = new (ast_list_t, .ast = var, .next = vars); spaces(&pos); - if (!match(&pos, ",")) - break; + if (!match(&pos, ",")) break; } REVERSE_LIST(vars); @@ -940,13 +909,13 @@ ast_t *parse_comprehension_suffix(parse_ctx_t *ctx, ast_t *expr) { ast_t *filter = NULL; if (match_word(&next_pos, "if")) { pos = next_pos; - filter = expect(ctx, pos-2, &pos, parse_expr, "I expected a condition for this 'if'"); + filter = expect(ctx, pos - 2, &pos, parse_expr, "I expected a condition for this 'if'"); } else if (match_word(&next_pos, "unless")) { pos = next_pos; - filter = expect(ctx, pos-2, &pos, parse_expr, "I expected a condition for this 'unless'"); + filter = expect(ctx, pos - 2, &pos, parse_expr, "I expected a condition for this 'unless'"); filter = WrapAST(filter, Not, filter); } - return NewAST(ctx->file, start, pos, Comprehension, .expr=expr, .vars=vars, .iter=iter, .filter=filter); + return NewAST(ctx->file, start, pos, Comprehension, .expr = expr, .vars = vars, .iter = iter, .filter = filter); } ast_t *parse_optional_conditional_suffix(parse_ctx_t *ctx, ast_t *stmt) { @@ -955,12 +924,12 @@ ast_t *parse_optional_conditional_suffix(parse_ctx_t *ctx, ast_t *stmt) { const char *start = stmt->start; const char *pos = stmt->end; if (match_word(&pos, "if")) { - ast_t *condition = expect(ctx, pos-2, &pos, parse_expr, "I expected a condition for this 'if'"); - return NewAST(ctx->file, start, pos, If, .condition=condition, .body=stmt); + ast_t *condition = expect(ctx, pos - 2, &pos, parse_expr, "I expected a condition for this 'if'"); + return NewAST(ctx->file, start, pos, If, .condition = condition, .body = stmt); } else if (match_word(&pos, "unless")) { - ast_t *condition = expect(ctx, pos-2, &pos, parse_expr, "I expected a condition for this 'unless'"); + ast_t *condition = expect(ctx, pos - 2, &pos, parse_expr, "I expected a condition for this 'unless'"); condition = WrapAST(condition, Not, condition); - return NewAST(ctx->file, start, pos, If, .condition=condition, .body=stmt); + return NewAST(ctx->file, start, pos, If, .condition = condition, .body = stmt); } else { return stmt; } @@ -972,22 +941,17 @@ PARSER(parse_if) { int64_t starting_indent = get_indent(ctx, pos); bool unless; - if (match_word(&pos, "if")) - unless = false; - else if (match_word(&pos, "unless")) - unless = true; - else - return NULL; + if (match_word(&pos, "if")) unless = false; + else if (match_word(&pos, "unless")) unless = true; + else return NULL; ast_t *condition = unless ? NULL : optional(ctx, &pos, parse_declaration); - if (!condition) - condition = expect(ctx, start, &pos, parse_expr, "I expected to find a condition for this 'if'"); + if (!condition) condition = expect(ctx, start, &pos, parse_expr, "I expected to find a condition for this 'if'"); - if (unless) - condition = WrapAST(condition, Not, condition); + if (unless) condition = WrapAST(condition, Not, condition); (void)match_word(&pos, "then"); // Optional 'then' - ast_t *body = expect(ctx, start, &pos, parse_block, "I expected a body for this 'if' statement"); + ast_t *body = expect(ctx, start, &pos, parse_block, "I expected a body for this 'if' statement"); const char *tmp = pos; whitespace(&tmp); @@ -997,10 +961,9 @@ PARSER(parse_if) { pos = tmp; spaces(&pos); else_body = optional(ctx, &pos, parse_if); - if (!else_body) - else_body = expect(ctx, else_start, &pos, parse_block, "I expected a body for this 'else'"); + if (!else_body) else_body = expect(ctx, else_start, &pos, parse_block, "I expected a body for this 'else'"); } - return NewAST(ctx->file, start, pos, If, .condition=condition, .body=body, .else_body=else_body); + return NewAST(ctx->file, start, pos, If, .condition = condition, .body = body, .else_body = else_body); } PARSER(parse_when) { @@ -1008,12 +971,10 @@ PARSER(parse_when) { const char *start = pos; int64_t starting_indent = get_indent(ctx, pos); - if (!match_word(&pos, "when")) - return NULL; + if (!match_word(&pos, "when")) return NULL; ast_t *subject = optional(ctx, &pos, parse_declaration); - if (!subject) subject = expect(ctx, start, &pos, parse_expr, - "I expected to find an expression for this 'when'"); + if (!subject) subject = expect(ctx, start, &pos, parse_expr, "I expected to find an expression for this 'when'"); when_clause_t *clauses = NULL; const char *tmp = pos; @@ -1023,14 +984,14 @@ PARSER(parse_when) { spaces(&pos); ast_t *pattern = expect(ctx, start, &pos, parse_expr, "I expected a pattern to match here"); spaces(&pos); - when_clause_t *new_clauses = new(when_clause_t, .pattern=pattern, .next=clauses); + when_clause_t *new_clauses = new (when_clause_t, .pattern = pattern, .next = clauses); while (match(&pos, ",")) { pattern = expect(ctx, start, &pos, parse_expr, "I expected a pattern to match here"); - new_clauses = new(when_clause_t, .pattern=pattern, .next=new_clauses); + new_clauses = new (when_clause_t, .pattern = pattern, .next = new_clauses); spaces(&pos); } (void)match_word(&pos, "then"); // Optional 'then' - ast_t *body = expect(ctx, start, &pos, parse_block, "I expected a body for this 'when' clause"); + ast_t *body = expect(ctx, start, &pos, parse_block, "I expected a body for this 'when' clause"); for (when_clause_t *c = new_clauses; c && c != clauses; c = c->next) { c->body = body; } @@ -1044,9 +1005,9 @@ PARSER(parse_when) { const char *else_start = pos; if (get_indent(ctx, tmp) == starting_indent && match_word(&tmp, "else")) { pos = tmp; - else_body = expect(ctx, else_start, &pos, parse_block, "I expected a body for this 'else'"); + else_body = expect(ctx, else_start, &pos, parse_block, "I expected a body for this 'else'"); } - return NewAST(ctx->file, start, pos, When, .subject=subject, .clauses=clauses, .else_body=else_body); + return NewAST(ctx->file, start, pos, When, .subject = subject, .clauses = clauses, .else_body = else_body); } PARSER(parse_for) { @@ -1058,12 +1019,10 @@ PARSER(parse_for) { ast_list_t *vars = NULL; for (;;) { ast_t *var = optional(ctx, &pos, parse_var); - if (var) - vars = new(ast_list_t, .ast=var, .next=vars); + if (var) vars = new (ast_list_t, .ast = var, .next = vars); spaces(&pos); - if (!match(&pos, ",")) - break; + if (!match(&pos, ",")) break; } spaces(&pos); @@ -1073,7 +1032,7 @@ PARSER(parse_for) { (void)match_word(&pos, "do"); // Optional 'do' - ast_t *body = expect(ctx, start, &pos, parse_block, "I expected a body for this 'for'"); + ast_t *body = expect(ctx, start, &pos, parse_block, "I expected a body for this 'for'"); const char *else_start = pos; whitespace(&else_start); @@ -1083,15 +1042,15 @@ PARSER(parse_for) { empty = expect(ctx, pos, &pos, parse_block, "I expected a body for this 'else'"); } REVERSE_LIST(vars); - return NewAST(ctx->file, start, pos, For, .vars=vars, .iter=iter, .body=body, .empty=empty); + return NewAST(ctx->file, start, pos, For, .vars = vars, .iter = iter, .body = body, .empty = empty); } PARSER(parse_do) { // do [<indent>] body const char *start = pos; if (!match_word(&pos, "do")) return NULL; - ast_t *body = expect(ctx, start, &pos, parse_block, "I expected a body for this 'do'"); - return NewAST(ctx->file, start, pos, Block, .statements=Match(body, Block)->statements); + ast_t *body = expect(ctx, start, &pos, parse_block, "I expected a body for this 'do'"); + return NewAST(ctx->file, start, pos, Block, .statements = Match(body, Block)->statements); } PARSER(parse_while) { @@ -1104,22 +1063,22 @@ PARSER(parse_while) { if (match_word(&tmp, "when")) { ast_t *when = expect(ctx, start, &pos, parse_when, "I expected a 'when' block after this"); if (!when->__data.When.else_body) when->__data.When.else_body = NewAST(ctx->file, pos, pos, Stop); - return NewAST(ctx->file, start, pos, While, .body=when); + return NewAST(ctx->file, start, pos, While, .body = when); } (void)match_word(&pos, "do"); // Optional 'do' ast_t *condition = expect(ctx, start, &pos, parse_expr, "I don't see a viable condition for this 'while'"); - ast_t *body = expect(ctx, start, &pos, parse_block, "I expected a body for this 'while'"); - return NewAST(ctx->file, start, pos, While, .condition=condition, .body=body); + ast_t *body = expect(ctx, start, &pos, parse_block, "I expected a body for this 'while'"); + return NewAST(ctx->file, start, pos, While, .condition = condition, .body = body); } PARSER(parse_repeat) { // repeat [<indent>] body const char *start = pos; if (!match_word(&pos, "repeat")) return NULL; - ast_t *body = expect(ctx, start, &pos, parse_block, "I expected a body for this 'repeat'"); - return NewAST(ctx->file, start, pos, Repeat, .body=body); + ast_t *body = expect(ctx, start, &pos, parse_block, "I expected a body for this 'repeat'"); + return NewAST(ctx->file, start, pos, Repeat, .body = body); } PARSER(parse_heap_alloc) { @@ -1130,16 +1089,14 @@ PARSER(parse_heap_alloc) { for (;;) { ast_t *new_term; - if ((new_term=parse_index_suffix(ctx, val)) - || (new_term=parse_fncall_suffix(ctx, val)) - || (new_term=parse_method_call_suffix(ctx, val)) - || (new_term=parse_field_suffix(ctx, val))) { + if ((new_term = parse_index_suffix(ctx, val)) || (new_term = parse_fncall_suffix(ctx, val)) + || (new_term = parse_method_call_suffix(ctx, val)) || (new_term = parse_field_suffix(ctx, val))) { val = new_term; } else break; } pos = val->end; - ast_t *ast = NewAST(ctx->file, start, pos, HeapAllocate, .value=val); + ast_t *ast = NewAST(ctx->file, start, pos, HeapAllocate, .value = val); for (;;) { ast_t *next = parse_optional_suffix(ctx, ast); if (!next) next = parse_non_optional_suffix(ctx, ast); @@ -1157,16 +1114,14 @@ PARSER(parse_stack_reference) { for (;;) { ast_t *new_term; - if ((new_term=parse_index_suffix(ctx, val)) - || (new_term=parse_fncall_suffix(ctx, val)) - || (new_term=parse_method_call_suffix(ctx, val)) - || (new_term=parse_field_suffix(ctx, val))) { + if ((new_term = parse_index_suffix(ctx, val)) || (new_term = parse_fncall_suffix(ctx, val)) + || (new_term = parse_method_call_suffix(ctx, val)) || (new_term = parse_field_suffix(ctx, val))) { val = new_term; } else break; } pos = val->end; - ast_t *ast = NewAST(ctx->file, start, pos, StackReference, .value=val); + ast_t *ast = NewAST(ctx->file, start, pos, StackReference, .value = val); for (;;) { ast_t *next = parse_optional_suffix(ctx, ast); if (!next) next = parse_non_optional_suffix(ctx, ast); @@ -1181,7 +1136,7 @@ PARSER(parse_not) { if (!match_word(&pos, "not")) return NULL; spaces(&pos); ast_t *val = expect(ctx, start, &pos, parse_term, "I expected an expression for this 'not'"); - return NewAST(ctx->file, start, pos, Not, .value=val); + return NewAST(ctx->file, start, pos, Not, .value = val); } PARSER(parse_negative) { @@ -1189,21 +1144,18 @@ PARSER(parse_negative) { if (!match(&pos, "-")) return NULL; spaces(&pos); ast_t *val = expect(ctx, start, &pos, parse_term, "I expected an expression for this '-'"); - return NewAST(ctx->file, start, pos, Negative, .value=val); + return NewAST(ctx->file, start, pos, Negative, .value = val); } PARSER(parse_bool) { const char *start = pos; - if (match_word(&pos, "yes")) - return NewAST(ctx->file, start, pos, Bool, .b=true); - else if (match_word(&pos, "no")) - return NewAST(ctx->file, start, pos, Bool, .b=false); - else - return NULL; + if (match_word(&pos, "yes")) return NewAST(ctx->file, start, pos, Bool, .b = true); + else if (match_word(&pos, "no")) return NewAST(ctx->file, start, pos, Bool, .b = false); + else return NULL; } -ast_list_t *_parse_text_helper(parse_ctx_t *ctx, const char **out_pos, char open_quote, char close_quote, char open_interp, bool allow_escapes) -{ +ast_list_t *_parse_text_helper(parse_ctx_t *ctx, const char **out_pos, char open_quote, char close_quote, + char open_interp, bool allow_escapes) { const char *pos = *out_pos; int64_t starting_indent = get_indent(ctx, pos); int64_t string_indent = starting_indent + SPACES_PER_INDENT; @@ -1213,27 +1165,29 @@ ast_list_t *_parse_text_helper(parse_ctx_t *ctx, const char **out_pos, char open int depth = 1; bool leading_newline = false; int64_t plain_span_len = 0; -#define FLUSH_PLAIN_SPAN() do { \ - if (plain_span_len > 0) { \ - chunk = Texts(chunk, Text$from_strn(pos - plain_span_len, (size_t)plain_span_len)); \ - plain_span_len = 0; \ - } } while (0) - for (const char *end = ctx->file->text + ctx->file->len; pos < end && depth > 0; ) { +#define FLUSH_PLAIN_SPAN() \ + do { \ + if (plain_span_len > 0) { \ + chunk = Texts(chunk, Text$from_strn(pos - plain_span_len, (size_t)plain_span_len)); \ + plain_span_len = 0; \ + } \ + } while (0) + for (const char *end = ctx->file->text + ctx->file->len; pos < end && depth > 0;) { const char *after_indentation = pos; if (*pos == open_interp) { // Interpolation FLUSH_PLAIN_SPAN(); const char *interp_start = pos; if (chunk.length > 0) { - ast_t *literal = NewAST(ctx->file, chunk_start, pos, TextLiteral, .text=chunk); - chunks = new(ast_list_t, .ast=literal, .next=chunks); + ast_t *literal = NewAST(ctx->file, chunk_start, pos, TextLiteral, .text = chunk); + chunks = new (ast_list_t, .ast = literal, .next = chunks); chunk = EMPTY_TEXT; } ++pos; ast_t *interp; if (*pos == ' ' || *pos == '\t') - parser_err(ctx, pos, pos+1, "Whitespace is not allowed before an interpolation here"); + parser_err(ctx, pos, pos + 1, "Whitespace is not allowed before an interpolation here"); interp = expect(ctx, interp_start, &pos, parse_term_no_suffix, "I expected an interpolation term here"); - chunks = new(ast_list_t, .ast=interp, .next=chunks); + chunks = new (ast_list_t, .ast = interp, .next = chunks); chunk_start = pos; } else if (allow_escapes && *pos == '\\') { FLUSH_PLAIN_SPAN(); @@ -1248,8 +1202,7 @@ ast_list_t *_parse_text_helper(parse_ctx_t *ctx, const char **out_pos, char open } else if (!leading_newline && *pos == close_quote) { // Nested pair end if (get_indent(ctx, pos) == starting_indent) { --depth; - if (depth == 0) - break; + if (depth == 0) break; } plain_span_len += 1; ++pos; @@ -1270,14 +1223,14 @@ ast_list_t *_parse_text_helper(parse_ctx_t *ctx, const char **out_pos, char open // Multi-line split continue; } else { - parser_err(ctx, pos, eol(pos), "This multi-line string should be either indented or have '..' at the front"); + parser_err(ctx, pos, eol(pos), + "This multi-line string should be either indented or have '..' at the front"); } } else { // Plain character ucs4_t codepoint; - const char *next = (const char*)u8_next(&codepoint, (const uint8_t*)pos); + const char *next = (const char *)u8_next(&codepoint, (const uint8_t *)pos); plain_span_len += (int64_t)(next - pos); - if (next == NULL) - break; + if (next == NULL) break; pos = next; } } @@ -1286,8 +1239,8 @@ ast_list_t *_parse_text_helper(parse_ctx_t *ctx, const char **out_pos, char open #undef FLUSH_PLAIN_SPAN if (chunk.length > 0) { - ast_t *literal = NewAST(ctx->file, chunk_start, pos, TextLiteral, .text=chunk); - chunks = new(ast_list_t, .ast=literal, .next=chunks); + ast_t *literal = NewAST(ctx->file, chunk_start, pos, TextLiteral, .text = chunk); + chunks = new (ast_list_t, .ast = literal, .next = chunks); chunk = EMPTY_TEXT; } @@ -1323,7 +1276,8 @@ PARSER(parse_text) { } static const char *quote_chars = "\"'`|/;([{<"; if (!strchr(quote_chars, *pos)) - parser_err(ctx, pos, pos+1, "This is not a valid string quotation character. Valid characters are: \"'`|/;([{<"); + parser_err(ctx, pos, pos + 1, + "This is not a valid string quotation character. Valid characters are: \"'`|/;([{<"); open_quote = *pos; ++pos; close_quote = closing[(int)open_quote] ? closing[(int)open_quote] : open_quote; @@ -1334,18 +1288,16 @@ PARSER(parse_text) { bool allow_escapes = (open_quote != '`'); ast_list_t *chunks = _parse_text_helper(ctx, &pos, open_quote, close_quote, open_interp, allow_escapes); bool colorize = match(&pos, "~") && match_word(&pos, "colorized"); - return NewAST(ctx->file, start, pos, TextJoin, .lang=lang, .children=chunks, .colorize=colorize); + return NewAST(ctx->file, start, pos, TextJoin, .lang = lang, .children = chunks, .colorize = colorize); } PARSER(parse_path) { // "(" ("~/" / "./" / "../" / "/") ... ")" const char *start = pos; - if (!match(&pos, "(")) - return NULL; + if (!match(&pos, "(")) return NULL; - if (!(*pos == '~' || *pos == '.' || *pos == '/')) - return NULL; + if (!(*pos == '~' || *pos == '.' || *pos == '/')) return NULL; const char *path_start = pos; size_t len = 1; @@ -1360,13 +1312,13 @@ PARSER(parse_path) { paren_depth -= 1; if (paren_depth <= 0) break; } else if (pos[len] == '\r' || pos[len] == '\n') { - parser_err(ctx, path_start, &pos[len-1], "This path was not closed"); + parser_err(ctx, path_start, &pos[len - 1], "This path was not closed"); } len += 1; } pos += len + 1; - char *path = String(string_slice(path_start, .length=len)); - for (char *src = path, *dest = path; ; ) { + char *path = String(string_slice(path_start, .length = len)); + for (char *src = path, *dest = path;;) { if (src[0] == '\\') { *(dest++) = src[1]; src += 2; @@ -1377,7 +1329,7 @@ PARSER(parse_path) { break; } } - return NewAST(ctx->file, start, pos, Path, .path=path); + return NewAST(ctx->file, start, pos, Path, .path = path); } PARSER(parse_pass) { @@ -1389,7 +1341,7 @@ PARSER(parse_defer) { const char *start = pos; if (!match_word(&pos, "defer")) return NULL; ast_t *body = expect(ctx, start, &pos, parse_block, "I expected a block to be deferred here"); - return NewAST(ctx->file, start, pos, Defer, .body=body); + return NewAST(ctx->file, start, pos, Defer, .body = body); } PARSER(parse_skip) { @@ -1399,7 +1351,7 @@ PARSER(parse_skip) { if (match_word(&pos, "for")) target = "for"; else if (match_word(&pos, "while")) target = "while"; else target = get_id(&pos); - ast_t *skip = NewAST(ctx->file, start, pos, Skip, .target=target); + ast_t *skip = NewAST(ctx->file, start, pos, Skip, .target = target); skip = parse_optional_conditional_suffix(ctx, skip); return skip; } @@ -1411,7 +1363,7 @@ PARSER(parse_stop) { if (match_word(&pos, "for")) target = "for"; else if (match_word(&pos, "while")) target = "while"; else target = get_id(&pos); - ast_t *stop = NewAST(ctx->file, start, pos, Stop, .target=target); + ast_t *stop = NewAST(ctx->file, start, pos, Stop, .target = target); stop = parse_optional_conditional_suffix(ctx, stop); return stop; } @@ -1420,112 +1372,87 @@ PARSER(parse_return) { const char *start = pos; if (!match_word(&pos, "return")) return NULL; ast_t *value = optional(ctx, &pos, parse_expr); - ast_t *ret = NewAST(ctx->file, start, pos, Return, .value=value); + ast_t *ret = NewAST(ctx->file, start, pos, Return, .value = value); ret = parse_optional_conditional_suffix(ctx, ret); return ret; } PARSER(parse_lambda) { const char *start = pos; - if (!match_word(&pos, "func")) - return NULL; + if (!match_word(&pos, "func")) return NULL; spaces(&pos); - if (!match(&pos, "(")) - return NULL; + if (!match(&pos, "(")) return NULL; arg_ast_t *args = parse_args(ctx, &pos); spaces(&pos); type_ast_t *ret = match(&pos, "->") ? optional(ctx, &pos, parse_type) : NULL; spaces(&pos); expect_closing(ctx, &pos, ")", "I was expecting a ')' to finish this anonymous function's arguments"); ast_t *body = optional(ctx, &pos, parse_block); - if (!body) body = NewAST(ctx->file, pos, pos, Block, .statements=NULL); - return NewAST(ctx->file, start, pos, Lambda, .id=ctx->next_lambda_id++, .args=args, .ret_type=ret, .body=body); + if (!body) body = NewAST(ctx->file, pos, pos, Block, .statements = NULL); + return NewAST(ctx->file, start, pos, Lambda, .id = ctx->next_lambda_id++, .args = args, .ret_type = ret, + .body = body); } PARSER(parse_none) { const char *start = pos; - if (!match_word(&pos, "none")) - return NULL; + if (!match_word(&pos, "none")) return NULL; return NewAST(ctx->file, start, pos, None); } PARSER(parse_deserialize) { const char *start = pos; - if (!match_word(&pos, "deserialize")) - return NULL; + if (!match_word(&pos, "deserialize")) return NULL; spaces(&pos); expect_str(ctx, start, &pos, "(", "I expected arguments for this `deserialize` call"); whitespace(&pos); ast_t *value = expect(ctx, start, &pos, parse_extended_expr, "I expected an expression here"); whitespace(&pos); - expect_str(ctx, start, &pos, "->", "I expected a `-> Type` for this `deserialize` call so I know what it deserializes to"); + expect_str(ctx, start, &pos, "->", + "I expected a `-> Type` for this `deserialize` call so I know what it deserializes to"); whitespace(&pos); type_ast_t *type = expect(ctx, start, &pos, parse_type, "I couldn't parse the type for this deserialization"); whitespace(&pos); expect_closing(ctx, &pos, ")", "I expected a closing ')' for this `deserialize` call"); - return NewAST(ctx->file, start, pos, Deserialize, .value=value, .type=type); + return NewAST(ctx->file, start, pos, Deserialize, .value = value, .type = type); } PARSER(parse_var) { const char *start = pos; - const char* name = get_id(&pos); + const char *name = get_id(&pos); if (!name) return NULL; - return NewAST(ctx->file, start, pos, Var, .name=name); + return NewAST(ctx->file, start, pos, Var, .name = name); } PARSER(parse_term_no_suffix) { spaces(&pos); ast_t *term = NULL; - (void)( - false - || (term=parse_none(ctx, pos)) - || (term=parse_num(ctx, pos)) // Must come before int - || (term=parse_int(ctx, pos)) - || (term=parse_negative(ctx, pos)) // Must come after num/int - || (term=parse_heap_alloc(ctx, pos)) - || (term=parse_stack_reference(ctx, pos)) - || (term=parse_bool(ctx, pos)) - || (term=parse_text(ctx, pos)) - || (term=parse_path(ctx, pos)) - || (term=parse_lambda(ctx, pos)) - || (term=parse_parens(ctx, pos)) - || (term=parse_table(ctx, pos)) - || (term=parse_set(ctx, pos)) - || (term=parse_deserialize(ctx, pos)) - || (term=parse_var(ctx, pos)) - || (term=parse_list(ctx, pos)) - || (term=parse_reduction(ctx, pos)) - || (term=parse_pass(ctx, pos)) - || (term=parse_defer(ctx, pos)) - || (term=parse_skip(ctx, pos)) - || (term=parse_stop(ctx, pos)) - || (term=parse_return(ctx, pos)) - || (term=parse_not(ctx, pos)) - || (term=parse_extern(ctx, pos)) - || (term=parse_inline_c(ctx, pos)) - ); + (void)(false || (term = parse_none(ctx, pos)) || (term = parse_num(ctx, pos)) // Must come before int + || (term = parse_int(ctx, pos)) || (term = parse_negative(ctx, pos)) // Must come after num/int + || (term = parse_heap_alloc(ctx, pos)) || (term = parse_stack_reference(ctx, pos)) + || (term = parse_bool(ctx, pos)) || (term = parse_text(ctx, pos)) || (term = parse_path(ctx, pos)) + || (term = parse_lambda(ctx, pos)) || (term = parse_parens(ctx, pos)) || (term = parse_table(ctx, pos)) + || (term = parse_set(ctx, pos)) || (term = parse_deserialize(ctx, pos)) || (term = parse_var(ctx, pos)) + || (term = parse_list(ctx, pos)) || (term = parse_reduction(ctx, pos)) || (term = parse_pass(ctx, pos)) + || (term = parse_defer(ctx, pos)) || (term = parse_skip(ctx, pos)) || (term = parse_stop(ctx, pos)) + || (term = parse_return(ctx, pos)) || (term = parse_not(ctx, pos)) || (term = parse_extern(ctx, pos)) + || (term = parse_inline_c(ctx, pos))); return term; } PARSER(parse_term) { const char *start = pos; - if (match(&pos, "???")) - parser_err(ctx, start, pos, "This value needs to be filled in!"); + if (match(&pos, "???")) parser_err(ctx, start, pos, "This value needs to be filled in!"); ast_t *term = parse_term_no_suffix(ctx, pos); if (!term) return NULL; - for (bool progress = true; progress; ) { + for (bool progress = true; progress;) { ast_t *new_term; - progress = (false - || (new_term=parse_index_suffix(ctx, term)) - || (new_term=parse_method_call_suffix(ctx, term)) - || (new_term=parse_field_suffix(ctx, term)) - || (new_term=parse_fncall_suffix(ctx, term)) - || (new_term=parse_optional_suffix(ctx, term)) - || (new_term=parse_non_optional_suffix(ctx, term)) - ); + progress = + (false || (new_term = parse_index_suffix(ctx, term)) || (new_term = parse_method_call_suffix(ctx, term)) + || (new_term = parse_field_suffix(ctx, term)) || (new_term = parse_fncall_suffix(ctx, term)) + || (new_term = parse_optional_suffix(ctx, term)) || (new_term = parse_non_optional_suffix(ctx, term))); if (progress) term = new_term; } return term; @@ -1560,18 +1487,16 @@ ast_t *parse_method_call_suffix(parse_ctx_t *ctx, ast_t *self) { if (name) parser_err(ctx, arg_start, pos, "I expected an argument here"); break; } - args = new(arg_ast_t, .name=name, .value=arg, .next=args); - if (!match_separator(&pos)) - break; + args = new (arg_ast_t, .name = name, .value = arg, .next = args); + if (!match_separator(&pos)) break; } REVERSE_LIST(args); whitespace(&pos); - if (!match(&pos, ")")) - parser_err(ctx, start, pos, "This parenthesis is unclosed"); + if (!match(&pos, ")")) parser_err(ctx, start, pos, "This parenthesis is unclosed"); - return NewAST(ctx->file, start, pos, MethodCall, .self=self, .name=fn, .args=args); + return NewAST(ctx->file, start, pos, MethodCall, .self = self, .name = fn, .args = args); } ast_t *parse_fncall_suffix(parse_ctx_t *ctx, ast_t *fn) { @@ -1596,26 +1521,22 @@ ast_t *parse_fncall_suffix(parse_ctx_t *ctx, ast_t *fn) { ast_t *arg = optional(ctx, &pos, parse_expr); if (!arg) { - if (name) - parser_err(ctx, arg_start, pos, "I expected an argument here"); + if (name) parser_err(ctx, arg_start, pos, "I expected an argument here"); break; } - args = new(arg_ast_t, .name=name, .value=arg, .next=args); - if (!match_separator(&pos)) - break; + args = new (arg_ast_t, .name = name, .value = arg, .next = args); + if (!match_separator(&pos)) break; } whitespace(&pos); - if (!match(&pos, ")")) - parser_err(ctx, start, pos, "This parenthesis is unclosed"); + if (!match(&pos, ")")) parser_err(ctx, start, pos, "This parenthesis is unclosed"); REVERSE_LIST(args); - return NewAST(ctx->file, start, pos, FunctionCall, .fn=fn, .args=args); + return NewAST(ctx->file, start, pos, FunctionCall, .fn = fn, .args = args); } -ast_e match_binary_operator(const char **pos) -{ +ast_e match_binary_operator(const char **pos) { switch (**pos) { case '+': { *pos += 1; @@ -1633,20 +1554,18 @@ ast_e match_binary_operator(const char **pos) case '<': { *pos += 1; if (match(pos, "=")) return LessThanOrEquals; // "<=" - else if (match(pos, ">")) return Compare; // "<>" + else if (match(pos, ">")) return Compare; // "<>" else if (match(pos, "<")) { - if (match(pos, "<")) - return UnsignedLeftShift; // "<<<" - return LeftShift; // "<<" + if (match(pos, "<")) return UnsignedLeftShift; // "<<<" + return LeftShift; // "<<" } else return LessThan; } case '>': { *pos += 1; if (match(pos, "=")) return GreaterThanOrEquals; // ">=" if (match(pos, ">")) { - if (match(pos, ">")) - return UnsignedRightShift; // ">>>" - return RightShift; // ">>" + if (match(pos, ">")) return UnsignedRightShift; // ">>>" + return RightShift; // ">>" } return GreaterThan; } @@ -1672,20 +1591,17 @@ static ast_t *parse_infix_expr(parse_ctx_t *ctx, const char *pos, int min_tightn int64_t starting_line = get_line_number(ctx->file, pos); int64_t starting_indent = get_indent(ctx, pos); spaces(&pos); - for (ast_e op; (op=match_binary_operator(&pos)) != Unknown && op_tightness[op] >= min_tightness; spaces(&pos)) { + for (ast_e op; (op = match_binary_operator(&pos)) != Unknown && op_tightness[op] >= min_tightness; spaces(&pos)) { ast_t *key = NULL; if (op == Min || op == Max) { - key = NewAST(ctx->file, pos, pos, Var, .name="$"); - for (bool progress = true; progress; ) { + key = NewAST(ctx->file, pos, pos, Var, .name = "$"); + for (bool progress = true; progress;) { ast_t *new_term; - progress = (false - || (new_term=parse_index_suffix(ctx, key)) - || (new_term=parse_method_call_suffix(ctx, key)) - || (new_term=parse_field_suffix(ctx, key)) - || (new_term=parse_fncall_suffix(ctx, key)) - || (new_term=parse_optional_suffix(ctx, key)) - || (new_term=parse_non_optional_suffix(ctx, key)) - ); + progress = + (false || (new_term = parse_index_suffix(ctx, key)) + || (new_term = parse_method_call_suffix(ctx, key)) || (new_term = parse_field_suffix(ctx, key)) + || (new_term = parse_fncall_suffix(ctx, key)) || (new_term = parse_optional_suffix(ctx, key)) + || (new_term = parse_non_optional_suffix(ctx, key))); if (progress) key = new_term; } if (key && key->tag == Var) key = NULL; @@ -1699,21 +1615,20 @@ static ast_t *parse_infix_expr(parse_ctx_t *ctx, const char *pos, int min_tightn ast_t *rhs = parse_infix_expr(ctx, pos, op_tightness[op] + 1); if (!rhs) break; pos = rhs->end; - + if (op == Min) { - return NewAST(ctx->file, lhs->start, rhs->end, Min, .lhs=lhs, .rhs=rhs, .key=key); + return NewAST(ctx->file, lhs->start, rhs->end, Min, .lhs = lhs, .rhs = rhs, .key = key); } else if (op == Max) { - return NewAST(ctx->file, lhs->start, rhs->end, Max, .lhs=lhs, .rhs=rhs, .key=key); + return NewAST(ctx->file, lhs->start, rhs->end, Max, .lhs = lhs, .rhs = rhs, .key = key); } else { - lhs = new(ast_t, .file=ctx->file, .start=lhs->start, .end=rhs->end, .tag=op, .__data.Plus.lhs=lhs, .__data.Plus.rhs=rhs); + lhs = new (ast_t, .file = ctx->file, .start = lhs->start, .end = rhs->end, .tag = op, + .__data.Plus.lhs = lhs, .__data.Plus.rhs = rhs); } } return lhs; } -PARSER(parse_expr) { - return parse_infix_expr(ctx, pos, 0); -} +PARSER(parse_expr) { return parse_infix_expr(ctx, pos, 0); } PARSER(parse_declaration) { const char *start = pos; @@ -1731,17 +1646,15 @@ PARSER(parse_declaration) { if (!val) { if (optional(ctx, &pos, parse_use)) parser_err(ctx, start, pos, "'use' statements are only allowed at the top level of a file"); - else - parser_err(ctx, pos, eol(pos), "This is not a valid expression"); + else parser_err(ctx, pos, eol(pos), "This is not a valid expression"); } } - return NewAST(ctx->file, start, pos, Declare, .var=var, .type=type, .value=val); + return NewAST(ctx->file, start, pos, Declare, .var = var, .type = type, .value = val); } PARSER(parse_top_declaration) { ast_t *declaration = parse_declaration(ctx, pos); - if (declaration) - declaration->__data.Declare.top_level = true; + if (declaration) declaration->__data.Declare.top_level = true; return declaration; } @@ -1766,7 +1679,8 @@ PARSER(parse_update) { else if (match(&pos, "xor=")) op = XorUpdate; else return NULL; ast_t *rhs = expect(ctx, start, &pos, parse_extended_expr, "I expected an expression here"); - return new(ast_t, .file=ctx->file, .start=start, .end=pos, .tag=op, .__data.PlusUpdate.lhs=lhs, .__data.PlusUpdate.rhs=rhs); + return new (ast_t, .file = ctx->file, .start = start, .end = pos, .tag = op, .__data.PlusUpdate.lhs = lhs, + .__data.PlusUpdate.rhs = rhs); } PARSER(parse_assignment) { @@ -1775,7 +1689,7 @@ PARSER(parse_assignment) { for (;;) { ast_t *lhs = optional(ctx, &pos, parse_term); if (!lhs) break; - targets = new(ast_list_t, .ast=lhs, .next=targets); + targets = new (ast_list_t, .ast = lhs, .next = targets); spaces(&pos); if (!match(&pos, ",")) break; whitespace(&pos); @@ -1791,7 +1705,7 @@ PARSER(parse_assignment) { for (;;) { ast_t *rhs = optional(ctx, &pos, parse_extended_expr); if (!rhs) break; - values = new(ast_list_t, .ast=rhs, .next=values); + values = new (ast_list_t, .ast = rhs, .next = values); spaces(&pos); if (!match(&pos, ",")) break; whitespace(&pos); @@ -1800,30 +1714,23 @@ PARSER(parse_assignment) { REVERSE_LIST(targets); REVERSE_LIST(values); - return NewAST(ctx->file, start, pos, Assign, .targets=targets, .values=values); + return NewAST(ctx->file, start, pos, Assign, .targets = targets, .values = values); } PARSER(parse_statement) { ast_t *stmt = NULL; - if ((stmt=parse_declaration(ctx, pos)) - || (stmt=parse_doctest(ctx, pos)) - || (stmt=parse_assert(ctx, pos))) + if ((stmt = parse_declaration(ctx, pos)) || (stmt = parse_doctest(ctx, pos)) || (stmt = parse_assert(ctx, pos))) return stmt; - if (!(false - || (stmt=parse_update(ctx, pos)) - || (stmt=parse_assignment(ctx, pos)) - )) + if (!(false || (stmt = parse_update(ctx, pos)) || (stmt = parse_assignment(ctx, pos)))) stmt = parse_extended_expr(ctx, pos); - - for (bool progress = (stmt != NULL); progress; ) { + + for (bool progress = (stmt != NULL); progress;) { ast_t *new_stmt; progress = false; if (stmt->tag == Var) { - progress = (false - || (new_stmt=parse_method_call_suffix(ctx, stmt)) - || (new_stmt=parse_fncall_suffix(ctx, stmt)) - ); + progress = (false || (new_stmt = parse_method_call_suffix(ctx, stmt)) + || (new_stmt = parse_fncall_suffix(ctx, stmt))); } else if (stmt->tag == FunctionCall) { new_stmt = parse_optional_conditional_suffix(ctx, stmt); progress = (new_stmt != stmt); @@ -1832,20 +1739,14 @@ PARSER(parse_statement) { if (progress) stmt = new_stmt; } return stmt; - } PARSER(parse_extended_expr) { ast_t *expr = NULL; - if (false - || (expr=optional(ctx, &pos, parse_for)) - || (expr=optional(ctx, &pos, parse_while)) - || (expr=optional(ctx, &pos, parse_if)) - || (expr=optional(ctx, &pos, parse_when)) - || (expr=optional(ctx, &pos, parse_repeat)) - || (expr=optional(ctx, &pos, parse_do)) - ) + if (false || (expr = optional(ctx, &pos, parse_for)) || (expr = optional(ctx, &pos, parse_while)) + || (expr = optional(ctx, &pos, parse_if)) || (expr = optional(ctx, &pos, parse_when)) + || (expr = optional(ctx, &pos, parse_repeat)) || (expr = optional(ctx, &pos, parse_do))) return expr; return parse_expr(ctx, pos); @@ -1863,7 +1764,7 @@ PARSER(parse_block) { spaces(&pos); ast_t *stmt = optional(ctx, &pos, parse_statement); if (!stmt) break; - statements = new(ast_list_t, .ast=stmt, .next=statements); + statements = new (ast_list_t, .ast = stmt, .next = statements); spaces(&pos); if (!match(&pos, ";")) break; } @@ -1872,7 +1773,7 @@ PARSER(parse_block) { } if (indent(ctx, &pos)) { - indented:; + indented:; int64_t block_indent = get_indent(ctx, pos); whitespace(&pos); while (*pos) { @@ -1889,17 +1790,15 @@ PARSER(parse_block) { parser_err(ctx, line_start, eol(pos), "'use' statements are only allowed at the top level"); spaces(&pos); - if (*pos && *pos != '\r' && *pos != '\n') - parser_err(ctx, pos, eol(pos), "I couldn't parse this line"); + if (*pos && *pos != '\r' && *pos != '\n') parser_err(ctx, pos, eol(pos), "I couldn't parse this line"); break; } - statements = new(ast_list_t, .ast=stmt, .next=statements); + statements = new (ast_list_t, .ast = stmt, .next = statements); whitespace(&pos); // Guard against having two valid statements on the same line, separated by spaces (but no newlines): if (!memchr(stmt->end, '\n', (size_t)(pos - stmt->end))) { - if (*pos) - parser_err(ctx, pos, eol(pos), "I don't know how to parse the rest of this line"); + if (*pos) parser_err(ctx, pos, eol(pos), "I don't know how to parse the rest of this line"); pos = stmt->end; break; } @@ -1911,7 +1810,7 @@ PARSER(parse_block) { } } REVERSE_LIST(statements); - return NewAST(ctx->file, start, pos, Block, .statements=statements); + return NewAST(ctx->file, start, pos, Block, .statements = statements); } PARSER(parse_namespace) { @@ -1924,18 +1823,12 @@ PARSER(parse_namespace) { whitespace(&next); if (get_indent(ctx, next) != indent) break; ast_t *stmt; - if ((stmt=optional(ctx, &pos, parse_struct_def)) - ||(stmt=optional(ctx, &pos, parse_func_def)) - ||(stmt=optional(ctx, &pos, parse_enum_def)) - ||(stmt=optional(ctx, &pos, parse_lang_def)) - ||(stmt=optional(ctx, &pos, parse_extend)) - ||(stmt=optional(ctx, &pos, parse_convert_def)) - ||(stmt=optional(ctx, &pos, parse_use)) - ||(stmt=optional(ctx, &pos, parse_extern)) - ||(stmt=optional(ctx, &pos, parse_inline_c)) - ||(stmt=optional(ctx, &pos, parse_declaration))) - { - statements = new(ast_list_t, .ast=stmt, .next=statements); + if ((stmt = optional(ctx, &pos, parse_struct_def)) || (stmt = optional(ctx, &pos, parse_func_def)) + || (stmt = optional(ctx, &pos, parse_enum_def)) || (stmt = optional(ctx, &pos, parse_lang_def)) + || (stmt = optional(ctx, &pos, parse_extend)) || (stmt = optional(ctx, &pos, parse_convert_def)) + || (stmt = optional(ctx, &pos, parse_use)) || (stmt = optional(ctx, &pos, parse_extern)) + || (stmt = optional(ctx, &pos, parse_inline_c)) || (stmt = optional(ctx, &pos, parse_declaration))) { + statements = new (ast_list_t, .ast = stmt, .next = statements); pos = stmt->end; whitespace(&pos); // TODO: check for newline // if (!(space_types & WHITESPACE_NEWLINES)) { @@ -1949,7 +1842,7 @@ PARSER(parse_namespace) { } } REVERSE_LIST(statements); - return NewAST(ctx->file, start, pos, Block, .statements=statements); + return NewAST(ctx->file, start, pos, Block, .statements = statements); } PARSER(parse_file_body) { @@ -1961,18 +1854,12 @@ PARSER(parse_file_body) { whitespace(&next); if (get_indent(ctx, next) != 0) break; ast_t *stmt; - if ((stmt=optional(ctx, &pos, parse_struct_def)) - ||(stmt=optional(ctx, &pos, parse_func_def)) - ||(stmt=optional(ctx, &pos, parse_enum_def)) - ||(stmt=optional(ctx, &pos, parse_lang_def)) - ||(stmt=optional(ctx, &pos, parse_extend)) - ||(stmt=optional(ctx, &pos, parse_convert_def)) - ||(stmt=optional(ctx, &pos, parse_use)) - ||(stmt=optional(ctx, &pos, parse_extern)) - ||(stmt=optional(ctx, &pos, parse_inline_c)) - ||(stmt=optional(ctx, &pos, parse_top_declaration))) - { - statements = new(ast_list_t, .ast=stmt, .next=statements); + if ((stmt = optional(ctx, &pos, parse_struct_def)) || (stmt = optional(ctx, &pos, parse_func_def)) + || (stmt = optional(ctx, &pos, parse_enum_def)) || (stmt = optional(ctx, &pos, parse_lang_def)) + || (stmt = optional(ctx, &pos, parse_extend)) || (stmt = optional(ctx, &pos, parse_convert_def)) + || (stmt = optional(ctx, &pos, parse_use)) || (stmt = optional(ctx, &pos, parse_extern)) + || (stmt = optional(ctx, &pos, parse_inline_c)) || (stmt = optional(ctx, &pos, parse_top_declaration))) { + statements = new (ast_list_t, .ast = stmt, .next = statements); pos = stmt->end; whitespace(&pos); // TODO: check for newline } else { @@ -1984,7 +1871,7 @@ PARSER(parse_file_body) { parser_err(ctx, pos, eol(pos), "I expect all top-level statements to be declarations of some kind"); } REVERSE_LIST(statements); - return NewAST(ctx->file, start, pos, Block, .statements=statements); + return NewAST(ctx->file, start, pos, Block, .statements = statements); } PARSER(parse_struct_def) { @@ -1999,8 +1886,7 @@ PARSER(parse_struct_def) { if (!name) parser_err(ctx, start, pos, "I expected a name for this struct"); spaces(&pos); - if (!match(&pos, "(")) - parser_err(ctx, pos, pos, "I expected a '(' and a list of fields here"); + if (!match(&pos, "(")) parser_err(ctx, pos, pos, "I expected a '(' and a list of fields here"); arg_ast_t *fields = parse_args(ctx, &pos); @@ -2015,18 +1901,16 @@ PARSER(parse_struct_def) { external = true; } else if (match_word(&pos, "opaque")) { if (fields) - parser_err(ctx, pos-strlen("opaque"), pos, "A struct can't be opaque if it has fields defined"); + parser_err(ctx, pos - strlen("opaque"), pos, "A struct can't be opaque if it has fields defined"); opaque = true; } else { break; } - if (!match_separator(&pos)) - break; + if (!match_separator(&pos)) break; } } - expect_closing(ctx, &pos, ")", "I wasn't able to parse the rest of this struct"); ast_t *namespace = NULL; @@ -2037,10 +1921,9 @@ PARSER(parse_struct_def) { pos = ns_pos; namespace = optional(ctx, &pos, parse_namespace); } - if (!namespace) - namespace = NewAST(ctx->file, pos, pos, Block, .statements=NULL); - return NewAST(ctx->file, start, pos, StructDef, .name=name, .fields=fields, .namespace=namespace, - .secret=secret, .external=external, .opaque=opaque); + if (!namespace) namespace = NewAST(ctx->file, pos, pos, Block, .statements = NULL); + return NewAST(ctx->file, start, pos, StructDef, .name = name, .fields = fields, .namespace = namespace, + .secret = secret, .external = external, .opaque = opaque); } PARSER(parse_enum_def) { @@ -2050,8 +1933,7 @@ PARSER(parse_enum_def) { int64_t starting_indent = get_indent(ctx, pos); spaces(&pos); const char *name = get_id(&pos); - if (!name) - parser_err(ctx, start, pos, "I expected a name for this enum"); + if (!name) parser_err(ctx, start, pos, "I expected a name for this enum"); spaces(&pos); if (!match(&pos, "(")) return NULL; @@ -2079,10 +1961,9 @@ PARSER(parse_enum_def) { fields = NULL; } - tags = new(tag_ast_t, .name=tag_name, .fields=fields, .secret=secret, .next=tags); + tags = new (tag_ast_t, .name = tag_name, .fields = fields, .secret = secret, .next = tags); - if (!match_separator(&pos)) - break; + if (!match_separator(&pos)) break; } whitespace(&pos); @@ -2090,8 +1971,7 @@ PARSER(parse_enum_def) { REVERSE_LIST(tags); - if (tags == NULL) - parser_err(ctx, start, pos, "This enum does not have any tags!"); + if (tags == NULL) parser_err(ctx, start, pos, "This enum does not have any tags!"); ast_t *namespace = NULL; const char *ns_pos = pos; @@ -2101,10 +1981,9 @@ PARSER(parse_enum_def) { pos = ns_pos; namespace = optional(ctx, &pos, parse_namespace); } - if (!namespace) - namespace = NewAST(ctx->file, pos, pos, Block, .statements=NULL); + if (!namespace) namespace = NewAST(ctx->file, pos, pos, Block, .statements = NULL); - return NewAST(ctx->file, start, pos, EnumDef, .name=name, .tags=tags, .namespace=namespace); + return NewAST(ctx->file, start, pos, EnumDef, .name = name, .tags = tags, .namespace = namespace); } PARSER(parse_lang_def) { @@ -2114,8 +1993,7 @@ PARSER(parse_lang_def) { int64_t starting_indent = get_indent(ctx, pos); spaces(&pos); const char *name = get_id(&pos); - if (!name) - parser_err(ctx, start, pos, "I expected a name for this lang"); + if (!name) parser_err(ctx, start, pos, "I expected a name for this lang"); spaces(&pos); ast_t *namespace = NULL; @@ -2126,10 +2004,9 @@ PARSER(parse_lang_def) { pos = ns_pos; namespace = optional(ctx, &pos, parse_namespace); } - if (!namespace) - namespace = NewAST(ctx->file, pos, pos, Block, .statements=NULL); + if (!namespace) namespace = NewAST(ctx->file, pos, pos, Block, .statements = NULL); - return NewAST(ctx->file, start, pos, LangDef, .name=name, .namespace=namespace); + return NewAST(ctx->file, start, pos, LangDef, .name = name, .namespace = namespace); } PARSER(parse_extend) { @@ -2139,8 +2016,7 @@ PARSER(parse_extend) { int64_t starting_indent = get_indent(ctx, pos); spaces(&pos); const char *name = get_id(&pos); - if (!name) - parser_err(ctx, start, pos, "I expected a name for this lang"); + if (!name) parser_err(ctx, start, pos, "I expected a name for this lang"); ast_t *body = NULL; const char *ns_pos = pos; @@ -2150,14 +2026,12 @@ PARSER(parse_extend) { pos = ns_pos; body = optional(ctx, &pos, parse_namespace); } - if (!body) - body = NewAST(ctx->file, pos, pos, Block, .statements=NULL); + if (!body) body = NewAST(ctx->file, pos, pos, Block, .statements = NULL); - return NewAST(ctx->file, start, pos, Extend, .name=name, .body=body); + return NewAST(ctx->file, start, pos, Extend, .name = name, .body = body); } -arg_ast_t *parse_args(parse_ctx_t *ctx, const char **pos) -{ +arg_ast_t *parse_args(parse_ctx_t *ctx, const char **pos) { arg_ast_t *args = NULL; for (;;) { const char *batch_start = *pos; @@ -2177,18 +2051,18 @@ arg_ast_t *parse_args(parse_ctx_t *ctx, const char **pos) whitespace(pos); if (match(pos, ":")) { - type = expect(ctx, *pos-1, pos, parse_type, "I expected a type here"); - names = new(name_list_t, .name=name, .next=names); + type = expect(ctx, *pos - 1, pos, parse_type, "I expected a type here"); + names = new (name_list_t, .name = name, .next = names); whitespace(pos); if (match(pos, "=")) - default_val = expect(ctx, *pos-1, pos, parse_term, "I expected a value after this '='"); + default_val = expect(ctx, *pos - 1, pos, parse_term, "I expected a value after this '='"); break; } else if (strncmp(*pos, "==", 2) != 0 && match(pos, "=")) { - default_val = expect(ctx, *pos-1, pos, parse_term, "I expected a value after this '='"); - names = new(name_list_t, .name=name, .next=names); + default_val = expect(ctx, *pos - 1, pos, parse_term, "I expected a value after this '='"); + names = new (name_list_t, .name = name, .next = names); break; } else if (name) { - names = new(name_list_t, .name=name, .next=names); + names = new (name_list_t, .name = name, .next = names); spaces(pos); if (!match(pos, ",")) break; } else { @@ -2197,14 +2071,15 @@ arg_ast_t *parse_args(parse_ctx_t *ctx, const char **pos) } if (!names) break; if (!default_val && !type) - parser_err(ctx, batch_start, *pos, "I expected a ':' and type, or '=' and a default value after this parameter (", names->name, ")"); + parser_err(ctx, batch_start, *pos, + "I expected a ':' and type, or '=' and a default value after this parameter (", names->name, + ")"); REVERSE_LIST(names); for (; names; names = names->next) - args = new(arg_ast_t, .name=names->name, .type=type, .value=default_val, .next=args); + args = new (arg_ast_t, .name = names->name, .type = type, .value = default_val, .next = args); - if (!match_separator(pos)) - break; + if (!match_separator(pos)) break; } REVERSE_LIST(args); @@ -2233,22 +2108,19 @@ PARSER(parse_func_def) { if (match_word(&pos, "inline")) { is_inline = true; } else if (match_word(&pos, "cached")) { - if (!cache_ast) cache_ast = NewAST(ctx->file, pos, pos, Int, .str="-1"); + if (!cache_ast) cache_ast = NewAST(ctx->file, pos, pos, Int, .str = "-1"); } else if (match_word(&pos, "cache_size")) { whitespace(&pos); - if (!match(&pos, "=")) - parser_err(ctx, flag_start, pos, "I expected a value for 'cache_size'"); + if (!match(&pos, "=")) parser_err(ctx, flag_start, pos, "I expected a value for 'cache_size'"); whitespace(&pos); cache_ast = expect(ctx, start, &pos, parse_expr, "I expected a maximum size for the cache"); } } expect_closing(ctx, &pos, ")", "I wasn't able to parse the rest of this function definition"); - ast_t *body = expect(ctx, start, &pos, parse_block, - "This function needs a body block"); - return NewAST(ctx->file, start, pos, FunctionDef, - .name=name, .args=args, .ret_type=ret_type, .body=body, .cache=cache_ast, - .is_inline=is_inline); + ast_t *body = expect(ctx, start, &pos, parse_block, "This function needs a body block"); + return NewAST(ctx->file, start, pos, FunctionDef, .name = name, .args = args, .ret_type = ret_type, .body = body, + .cache = cache_ast, .is_inline = is_inline); } PARSER(parse_convert_def) { @@ -2270,33 +2142,30 @@ PARSER(parse_convert_def) { if (match_word(&pos, "inline")) { is_inline = true; } else if (match_word(&pos, "cached")) { - if (!cache_ast) cache_ast = NewAST(ctx->file, pos, pos, Int, .str="-1"); + if (!cache_ast) cache_ast = NewAST(ctx->file, pos, pos, Int, .str = "-1"); } else if (match_word(&pos, "cache_size")) { whitespace(&pos); - if (!match(&pos, "=")) - parser_err(ctx, flag_start, pos, "I expected a value for 'cache_size'"); + if (!match(&pos, "=")) parser_err(ctx, flag_start, pos, "I expected a value for 'cache_size'"); whitespace(&pos); cache_ast = expect(ctx, start, &pos, parse_expr, "I expected a maximum size for the cache"); } } expect_closing(ctx, &pos, ")", "I wasn't able to parse the rest of this function definition"); - ast_t *body = expect(ctx, start, &pos, parse_block, - "This function needs a body block"); - return NewAST(ctx->file, start, pos, ConvertDef, - .args=args, .ret_type=ret_type, .body=body, .cache=cache_ast, .is_inline=is_inline); + ast_t *body = expect(ctx, start, &pos, parse_block, "This function needs a body block"); + return NewAST(ctx->file, start, pos, ConvertDef, .args = args, .ret_type = ret_type, .body = body, + .cache = cache_ast, .is_inline = is_inline); } PARSER(parse_extern) { const char *start = pos; if (!match_word(&pos, "extern")) return NULL; spaces(&pos); - const char* name = get_id(&pos); + const char *name = get_id(&pos); spaces(&pos); - if (!match(&pos, ":")) - parser_err(ctx, start, pos, "I couldn't get a type for this extern"); + if (!match(&pos, ":")) parser_err(ctx, start, pos, "I couldn't get a type for this extern"); type_ast_t *type = expect(ctx, start, &pos, parse_type, "I couldn't parse the type for this extern"); - return NewAST(ctx->file, start, pos, Extern, .name=name, .type=type); + return NewAST(ctx->file, start, pos, Extern, .name = name, .type = type); } PARSER(parse_inline_c) { @@ -2309,22 +2178,20 @@ PARSER(parse_inline_c) { if (match(&pos, ":")) { type = expect(ctx, start, &pos, parse_type, "I couldn't parse the type for this C_code code"); spaces(&pos); - if (!match(&pos, "(")) - parser_err(ctx, start, pos, "I expected a '(' here"); - chunks = new(ast_list_t, .ast=NewAST(ctx->file, pos, pos, TextLiteral, Text("({")), - .next=_parse_text_helper(ctx, &pos, '(', ')', '@', false)); + if (!match(&pos, "(")) parser_err(ctx, start, pos, "I expected a '(' here"); + chunks = new (ast_list_t, .ast = NewAST(ctx->file, pos, pos, TextLiteral, Text("({")), + .next = _parse_text_helper(ctx, &pos, '(', ')', '@', false)); if (type) { REVERSE_LIST(chunks); - chunks = new(ast_list_t, .ast=NewAST(ctx->file, pos, pos, TextLiteral, Text("; })")), .next=chunks); + chunks = new (ast_list_t, .ast = NewAST(ctx->file, pos, pos, TextLiteral, Text("; })")), .next = chunks); REVERSE_LIST(chunks); } } else { - if (!match(&pos, "{")) - parser_err(ctx, start, pos, "I expected a '{' here"); + if (!match(&pos, "{")) parser_err(ctx, start, pos, "I expected a '{' here"); chunks = _parse_text_helper(ctx, &pos, '{', '}', '@', false); } - return NewAST(ctx->file, start, pos, InlineCCode, .chunks=chunks, .type_ast=type); + return NewAST(ctx->file, start, pos, InlineCCode, .chunks = chunks, .type_ast = type); } PARSER(parse_doctest) { @@ -2340,7 +2207,7 @@ PARSER(parse_doctest) { } else { pos = expr->end; } - return NewAST(ctx->file, start, pos, DocTest, .expr=expr, .expected=expected); + return NewAST(ctx->file, start, pos, DocTest, .expr = expr, .expected = expected); } PARSER(parse_assert) { @@ -2356,7 +2223,7 @@ PARSER(parse_assert) { } else { pos = expr->end; } - return NewAST(ctx->file, start, pos, Assert, .expr=expr, .message=message); + return NewAST(ctx->file, start, pos, Assert, .expr = expr, .message = message); } PARSER(parse_use) { @@ -2373,12 +2240,12 @@ PARSER(parse_use) { if (!match_word(&pos, "use")) return NULL; spaces(&pos); size_t name_len = strcspn(pos, " \t\r\n;"); - if (name_len < 1) - parser_err(ctx, start, pos, "There is no module name here to use"); + if (name_len < 1) parser_err(ctx, start, pos, "There is no module name here to use"); char *name = GC_strndup(pos, name_len); pos += name_len; - while (match(&pos, ";")) continue; - int what; + while (match(&pos, ";")) + continue; + int what; if (name[0] == '<' || ends_with(name, ".h")) { what = USE_HEADER; } else if (starts_with(name, "-l")) { @@ -2387,17 +2254,17 @@ PARSER(parse_use) { what = USE_C_CODE; } else if (ends_with(name, ".S") || ends_with(name, ".s")) { what = USE_ASM; - } else if (starts_with(name, "./") || starts_with(name, "/") || starts_with(name, "../") || starts_with(name, "~/")) { + } else if (starts_with(name, "./") || starts_with(name, "/") || starts_with(name, "../") + || starts_with(name, "~/")) { what = USE_LOCAL; } else { what = USE_MODULE; } - return NewAST(ctx->file, start, pos, Use, .var=var, .path=name, .what=what); + return NewAST(ctx->file, start, pos, Use, .var = var, .path = name, .what = what); } ast_t *parse_file(const char *path, jmp_buf *on_err) { - if (path[0] != '<' && path[0] != '/') - fail("Path is not fully resolved: ", path); + if (path[0] != '<' && path[0] != '/') fail("Path is not fully resolved: ", path); // NOTE: this cache leaks a bounded amount of memory. The cache will never // hold more than PARSE_CACHE_SIZE entries (see below), but each entry's // AST holds onto a reference to the file it came from, so they could @@ -2417,8 +2284,8 @@ ast_t *parse_file(const char *path, jmp_buf *on_err) { } parse_ctx_t ctx = { - .file=file, - .on_err=on_err, + .file = file, + .on_err = on_err, }; const char *pos = file->text; @@ -2437,7 +2304,10 @@ ast_t *parse_file(const char *path, jmp_buf *on_err) { if (cached.entries.length > PARSE_CACHE_SIZE) { // FIXME: this currently evicts the first entry, but it should be more like // an LRU cache - struct {const char *path; ast_t *ast; } *to_remove = Table$entry(cached, 1); + struct { + const char *path; + ast_t *ast; + } *to_remove = Table$entry(cached, 1); Table$str_remove(&cached, to_remove->path); } @@ -2449,8 +2319,8 @@ ast_t *parse_file(const char *path, jmp_buf *on_err) { type_ast_t *parse_type_str(const char *str) { file_t *file = spoof_file("<type>", str); parse_ctx_t ctx = { - .file=file, - .on_err=NULL, + .file = file, + .on_err = NULL, }; const char *pos = file->text; @@ -2468,8 +2338,8 @@ type_ast_t *parse_type_str(const char *str) { ast_t *parse(const char *str) { file_t *file = spoof_file("<string>", str); parse_ctx_t ctx = { - .file=file, - .on_err=NULL, + .file = file, + .on_err = NULL, }; const char *pos = file->text; @@ -2485,8 +2355,8 @@ ast_t *parse(const char *str) { ast_t *parse_expression(const char *str) { file_t *file = spoof_file("<string>", str); parse_ctx_t ctx = { - .file=file, - .on_err=NULL, + .file = file, + .on_err = NULL, }; const char *pos = file->text; |
