@sshadows/tree-sitter-al 3.2.1 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/grammar.js +27 -0
- package/package.json +1 -1
- package/queries/highlights.scm +2 -0
- package/src/grammar.json +16 -0
- package/src/node-types.json +10 -0
- package/src/parser.c +77247 -56269
- package/src/scanner.c +51 -38
- package/tree-sitter-al.wasm +0 -0
package/src/scanner.c
CHANGED
|
@@ -85,44 +85,24 @@ static bool peek_keyword_ci(TSLexer *lexer, const char *keyword) {
|
|
|
85
85
|
return true;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
//
|
|
89
|
-
//
|
|
90
|
-
//
|
|
91
|
-
static
|
|
88
|
+
// Directives that grammar.js declares as `extras`. They are transparent to the
|
|
89
|
+
// parse tree, so a lookahead scanning for a structural directive must step over
|
|
90
|
+
// them rather than stop on them. Keep in sync with the `extras` array.
|
|
91
|
+
static const char *const TRANSPARENT_DIRECTIVES[] = {
|
|
92
|
+
"pragma", "endregion", "region", "define", "undef", NULL,
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
// Skip whitespace and any transparent-directive lines, then peek for a keyword.
|
|
96
|
+
//
|
|
97
|
+
// Used when scanning ahead for split-construct patterns (e.g.
|
|
98
|
+
// PREPROC_SPLIT_BEGIN checking for #endif). Consuming '#' is irreversible
|
|
99
|
+
// within one scan, so the directive word after '#' is read ONCE into a buffer
|
|
100
|
+
// and then classified — matching candidates one after another would burn the
|
|
101
|
+
// shared prefix of e.g. "endif"/"endregion" on the first failed attempt.
|
|
102
|
+
static bool peek_keyword_ci_skip_extras(TSLexer *lexer, const char *keyword) {
|
|
92
103
|
while (true) {
|
|
93
104
|
skip_whitespace(lexer);
|
|
94
|
-
|
|
95
|
-
if (lexer->lookahead == '#') {
|
|
96
|
-
lexer->advance(lexer, false);
|
|
97
|
-
// Check if 'pragma' follows
|
|
98
|
-
const char *pragma = "pragma";
|
|
99
|
-
bool is_pragma = true;
|
|
100
|
-
for (int i = 0; pragma[i] != '\0'; i++) {
|
|
101
|
-
if (towlower(lexer->lookahead) != pragma[i]) { is_pragma = false; break; }
|
|
102
|
-
lexer->advance(lexer, false);
|
|
103
|
-
}
|
|
104
|
-
if (is_pragma && (lexer->lookahead == ' ' || lexer->lookahead == '\t' ||
|
|
105
|
-
lexer->lookahead == '\r' || lexer->lookahead == '\n' ||
|
|
106
|
-
lexer->lookahead == '\0')) {
|
|
107
|
-
// Skip rest of this #pragma line
|
|
108
|
-
while (lexer->lookahead != '\0' && lexer->lookahead != '\n') {
|
|
109
|
-
lexer->advance(lexer, false);
|
|
110
|
-
}
|
|
111
|
-
continue; // loop back to skip more whitespace/#pragma lines
|
|
112
|
-
} else {
|
|
113
|
-
// '#' was followed by something other than 'pragma' — check for keyword
|
|
114
|
-
// We already consumed '#'; now match rest of keyword (which starts with '#')
|
|
115
|
-
// Since we consumed '#', match from position 1 of keyword
|
|
116
|
-
if (keyword[0] != '#') return false;
|
|
117
|
-
const char *rest = keyword + 1;
|
|
118
|
-
for (int i = 0; rest[i] != '\0'; i++) {
|
|
119
|
-
if (towlower(lexer->lookahead) != rest[i]) return false;
|
|
120
|
-
lexer->advance(lexer, false);
|
|
121
|
-
}
|
|
122
|
-
if (is_identifier_char(lexer->lookahead)) return false;
|
|
123
|
-
return true;
|
|
124
|
-
}
|
|
125
|
-
} else {
|
|
105
|
+
if (lexer->lookahead != '#') {
|
|
126
106
|
// Not '#' — try to match keyword directly
|
|
127
107
|
for (int i = 0; keyword[i] != '\0'; i++) {
|
|
128
108
|
if (towlower(lexer->lookahead) != keyword[i]) return false;
|
|
@@ -131,6 +111,38 @@ static bool peek_keyword_ci_skip_pragma(TSLexer *lexer, const char *keyword) {
|
|
|
131
111
|
if (is_identifier_char(lexer->lookahead)) return false;
|
|
132
112
|
return true;
|
|
133
113
|
}
|
|
114
|
+
|
|
115
|
+
lexer->advance(lexer, false);
|
|
116
|
+
// Horizontal whitespace only: '# pragma' is one directive, but '#' and a
|
|
117
|
+
// word on the NEXT line are not (matching the extras regexes' `[ \t]*`).
|
|
118
|
+
while (lexer->lookahead == ' ' || lexer->lookahead == '\t') {
|
|
119
|
+
lexer->advance(lexer, false);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Read the directive word. Longest AL directive is "endregion" (9).
|
|
123
|
+
char word[16];
|
|
124
|
+
size_t len = 0;
|
|
125
|
+
while (is_identifier_char(lexer->lookahead)) {
|
|
126
|
+
if (len < sizeof(word) - 1) word[len] = (char)towlower(lexer->lookahead);
|
|
127
|
+
len++;
|
|
128
|
+
lexer->advance(lexer, false);
|
|
129
|
+
}
|
|
130
|
+
if (len >= sizeof(word)) return false; // too long to be any directive
|
|
131
|
+
word[len] = '\0';
|
|
132
|
+
|
|
133
|
+
// The caller's keyword includes the '#' we just consumed.
|
|
134
|
+
if (keyword[0] == '#' && strcmp(word, keyword + 1) == 0) return true;
|
|
135
|
+
|
|
136
|
+
bool transparent = false;
|
|
137
|
+
for (int i = 0; TRANSPARENT_DIRECTIVES[i] != NULL; i++) {
|
|
138
|
+
if (strcmp(word, TRANSPARENT_DIRECTIVES[i]) == 0) { transparent = true; break; }
|
|
139
|
+
}
|
|
140
|
+
if (!transparent) return false;
|
|
141
|
+
|
|
142
|
+
// Skip the rest of this directive's line, then look again.
|
|
143
|
+
while (lexer->lookahead != '\0' && lexer->lookahead != '\n') {
|
|
144
|
+
lexer->advance(lexer, false);
|
|
145
|
+
}
|
|
134
146
|
}
|
|
135
147
|
}
|
|
136
148
|
|
|
@@ -223,12 +235,13 @@ bool tree_sitter_al_external_scanner_scan(
|
|
|
223
235
|
// including '#'). PREPROC_OPEN/CLOSE manually advance past '#' before calling
|
|
224
236
|
// read_keyword_ci("if"/"endif"). These are DIFFERENT conventions — do not mix.
|
|
225
237
|
//
|
|
226
|
-
// #pragma
|
|
238
|
+
// #pragma/#region/#define and friends are transparent extras — we skip them
|
|
239
|
+
// when scanning ahead for #endif (see TRANSPARENT_DIRECTIVES).
|
|
227
240
|
if (valid_symbols[PREPROC_SPLIT_BEGIN] && state->depth > 0) {
|
|
228
241
|
skip_whitespace(lexer);
|
|
229
242
|
if (read_keyword_ci(lexer, "begin")) {
|
|
230
243
|
lexer->mark_end(lexer); // token covers only 'begin'
|
|
231
|
-
if (
|
|
244
|
+
if (peek_keyword_ci_skip_extras(lexer, "#endif")) {
|
|
232
245
|
lexer->result_symbol = PREPROC_SPLIT_BEGIN;
|
|
233
246
|
return true;
|
|
234
247
|
}
|
package/tree-sitter-al.wasm
CHANGED
|
Binary file
|