Parser: cover full Jai corpus
This commit is contained in:
41
AGENTS.md
41
AGENTS.md
@@ -67,15 +67,16 @@ dev.hgh.jai.lexer.JaiCorpusLexerTest tests=2 <- the Tier 0 gate
|
|||||||
dev.hgh.jai.lexer.JaiLexerTest tests=13
|
dev.hgh.jai.lexer.JaiLexerTest tests=13
|
||||||
dev.hgh.jai.parser.JaiCorpusParserTest tests=1 <- the Tier 3 gate
|
dev.hgh.jai.parser.JaiCorpusParserTest tests=1 <- the Tier 3 gate
|
||||||
dev.hgh.jai.parser.JaiParserGoldenTest tests=5 <- Tier 2 golden trees
|
dev.hgh.jai.parser.JaiParserGoldenTest tests=5 <- Tier 2 golden trees
|
||||||
|
dev.hgh.jai.parser.JaiParserLongTailTest tests=1 <- focused parser regressions
|
||||||
dev.hgh.jai.parser.DebugParseTest tests=2 <- scratch harness, inert
|
dev.hgh.jai.parser.DebugParseTest tests=2 <- scratch harness, inert
|
||||||
-> total 42, failures+errors 0
|
-> total 43, failures+errors 0
|
||||||
```
|
```
|
||||||
|
|
||||||
The corpus gates report what they actually did; check both lines are still there:
|
The corpus gates report what they actually did; check both lines are still there:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
Tier 0: lexed 714 files, 17154195 chars, 3009381 tokens cleanly.
|
Tier 0: lexed 714 files, 17154195 chars, 3009381 tokens cleanly.
|
||||||
Tier 3: parsed 714 files, 656 clean (91.9%), 58 with errors, 58 PsiErrorElements total.
|
Tier 3: parsed 714 files, 714 clean (100.0%), 0 with errors, 0 PsiErrorElements total.
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -105,7 +106,7 @@ Tier 3: parsed 714 files, 656 clean (91.9%), 58 with errors, 58 PsiErrorElements
|
|||||||
against IU-253/261/262.
|
against IU-253/261/262.
|
||||||
- **Phase 3** — `src/main/grammar/Jai.bnf` → Grammar-Kit parser and PSI in
|
- **Phase 3** — `src/main/grammar/Jai.bnf` → Grammar-Kit parser and PSI in
|
||||||
`src/main/gen`, `JaiParserDefinition`, `JaiParserUtil`. Tier 2 golden trees and
|
`src/main/gen`, `JaiParserDefinition`, `JaiParserUtil`. Tier 2 golden trees and
|
||||||
the Tier 3 corpus gate are green at **91.9% (656/714 files parse with zero
|
the Tier 3 corpus gate are green at **100.0% (714/714 files parse with zero
|
||||||
`PsiErrorElement`)**.
|
`PsiErrorElement`)**.
|
||||||
|
|
||||||
### Lexer design facts worth knowing before touching it
|
### Lexer design facts worth knowing before touching it
|
||||||
@@ -164,24 +165,13 @@ Tier 3: parsed 714 files, 656 clean (91.9%), 58 with errors, 58 PsiErrorElements
|
|||||||
|
|
||||||
### Not done — pick up here
|
### Not done — pick up here
|
||||||
|
|
||||||
1. **Finish the Tier 3 corpus gate.** 58 of 714 files still produce a
|
1. **Phase 4** — structure view, folding, `#import`/`#load` reference resolution
|
||||||
`PsiErrorElement`, all long-tail: each construct below appears in one or two
|
and go-to-definition. The PSI is in place, and the Tier 3 corpus gate is now
|
||||||
files. `JaiCorpusParserTest` prints a histogram of the token at each failure
|
fully green. `JaiFileTypeTest` used to document being blocked on the
|
||||||
point plus a spread of sample errors; that output is the work list.
|
`ParserDefinition`; that no longer applies.
|
||||||
`MIN_CLEAN_FILES` is the ratchet — raise it, never lower it. Known gaps:
|
`#asm` bodies are intentionally consumed opaquely, so nothing inside them has
|
||||||
- mixed declare/assign target lists: `success=, output, error := adb(…)` and
|
PSI yet.
|
||||||
`renderer:, success = make_renderer(…)`;
|
2. **Phases 5+** — see the plan.
|
||||||
- `using,except SKIP_THESE name: T;` (a bare identifier operand, where
|
|
||||||
`using,except(x)` and `using,except .["x"]` already work);
|
|
||||||
- `#asm` bodies are consumed opaquely, so nothing inside them has PSI.
|
|
||||||
|
|
||||||
Decide explicitly whether the remainder becomes a committed allowlist (the
|
|
||||||
plan's §3 suggestion) or gets fixed.
|
|
||||||
2. **Phase 4** — structure view, folding, `#import`/`#load` reference resolution
|
|
||||||
and go-to-definition. The PSI is in place, so these are now unblocked.
|
|
||||||
`JaiFileTypeTest` used to document being blocked on the `ParserDefinition`;
|
|
||||||
that no longer applies.
|
|
||||||
3. **Phases 5+** — see the plan.
|
|
||||||
|
|
||||||
### Open questions for the user (unanswered)
|
### Open questions for the user (unanswered)
|
||||||
|
|
||||||
@@ -224,10 +214,11 @@ the same sweep to assert every token type the corpus produces (105 of them) has
|
|||||||
a colour. Keep new lexer work under these gates rather than adding snippets.
|
a colour. Keep new lexer work under these gates rather than adding snippets.
|
||||||
|
|
||||||
Same idea for the parser, and it is now live: `JaiCorpusParserTest` parses all
|
Same idea for the parser, and it is now live: `JaiCorpusParserTest` parses all
|
||||||
714 files and counts `PsiErrorElement`s — currently **656 clean (91.9%)**. It
|
714 files and counts `PsiErrorElement`s — currently **714 clean (100.0%)**. The
|
||||||
prints a histogram of the source text at each failure point, which is the
|
ratchet is set to the full corpus size, so any parser regression fails the test.
|
||||||
fastest way to find the next construct worth supporting. The idempotence sweep
|
`#asm` bodies remain opaque by design; they are tiled as one directive body but
|
||||||
for the formatter is the same idea again, later.
|
have no inner PSI. The idempotence sweep for the formatter is the same idea
|
||||||
|
again, later.
|
||||||
|
|
||||||
The fast loop for grammar work:
|
The fast loop for grammar work:
|
||||||
|
|
||||||
|
|||||||
@@ -13,12 +13,11 @@
|
|||||||
- Comment/uncomment support and brace matching, including `.{` and `.[`
|
- Comment/uncomment support and brace matching, including `.{` and `.[`
|
||||||
- Grammar-Kit parser and PSI for the whole language: declarations, procedures,
|
- Grammar-Kit parser and PSI for the whole language: declarations, procedures,
|
||||||
structs, enums, unions, control flow including the `if x == { case }` switch
|
structs, enums, unions, control flow including the `if x == { case }` switch
|
||||||
form, and compiler directives. 91.9% of the 714-file Jai distribution parses
|
form, and compiler directives. The full 714-file Jai distribution parses with
|
||||||
with no syntax errors.
|
no syntax errors; `JaiCorpusParserTest` is ratcheted at 100%.
|
||||||
|
|
||||||
### Known gaps
|
### Known gaps
|
||||||
|
|
||||||
- 58 files in the Jai distribution still report a syntax error; each involves a
|
- `#asm` bodies are parsed as opaque blocks, so their assembly instructions do
|
||||||
construct used in only one or two files. `#asm` bodies are parsed as opaque
|
not have inner PSI.
|
||||||
blocks.
|
|
||||||
- Structure view, folding, and go-to-definition are not implemented yet.
|
- Structure view, folding, and go-to-definition are not implemented yet.
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ Each phase has a machine-checkable gate. Do not advance without a green gate.
|
|||||||
| **0** | Env fix, git, AGENTS.md, template stripped | `./jaigradle check` green with no `JAVA_HOME` |
|
| **0** | Env fix, git, AGENTS.md, template stripped | `./jaigradle check` green with no `JAVA_HOME` |
|
||||||
| **1** | File type, icon, `JaiTokenTypes`, hand-written lexer | Tier 0 corpus invariants pass on all 714 files |
|
| **1** | File type, icon, `JaiTokenTypes`, hand-written lexer | Tier 0 corpus invariants pass on all 714 files |
|
||||||
| **2** | `SyntaxHighlighter`, color settings page, commenter, brace matcher | Tier 1 golden dumps; highlighter maps every token type |
|
| **2** | `SyntaxHighlighter`, color settings page, commenter, brace matcher | Tier 1 golden dumps; highlighter maps every token type |
|
||||||
| **3** | `.bnf` grammar, generated parser + PSI, `ParserDefinition` | Tier 2 golden trees; Tier 3 corpus parse ≥ target — **done, 91.9% (656/714)** |
|
| **3** | `.bnf` grammar, generated parser + PSI, `ParserDefinition` | Tier 2 golden trees; Tier 3 corpus parse ≥ target — **done, 100.0% (714/714)** |
|
||||||
| **4** | Structure view, folding, `#import`/`#load` reference resolution + go-to-definition | Tier 4 fixture tests |
|
| **4** | Structure view, folding, `#import`/`#load` reference resolution + go-to-definition | Tier 4 fixture tests |
|
||||||
| **5** | Completion (keywords, directives, module names), rename, find-usages | Tier 4 fixture tests |
|
| **5** | Completion (keywords, directives, module names), rename, find-usages | Tier 4 fixture tests |
|
||||||
| **6** | Formatter, code style settings | Formatter round-trip: formatting the corpus is idempotent |
|
| **6** | Formatter, code style settings | Formatter round-trip: formatting the corpus is idempotent |
|
||||||
|
|||||||
9
jaitest
9
jaitest
@@ -26,7 +26,14 @@ fi
|
|||||||
|
|
||||||
run_gradle() {
|
run_gradle() {
|
||||||
rm -rf build/reports/tests/test
|
rm -rf build/reports/tests/test
|
||||||
./jaigradle cleanTest test "${extra[@]}" "$@" 2>&1 | grep -vE '^\[[0-9.]+s\]\[warning\]\[cds\]'
|
# Bash 3.2 (the macOS system shell) treats an empty array expansion as an
|
||||||
|
# unset variable under `set -u`. Keep the no-extra-args invocation separate
|
||||||
|
# so the wrapper works both with and without a test filter.
|
||||||
|
if ((${#extra[@]})); then
|
||||||
|
./jaigradle cleanTest test "${extra[@]}" "$@" 2>&1 | grep -vE '^\[[0-9.]+s\]\[warning\]\[cds\]'
|
||||||
|
else
|
||||||
|
./jaigradle cleanTest test "$@" 2>&1 | grep -vE '^\[[0-9.]+s\]\[warning\]\[cds\]'
|
||||||
|
fi
|
||||||
return "${PIPESTATUS[0]}"
|
return "${PIPESTATUS[0]}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ public class JaiParser implements PsiParser, LightPsiParser {
|
|||||||
PROC_LITERAL_EXPR, RANGE_EXPR, REF_EXPR, RELATIONAL_EXPR,
|
PROC_LITERAL_EXPR, RANGE_EXPR, REF_EXPR, RELATIONAL_EXPR,
|
||||||
SHIFT_EXPR, STRUCT_EXPR, STRUCT_LITERAL_EXPR, TYPED_ARRAY_LITERAL_EXPR,
|
SHIFT_EXPR, STRUCT_EXPR, STRUCT_LITERAL_EXPR, TYPED_ARRAY_LITERAL_EXPR,
|
||||||
TYPED_STRUCT_LITERAL_EXPR, UNARY_DOT_EXPR, UNINITIALIZED_EXPR, UNION_EXPR,
|
TYPED_STRUCT_LITERAL_EXPR, UNARY_DOT_EXPR, UNINITIALIZED_EXPR, UNION_EXPR,
|
||||||
ZERO_LITERAL_EXPR),
|
VALUE_LITERAL_EXPR, ZERO_LITERAL_EXPR),
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ********************************************************** */
|
/* ********************************************************** */
|
||||||
@@ -215,18 +215,37 @@ public class JaiParser implements PsiParser, LightPsiParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ********************************************************** */
|
/* ********************************************************** */
|
||||||
// <<exprNoAssign>> (',' <<exprNoAssign>>)*
|
// <<exprNoAssign>> [':']
|
||||||
|
static boolean assignTarget_(PsiBuilder builder_, int level_) {
|
||||||
|
if (!recursion_guard_(builder_, level_, "assignTarget_")) return false;
|
||||||
|
boolean result_;
|
||||||
|
Marker marker_ = enter_section_(builder_);
|
||||||
|
result_ = exprNoAssign(builder_, level_ + 1);
|
||||||
|
result_ = result_ && assignTarget__1(builder_, level_ + 1);
|
||||||
|
exit_section_(builder_, marker_, null, result_);
|
||||||
|
return result_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// [':']
|
||||||
|
private static boolean assignTarget__1(PsiBuilder builder_, int level_) {
|
||||||
|
if (!recursion_guard_(builder_, level_, "assignTarget__1")) return false;
|
||||||
|
consumeToken(builder_, COLON);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ********************************************************** */
|
||||||
|
// assignTarget_ (',' assignTarget_)*
|
||||||
static boolean assignTargets_(PsiBuilder builder_, int level_) {
|
static boolean assignTargets_(PsiBuilder builder_, int level_) {
|
||||||
if (!recursion_guard_(builder_, level_, "assignTargets_")) return false;
|
if (!recursion_guard_(builder_, level_, "assignTargets_")) return false;
|
||||||
boolean result_;
|
boolean result_;
|
||||||
Marker marker_ = enter_section_(builder_);
|
Marker marker_ = enter_section_(builder_);
|
||||||
result_ = exprNoAssign(builder_, level_ + 1);
|
result_ = assignTarget_(builder_, level_ + 1);
|
||||||
result_ = result_ && assignTargets__1(builder_, level_ + 1);
|
result_ = result_ && assignTargets__1(builder_, level_ + 1);
|
||||||
exit_section_(builder_, marker_, null, result_);
|
exit_section_(builder_, marker_, null, result_);
|
||||||
return result_;
|
return result_;
|
||||||
}
|
}
|
||||||
|
|
||||||
// (',' <<exprNoAssign>>)*
|
// (',' assignTarget_)*
|
||||||
private static boolean assignTargets__1(PsiBuilder builder_, int level_) {
|
private static boolean assignTargets__1(PsiBuilder builder_, int level_) {
|
||||||
if (!recursion_guard_(builder_, level_, "assignTargets__1")) return false;
|
if (!recursion_guard_(builder_, level_, "assignTargets__1")) return false;
|
||||||
while (true) {
|
while (true) {
|
||||||
@@ -237,13 +256,13 @@ public class JaiParser implements PsiParser, LightPsiParser {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ',' <<exprNoAssign>>
|
// ',' assignTarget_
|
||||||
private static boolean assignTargets__1_0(PsiBuilder builder_, int level_) {
|
private static boolean assignTargets__1_0(PsiBuilder builder_, int level_) {
|
||||||
if (!recursion_guard_(builder_, level_, "assignTargets__1_0")) return false;
|
if (!recursion_guard_(builder_, level_, "assignTargets__1_0")) return false;
|
||||||
boolean result_;
|
boolean result_;
|
||||||
Marker marker_ = enter_section_(builder_);
|
Marker marker_ = enter_section_(builder_);
|
||||||
result_ = consumeToken(builder_, COMMA);
|
result_ = consumeToken(builder_, COMMA);
|
||||||
result_ = result_ && exprNoAssign(builder_, level_ + 1);
|
result_ = result_ && assignTarget_(builder_, level_ + 1);
|
||||||
exit_section_(builder_, marker_, null, result_);
|
exit_section_(builder_, marker_, null, result_);
|
||||||
return result_;
|
return result_;
|
||||||
}
|
}
|
||||||
@@ -494,14 +513,13 @@ public class JaiParser implements PsiParser, LightPsiParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ********************************************************** */
|
/* ********************************************************** */
|
||||||
// ['`'] IDENT
|
// ['`'] (IDENT | keywordAsName_)
|
||||||
public static boolean declName(PsiBuilder builder_, int level_) {
|
public static boolean declName(PsiBuilder builder_, int level_) {
|
||||||
if (!recursion_guard_(builder_, level_, "declName")) return false;
|
if (!recursion_guard_(builder_, level_, "declName")) return false;
|
||||||
if (!nextTokenIs(builder_, "<decl name>", BACKTICK, IDENT)) return false;
|
|
||||||
boolean result_;
|
boolean result_;
|
||||||
Marker marker_ = enter_section_(builder_, level_, _NONE_, DECL_NAME, "<decl name>");
|
Marker marker_ = enter_section_(builder_, level_, _NONE_, DECL_NAME, "<decl name>");
|
||||||
result_ = declName_0(builder_, level_ + 1);
|
result_ = declName_0(builder_, level_ + 1);
|
||||||
result_ = result_ && consumeToken(builder_, IDENT);
|
result_ = result_ && declName_1(builder_, level_ + 1);
|
||||||
exit_section_(builder_, level_, marker_, result_, false, null);
|
exit_section_(builder_, level_, marker_, result_, false, null);
|
||||||
return result_;
|
return result_;
|
||||||
}
|
}
|
||||||
@@ -513,20 +531,29 @@ public class JaiParser implements PsiParser, LightPsiParser {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IDENT | keywordAsName_
|
||||||
|
private static boolean declName_1(PsiBuilder builder_, int level_) {
|
||||||
|
if (!recursion_guard_(builder_, level_, "declName_1")) return false;
|
||||||
|
boolean result_;
|
||||||
|
result_ = consumeToken(builder_, IDENT);
|
||||||
|
if (!result_) result_ = keywordAsName_(builder_, level_ + 1);
|
||||||
|
return result_;
|
||||||
|
}
|
||||||
|
|
||||||
/* ********************************************************** */
|
/* ********************************************************** */
|
||||||
// declName (',' declName)*
|
// declName (['='] ',' declName ['='])* ['=']
|
||||||
public static boolean declNames(PsiBuilder builder_, int level_) {
|
public static boolean declNames(PsiBuilder builder_, int level_) {
|
||||||
if (!recursion_guard_(builder_, level_, "declNames")) return false;
|
if (!recursion_guard_(builder_, level_, "declNames")) return false;
|
||||||
if (!nextTokenIs(builder_, "<decl names>", BACKTICK, IDENT)) return false;
|
|
||||||
boolean result_;
|
boolean result_;
|
||||||
Marker marker_ = enter_section_(builder_, level_, _NONE_, DECL_NAMES, "<decl names>");
|
Marker marker_ = enter_section_(builder_, level_, _NONE_, DECL_NAMES, "<decl names>");
|
||||||
result_ = declName(builder_, level_ + 1);
|
result_ = declName(builder_, level_ + 1);
|
||||||
result_ = result_ && declNames_1(builder_, level_ + 1);
|
result_ = result_ && declNames_1(builder_, level_ + 1);
|
||||||
|
result_ = result_ && declNames_2(builder_, level_ + 1);
|
||||||
exit_section_(builder_, level_, marker_, result_, false, null);
|
exit_section_(builder_, level_, marker_, result_, false, null);
|
||||||
return result_;
|
return result_;
|
||||||
}
|
}
|
||||||
|
|
||||||
// (',' declName)*
|
// (['='] ',' declName ['='])*
|
||||||
private static boolean declNames_1(PsiBuilder builder_, int level_) {
|
private static boolean declNames_1(PsiBuilder builder_, int level_) {
|
||||||
if (!recursion_guard_(builder_, level_, "declNames_1")) return false;
|
if (!recursion_guard_(builder_, level_, "declNames_1")) return false;
|
||||||
while (true) {
|
while (true) {
|
||||||
@@ -537,17 +564,40 @@ public class JaiParser implements PsiParser, LightPsiParser {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ',' declName
|
// ['='] ',' declName ['=']
|
||||||
private static boolean declNames_1_0(PsiBuilder builder_, int level_) {
|
private static boolean declNames_1_0(PsiBuilder builder_, int level_) {
|
||||||
if (!recursion_guard_(builder_, level_, "declNames_1_0")) return false;
|
if (!recursion_guard_(builder_, level_, "declNames_1_0")) return false;
|
||||||
boolean result_;
|
boolean result_;
|
||||||
Marker marker_ = enter_section_(builder_);
|
Marker marker_ = enter_section_(builder_);
|
||||||
result_ = consumeToken(builder_, COMMA);
|
result_ = declNames_1_0_0(builder_, level_ + 1);
|
||||||
|
result_ = result_ && consumeToken(builder_, COMMA);
|
||||||
result_ = result_ && declName(builder_, level_ + 1);
|
result_ = result_ && declName(builder_, level_ + 1);
|
||||||
|
result_ = result_ && declNames_1_0_3(builder_, level_ + 1);
|
||||||
exit_section_(builder_, marker_, null, result_);
|
exit_section_(builder_, marker_, null, result_);
|
||||||
return result_;
|
return result_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ['=']
|
||||||
|
private static boolean declNames_1_0_0(PsiBuilder builder_, int level_) {
|
||||||
|
if (!recursion_guard_(builder_, level_, "declNames_1_0_0")) return false;
|
||||||
|
consumeToken(builder_, EQ);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ['=']
|
||||||
|
private static boolean declNames_1_0_3(PsiBuilder builder_, int level_) {
|
||||||
|
if (!recursion_guard_(builder_, level_, "declNames_1_0_3")) return false;
|
||||||
|
consumeToken(builder_, EQ);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ['=']
|
||||||
|
private static boolean declNames_2(PsiBuilder builder_, int level_) {
|
||||||
|
if (!recursion_guard_(builder_, level_, "declNames_2")) return false;
|
||||||
|
consumeToken(builder_, EQ);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/* ********************************************************** */
|
/* ********************************************************** */
|
||||||
// ':' [<<exprNoAssign>>] procModifier* [',' assignTargets_] [('=' | ':') initializerList_]
|
// ':' [<<exprNoAssign>>] procModifier* [',' assignTargets_] [('=' | ':') initializerList_]
|
||||||
// | ':=' initializerList_
|
// | ':=' initializerList_
|
||||||
@@ -1274,7 +1324,7 @@ public class JaiParser implements PsiParser, LightPsiParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ********************************************************** */
|
/* ********************************************************** */
|
||||||
// '<=' expr | '*=' expr | '<' | '*'
|
// '<=' expr | '*=' expr | '<' | '*' | ','
|
||||||
static boolean forModifier_(PsiBuilder builder_, int level_) {
|
static boolean forModifier_(PsiBuilder builder_, int level_) {
|
||||||
if (!recursion_guard_(builder_, level_, "forModifier_")) return false;
|
if (!recursion_guard_(builder_, level_, "forModifier_")) return false;
|
||||||
boolean result_;
|
boolean result_;
|
||||||
@@ -1283,6 +1333,7 @@ public class JaiParser implements PsiParser, LightPsiParser {
|
|||||||
if (!result_) result_ = forModifier__1(builder_, level_ + 1);
|
if (!result_) result_ = forModifier__1(builder_, level_ + 1);
|
||||||
if (!result_) result_ = consumeToken(builder_, LT);
|
if (!result_) result_ = consumeToken(builder_, LT);
|
||||||
if (!result_) result_ = consumeToken(builder_, STAR);
|
if (!result_) result_ = consumeToken(builder_, STAR);
|
||||||
|
if (!result_) result_ = consumeToken(builder_, COMMA);
|
||||||
exit_section_(builder_, marker_, null, result_);
|
exit_section_(builder_, marker_, null, result_);
|
||||||
return result_;
|
return result_;
|
||||||
}
|
}
|
||||||
@@ -1442,59 +1493,73 @@ public class JaiParser implements PsiParser, LightPsiParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ********************************************************** */
|
/* ********************************************************** */
|
||||||
// ifKeyword_ [<<dir "#complete">>] expr (switchBody | ['then'] statement [elseClause])
|
// nestedInlineIfStatement
|
||||||
|
// | inlineIfDirectiveStatement
|
||||||
|
// | ifKeyword_ [<<dir "#complete">>] expr (switchBody | ['then'] statement [elseClause])
|
||||||
public static boolean ifStatement(PsiBuilder builder_, int level_) {
|
public static boolean ifStatement(PsiBuilder builder_, int level_) {
|
||||||
if (!recursion_guard_(builder_, level_, "ifStatement")) return false;
|
if (!recursion_guard_(builder_, level_, "ifStatement")) return false;
|
||||||
boolean result_;
|
boolean result_;
|
||||||
Marker marker_ = enter_section_(builder_, level_, _NONE_, IF_STATEMENT, "<if statement>");
|
Marker marker_ = enter_section_(builder_, level_, _NONE_, IF_STATEMENT, "<if statement>");
|
||||||
result_ = ifKeyword_(builder_, level_ + 1);
|
result_ = nestedInlineIfStatement(builder_, level_ + 1);
|
||||||
result_ = result_ && ifStatement_1(builder_, level_ + 1);
|
if (!result_) result_ = inlineIfDirectiveStatement(builder_, level_ + 1);
|
||||||
result_ = result_ && expr(builder_, level_ + 1, -1);
|
if (!result_) result_ = ifStatement_2(builder_, level_ + 1);
|
||||||
result_ = result_ && ifStatement_3(builder_, level_ + 1);
|
|
||||||
exit_section_(builder_, level_, marker_, result_, false, null);
|
exit_section_(builder_, level_, marker_, result_, false, null);
|
||||||
return result_;
|
return result_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ifKeyword_ [<<dir "#complete">>] expr (switchBody | ['then'] statement [elseClause])
|
||||||
|
private static boolean ifStatement_2(PsiBuilder builder_, int level_) {
|
||||||
|
if (!recursion_guard_(builder_, level_, "ifStatement_2")) return false;
|
||||||
|
boolean result_;
|
||||||
|
Marker marker_ = enter_section_(builder_);
|
||||||
|
result_ = ifKeyword_(builder_, level_ + 1);
|
||||||
|
result_ = result_ && ifStatement_2_1(builder_, level_ + 1);
|
||||||
|
result_ = result_ && expr(builder_, level_ + 1, -1);
|
||||||
|
result_ = result_ && ifStatement_2_3(builder_, level_ + 1);
|
||||||
|
exit_section_(builder_, marker_, null, result_);
|
||||||
|
return result_;
|
||||||
|
}
|
||||||
|
|
||||||
// [<<dir "#complete">>]
|
// [<<dir "#complete">>]
|
||||||
private static boolean ifStatement_1(PsiBuilder builder_, int level_) {
|
private static boolean ifStatement_2_1(PsiBuilder builder_, int level_) {
|
||||||
if (!recursion_guard_(builder_, level_, "ifStatement_1")) return false;
|
if (!recursion_guard_(builder_, level_, "ifStatement_2_1")) return false;
|
||||||
dir(builder_, level_ + 1, "#complete");
|
dir(builder_, level_ + 1, "#complete");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// switchBody | ['then'] statement [elseClause]
|
// switchBody | ['then'] statement [elseClause]
|
||||||
private static boolean ifStatement_3(PsiBuilder builder_, int level_) {
|
private static boolean ifStatement_2_3(PsiBuilder builder_, int level_) {
|
||||||
if (!recursion_guard_(builder_, level_, "ifStatement_3")) return false;
|
if (!recursion_guard_(builder_, level_, "ifStatement_2_3")) return false;
|
||||||
boolean result_;
|
boolean result_;
|
||||||
Marker marker_ = enter_section_(builder_);
|
Marker marker_ = enter_section_(builder_);
|
||||||
result_ = switchBody(builder_, level_ + 1);
|
result_ = switchBody(builder_, level_ + 1);
|
||||||
if (!result_) result_ = ifStatement_3_1(builder_, level_ + 1);
|
if (!result_) result_ = ifStatement_2_3_1(builder_, level_ + 1);
|
||||||
exit_section_(builder_, marker_, null, result_);
|
exit_section_(builder_, marker_, null, result_);
|
||||||
return result_;
|
return result_;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ['then'] statement [elseClause]
|
// ['then'] statement [elseClause]
|
||||||
private static boolean ifStatement_3_1(PsiBuilder builder_, int level_) {
|
private static boolean ifStatement_2_3_1(PsiBuilder builder_, int level_) {
|
||||||
if (!recursion_guard_(builder_, level_, "ifStatement_3_1")) return false;
|
if (!recursion_guard_(builder_, level_, "ifStatement_2_3_1")) return false;
|
||||||
boolean result_;
|
boolean result_;
|
||||||
Marker marker_ = enter_section_(builder_);
|
Marker marker_ = enter_section_(builder_);
|
||||||
result_ = ifStatement_3_1_0(builder_, level_ + 1);
|
result_ = ifStatement_2_3_1_0(builder_, level_ + 1);
|
||||||
result_ = result_ && statement(builder_, level_ + 1);
|
result_ = result_ && statement(builder_, level_ + 1);
|
||||||
result_ = result_ && ifStatement_3_1_2(builder_, level_ + 1);
|
result_ = result_ && ifStatement_2_3_1_2(builder_, level_ + 1);
|
||||||
exit_section_(builder_, marker_, null, result_);
|
exit_section_(builder_, marker_, null, result_);
|
||||||
return result_;
|
return result_;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ['then']
|
// ['then']
|
||||||
private static boolean ifStatement_3_1_0(PsiBuilder builder_, int level_) {
|
private static boolean ifStatement_2_3_1_0(PsiBuilder builder_, int level_) {
|
||||||
if (!recursion_guard_(builder_, level_, "ifStatement_3_1_0")) return false;
|
if (!recursion_guard_(builder_, level_, "ifStatement_2_3_1_0")) return false;
|
||||||
consumeToken(builder_, KW_THEN);
|
consumeToken(builder_, KW_THEN);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// [elseClause]
|
// [elseClause]
|
||||||
private static boolean ifStatement_3_1_2(PsiBuilder builder_, int level_) {
|
private static boolean ifStatement_2_3_1_2(PsiBuilder builder_, int level_) {
|
||||||
if (!recursion_guard_(builder_, level_, "ifStatement_3_1_2")) return false;
|
if (!recursion_guard_(builder_, level_, "ifStatement_2_3_1_2")) return false;
|
||||||
elseClause(builder_, level_ + 1);
|
elseClause(builder_, level_ + 1);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1567,6 +1632,27 @@ public class JaiParser implements PsiParser, LightPsiParser {
|
|||||||
return result_;
|
return result_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ********************************************************** */
|
||||||
|
// <<dir "#if">> IDENT statement [elseClause]
|
||||||
|
static boolean inlineIfDirectiveStatement(PsiBuilder builder_, int level_) {
|
||||||
|
if (!recursion_guard_(builder_, level_, "inlineIfDirectiveStatement")) return false;
|
||||||
|
boolean result_;
|
||||||
|
Marker marker_ = enter_section_(builder_);
|
||||||
|
result_ = dir(builder_, level_ + 1, "#if");
|
||||||
|
result_ = result_ && consumeToken(builder_, IDENT);
|
||||||
|
result_ = result_ && statement(builder_, level_ + 1);
|
||||||
|
result_ = result_ && inlineIfDirectiveStatement_3(builder_, level_ + 1);
|
||||||
|
exit_section_(builder_, marker_, null, result_);
|
||||||
|
return result_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// [elseClause]
|
||||||
|
private static boolean inlineIfDirectiveStatement_3(PsiBuilder builder_, int level_) {
|
||||||
|
if (!recursion_guard_(builder_, level_, "inlineIfDirectiveStatement_3")) return false;
|
||||||
|
elseClause(builder_, level_ + 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/* ********************************************************** */
|
/* ********************************************************** */
|
||||||
// 'interface' [expr]
|
// 'interface' [expr]
|
||||||
public static boolean interfaceExpr(PsiBuilder builder_, int level_) {
|
public static boolean interfaceExpr(PsiBuilder builder_, int level_) {
|
||||||
@@ -1681,18 +1767,30 @@ public class JaiParser implements PsiParser, LightPsiParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ********************************************************** */
|
/* ********************************************************** */
|
||||||
// IDENT | NUMBER | keywordAsName_
|
// 'operator' operatorSymbol | IDENT | NUMBER | keywordAsName_
|
||||||
public static boolean memberName(PsiBuilder builder_, int level_) {
|
public static boolean memberName(PsiBuilder builder_, int level_) {
|
||||||
if (!recursion_guard_(builder_, level_, "memberName")) return false;
|
if (!recursion_guard_(builder_, level_, "memberName")) return false;
|
||||||
boolean result_;
|
boolean result_;
|
||||||
Marker marker_ = enter_section_(builder_, level_, _NONE_, MEMBER_NAME, "<member name>");
|
Marker marker_ = enter_section_(builder_, level_, _NONE_, MEMBER_NAME, "<member name>");
|
||||||
result_ = consumeToken(builder_, IDENT);
|
result_ = memberName_0(builder_, level_ + 1);
|
||||||
|
if (!result_) result_ = consumeToken(builder_, IDENT);
|
||||||
if (!result_) result_ = consumeToken(builder_, NUMBER);
|
if (!result_) result_ = consumeToken(builder_, NUMBER);
|
||||||
if (!result_) result_ = keywordAsName_(builder_, level_ + 1);
|
if (!result_) result_ = keywordAsName_(builder_, level_ + 1);
|
||||||
exit_section_(builder_, level_, marker_, result_, false, null);
|
exit_section_(builder_, level_, marker_, result_, false, null);
|
||||||
return result_;
|
return result_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 'operator' operatorSymbol
|
||||||
|
private static boolean memberName_0(PsiBuilder builder_, int level_) {
|
||||||
|
if (!recursion_guard_(builder_, level_, "memberName_0")) return false;
|
||||||
|
boolean result_;
|
||||||
|
Marker marker_ = enter_section_(builder_);
|
||||||
|
result_ = consumeToken(builder_, KW_OPERATOR);
|
||||||
|
result_ = result_ && operatorSymbol(builder_, level_ + 1);
|
||||||
|
exit_section_(builder_, marker_, null, result_);
|
||||||
|
return result_;
|
||||||
|
}
|
||||||
|
|
||||||
/* ********************************************************** */
|
/* ********************************************************** */
|
||||||
// <<isNamedArgumentAhead>> memberName '=' (block | jumpArgument_ | expr)
|
// <<isNamedArgumentAhead>> memberName '=' (block | jumpArgument_ | expr)
|
||||||
public static boolean namedArgument(PsiBuilder builder_, int level_) {
|
public static boolean namedArgument(PsiBuilder builder_, int level_) {
|
||||||
@@ -1717,6 +1815,12 @@ public class JaiParser implements PsiParser, LightPsiParser {
|
|||||||
return result_;
|
return result_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ********************************************************** */
|
||||||
|
// <<parseNestedInlineIfStatement>>
|
||||||
|
static boolean nestedInlineIfStatement(PsiBuilder builder_, int level_) {
|
||||||
|
return parseNestedInlineIfStatement(builder_, level_ + 1);
|
||||||
|
}
|
||||||
|
|
||||||
/* ********************************************************** */
|
/* ********************************************************** */
|
||||||
// NOTE+
|
// NOTE+
|
||||||
public static boolean noteStatement(PsiBuilder builder_, int level_) {
|
public static boolean noteStatement(PsiBuilder builder_, int level_) {
|
||||||
@@ -1752,7 +1856,7 @@ public class JaiParser implements PsiParser, LightPsiParser {
|
|||||||
|
|
||||||
/* ********************************************************** */
|
/* ********************************************************** */
|
||||||
// '*' '[' ']' | '[' ']' '=' | '[' ']'
|
// '*' '[' ']' | '[' ']' '=' | '[' ']'
|
||||||
// | '+' | '-' | '*' | '/' | '%' | '==' | '!=' | '<' | '>' | '<=' | '>='
|
// | '+' | '-' | '*' | '/' | '%' | '==' | '!=' | '!' | '<' | '>' | '<=' | '>='
|
||||||
// | '&' | '|' | '^' | '~' | '<<' | '>>' | '<<<' | '>>>'
|
// | '&' | '|' | '^' | '~' | '<<' | '>>' | '<<<' | '>>>'
|
||||||
// | '+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^='
|
// | '+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^='
|
||||||
// | '<<=' | '>>=' | '<<<=' | '>>>='
|
// | '<<=' | '>>=' | '<<<=' | '>>>='
|
||||||
@@ -1770,6 +1874,7 @@ public class JaiParser implements PsiParser, LightPsiParser {
|
|||||||
if (!result_) result_ = consumeToken(builder_, PERCENT);
|
if (!result_) result_ = consumeToken(builder_, PERCENT);
|
||||||
if (!result_) result_ = consumeToken(builder_, EQ_EQ);
|
if (!result_) result_ = consumeToken(builder_, EQ_EQ);
|
||||||
if (!result_) result_ = consumeToken(builder_, NOT_EQ);
|
if (!result_) result_ = consumeToken(builder_, NOT_EQ);
|
||||||
|
if (!result_) result_ = consumeToken(builder_, NOT);
|
||||||
if (!result_) result_ = consumeToken(builder_, LT);
|
if (!result_) result_ = consumeToken(builder_, LT);
|
||||||
if (!result_) result_ = consumeToken(builder_, GT);
|
if (!result_) result_ = consumeToken(builder_, GT);
|
||||||
if (!result_) result_ = consumeToken(builder_, LT_EQ);
|
if (!result_) result_ = consumeToken(builder_, LT_EQ);
|
||||||
@@ -1841,17 +1946,53 @@ public class JaiParser implements PsiParser, LightPsiParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ********************************************************** */
|
/* ********************************************************** */
|
||||||
// <<dirPlain>> | 'using'
|
// <<dirPlain>> | 'using' [directiveFlag_ [<<exprNoAssign>>]]
|
||||||
static boolean paramModifier_(PsiBuilder builder_, int level_) {
|
static boolean paramModifier_(PsiBuilder builder_, int level_) {
|
||||||
if (!recursion_guard_(builder_, level_, "paramModifier_")) return false;
|
if (!recursion_guard_(builder_, level_, "paramModifier_")) return false;
|
||||||
boolean result_;
|
boolean result_;
|
||||||
Marker marker_ = enter_section_(builder_);
|
Marker marker_ = enter_section_(builder_);
|
||||||
result_ = dirPlain(builder_, level_ + 1);
|
result_ = dirPlain(builder_, level_ + 1);
|
||||||
if (!result_) result_ = consumeToken(builder_, KW_USING);
|
if (!result_) result_ = paramModifier__1(builder_, level_ + 1);
|
||||||
exit_section_(builder_, marker_, null, result_);
|
exit_section_(builder_, marker_, null, result_);
|
||||||
return result_;
|
return result_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 'using' [directiveFlag_ [<<exprNoAssign>>]]
|
||||||
|
private static boolean paramModifier__1(PsiBuilder builder_, int level_) {
|
||||||
|
if (!recursion_guard_(builder_, level_, "paramModifier__1")) return false;
|
||||||
|
boolean result_;
|
||||||
|
Marker marker_ = enter_section_(builder_);
|
||||||
|
result_ = consumeToken(builder_, KW_USING);
|
||||||
|
result_ = result_ && paramModifier__1_1(builder_, level_ + 1);
|
||||||
|
exit_section_(builder_, marker_, null, result_);
|
||||||
|
return result_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// [directiveFlag_ [<<exprNoAssign>>]]
|
||||||
|
private static boolean paramModifier__1_1(PsiBuilder builder_, int level_) {
|
||||||
|
if (!recursion_guard_(builder_, level_, "paramModifier__1_1")) return false;
|
||||||
|
paramModifier__1_1_0(builder_, level_ + 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// directiveFlag_ [<<exprNoAssign>>]
|
||||||
|
private static boolean paramModifier__1_1_0(PsiBuilder builder_, int level_) {
|
||||||
|
if (!recursion_guard_(builder_, level_, "paramModifier__1_1_0")) return false;
|
||||||
|
boolean result_;
|
||||||
|
Marker marker_ = enter_section_(builder_);
|
||||||
|
result_ = directiveFlag_(builder_, level_ + 1);
|
||||||
|
result_ = result_ && paramModifier__1_1_0_1(builder_, level_ + 1);
|
||||||
|
exit_section_(builder_, marker_, null, result_);
|
||||||
|
return result_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// [<<exprNoAssign>>]
|
||||||
|
private static boolean paramModifier__1_1_0_1(PsiBuilder builder_, int level_) {
|
||||||
|
if (!recursion_guard_(builder_, level_, "paramModifier__1_1_0_1")) return false;
|
||||||
|
exprNoAssign(builder_, level_ + 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/* ********************************************************** */
|
/* ********************************************************** */
|
||||||
// ['$' | '$$'] IDENT
|
// ['$' | '$$'] IDENT
|
||||||
public static boolean paramName(PsiBuilder builder_, int level_) {
|
public static boolean paramName(PsiBuilder builder_, int level_) {
|
||||||
@@ -2212,18 +2353,29 @@ public class JaiParser implements PsiParser, LightPsiParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ********************************************************** */
|
/* ********************************************************** */
|
||||||
// directiveExpr
|
// !<<nonProcDirectiveAhead>> directiveExpr
|
||||||
public static boolean procModifier(PsiBuilder builder_, int level_) {
|
public static boolean procModifier(PsiBuilder builder_, int level_) {
|
||||||
if (!recursion_guard_(builder_, level_, "procModifier")) return false;
|
if (!recursion_guard_(builder_, level_, "procModifier")) return false;
|
||||||
boolean result_;
|
boolean result_;
|
||||||
Marker marker_ = enter_section_(builder_, level_, _NONE_, PROC_MODIFIER, "<proc modifier>");
|
Marker marker_ = enter_section_(builder_, level_, _NONE_, PROC_MODIFIER, "<proc modifier>");
|
||||||
result_ = directiveExpr(builder_, level_ + 1);
|
result_ = procModifier_0(builder_, level_ + 1);
|
||||||
|
result_ = result_ && directiveExpr(builder_, level_ + 1);
|
||||||
|
exit_section_(builder_, level_, marker_, result_, false, null);
|
||||||
|
return result_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// !<<nonProcDirectiveAhead>>
|
||||||
|
private static boolean procModifier_0(PsiBuilder builder_, int level_) {
|
||||||
|
if (!recursion_guard_(builder_, level_, "procModifier_0")) return false;
|
||||||
|
boolean result_;
|
||||||
|
Marker marker_ = enter_section_(builder_, level_, _NOT_);
|
||||||
|
result_ = !nonProcDirectiveAhead(builder_, level_ + 1);
|
||||||
exit_section_(builder_, level_, marker_, result_, false, null);
|
exit_section_(builder_, level_, marker_, result_, false, null);
|
||||||
return result_;
|
return result_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ********************************************************** */
|
/* ********************************************************** */
|
||||||
// ['`'] 'push_context' directiveFlag_* [expr] block
|
// ['`'] 'push_context' directiveFlag_* [expr] (block | semi)
|
||||||
public static boolean pushContextStatement(PsiBuilder builder_, int level_) {
|
public static boolean pushContextStatement(PsiBuilder builder_, int level_) {
|
||||||
if (!recursion_guard_(builder_, level_, "pushContextStatement")) return false;
|
if (!recursion_guard_(builder_, level_, "pushContextStatement")) return false;
|
||||||
if (!nextTokenIs(builder_, "<push context statement>", BACKTICK, KW_PUSH_CONTEXT)) return false;
|
if (!nextTokenIs(builder_, "<push context statement>", BACKTICK, KW_PUSH_CONTEXT)) return false;
|
||||||
@@ -2233,7 +2385,7 @@ public class JaiParser implements PsiParser, LightPsiParser {
|
|||||||
result_ = result_ && consumeToken(builder_, KW_PUSH_CONTEXT);
|
result_ = result_ && consumeToken(builder_, KW_PUSH_CONTEXT);
|
||||||
result_ = result_ && pushContextStatement_2(builder_, level_ + 1);
|
result_ = result_ && pushContextStatement_2(builder_, level_ + 1);
|
||||||
result_ = result_ && pushContextStatement_3(builder_, level_ + 1);
|
result_ = result_ && pushContextStatement_3(builder_, level_ + 1);
|
||||||
result_ = result_ && block(builder_, level_ + 1);
|
result_ = result_ && pushContextStatement_4(builder_, level_ + 1);
|
||||||
exit_section_(builder_, level_, marker_, result_, false, null);
|
exit_section_(builder_, level_, marker_, result_, false, null);
|
||||||
return result_;
|
return result_;
|
||||||
}
|
}
|
||||||
@@ -2263,6 +2415,15 @@ public class JaiParser implements PsiParser, LightPsiParser {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// block | semi
|
||||||
|
private static boolean pushContextStatement_4(PsiBuilder builder_, int level_) {
|
||||||
|
if (!recursion_guard_(builder_, level_, "pushContextStatement_4")) return false;
|
||||||
|
boolean result_;
|
||||||
|
result_ = block(builder_, level_ + 1);
|
||||||
|
if (!result_) result_ = semi(builder_, level_ + 1);
|
||||||
|
return result_;
|
||||||
|
}
|
||||||
|
|
||||||
/* ********************************************************** */
|
/* ********************************************************** */
|
||||||
// IDENT
|
// IDENT
|
||||||
public static boolean refExpr(PsiBuilder builder_, int level_) {
|
public static boolean refExpr(PsiBuilder builder_, int level_) {
|
||||||
@@ -2447,7 +2608,7 @@ public class JaiParser implements PsiParser, LightPsiParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ********************************************************** */
|
/* ********************************************************** */
|
||||||
// ['`'] 'return' [expr (',' expr)*] semiWithNotes_
|
// ['`'] 'return' [expr (',' expr)* [',']] semiWithNotes_
|
||||||
public static boolean returnStatement(PsiBuilder builder_, int level_) {
|
public static boolean returnStatement(PsiBuilder builder_, int level_) {
|
||||||
if (!recursion_guard_(builder_, level_, "returnStatement")) return false;
|
if (!recursion_guard_(builder_, level_, "returnStatement")) return false;
|
||||||
if (!nextTokenIs(builder_, "<return statement>", BACKTICK, KW_RETURN)) return false;
|
if (!nextTokenIs(builder_, "<return statement>", BACKTICK, KW_RETURN)) return false;
|
||||||
@@ -2468,20 +2629,21 @@ public class JaiParser implements PsiParser, LightPsiParser {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// [expr (',' expr)*]
|
// [expr (',' expr)* [',']]
|
||||||
private static boolean returnStatement_2(PsiBuilder builder_, int level_) {
|
private static boolean returnStatement_2(PsiBuilder builder_, int level_) {
|
||||||
if (!recursion_guard_(builder_, level_, "returnStatement_2")) return false;
|
if (!recursion_guard_(builder_, level_, "returnStatement_2")) return false;
|
||||||
returnStatement_2_0(builder_, level_ + 1);
|
returnStatement_2_0(builder_, level_ + 1);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// expr (',' expr)*
|
// expr (',' expr)* [',']
|
||||||
private static boolean returnStatement_2_0(PsiBuilder builder_, int level_) {
|
private static boolean returnStatement_2_0(PsiBuilder builder_, int level_) {
|
||||||
if (!recursion_guard_(builder_, level_, "returnStatement_2_0")) return false;
|
if (!recursion_guard_(builder_, level_, "returnStatement_2_0")) return false;
|
||||||
boolean result_;
|
boolean result_;
|
||||||
Marker marker_ = enter_section_(builder_);
|
Marker marker_ = enter_section_(builder_);
|
||||||
result_ = expr(builder_, level_ + 1, -1);
|
result_ = expr(builder_, level_ + 1, -1);
|
||||||
result_ = result_ && returnStatement_2_0_1(builder_, level_ + 1);
|
result_ = result_ && returnStatement_2_0_1(builder_, level_ + 1);
|
||||||
|
result_ = result_ && returnStatement_2_0_2(builder_, level_ + 1);
|
||||||
exit_section_(builder_, marker_, null, result_);
|
exit_section_(builder_, marker_, null, result_);
|
||||||
return result_;
|
return result_;
|
||||||
}
|
}
|
||||||
@@ -2508,6 +2670,13 @@ public class JaiParser implements PsiParser, LightPsiParser {
|
|||||||
return result_;
|
return result_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// [',']
|
||||||
|
private static boolean returnStatement_2_0_2(PsiBuilder builder_, int level_) {
|
||||||
|
if (!recursion_guard_(builder_, level_, "returnStatement_2_0_2")) return false;
|
||||||
|
consumeToken(builder_, COMMA);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/* ********************************************************** */
|
/* ********************************************************** */
|
||||||
// <<isReturnGroupAhead>> returnGroup
|
// <<isReturnGroupAhead>> returnGroup
|
||||||
// | returnItem (',' <<notPolyParamAhead>> returnItem)*
|
// | returnItem (',' <<notPolyParamAhead>> returnItem)*
|
||||||
@@ -2931,7 +3100,28 @@ public class JaiParser implements PsiParser, LightPsiParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ********************************************************** */
|
/* ********************************************************** */
|
||||||
// 'while' [<<isLabelAhead>> IDENT ':'] expr statement
|
// '{' [argList] '}'
|
||||||
|
public static boolean valueLiteralExpr(PsiBuilder builder_, int level_) {
|
||||||
|
if (!recursion_guard_(builder_, level_, "valueLiteralExpr")) return false;
|
||||||
|
if (!nextTokenIs(builder_, LBRACE)) return false;
|
||||||
|
boolean result_;
|
||||||
|
Marker marker_ = enter_section_(builder_);
|
||||||
|
result_ = consumeToken(builder_, LBRACE);
|
||||||
|
result_ = result_ && valueLiteralExpr_1(builder_, level_ + 1);
|
||||||
|
result_ = result_ && consumeToken(builder_, RBRACE);
|
||||||
|
exit_section_(builder_, marker_, VALUE_LITERAL_EXPR, result_);
|
||||||
|
return result_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// [argList]
|
||||||
|
private static boolean valueLiteralExpr_1(PsiBuilder builder_, int level_) {
|
||||||
|
if (!recursion_guard_(builder_, level_, "valueLiteralExpr_1")) return false;
|
||||||
|
argList(builder_, level_ + 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ********************************************************** */
|
||||||
|
// 'while' [<<isLabelAhead>> ['`'] IDENT (':' | ':=')] expr statement
|
||||||
public static boolean whileStatement(PsiBuilder builder_, int level_) {
|
public static boolean whileStatement(PsiBuilder builder_, int level_) {
|
||||||
if (!recursion_guard_(builder_, level_, "whileStatement")) return false;
|
if (!recursion_guard_(builder_, level_, "whileStatement")) return false;
|
||||||
if (!nextTokenIs(builder_, KW_WHILE)) return false;
|
if (!nextTokenIs(builder_, KW_WHILE)) return false;
|
||||||
@@ -2945,24 +3135,42 @@ public class JaiParser implements PsiParser, LightPsiParser {
|
|||||||
return result_;
|
return result_;
|
||||||
}
|
}
|
||||||
|
|
||||||
// [<<isLabelAhead>> IDENT ':']
|
// [<<isLabelAhead>> ['`'] IDENT (':' | ':=')]
|
||||||
private static boolean whileStatement_1(PsiBuilder builder_, int level_) {
|
private static boolean whileStatement_1(PsiBuilder builder_, int level_) {
|
||||||
if (!recursion_guard_(builder_, level_, "whileStatement_1")) return false;
|
if (!recursion_guard_(builder_, level_, "whileStatement_1")) return false;
|
||||||
whileStatement_1_0(builder_, level_ + 1);
|
whileStatement_1_0(builder_, level_ + 1);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// <<isLabelAhead>> IDENT ':'
|
// <<isLabelAhead>> ['`'] IDENT (':' | ':=')
|
||||||
private static boolean whileStatement_1_0(PsiBuilder builder_, int level_) {
|
private static boolean whileStatement_1_0(PsiBuilder builder_, int level_) {
|
||||||
if (!recursion_guard_(builder_, level_, "whileStatement_1_0")) return false;
|
if (!recursion_guard_(builder_, level_, "whileStatement_1_0")) return false;
|
||||||
boolean result_;
|
boolean result_;
|
||||||
Marker marker_ = enter_section_(builder_);
|
Marker marker_ = enter_section_(builder_);
|
||||||
result_ = isLabelAhead(builder_, level_ + 1);
|
result_ = isLabelAhead(builder_, level_ + 1);
|
||||||
result_ = result_ && consumeTokens(builder_, 0, IDENT, COLON);
|
result_ = result_ && whileStatement_1_0_1(builder_, level_ + 1);
|
||||||
|
result_ = result_ && consumeToken(builder_, IDENT);
|
||||||
|
result_ = result_ && whileStatement_1_0_3(builder_, level_ + 1);
|
||||||
exit_section_(builder_, marker_, null, result_);
|
exit_section_(builder_, marker_, null, result_);
|
||||||
return result_;
|
return result_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ['`']
|
||||||
|
private static boolean whileStatement_1_0_1(PsiBuilder builder_, int level_) {
|
||||||
|
if (!recursion_guard_(builder_, level_, "whileStatement_1_0_1")) return false;
|
||||||
|
consumeToken(builder_, BACKTICK);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ':' | ':='
|
||||||
|
private static boolean whileStatement_1_0_3(PsiBuilder builder_, int level_) {
|
||||||
|
if (!recursion_guard_(builder_, level_, "whileStatement_1_0_3")) return false;
|
||||||
|
boolean result_;
|
||||||
|
result_ = consumeToken(builder_, COLON);
|
||||||
|
if (!result_) result_ = consumeToken(builder_, COLON_EQ);
|
||||||
|
return result_;
|
||||||
|
}
|
||||||
|
|
||||||
/* ********************************************************** */
|
/* ********************************************************** */
|
||||||
// '{' '}'
|
// '{' '}'
|
||||||
public static boolean zeroLiteralExpr(PsiBuilder builder_, int level_) {
|
public static boolean zeroLiteralExpr(PsiBuilder builder_, int level_) {
|
||||||
@@ -2979,7 +3187,7 @@ public class JaiParser implements PsiParser, LightPsiParser {
|
|||||||
// Expression root: expr
|
// Expression root: expr
|
||||||
// Operator priority table:
|
// Operator priority table:
|
||||||
// 0: BINARY(assignExpr)
|
// 0: BINARY(assignExpr)
|
||||||
// 1: BINARY(lambdaExpr)
|
// 1: POSTFIX(lambdaExpr)
|
||||||
// 2: BINARY(orExpr)
|
// 2: BINARY(orExpr)
|
||||||
// 3: BINARY(andExpr)
|
// 3: BINARY(andExpr)
|
||||||
// 4: BINARY(equalityExpr)
|
// 4: BINARY(equalityExpr)
|
||||||
@@ -3034,8 +3242,8 @@ public class JaiParser implements PsiParser, LightPsiParser {
|
|||||||
result_ = expr(builder_, level_, -1);
|
result_ = expr(builder_, level_, -1);
|
||||||
exit_section_(builder_, level_, marker_, ASSIGN_EXPR, result_, true, null);
|
exit_section_(builder_, level_, marker_, ASSIGN_EXPR, result_, true, null);
|
||||||
}
|
}
|
||||||
else if (priority_ < 1 && consumeTokenSmart(builder_, QUICK_LAMBDA)) {
|
else if (priority_ < 1 && lambdaExpr_0(builder_, level_ + 1)) {
|
||||||
result_ = expr(builder_, level_, 0);
|
result_ = true;
|
||||||
exit_section_(builder_, level_, marker_, LAMBDA_EXPR, result_, true, null);
|
exit_section_(builder_, level_, marker_, LAMBDA_EXPR, result_, true, null);
|
||||||
}
|
}
|
||||||
else if (priority_ < 2 && consumeTokenSmart(builder_, OR_OR)) {
|
else if (priority_ < 2 && consumeTokenSmart(builder_, OR_OR)) {
|
||||||
@@ -3118,6 +3326,26 @@ public class JaiParser implements PsiParser, LightPsiParser {
|
|||||||
return result_;
|
return result_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// '=>' (block | expr)
|
||||||
|
private static boolean lambdaExpr_0(PsiBuilder builder_, int level_) {
|
||||||
|
if (!recursion_guard_(builder_, level_, "lambdaExpr_0")) return false;
|
||||||
|
boolean result_;
|
||||||
|
Marker marker_ = enter_section_(builder_);
|
||||||
|
result_ = consumeTokenSmart(builder_, QUICK_LAMBDA);
|
||||||
|
result_ = result_ && lambdaExpr_0_1(builder_, level_ + 1);
|
||||||
|
exit_section_(builder_, marker_, null, result_);
|
||||||
|
return result_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// block | expr
|
||||||
|
private static boolean lambdaExpr_0_1(PsiBuilder builder_, int level_) {
|
||||||
|
if (!recursion_guard_(builder_, level_, "lambdaExpr_0_1")) return false;
|
||||||
|
boolean result_;
|
||||||
|
result_ = block(builder_, level_ + 1);
|
||||||
|
if (!result_) result_ = expr(builder_, level_ + 1, -1);
|
||||||
|
return result_;
|
||||||
|
}
|
||||||
|
|
||||||
// <<eqEqNotSwitch>> | '!=' | '==='
|
// <<eqEqNotSwitch>> | '!=' | '==='
|
||||||
private static boolean equalityExpr_0(PsiBuilder builder_, int level_) {
|
private static boolean equalityExpr_0(PsiBuilder builder_, int level_) {
|
||||||
if (!recursion_guard_(builder_, level_, "equalityExpr_0")) return false;
|
if (!recursion_guard_(builder_, level_, "equalityExpr_0")) return false;
|
||||||
@@ -3531,6 +3759,7 @@ public class JaiParser implements PsiParser, LightPsiParser {
|
|||||||
// | parenExpr
|
// | parenExpr
|
||||||
// | structLiteralExpr
|
// | structLiteralExpr
|
||||||
// | arrayLiteralExpr
|
// | arrayLiteralExpr
|
||||||
|
// | valueLiteralExpr
|
||||||
// | unaryDotExpr
|
// | unaryDotExpr
|
||||||
// | backtickExpr
|
// | backtickExpr
|
||||||
// | builtinCallExpr
|
// | builtinCallExpr
|
||||||
@@ -3551,6 +3780,7 @@ public class JaiParser implements PsiParser, LightPsiParser {
|
|||||||
if (!result_) result_ = parenExpr(builder_, level_ + 1);
|
if (!result_) result_ = parenExpr(builder_, level_ + 1);
|
||||||
if (!result_) result_ = structLiteralExpr(builder_, level_ + 1);
|
if (!result_) result_ = structLiteralExpr(builder_, level_ + 1);
|
||||||
if (!result_) result_ = arrayLiteralExpr(builder_, level_ + 1);
|
if (!result_) result_ = arrayLiteralExpr(builder_, level_ + 1);
|
||||||
|
if (!result_) result_ = valueLiteralExpr(builder_, level_ + 1);
|
||||||
if (!result_) result_ = unaryDotExpr(builder_, level_ + 1);
|
if (!result_) result_ = unaryDotExpr(builder_, level_ + 1);
|
||||||
if (!result_) result_ = backtickExpr(builder_, level_ + 1);
|
if (!result_) result_ = backtickExpr(builder_, level_ + 1);
|
||||||
if (!result_) result_ = builtinCallExpr(builder_, level_ + 1);
|
if (!result_) result_ = builtinCallExpr(builder_, level_ + 1);
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ public interface JaiIfStatement extends PsiElement {
|
|||||||
@Nullable
|
@Nullable
|
||||||
JaiElseClause getElseClause();
|
JaiElseClause getElseClause();
|
||||||
|
|
||||||
@NotNull
|
@Nullable
|
||||||
JaiExpr getExpr();
|
JaiExpr getExpr();
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ import com.intellij.psi.PsiElement;
|
|||||||
|
|
||||||
public interface JaiLambdaExpr extends JaiExpr {
|
public interface JaiLambdaExpr extends JaiExpr {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
JaiBlock getBlock();
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
List<JaiExpr> getExprList();
|
List<JaiExpr> getExprList();
|
||||||
|
|
||||||
|
|||||||
@@ -7,4 +7,7 @@ import com.intellij.psi.PsiElement;
|
|||||||
|
|
||||||
public interface JaiMemberName extends PsiElement {
|
public interface JaiMemberName extends PsiElement {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
JaiOperatorSymbol getOperatorSymbol();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import com.intellij.psi.PsiElement;
|
|||||||
|
|
||||||
public interface JaiPushContextStatement extends PsiElement {
|
public interface JaiPushContextStatement extends PsiElement {
|
||||||
|
|
||||||
@NotNull
|
@Nullable
|
||||||
JaiBlock getBlock();
|
JaiBlock getBlock();
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ public interface JaiTypes {
|
|||||||
IElementType UNINITIALIZED_EXPR = new JaiElementType("UNINITIALIZED_EXPR");
|
IElementType UNINITIALIZED_EXPR = new JaiElementType("UNINITIALIZED_EXPR");
|
||||||
IElementType UNION_EXPR = new JaiElementType("UNION_EXPR");
|
IElementType UNION_EXPR = new JaiElementType("UNION_EXPR");
|
||||||
IElementType USING_STATEMENT = new JaiElementType("USING_STATEMENT");
|
IElementType USING_STATEMENT = new JaiElementType("USING_STATEMENT");
|
||||||
|
IElementType VALUE_LITERAL_EXPR = new JaiElementType("VALUE_LITERAL_EXPR");
|
||||||
IElementType WHILE_STATEMENT = new JaiElementType("WHILE_STATEMENT");
|
IElementType WHILE_STATEMENT = new JaiElementType("WHILE_STATEMENT");
|
||||||
IElementType ZERO_LITERAL_EXPR = new JaiElementType("ZERO_LITERAL_EXPR");
|
IElementType ZERO_LITERAL_EXPR = new JaiElementType("ZERO_LITERAL_EXPR");
|
||||||
|
|
||||||
@@ -430,6 +431,9 @@ public interface JaiTypes {
|
|||||||
else if (type == USING_STATEMENT) {
|
else if (type == USING_STATEMENT) {
|
||||||
return new JaiUsingStatementImpl(node);
|
return new JaiUsingStatementImpl(node);
|
||||||
}
|
}
|
||||||
|
else if (type == VALUE_LITERAL_EXPR) {
|
||||||
|
return new JaiValueLiteralExprImpl(node);
|
||||||
|
}
|
||||||
else if (type == WHILE_STATEMENT) {
|
else if (type == WHILE_STATEMENT) {
|
||||||
return new JaiWhileStatementImpl(node);
|
return new JaiWhileStatementImpl(node);
|
||||||
}
|
}
|
||||||
|
|||||||
13
src/main/gen/dev/hgh/jai/psi/JaiValueLiteralExpr.java
Normal file
13
src/main/gen/dev/hgh/jai/psi/JaiValueLiteralExpr.java
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
// This is a generated file. Not intended for manual editing.
|
||||||
|
package dev.hgh.jai.psi;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import org.jetbrains.annotations.*;
|
||||||
|
import com.intellij.psi.PsiElement;
|
||||||
|
|
||||||
|
public interface JaiValueLiteralExpr extends JaiExpr {
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
List<JaiArgument> getArgumentList();
|
||||||
|
|
||||||
|
}
|
||||||
@@ -319,6 +319,10 @@ public class JaiVisitor extends PsiElementVisitor {
|
|||||||
visitPsiElement(o);
|
visitPsiElement(o);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void visitValueLiteralExpr(@NotNull JaiValueLiteralExpr o) {
|
||||||
|
visitExpr(o);
|
||||||
|
}
|
||||||
|
|
||||||
public void visitWhileStatement(@NotNull JaiWhileStatement o) {
|
public void visitWhileStatement(@NotNull JaiWhileStatement o) {
|
||||||
visitPsiElement(o);
|
visitPsiElement(o);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,9 +34,9 @@ public class JaiIfStatementImpl extends ASTWrapperPsiElement implements JaiIfSta
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@NotNull
|
@Nullable
|
||||||
public JaiExpr getExpr() {
|
public JaiExpr getExpr() {
|
||||||
return findNotNullChildByClass(JaiExpr.class);
|
return findChildByClass(JaiExpr.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -27,6 +27,12 @@ public class JaiLambdaExprImpl extends JaiExprImpl implements JaiLambdaExpr {
|
|||||||
else super.accept(visitor);
|
else super.accept(visitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
public JaiBlock getBlock() {
|
||||||
|
return findChildByClass(JaiBlock.class);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@NotNull
|
@NotNull
|
||||||
public List<JaiExpr> getExprList() {
|
public List<JaiExpr> getExprList() {
|
||||||
|
|||||||
@@ -27,4 +27,10 @@ public class JaiMemberNameImpl extends ASTWrapperPsiElement implements JaiMember
|
|||||||
else super.accept(visitor);
|
else super.accept(visitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
public JaiOperatorSymbol getOperatorSymbol() {
|
||||||
|
return findChildByClass(JaiOperatorSymbol.class);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,9 +28,9 @@ public class JaiPushContextStatementImpl extends ASTWrapperPsiElement implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@NotNull
|
@Nullable
|
||||||
public JaiBlock getBlock() {
|
public JaiBlock getBlock() {
|
||||||
return findNotNullChildByClass(JaiBlock.class);
|
return findChildByClass(JaiBlock.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
// This is a generated file. Not intended for manual editing.
|
||||||
|
package dev.hgh.jai.psi.impl;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import org.jetbrains.annotations.*;
|
||||||
|
import com.intellij.lang.ASTNode;
|
||||||
|
import com.intellij.psi.PsiElement;
|
||||||
|
import com.intellij.psi.PsiElementVisitor;
|
||||||
|
import com.intellij.psi.util.PsiTreeUtil;
|
||||||
|
import static dev.hgh.jai.psi.JaiTypes.*;
|
||||||
|
import dev.hgh.jai.psi.*;
|
||||||
|
|
||||||
|
public class JaiValueLiteralExprImpl extends JaiExprImpl implements JaiValueLiteralExpr {
|
||||||
|
|
||||||
|
public JaiValueLiteralExprImpl(@NotNull ASTNode node) {
|
||||||
|
super(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(@NotNull JaiVisitor visitor) {
|
||||||
|
visitor.visitValueLiteralExpr(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(@NotNull PsiElementVisitor visitor) {
|
||||||
|
if (visitor instanceof JaiVisitor) accept((JaiVisitor)visitor);
|
||||||
|
else super.accept(visitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@NotNull
|
||||||
|
public List<JaiArgument> getArgumentList() {
|
||||||
|
return PsiTreeUtil.getChildrenOfTypeAsList(this, JaiArgument.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -181,7 +181,11 @@ directiveStatement ::= <<dirBare>> directiveFlag_* [';']
|
|||||||
|
|
||||||
// `#if` is spelled as a directive but is structurally an `if`, and it pairs with the
|
// `#if` is spelled as a directive but is structurally an `if`, and it pairs with the
|
||||||
// ordinary `else` keyword (see modules/File_Watcher/module.jai).
|
// ordinary `else` keyword (see modules/File_Watcher/module.jai).
|
||||||
ifStatement ::= ifKeyword_ [<<dir "#complete">>] expr (switchBody | ['then'] statement [elseClause])
|
ifStatement ::= nestedInlineIfStatement
|
||||||
|
| inlineIfDirectiveStatement
|
||||||
|
| ifKeyword_ [<<dir "#complete">>] expr (switchBody | ['then'] statement [elseClause])
|
||||||
|
private nestedInlineIfStatement ::= <<parseNestedInlineIfStatement>>
|
||||||
|
private inlineIfDirectiveStatement ::= <<dir "#if">> IDENT statement [elseClause]
|
||||||
private ifKeyword_ ::= 'if' | <<dir "#if">>
|
private ifKeyword_ ::= 'if' | <<dir "#if">>
|
||||||
elseClause ::= 'else' statement
|
elseClause ::= 'else' statement
|
||||||
|
|
||||||
@@ -190,21 +194,21 @@ elseClause ::= 'else' statement
|
|||||||
switchBody ::= '==' '{' caseClause* '}'
|
switchBody ::= '==' '{' caseClause* '}'
|
||||||
caseClause ::= 'case' [expr] [';'] statement*
|
caseClause ::= 'case' [expr] [';'] statement*
|
||||||
|
|
||||||
whileStatement ::= 'while' [<<isLabelAhead>> IDENT ':'] expr statement
|
whileStatement ::= 'while' [<<isLabelAhead>> ['`'] IDENT (':' | ':=')] expr statement
|
||||||
|
|
||||||
// `for < * name, index : iterable` — `<` reverses, `*` iterates by pointer, and
|
// `for < * name, index : iterable` — `<` reverses, `*` iterates by pointer, and
|
||||||
// `for :expansion` selects a named for_expansion.
|
// `for :expansion` selects a named for_expansion.
|
||||||
forStatement ::= 'for' [':' expr] forModifier_* [<<isForNamesAhead>> forNames ':'] expr statement
|
forStatement ::= 'for' [':' expr] forModifier_* [<<isForNamesAhead>> forNames ':'] expr statement
|
||||||
private forModifier_ ::= '<=' expr | '*=' expr | '<' | '*'
|
private forModifier_ ::= '<=' expr | '*=' expr | '<' | '*' | ','
|
||||||
forNames ::= forName (',' forName)*
|
forNames ::= forName (',' forName)*
|
||||||
forName ::= ['`'] IDENT
|
forName ::= ['`'] IDENT
|
||||||
|
|
||||||
returnStatement ::= ['`'] 'return' [expr (',' expr)*] semiWithNotes_
|
returnStatement ::= ['`'] 'return' [expr (',' expr)* [',']] semiWithNotes_
|
||||||
breakStatement ::= 'break' [IDENT] semi
|
breakStatement ::= 'break' [IDENT] semi
|
||||||
continueStatement ::= 'continue' [IDENT] semi
|
continueStatement ::= 'continue' [IDENT] semi
|
||||||
removeStatement ::= 'remove' [expr] semi
|
removeStatement ::= 'remove' [expr] semi
|
||||||
deferStatement ::= ['`'] 'defer' statement
|
deferStatement ::= ['`'] 'defer' statement
|
||||||
pushContextStatement ::= ['`'] 'push_context' directiveFlag_* [expr] block
|
pushContextStatement ::= ['`'] 'push_context' directiveFlag_* [expr] (block | semi)
|
||||||
usingStatement ::= 'using' [',' expr] expr semi
|
usingStatement ::= 'using' [',' expr] expr semi
|
||||||
|
|
||||||
expressionStatement ::= expr (',' expr)* semiWithNotes_
|
expressionStatement ::= expr (',' expr)* semiWithNotes_
|
||||||
@@ -221,8 +225,8 @@ private declModifier_ ::= <<dirWithBlock>> directiveFlag_* block
|
|||||||
| <<dirPlain>> directiveFlag_* [parenExpr | STRING]
|
| <<dirPlain>> directiveFlag_* [parenExpr | STRING]
|
||||||
| usingModifier_
|
| usingModifier_
|
||||||
private usingModifier_ ::= 'using' [directiveFlag_ [<<exprNoAssign>>]]
|
private usingModifier_ ::= 'using' [directiveFlag_ [<<exprNoAssign>>]]
|
||||||
declNames ::= declName (',' declName)*
|
declNames ::= declName (['='] ',' declName ['='])* ['=']
|
||||||
declName ::= ['`'] IDENT
|
declName ::= ['`'] (IDENT | keywordAsName_)
|
||||||
private declTail_ ::= ':' [<<exprNoAssign>>] procModifier* [',' assignTargets_] [('=' | ':') initializerList_]
|
private declTail_ ::= ':' [<<exprNoAssign>>] procModifier* [',' assignTargets_] [('=' | ':') initializerList_]
|
||||||
| ':=' initializerList_
|
| ':=' initializerList_
|
||||||
| '::' initializerList_
|
| '::' initializerList_
|
||||||
@@ -230,7 +234,8 @@ private declTail_ ::= ':' [<<exprNoAssign>>] procModifier* [',' assignTargets_]
|
|||||||
private initializerList_ ::= initializer (',' initializer)*
|
private initializerList_ ::= initializer (',' initializer)*
|
||||||
// `success:, apk.zip_dir = load_zip_directory(path);` declares the first name and
|
// `success:, apk.zip_dir = load_zip_directory(path);` declares the first name and
|
||||||
// assigns to the remaining lvalues.
|
// assigns to the remaining lvalues.
|
||||||
private assignTargets_ ::= <<exprNoAssign>> (',' <<exprNoAssign>>)*
|
private assignTargets_ ::= assignTarget_ (',' assignTarget_)*
|
||||||
|
private assignTarget_ ::= <<exprNoAssign>> [':']
|
||||||
initializer ::= '---' | expr
|
initializer ::= '---' | expr
|
||||||
|
|
||||||
// `operator + :: (a: T, b: T) -> T { }`
|
// `operator + :: (a: T, b: T) -> T { }`
|
||||||
@@ -238,7 +243,7 @@ operatorDeclaration ::= 'operator' operatorSymbol '::' expr semiWithNotes_
|
|||||||
// Longest first: `*` would otherwise match the start of `*[]`, and `[]` the start
|
// Longest first: `*` would otherwise match the start of `*[]`, and `[]` the start
|
||||||
// of `[]=`. Single-token operators cannot collide, since `+=` is one token.
|
// of `[]=`. Single-token operators cannot collide, since `+=` is one token.
|
||||||
operatorSymbol ::= '*' '[' ']' | '[' ']' '=' | '[' ']'
|
operatorSymbol ::= '*' '[' ']' | '[' ']' '=' | '[' ']'
|
||||||
| '+' | '-' | '*' | '/' | '%' | '==' | '!=' | '<' | '>' | '<=' | '>='
|
| '+' | '-' | '*' | '/' | '%' | '==' | '!=' | '!' | '<' | '>' | '<=' | '>='
|
||||||
| '&' | '|' | '^' | '~' | '<<' | '>>' | '<<<' | '>>>'
|
| '&' | '|' | '^' | '~' | '<<' | '>>' | '<<<' | '>>>'
|
||||||
| '+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^='
|
| '+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^='
|
||||||
| '<<=' | '>>=' | '<<<=' | '>>>='
|
| '<<=' | '>>=' | '<<<=' | '>>>='
|
||||||
@@ -278,7 +283,7 @@ assignExpr ::= expr assignOp_ expr { rightAssociative=true }
|
|||||||
private assignOp_ ::= '=' | '+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^='
|
private assignOp_ ::= '=' | '+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^='
|
||||||
| '<<=' | '>>=' | '<<<=' | '>>>=' | '&&=' | '||='
|
| '<<=' | '>>=' | '<<<=' | '>>>=' | '&&=' | '||='
|
||||||
|
|
||||||
lambdaExpr ::= expr '=>' expr { rightAssociative=true }
|
lambdaExpr ::= expr '=>' (block | expr) { rightAssociative=true }
|
||||||
|
|
||||||
orExpr ::= expr '||' expr
|
orExpr ::= expr '||' expr
|
||||||
andExpr ::= expr '&&' expr
|
andExpr ::= expr '&&' expr
|
||||||
@@ -321,7 +326,7 @@ derefExpr ::= expr '.*'
|
|||||||
typedStructLiteralExpr ::= expr '.{' [argList] '}'
|
typedStructLiteralExpr ::= expr '.{' [argList] '}'
|
||||||
typedArrayLiteralExpr ::= expr '.[' [argList] ']'
|
typedArrayLiteralExpr ::= expr '.[' [argList] ']'
|
||||||
|
|
||||||
memberName ::= IDENT | NUMBER | keywordAsName_
|
memberName ::= 'operator' operatorSymbol | IDENT | NUMBER | keywordAsName_
|
||||||
// A few keywords double as field names in the distribution (`context.print_style`,
|
// A few keywords double as field names in the distribution (`context.print_style`,
|
||||||
// `.type`, `it.operator`), so member access accepts them.
|
// `.type`, `it.operator`), so member access accepts them.
|
||||||
private keywordAsName_ ::= 'if' | 'for' | 'case' | 'enum' | 'cast' | 'union' | 'struct'
|
private keywordAsName_ ::= 'if' | 'for' | 'case' | 'enum' | 'cast' | 'union' | 'struct'
|
||||||
@@ -345,6 +350,7 @@ primaryExpr ::= procLiteralExpr
|
|||||||
| parenExpr
|
| parenExpr
|
||||||
| structLiteralExpr
|
| structLiteralExpr
|
||||||
| arrayLiteralExpr
|
| arrayLiteralExpr
|
||||||
|
| valueLiteralExpr
|
||||||
| unaryDotExpr
|
| unaryDotExpr
|
||||||
| backtickExpr
|
| backtickExpr
|
||||||
| builtinCallExpr
|
| builtinCallExpr
|
||||||
@@ -360,6 +366,7 @@ uninitializedExpr ::= '---' | '--'
|
|||||||
// `table.* = {};` zero-initialises. Only the empty form is an expression; a
|
// `table.* = {};` zero-initialises. Only the empty form is an expression; a
|
||||||
// non-empty `{ … }` in expression position would collide with a block statement.
|
// non-empty `{ … }` in expression position would collide with a block statement.
|
||||||
zeroLiteralExpr ::= '{' '}'
|
zeroLiteralExpr ::= '{' '}'
|
||||||
|
valueLiteralExpr ::= '{' [argList] '}'
|
||||||
backtickExpr ::= '`' (IDENT | 'defer' | 'return' | 'push_context' | 'operator')
|
backtickExpr ::= '`' (IDENT | 'defer' | 'return' | 'push_context' | 'operator')
|
||||||
unaryDotExpr ::= '.' memberName
|
unaryDotExpr ::= '.' memberName
|
||||||
structLiteralExpr ::= '.{' [argList] '}'
|
structLiteralExpr ::= '.{' [argList] '}'
|
||||||
@@ -378,7 +385,7 @@ procLiteralExpr ::= <<isProcHeaderAhead>> '(' [paramList] ')' ['->' returnTypes_
|
|||||||
|
|
||||||
private paramList ::= parameter (',' parameter)* [',']
|
private paramList ::= parameter (',' parameter)* [',']
|
||||||
parameter ::= paramModifier_* parameterTail_
|
parameter ::= paramModifier_* parameterTail_
|
||||||
private paramModifier_ ::= <<dirPlain>> | 'using'
|
private paramModifier_ ::= <<dirPlain>> | 'using' [directiveFlag_ [<<exprNoAssign>>]]
|
||||||
private parameterTail_ ::= <<isParamNameAhead>> paramNames paramTypeTail_ | '..' | expr
|
private parameterTail_ ::= <<isParamNameAhead>> paramNames paramTypeTail_ | '..' | expr
|
||||||
paramNames ::= paramName (',' paramName)*
|
paramNames ::= paramName (',' paramName)*
|
||||||
paramName ::= ['$' | '$$'] IDENT
|
paramName ::= ['$' | '$$'] IDENT
|
||||||
@@ -399,7 +406,7 @@ private returnDefault_ ::= '---' | <<exprNoAssign>>
|
|||||||
// `#foreign lib`, `#modify { ... }`, `#c_call`, `#expand`, …
|
// `#foreign lib`, `#modify { ... }`, `#c_call`, `#expand`, …
|
||||||
// `#foreign lib "symbol"`, `#modify { ... }`, `#c_call`, `#expand`, `#align 8` — the
|
// `#foreign lib "symbol"`, `#modify { ... }`, `#c_call`, `#expand`, `#align 8` — the
|
||||||
// same shape as any other directive, so there is one rule for both positions.
|
// same shape as any other directive, so there is one rule for both positions.
|
||||||
procModifier ::= directiveExpr
|
procModifier ::= !<<nonProcDirectiveAhead>> directiveExpr
|
||||||
|
|
||||||
structExpr ::= 'struct' ['(' [paramList] ')'] structModifier_* block
|
structExpr ::= 'struct' ['(' [paramList] ')'] structModifier_* block
|
||||||
enumExpr ::= ('enum' | 'enum_flags') [!'{' expr] structModifier_* block
|
enumExpr ::= ('enum' | 'enum_flags') [!'{' expr] structModifier_* block
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ object JaiParserUtil : GeneratedParserUtilBase() {
|
|||||||
"insert",
|
"insert",
|
||||||
"code",
|
"code",
|
||||||
"run_and_insert",
|
"run_and_insert",
|
||||||
"compile_time",
|
|
||||||
"bytes",
|
"bytes",
|
||||||
"no_reset",
|
"no_reset",
|
||||||
)
|
)
|
||||||
@@ -79,6 +78,9 @@ object JaiParserUtil : GeneratedParserUtilBase() {
|
|||||||
"poke_name",
|
"poke_name",
|
||||||
"dump",
|
"dump",
|
||||||
"foreign",
|
"foreign",
|
||||||
|
"compiler",
|
||||||
|
"intrinsic",
|
||||||
|
"program_export",
|
||||||
"define",
|
"define",
|
||||||
"include",
|
"include",
|
||||||
"undef",
|
"undef",
|
||||||
@@ -100,7 +102,40 @@ object JaiParserUtil : GeneratedParserUtilBase() {
|
|||||||
* that declaration's scope. Taken from the corpus, where only these four appear
|
* that declaration's scope. Taken from the corpus, where only these four appear
|
||||||
* directly in front of a `name :` / `name ::` / `name :=`.
|
* directly in front of a `name :` / `name ::` / `name :=`.
|
||||||
*/
|
*/
|
||||||
private val DIRECTIVES_ON_DECLARATIONS = setOf("as", "overlay", "add_context", "no_reset")
|
private val DIRECTIVES_ON_DECLARATIONS =
|
||||||
|
setOf("as", "overlay", "add_context", "no_reset", "program_export")
|
||||||
|
|
||||||
|
/** Scope switches are statements, never procedure modifiers. */
|
||||||
|
private val NON_PROC_DIRECTIVES = setOf("scope_file", "scope_module", "scope_export")
|
||||||
|
|
||||||
|
/** Keyword tokens accepted by the grammar's `keywordAsName_` name rule. */
|
||||||
|
private val DECL_NAME_KEYWORDS =
|
||||||
|
setOf(
|
||||||
|
T.KW_IF,
|
||||||
|
T.KW_FOR,
|
||||||
|
T.KW_CASE,
|
||||||
|
T.KW_ENUM,
|
||||||
|
T.KW_CAST,
|
||||||
|
T.KW_UNION,
|
||||||
|
T.KW_STRUCT,
|
||||||
|
T.KW_REMOVE,
|
||||||
|
T.KW_INLINE,
|
||||||
|
T.KW_CONTEXT,
|
||||||
|
T.KW_OPERATOR,
|
||||||
|
T.KW_INTERFACE,
|
||||||
|
T.KW_TYPE_INFO,
|
||||||
|
T.KW_USING,
|
||||||
|
T.KW_DEFER,
|
||||||
|
T.KW_RETURN,
|
||||||
|
T.KW_BREAK,
|
||||||
|
T.KW_CONTINUE,
|
||||||
|
T.KW_THEN,
|
||||||
|
T.KW_ELSE,
|
||||||
|
T.KW_WHILE,
|
||||||
|
T.KW_NULL,
|
||||||
|
T.KW_TRUE,
|
||||||
|
T.KW_FALSE,
|
||||||
|
)
|
||||||
|
|
||||||
// ---------------------------------------------------------------- directives
|
// ---------------------------------------------------------------- directives
|
||||||
|
|
||||||
@@ -154,6 +189,22 @@ object JaiParserUtil : GeneratedParserUtilBase() {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True when the current directive is a scope switch, which is a statement
|
||||||
|
* rather than a procedure modifier. Keeping this as a lookahead predicate lets
|
||||||
|
* the grammar's normal `directiveExpr` consume the modifier's operand, such
|
||||||
|
* as the library name in `#foreign kernel32`.
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun nonProcDirectiveAhead(
|
||||||
|
b: PsiBuilder,
|
||||||
|
level: Int,
|
||||||
|
): Boolean {
|
||||||
|
if (b.tokenType !== T.DIRECTIVE) return false
|
||||||
|
val text = b.tokenText ?: return false
|
||||||
|
return text.removePrefix("#") in NON_PROC_DIRECTIVES
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A directive that takes neither an operand nor a block, so it stands alone as a
|
* A directive that takes neither an operand nor a block, so it stands alone as a
|
||||||
* statement without a `;` — `#scope_file`, `#no_padding`, `#compiler`.
|
* statement without a `;` — `#scope_file`, `#no_padding`, `#compiler`.
|
||||||
@@ -166,7 +217,12 @@ object JaiParserUtil : GeneratedParserUtilBase() {
|
|||||||
if (b.tokenType !== T.DIRECTIVE) return false
|
if (b.tokenType !== T.DIRECTIVE) return false
|
||||||
val text = b.tokenText ?: return false
|
val text = b.tokenText ?: return false
|
||||||
val name = text.removePrefix("#")
|
val name = text.removePrefix("#")
|
||||||
if (name in DIRECTIVES_WITH_OPERAND || name in DIRECTIVES_WITH_BLOCK) return false
|
if (name in DIRECTIVES_WITH_BLOCK) return false
|
||||||
|
if (name in DIRECTIVES_WITH_OPERAND &&
|
||||||
|
!(name == "program_export" && b.lookAhead(1) !== T.STRING)
|
||||||
|
) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
b.advanceLexer()
|
b.advanceLexer()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -345,6 +401,8 @@ object JaiParserUtil : GeneratedParserUtilBase() {
|
|||||||
* `#as using,except(vtable) iunknown: IUnknown;` and the macro-exported
|
* `#as using,except(vtable) iunknown: IUnknown;` and the macro-exported
|
||||||
* `` `it := entry.value; ``.
|
* `` `it := entry.value; ``.
|
||||||
*/
|
*/
|
||||||
|
private fun isDeclarationNameToken(token: IElementType?): Boolean = token === T.IDENT || token in DECL_NAME_KEYWORDS
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun isDeclarationAhead(
|
fun isDeclarationAhead(
|
||||||
b: PsiBuilder,
|
b: PsiBuilder,
|
||||||
@@ -352,17 +410,24 @@ object JaiParserUtil : GeneratedParserUtilBase() {
|
|||||||
): Boolean {
|
): Boolean {
|
||||||
var i = 0
|
var i = 0
|
||||||
var inPrefix = false
|
var inPrefix = false
|
||||||
|
var sawUsingPrefix = false
|
||||||
loop@ while (true) {
|
loop@ while (true) {
|
||||||
when (b.lookAhead(i)) {
|
when (b.lookAhead(i)) {
|
||||||
T.DIRECTIVE -> {
|
T.DIRECTIVE -> {
|
||||||
|
// After `using,except`, a directive is the operand of the
|
||||||
|
// using modifier (`using,except #run ... name: T`), not a
|
||||||
|
// second declaration modifier.
|
||||||
|
if (sawUsingPrefix && !inPrefix) break@loop
|
||||||
if (!isDeclarationDirective(b, i)) break@loop
|
if (!isDeclarationDirective(b, i)) break@loop
|
||||||
i++
|
i++
|
||||||
inPrefix = true
|
inPrefix = true
|
||||||
|
if (b.lookAhead(i) === T.STRING) i++
|
||||||
}
|
}
|
||||||
|
|
||||||
T.KW_USING -> {
|
T.KW_USING -> {
|
||||||
i++
|
i++
|
||||||
inPrefix = true
|
inPrefix = true
|
||||||
|
sawUsingPrefix = true
|
||||||
}
|
}
|
||||||
|
|
||||||
T.LPAREN, T.LBRACKET, T.BEGIN_ARRAY_LITERAL, T.BEGIN_STRUCT_LITERAL -> {
|
T.LPAREN, T.LBRACKET, T.BEGIN_ARRAY_LITERAL, T.BEGIN_STRUCT_LITERAL -> {
|
||||||
@@ -376,6 +441,17 @@ object JaiParserUtil : GeneratedParserUtilBase() {
|
|||||||
if (!inPrefix || b.lookAhead(i + 1) !== T.IDENT) break@loop
|
if (!inPrefix || b.lookAhead(i + 1) !== T.IDENT) break@loop
|
||||||
i += 2
|
i += 2
|
||||||
inPrefix = false
|
inPrefix = false
|
||||||
|
// `using,except SKIP_THESE orientation: T` has a bare
|
||||||
|
// identifier as the flag's operand. The declaration grammar
|
||||||
|
// consumes it through `exprNoAssign`; skip it here so the
|
||||||
|
// actual field name is what declNames sees.
|
||||||
|
if (sawUsingPrefix &&
|
||||||
|
b.lookAhead(i) === T.IDENT &&
|
||||||
|
b.lookAhead(i + 1) === T.IDENT &&
|
||||||
|
b.lookAhead(i + 2) === T.COLON
|
||||||
|
) {
|
||||||
|
i++
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
@@ -383,14 +459,66 @@ object JaiParserUtil : GeneratedParserUtilBase() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// A using-except operand can be a directive expression, for example:
|
||||||
|
// `using,except #run names_to_skip(Type) business: Business;`. The
|
||||||
|
// prefix loop intentionally stops at that directive, so find the first
|
||||||
|
// declaration name after its (possibly grouped) operand.
|
||||||
|
if (sawUsingPrefix && !isDeclarationNameToken(b.lookAhead(i))) {
|
||||||
|
var candidate = i
|
||||||
|
while (candidate <= MAX_LOOKAHEAD) {
|
||||||
|
when (b.lookAhead(candidate)) {
|
||||||
|
T.LPAREN, T.LBRACKET, T.BEGIN_ARRAY_LITERAL, T.BEGIN_STRUCT_LITERAL -> {
|
||||||
|
candidate = skipBalancedGroup(b, candidate)
|
||||||
|
if (candidate < 0) return false
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
T.SEMICOLON, T.RBRACE -> {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
if (isDeclarationNameToken(b.lookAhead(candidate)) &&
|
||||||
|
b.lookAhead(candidate + 1) in setOf(T.COLON, T.COLON_COLON, T.COLON_EQ)
|
||||||
|
) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
candidate++
|
||||||
|
}
|
||||||
|
}
|
||||||
while (true) {
|
while (true) {
|
||||||
if (b.lookAhead(i) === T.BACKTICK) i++
|
if (b.lookAhead(i) === T.BACKTICK) i++
|
||||||
if (b.lookAhead(i) !== T.IDENT) return false
|
if (!isDeclarationNameToken(b.lookAhead(i))) return false
|
||||||
i++
|
i++
|
||||||
when (b.lookAhead(i)) {
|
when (b.lookAhead(i)) {
|
||||||
T.COLON, T.COLON_COLON, T.COLON_EQ -> return true
|
T.COLON, T.COLON_COLON, T.COLON_EQ -> {
|
||||||
T.COMMA -> i++
|
return true
|
||||||
else -> return false
|
}
|
||||||
|
|
||||||
|
// In a mixed declaration/assignment list, an existing lvalue is
|
||||||
|
// marked with `=` before the next declared name:
|
||||||
|
// `success, output=, error := adb(...)`.
|
||||||
|
T.EQ -> {
|
||||||
|
when (b.lookAhead(i + 1)) {
|
||||||
|
T.COMMA -> i += 2
|
||||||
|
|
||||||
|
// The final assigned target may be followed directly by
|
||||||
|
// the declaration's `:=` tail:
|
||||||
|
// `target, existing= := make_target()`.
|
||||||
|
T.COLON_EQ -> return true
|
||||||
|
|
||||||
|
else -> return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
T.COMMA -> {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -526,12 +654,43 @@ object JaiParserUtil : GeneratedParserUtilBase() {
|
|||||||
return t !== T.DOLLAR && t !== T.DOUBLE_DOLLAR
|
return t !== T.DOLLAR && t !== T.DOUBLE_DOLLAR
|
||||||
}
|
}
|
||||||
|
|
||||||
/** `IDENT ':'` — a loop label. */
|
/** Consumes `if (condition) #if FLAG statement` as one nested statement. */
|
||||||
|
@JvmStatic
|
||||||
|
fun parseNestedInlineIfStatement(
|
||||||
|
b: PsiBuilder,
|
||||||
|
level: Int,
|
||||||
|
): Boolean {
|
||||||
|
val marker = b.mark()
|
||||||
|
|
||||||
|
fun fail(): Boolean {
|
||||||
|
marker.rollbackTo()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (b.tokenType !== T.KW_IF || b.lookAhead(1) !== T.LPAREN) return fail()
|
||||||
|
b.advanceLexer()
|
||||||
|
if (!JaiParser.parenExpr(b, level + 1)) return fail()
|
||||||
|
if (b.tokenType !== T.DIRECTIVE || b.tokenText != "#if") return fail()
|
||||||
|
b.advanceLexer()
|
||||||
|
if (b.tokenType !== T.IDENT) return fail()
|
||||||
|
b.advanceLexer()
|
||||||
|
if (!JaiParser.statement(b, level + 1)) return fail()
|
||||||
|
|
||||||
|
marker.drop()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/** `IDENT ':'` or `IDENT ':='` — a loop label. */
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun isLabelAhead(
|
fun isLabelAhead(
|
||||||
b: PsiBuilder,
|
b: PsiBuilder,
|
||||||
level: Int,
|
level: Int,
|
||||||
): Boolean = b.tokenType === T.IDENT && b.lookAhead(1) === T.COLON
|
): Boolean {
|
||||||
|
val nameOffset = if (b.tokenType === T.BACKTICK) 1 else 0
|
||||||
|
if (b.lookAhead(nameOffset) !== T.IDENT) return false
|
||||||
|
val separator = b.lookAhead(nameOffset + 1)
|
||||||
|
return separator === T.COLON || separator === T.COLON_EQ
|
||||||
|
}
|
||||||
|
|
||||||
private const val MAX_LOOKAHEAD = 4096
|
private const val MAX_LOOKAHEAD = 4096
|
||||||
|
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ class JaiCorpusParserTest : BasePlatformTestCase() {
|
|||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
/** Ratchet. Raise it as the grammar improves; never lower it. */
|
/** Ratchet. Raise it as the grammar improves; never lower it. */
|
||||||
const val MIN_CLEAN_FILES = 650
|
const val MIN_CLEAN_FILES = 714
|
||||||
const val REPORTED_ERRORS = 25
|
const val REPORTED_ERRORS = 25
|
||||||
const val REPORTED_KINDS = 20
|
const val REPORTED_KINDS = 20
|
||||||
const val GOT_WIDTH = 14
|
const val GOT_WIDTH = 14
|
||||||
|
|||||||
68
src/test/kotlin/dev/hgh/jai/parser/JaiParserLongTailTest.kt
Normal file
68
src/test/kotlin/dev/hgh/jai/parser/JaiParserLongTailTest.kt
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
package dev.hgh.jai.parser
|
||||||
|
|
||||||
|
import com.intellij.psi.PsiErrorElement
|
||||||
|
import com.intellij.psi.PsiFileFactory
|
||||||
|
import com.intellij.psi.PsiRecursiveElementWalkingVisitor
|
||||||
|
import com.intellij.testFramework.fixtures.BasePlatformTestCase
|
||||||
|
import dev.hgh.jai.JaiLanguage
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Focused regressions for syntax recovered while closing the corpus gate.
|
||||||
|
*
|
||||||
|
* [JaiCorpusParserTest] proves acceptance across the full local distribution;
|
||||||
|
* these small examples make the individual grammar contracts easy to exercise
|
||||||
|
* while iterating on generated PSI.
|
||||||
|
*/
|
||||||
|
class JaiParserLongTailTest : BasePlatformTestCase() {
|
||||||
|
fun testLongTailConstructsParseWithoutErrors() {
|
||||||
|
val snippets =
|
||||||
|
listOf(
|
||||||
|
"result=, output, error := call();",
|
||||||
|
"""
|
||||||
|
Thing :: struct {
|
||||||
|
using,except SKIP_THESE field: Field;
|
||||||
|
}
|
||||||
|
proc :: (using,except(x,y,z) value: Value) {}
|
||||||
|
""".trimIndent(),
|
||||||
|
"""
|
||||||
|
factory :: () {
|
||||||
|
callback = p => { release(p); };
|
||||||
|
result := { callback, p };
|
||||||
|
}
|
||||||
|
""".trimIndent(),
|
||||||
|
"""
|
||||||
|
while `loop := ready { break loop; }
|
||||||
|
if #compile_time { report(); } else { fallback(); }
|
||||||
|
if (release) #if ENABLE_STATISTICS report();
|
||||||
|
""".trimIndent(),
|
||||||
|
"""
|
||||||
|
operator ! :: inline (value: Flag) -> bool { return value; }
|
||||||
|
push_context,defer_pop ctx;
|
||||||
|
return first, second,;
|
||||||
|
""".trimIndent(),
|
||||||
|
)
|
||||||
|
val factory = PsiFileFactory.getInstance(project)
|
||||||
|
|
||||||
|
for ((index, text) in snippets.withIndex()) {
|
||||||
|
val file = factory.createFileFromText("long-tail-$index.jai", JaiLanguage, text)
|
||||||
|
val errors = errorsIn(file)
|
||||||
|
assertTrue(
|
||||||
|
"Long-tail snippet $index produced parser errors: ${errors.joinToString { it.errorDescription }}\n$text",
|
||||||
|
errors.isEmpty(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun errorsIn(file: com.intellij.psi.PsiFile): List<PsiErrorElement> {
|
||||||
|
val errors = mutableListOf<PsiErrorElement>()
|
||||||
|
file.accept(
|
||||||
|
object : PsiRecursiveElementWalkingVisitor() {
|
||||||
|
override fun visitErrorElement(element: PsiErrorElement) {
|
||||||
|
errors.add(element)
|
||||||
|
super.visitErrorElement(element)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return errors
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user