diff --git a/build.gradle.kts b/build.gradle.kts index d07111b..4e2ae5c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,6 +7,7 @@ plugins { id("org.jetbrains.intellij.platform.grammarkit") } +// Grammar-Kit output. Committed so diffs are reviewable, never hand-edited. sourceSets { main { java.srcDirs("src/main/gen") @@ -33,4 +34,27 @@ tasks.generateParser { pathToParser.set("dev/hgh/jai/parser/JaiParser.java") pathToPsiRoot.set("dev/hgh/jai/psi") purgeOldFiles.set(true) + + // purgeOldFiles is not enough: Grammar-Kit rewrites only the files whose rule + // changed, so a PSI interface can keep an accessor its regenerated Impl no longer + // has (`JaiDeclaration.getArgumentList()` with no override) and the build fails on + // generated code. Always regenerate the whole tree. + doFirst { + delete(file("src/main/gen")) + } +} + +// Without this the compile tasks read src/main/gen with no ordering guarantee, so an +// edit to Jai.bnf could be compiled against the *previous* generated parser and the +// tests would quietly assert the old grammar. Editing Jai.bnf must be enough. +tasks.compileJava { dependsOn(tasks.generateParser) } + +tasks.compileKotlin { dependsOn(tasks.generateParser) } + +// Forward -Djai.debug.* to the test JVM for the scratch parser harness. +tasks.test { + System.getProperties().forEach { key, value -> + val name = key.toString() + if (name.startsWith("jai.debug.")) systemProperty(name, value.toString()) + } } diff --git a/src/main/gen/dev/hgh/jai/parser/JaiParser.java b/src/main/gen/dev/hgh/jai/parser/JaiParser.java index 4a6c3e9..2554f88 100644 --- a/src/main/gen/dev/hgh/jai/parser/JaiParser.java +++ b/src/main/gen/dev/hgh/jai/parser/JaiParser.java @@ -51,43 +51,98 @@ public class JaiParser implements PsiParser, LightPsiParser { }; /* ********************************************************** */ - // argument ((',' | ',,') argument)* [','] + // ',,' argument ((',' | ',,') argument)* + // | argument ((',' | ',,') argument)* [','] static boolean argList(PsiBuilder builder_, int level_) { if (!recursion_guard_(builder_, level_, "argList")) return false; boolean result_; Marker marker_ = enter_section_(builder_); - result_ = argument(builder_, level_ + 1); - result_ = result_ && argList_1(builder_, level_ + 1); - result_ = result_ && argList_2(builder_, level_ + 1); + result_ = argList_0(builder_, level_ + 1); + if (!result_) result_ = argList_1(builder_, level_ + 1); + exit_section_(builder_, marker_, null, result_); + return result_; + } + + // ',,' argument ((',' | ',,') argument)* + private static boolean argList_0(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "argList_0")) return false; + boolean result_; + Marker marker_ = enter_section_(builder_); + result_ = consumeToken(builder_, DOUBLE_COMMA); + result_ = result_ && argument(builder_, level_ + 1); + result_ = result_ && argList_0_2(builder_, level_ + 1); exit_section_(builder_, marker_, null, result_); return result_; } // ((',' | ',,') argument)* - private static boolean argList_1(PsiBuilder builder_, int level_) { - if (!recursion_guard_(builder_, level_, "argList_1")) return false; + private static boolean argList_0_2(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "argList_0_2")) return false; while (true) { int pos_ = current_position_(builder_); - if (!argList_1_0(builder_, level_ + 1)) break; - if (!empty_element_parsed_guard_(builder_, "argList_1", pos_)) break; + if (!argList_0_2_0(builder_, level_ + 1)) break; + if (!empty_element_parsed_guard_(builder_, "argList_0_2", pos_)) break; } return true; } // (',' | ',,') argument - private static boolean argList_1_0(PsiBuilder builder_, int level_) { - if (!recursion_guard_(builder_, level_, "argList_1_0")) return false; + private static boolean argList_0_2_0(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "argList_0_2_0")) return false; boolean result_; Marker marker_ = enter_section_(builder_); - result_ = argList_1_0_0(builder_, level_ + 1); + result_ = argList_0_2_0_0(builder_, level_ + 1); result_ = result_ && argument(builder_, level_ + 1); exit_section_(builder_, marker_, null, result_); return result_; } // ',' | ',,' - private static boolean argList_1_0_0(PsiBuilder builder_, int level_) { - if (!recursion_guard_(builder_, level_, "argList_1_0_0")) return false; + private static boolean argList_0_2_0_0(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "argList_0_2_0_0")) return false; + boolean result_; + result_ = consumeToken(builder_, COMMA); + if (!result_) result_ = consumeToken(builder_, DOUBLE_COMMA); + return result_; + } + + // argument ((',' | ',,') argument)* [','] + private static boolean argList_1(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "argList_1")) return false; + boolean result_; + Marker marker_ = enter_section_(builder_); + result_ = argument(builder_, level_ + 1); + result_ = result_ && argList_1_1(builder_, level_ + 1); + result_ = result_ && argList_1_2(builder_, level_ + 1); + exit_section_(builder_, marker_, null, result_); + return result_; + } + + // ((',' | ',,') argument)* + private static boolean argList_1_1(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "argList_1_1")) return false; + while (true) { + int pos_ = current_position_(builder_); + if (!argList_1_1_0(builder_, level_ + 1)) break; + if (!empty_element_parsed_guard_(builder_, "argList_1_1", pos_)) break; + } + return true; + } + + // (',' | ',,') argument + private static boolean argList_1_1_0(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "argList_1_1_0")) return false; + boolean result_; + Marker marker_ = enter_section_(builder_); + result_ = argList_1_1_0_0(builder_, level_ + 1); + result_ = result_ && argument(builder_, level_ + 1); + exit_section_(builder_, marker_, null, result_); + return result_; + } + + // ',' | ',,' + private static boolean argList_1_1_0_0(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "argList_1_1_0_0")) return false; boolean result_; result_ = consumeToken(builder_, COMMA); if (!result_) result_ = consumeToken(builder_, DOUBLE_COMMA); @@ -95,8 +150,8 @@ public class JaiParser implements PsiParser, LightPsiParser { } // [','] - private static boolean argList_2(PsiBuilder builder_, int level_) { - if (!recursion_guard_(builder_, level_, "argList_2")) return false; + private static boolean argList_1_2(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "argList_1_2")) return false; consumeToken(builder_, COMMA); return true; } @@ -1510,6 +1565,35 @@ public class JaiParser implements PsiParser, LightPsiParser { return true; } + /* ********************************************************** */ + // ('break' | 'continue' | 'remove') [expr] + static boolean jumpArgument_(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "jumpArgument_")) return false; + boolean result_; + Marker marker_ = enter_section_(builder_); + result_ = jumpArgument__0(builder_, level_ + 1); + result_ = result_ && jumpArgument__1(builder_, level_ + 1); + exit_section_(builder_, marker_, null, result_); + return result_; + } + + // 'break' | 'continue' | 'remove' + private static boolean jumpArgument__0(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "jumpArgument__0")) return false; + boolean result_; + result_ = consumeToken(builder_, KW_BREAK); + if (!result_) result_ = consumeToken(builder_, KW_CONTINUE); + if (!result_) result_ = consumeToken(builder_, KW_REMOVE); + return result_; + } + + // [expr] + private static boolean jumpArgument__1(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "jumpArgument__1")) return false; + expr(builder_, level_ + 1, -1); + return true; + } + /* ********************************************************** */ // 'if' | 'for' | 'case' | 'enum' | 'cast' | 'union' | 'struct' // | 'remove' | 'inline' | 'context' | 'operator' | 'interface' @@ -1576,7 +1660,7 @@ public class JaiParser implements PsiParser, LightPsiParser { } /* ********************************************************** */ - // <> memberName '=' (block | expr) + // <> memberName '=' (block | jumpArgument_ | expr) public static boolean namedArgument(PsiBuilder builder_, int level_) { if (!recursion_guard_(builder_, level_, "namedArgument")) return false; boolean result_; @@ -1589,11 +1673,12 @@ public class JaiParser implements PsiParser, LightPsiParser { return result_; } - // block | expr + // block | jumpArgument_ | expr private static boolean namedArgument_3(PsiBuilder builder_, int level_) { if (!recursion_guard_(builder_, level_, "namedArgument_3")) return false; boolean result_; result_ = block(builder_, level_ + 1); + if (!result_) result_ = jumpArgument_(builder_, level_ + 1); if (!result_) result_ = expr(builder_, level_ + 1, -1); return result_; } @@ -1636,7 +1721,7 @@ public class JaiParser implements PsiParser, LightPsiParser { // | '&' | '|' | '^' | '~' | '<<' | '>>' | '<<<' | '>>>' // | '+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' // | '<<=' | '>>=' | '<<<=' | '>>>=' - // | '[' ']' '=' | '[' ']' + // | '*' '[' ']' | '[' ']' '=' | '[' ']' public static boolean operatorSymbol(PsiBuilder builder_, int level_) { if (!recursion_guard_(builder_, level_, "operatorSymbol")) return false; boolean result_; @@ -1672,6 +1757,7 @@ public class JaiParser implements PsiParser, LightPsiParser { if (!result_) result_ = consumeToken(builder_, SHR_EQ); if (!result_) result_ = consumeToken(builder_, ROL_EQ); if (!result_) result_ = consumeToken(builder_, ROR_EQ); + if (!result_) result_ = parseTokens(builder_, 0, STAR, LBRACKET, RBRACKET); if (!result_) result_ = parseTokens(builder_, 0, LBRACKET, RBRACKET, EQ); if (!result_) result_ = parseTokens(builder_, 0, LBRACKET, RBRACKET); exit_section_(builder_, level_, marker_, result_, false, null); @@ -2176,6 +2262,18 @@ public class JaiParser implements PsiParser, LightPsiParser { return true; } + /* ********************************************************** */ + // '---' | <> + static boolean returnDefault_(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "returnDefault_")) return false; + boolean result_; + Marker marker_ = enter_section_(builder_); + result_ = consumeToken(builder_, TRIPLE_MINUS); + if (!result_) result_ = exprNoAssign(builder_, level_ + 1); + exit_section_(builder_, marker_, null, result_); + return result_; + } + /* ********************************************************** */ // '(' [returnItem (',' returnItem)*] ')' public static boolean returnGroup(PsiBuilder builder_, int level_) { @@ -2231,31 +2329,85 @@ public class JaiParser implements PsiParser, LightPsiParser { } /* ********************************************************** */ - // [<> IDENT ':'] <> + // <> IDENT (':' [<>] ['=' returnDefault_] | ':=' returnDefault_) + // | <> public static boolean returnItem(PsiBuilder builder_, int level_) { if (!recursion_guard_(builder_, level_, "returnItem")) return false; boolean result_; Marker marker_ = enter_section_(builder_, level_, _NONE_, RETURN_ITEM, ""); result_ = returnItem_0(builder_, level_ + 1); - result_ = result_ && exprNoAssign(builder_, level_ + 1); + if (!result_) result_ = exprNoAssign(builder_, level_ + 1); exit_section_(builder_, level_, marker_, result_, false, null); return result_; } - // [<> IDENT ':'] + // <> IDENT (':' [<>] ['=' returnDefault_] | ':=' returnDefault_) private static boolean returnItem_0(PsiBuilder builder_, int level_) { if (!recursion_guard_(builder_, level_, "returnItem_0")) return false; - returnItem_0_0(builder_, level_ + 1); - return true; - } - - // <> IDENT ':' - private static boolean returnItem_0_0(PsiBuilder builder_, int level_) { - if (!recursion_guard_(builder_, level_, "returnItem_0_0")) return false; boolean result_; Marker marker_ = enter_section_(builder_); result_ = isParamNameAhead(builder_, level_ + 1); - result_ = result_ && consumeTokens(builder_, 0, IDENT, COLON); + result_ = result_ && consumeToken(builder_, IDENT); + result_ = result_ && returnItem_0_2(builder_, level_ + 1); + exit_section_(builder_, marker_, null, result_); + return result_; + } + + // ':' [<>] ['=' returnDefault_] | ':=' returnDefault_ + private static boolean returnItem_0_2(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "returnItem_0_2")) return false; + boolean result_; + Marker marker_ = enter_section_(builder_); + result_ = returnItem_0_2_0(builder_, level_ + 1); + if (!result_) result_ = returnItem_0_2_1(builder_, level_ + 1); + exit_section_(builder_, marker_, null, result_); + return result_; + } + + // ':' [<>] ['=' returnDefault_] + private static boolean returnItem_0_2_0(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "returnItem_0_2_0")) return false; + boolean result_; + Marker marker_ = enter_section_(builder_); + result_ = consumeToken(builder_, COLON); + result_ = result_ && returnItem_0_2_0_1(builder_, level_ + 1); + result_ = result_ && returnItem_0_2_0_2(builder_, level_ + 1); + exit_section_(builder_, marker_, null, result_); + return result_; + } + + // [<>] + private static boolean returnItem_0_2_0_1(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "returnItem_0_2_0_1")) return false; + exprNoAssign(builder_, level_ + 1); + return true; + } + + // ['=' returnDefault_] + private static boolean returnItem_0_2_0_2(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "returnItem_0_2_0_2")) return false; + returnItem_0_2_0_2_0(builder_, level_ + 1); + return true; + } + + // '=' returnDefault_ + private static boolean returnItem_0_2_0_2_0(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "returnItem_0_2_0_2_0")) return false; + boolean result_; + Marker marker_ = enter_section_(builder_); + result_ = consumeToken(builder_, EQ); + result_ = result_ && returnDefault_(builder_, level_ + 1); + exit_section_(builder_, marker_, null, result_); + return result_; + } + + // ':=' returnDefault_ + private static boolean returnItem_0_2_1(PsiBuilder builder_, int level_) { + if (!recursion_guard_(builder_, level_, "returnItem_0_2_1")) return false; + boolean result_; + Marker marker_ = enter_section_(builder_); + result_ = consumeToken(builder_, COLON_EQ); + result_ = result_ && returnDefault_(builder_, level_ + 1); exit_section_(builder_, marker_, null, result_); return result_; } @@ -2672,7 +2824,7 @@ public class JaiParser implements PsiParser, LightPsiParser { } /* ********************************************************** */ - // 'using' [directiveFlag_ ['(' [argList] ')']] + // 'using' [directiveFlag_ [<>]] static boolean usingModifier_(PsiBuilder builder_, int level_) { if (!recursion_guard_(builder_, level_, "usingModifier_")) return false; if (!nextTokenIs(builder_, KW_USING)) return false; @@ -2684,14 +2836,14 @@ public class JaiParser implements PsiParser, LightPsiParser { return result_; } - // [directiveFlag_ ['(' [argList] ')']] + // [directiveFlag_ [<>]] private static boolean usingModifier__1(PsiBuilder builder_, int level_) { if (!recursion_guard_(builder_, level_, "usingModifier__1")) return false; usingModifier__1_0(builder_, level_ + 1); return true; } - // directiveFlag_ ['(' [argList] ')'] + // directiveFlag_ [<>] private static boolean usingModifier__1_0(PsiBuilder builder_, int level_) { if (!recursion_guard_(builder_, level_, "usingModifier__1_0")) return false; boolean result_; @@ -2702,29 +2854,10 @@ public class JaiParser implements PsiParser, LightPsiParser { return result_; } - // ['(' [argList] ')'] + // [<>] private static boolean usingModifier__1_0_1(PsiBuilder builder_, int level_) { if (!recursion_guard_(builder_, level_, "usingModifier__1_0_1")) return false; - usingModifier__1_0_1_0(builder_, level_ + 1); - return true; - } - - // '(' [argList] ')' - private static boolean usingModifier__1_0_1_0(PsiBuilder builder_, int level_) { - if (!recursion_guard_(builder_, level_, "usingModifier__1_0_1_0")) return false; - boolean result_; - Marker marker_ = enter_section_(builder_); - result_ = consumeToken(builder_, LPAREN); - result_ = result_ && usingModifier__1_0_1_0_1(builder_, level_ + 1); - result_ = result_ && consumeToken(builder_, RPAREN); - exit_section_(builder_, marker_, null, result_); - return result_; - } - - // [argList] - private static boolean usingModifier__1_0_1_0_1(PsiBuilder builder_, int level_) { - if (!recursion_guard_(builder_, level_, "usingModifier__1_0_1_0_1")) return false; - argList(builder_, level_ + 1); + exprNoAssign(builder_, level_ + 1); return true; } diff --git a/src/main/gen/dev/hgh/jai/psi/JaiDeclaration.java b/src/main/gen/dev/hgh/jai/psi/JaiDeclaration.java index fdb1bc6..3df0b4c 100644 --- a/src/main/gen/dev/hgh/jai/psi/JaiDeclaration.java +++ b/src/main/gen/dev/hgh/jai/psi/JaiDeclaration.java @@ -7,9 +7,6 @@ import com.intellij.psi.PsiElement; public interface JaiDeclaration extends PsiElement { - @NotNull - List getArgumentList(); - @NotNull List getBlockList(); diff --git a/src/main/gen/dev/hgh/jai/psi/impl/JaiDeclarationImpl.java b/src/main/gen/dev/hgh/jai/psi/impl/JaiDeclarationImpl.java index 3668771..5649d9e 100644 --- a/src/main/gen/dev/hgh/jai/psi/impl/JaiDeclarationImpl.java +++ b/src/main/gen/dev/hgh/jai/psi/impl/JaiDeclarationImpl.java @@ -27,12 +27,6 @@ public class JaiDeclarationImpl extends ASTWrapperPsiElement implements JaiDecla else super.accept(visitor); } - @Override - @NotNull - public List getArgumentList() { - return PsiTreeUtil.getChildrenOfTypeAsList(this, JaiArgument.class); - } - @Override @NotNull public List getBlockList() { diff --git a/src/main/grammar/Jai.bnf b/src/main/grammar/Jai.bnf index 5a907f2..fa1f6c6 100644 --- a/src/main/grammar/Jai.bnf +++ b/src/main/grammar/Jai.bnf @@ -220,7 +220,7 @@ declaration ::= <> declModifier_* declNames declTail_ semiWi private declModifier_ ::= <> directiveFlag_* block | <> directiveFlag_* [parenExpr | STRING] | usingModifier_ -private usingModifier_ ::= 'using' [directiveFlag_ ['(' [argList] ')']] +private usingModifier_ ::= 'using' [directiveFlag_ [<>]] declNames ::= declName (',' declName)* declName ::= ['`'] IDENT private declTail_ ::= ':' [<>] procModifier* [',' assignTargets_] [('=' | ':') initializerList_] @@ -239,7 +239,7 @@ operatorSymbol ::= '+' | '-' | '*' | '/' | '%' | '==' | '!=' | '<' | '>' | '<=' | '&' | '|' | '^' | '~' | '<<' | '>>' | '<<<' | '>>>' | '+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' | '<<=' | '>>=' | '<<<=' | '>>>=' - | '[' ']' '=' | '[' ']' + | '*' '[' ']' | '[' ']' '=' | '[' ']' // -------------------------------------------------------------------- expressions @@ -327,9 +327,12 @@ private keywordAsName_ ::= 'if' | 'for' | 'case' | 'enum' | 'cast' | 'union' | ' | 'continue' | 'then' | 'else' | 'while' | 'null' | 'true' | 'false' // `,,` introduces an inline context change: `join(parts,, allocator = temp)`. -private argList ::= argument ((',' | ',,') argument)* [','] +private argList ::= ',,' argument ((',' | ',,') argument)* + | argument ((',' | ',,') argument)* [','] argument ::= '---' | namedArgument | expr -namedArgument ::= <> memberName '=' (block | expr) +namedArgument ::= <> memberName '=' (block | jumpArgument_ | expr) +// `break=break bucket` supplies the loop-exit code to a for_expansion. +private jumpArgument_ ::= ('break' | 'continue' | 'remove') [expr] primaryExpr ::= procLiteralExpr | structExpr @@ -382,7 +385,12 @@ private paramTypeTail_ ::= ':' [<>] ['=' initializer] private returnTypes_ ::= <> returnGroup | returnItem (',' returnItem)* returnGroup ::= '(' [returnItem (',' returnItem)*] ')' -returnItem ::= [<> IDENT ':'] <> +returnItem ::= <> IDENT (':' [<>] ['=' returnDefault_] | ':=' returnDefault_) + | <> +// Deliberately not `initializer`: that rule creates its own PSI element, and doing so +// here makes the enclosing procLiteralExpr end early, so `-> a: int = 1 { }` loses its +// body. A return default cannot be an assignment either way. +private returnDefault_ ::= '---' | <> // `#foreign lib`, `#modify { ... }`, `#c_call`, `#expand`, … // `#foreign lib "symbol"`, `#modify { ... }`, `#c_call`, `#expand`, `#align 8` — the diff --git a/src/main/kotlin/dev/hgh/jai/parser/JaiParserUtil.kt b/src/main/kotlin/dev/hgh/jai/parser/JaiParserUtil.kt index 80e36aa..6005d4e 100644 --- a/src/main/kotlin/dev/hgh/jai/parser/JaiParserUtil.kt +++ b/src/main/kotlin/dev/hgh/jai/parser/JaiParserUtil.kt @@ -347,8 +347,8 @@ object JaiParserUtil : GeneratedParserUtilBase() { inPrefix = true } - T.LPAREN -> { - i = skipBalancedParens(b, i) + T.LPAREN, T.LBRACKET, T.BEGIN_ARRAY_LITERAL, T.BEGIN_STRUCT_LITERAL -> { + i = skipBalancedGroup(b, i) if (i < 0) return false } @@ -377,8 +377,12 @@ object JaiParserUtil : GeneratedParserUtilBase() { } } - /** Index just past the `)` matching the `(` at [start], or -1 if unbalanced. */ - private fun skipBalancedParens( + /** + * Index just past the closer matching the bracket at [start], or -1 if unbalanced. + * Covers `(…)`, `[…]`, `.[…]` and `.{…}`, which all appear in declaration + * prefixes: `#overlay (x) using …`, `using,except .["x", "y"] …`. + */ + private fun skipBalancedGroup( b: PsiBuilder, start: Int, ): Int { @@ -386,11 +390,11 @@ object JaiParserUtil : GeneratedParserUtilBase() { var i = start while (i - start <= MAX_LOOKAHEAD) { when (b.lookAhead(i) ?: return -1) { - T.LPAREN -> { + T.LPAREN, T.LBRACKET, T.BEGIN_ARRAY_LITERAL, T.LBRACE, T.BEGIN_STRUCT_LITERAL -> { depth++ } - T.RPAREN -> { + T.RPAREN, T.RBRACKET, T.RBRACE -> { depth-- if (depth == 0) return i + 1 } diff --git a/src/test/kotlin/dev/hgh/jai/parser/DebugParseTest.kt b/src/test/kotlin/dev/hgh/jai/parser/DebugParseTest.kt index c336e4b..926ea75 100644 --- a/src/test/kotlin/dev/hgh/jai/parser/DebugParseTest.kt +++ b/src/test/kotlin/dev/hgh/jai/parser/DebugParseTest.kt @@ -15,12 +15,11 @@ import java.io.File class DebugParseTest : BasePlatformTestCase() { private val snippets = listOf( - "#scope_file\n\n\nbig_endian :: () -> bool {\n return true;\n}\n", - "S :: struct {\n #as using,except(vtable) iunknown: IUnknown;\n}\n", - "using,except(FT_Init) new_module :: #import \"freetype\";\n", - "operator []= :: inline (a: *B, i: int, v: bool) { }\n", - "inotify_init :: (flags: s32 = 0) -> s32 #foreign libc \"inotify_init1\";\n", - "F :: (a: *L) -> E #foreign ft #deprecated \"msg\";\n", + "f :: () -> a: int = 1;\n", + "f :: () -> a: int = 1 { }\n", + "f :: () -> a: int = xx { }\n", + "f :: () -> a: int = 1 #foreign lib;\n", + "g :: () { x := 1; }\n", ) /** Corpus files to dump the first error of. */ @@ -53,11 +52,17 @@ class DebugParseTest : BasePlatformTestCase() { val error = firstError(file) if (error == null) { println("ok ${text.replace("\n", "\\n")}") + if (System.getProperty("jai.debug.tree") != null) { + println(com.intellij.psi.impl.DebugUtil.psiToString(file, true, false)) + } } else { failures++ val at = text.substring(error.textOffset).take(28).replace("\n", "\\n") println("FAIL ${text.replace("\n", "\\n")}") println(" ${error.errorDescription} | at '$at'") + if (System.getProperty("jai.debug.tree") != null) { + println(com.intellij.psi.impl.DebugUtil.psiToString(file, true, false)) + } } } println("$failures/${snippets.size} snippets failed") diff --git a/src/test/kotlin/dev/hgh/jai/parser/JaiCorpusParserTest.kt b/src/test/kotlin/dev/hgh/jai/parser/JaiCorpusParserTest.kt index 218ffdc..1cbded9 100644 --- a/src/test/kotlin/dev/hgh/jai/parser/JaiCorpusParserTest.kt +++ b/src/test/kotlin/dev/hgh/jai/parser/JaiCorpusParserTest.kt @@ -106,7 +106,7 @@ class JaiCorpusParserTest : BasePlatformTestCase() { private companion object { /** Ratchet. Raise it as the grammar improves; never lower it. */ - const val MIN_CLEAN_FILES = 640 + const val MIN_CLEAN_FILES = 645 const val REPORTED_ERRORS = 25 const val REPORTED_KINDS = 20 const val GOT_WIDTH = 14