diff --git a/src/main/gen/dev/hgh/jai/parser/JaiParser.java b/src/main/gen/dev/hgh/jai/parser/JaiParser.java
index 2554f88..fe8580d 100644
--- a/src/main/gen/dev/hgh/jai/parser/JaiParser.java
+++ b/src/main/gen/dev/hgh/jai/parser/JaiParser.java
@@ -714,6 +714,7 @@ public class JaiParser implements PsiParser, LightPsiParser {
// | <
> directiveFlag_* [<>] [STRING]
// | <> directiveFlag_* [<>] [STRING]
// | <> directiveFlag_* ['(' [argList] ')'] [block | expr]
+ // | <> directiveFlag_* (block | statement)
// | <> expr [expr]
// | <> '(' [paramList] ')' ['(' [paramList] ')'] [block]
// | <> directiveFlag_* block
@@ -732,6 +733,7 @@ public class JaiParser implements PsiParser, LightPsiParser {
if (!result_) result_ = directiveExpr_6(builder_, level_ + 1);
if (!result_) result_ = directiveExpr_7(builder_, level_ + 1);
if (!result_) result_ = directiveExpr_8(builder_, level_ + 1);
+ if (!result_) result_ = directiveExpr_9(builder_, level_ + 1);
exit_section_(builder_, level_, marker_, result_, false, null);
return result_;
}
@@ -942,111 +944,120 @@ public class JaiParser implements PsiParser, LightPsiParser {
return result_;
}
- // <> expr [expr]
+ // <> directiveFlag_* (block | statement)
private static boolean directiveExpr_4(PsiBuilder builder_, int level_) {
if (!recursion_guard_(builder_, level_, "directiveExpr_4")) return false;
boolean result_;
Marker marker_ = enter_section_(builder_);
- result_ = dir(builder_, level_ + 1, "#assert");
- result_ = result_ && expr(builder_, level_ + 1, -1);
+ result_ = dir(builder_, level_ + 1, "#code");
+ result_ = result_ && directiveExpr_4_1(builder_, level_ + 1);
result_ = result_ && directiveExpr_4_2(builder_, level_ + 1);
exit_section_(builder_, marker_, null, result_);
return result_;
}
- // [expr]
+ // directiveFlag_*
+ private static boolean directiveExpr_4_1(PsiBuilder builder_, int level_) {
+ if (!recursion_guard_(builder_, level_, "directiveExpr_4_1")) return false;
+ while (true) {
+ int pos_ = current_position_(builder_);
+ if (!directiveFlag_(builder_, level_ + 1)) break;
+ if (!empty_element_parsed_guard_(builder_, "directiveExpr_4_1", pos_)) break;
+ }
+ return true;
+ }
+
+ // block | statement
private static boolean directiveExpr_4_2(PsiBuilder builder_, int level_) {
if (!recursion_guard_(builder_, level_, "directiveExpr_4_2")) return false;
+ boolean result_;
+ result_ = block(builder_, level_ + 1);
+ if (!result_) result_ = statement(builder_, level_ + 1);
+ return result_;
+ }
+
+ // <> expr [expr]
+ private static boolean directiveExpr_5(PsiBuilder builder_, int level_) {
+ if (!recursion_guard_(builder_, level_, "directiveExpr_5")) return false;
+ boolean result_;
+ Marker marker_ = enter_section_(builder_);
+ result_ = dir(builder_, level_ + 1, "#assert");
+ result_ = result_ && expr(builder_, level_ + 1, -1);
+ result_ = result_ && directiveExpr_5_2(builder_, level_ + 1);
+ exit_section_(builder_, marker_, null, result_);
+ return result_;
+ }
+
+ // [expr]
+ private static boolean directiveExpr_5_2(PsiBuilder builder_, int level_) {
+ if (!recursion_guard_(builder_, level_, "directiveExpr_5_2")) return false;
expr(builder_, level_ + 1, -1);
return true;
}
// <> '(' [paramList] ')' ['(' [paramList] ')'] [block]
- private static boolean directiveExpr_5(PsiBuilder builder_, int level_) {
- if (!recursion_guard_(builder_, level_, "directiveExpr_5")) return false;
+ private static boolean directiveExpr_6(PsiBuilder builder_, int level_) {
+ if (!recursion_guard_(builder_, level_, "directiveExpr_6")) return false;
boolean result_;
Marker marker_ = enter_section_(builder_);
result_ = dir(builder_, level_ + 1, "#module_parameters");
result_ = result_ && consumeToken(builder_, LPAREN);
- result_ = result_ && directiveExpr_5_2(builder_, level_ + 1);
+ result_ = result_ && directiveExpr_6_2(builder_, level_ + 1);
result_ = result_ && consumeToken(builder_, RPAREN);
- result_ = result_ && directiveExpr_5_4(builder_, level_ + 1);
- result_ = result_ && directiveExpr_5_5(builder_, level_ + 1);
+ result_ = result_ && directiveExpr_6_4(builder_, level_ + 1);
+ result_ = result_ && directiveExpr_6_5(builder_, level_ + 1);
exit_section_(builder_, marker_, null, result_);
return result_;
}
// [paramList]
- private static boolean directiveExpr_5_2(PsiBuilder builder_, int level_) {
- if (!recursion_guard_(builder_, level_, "directiveExpr_5_2")) return false;
+ private static boolean directiveExpr_6_2(PsiBuilder builder_, int level_) {
+ if (!recursion_guard_(builder_, level_, "directiveExpr_6_2")) return false;
paramList(builder_, level_ + 1);
return true;
}
// ['(' [paramList] ')']
- private static boolean directiveExpr_5_4(PsiBuilder builder_, int level_) {
- if (!recursion_guard_(builder_, level_, "directiveExpr_5_4")) return false;
- directiveExpr_5_4_0(builder_, level_ + 1);
+ private static boolean directiveExpr_6_4(PsiBuilder builder_, int level_) {
+ if (!recursion_guard_(builder_, level_, "directiveExpr_6_4")) return false;
+ directiveExpr_6_4_0(builder_, level_ + 1);
return true;
}
// '(' [paramList] ')'
- private static boolean directiveExpr_5_4_0(PsiBuilder builder_, int level_) {
- if (!recursion_guard_(builder_, level_, "directiveExpr_5_4_0")) return false;
+ private static boolean directiveExpr_6_4_0(PsiBuilder builder_, int level_) {
+ if (!recursion_guard_(builder_, level_, "directiveExpr_6_4_0")) return false;
boolean result_;
Marker marker_ = enter_section_(builder_);
result_ = consumeToken(builder_, LPAREN);
- result_ = result_ && directiveExpr_5_4_0_1(builder_, level_ + 1);
+ result_ = result_ && directiveExpr_6_4_0_1(builder_, level_ + 1);
result_ = result_ && consumeToken(builder_, RPAREN);
exit_section_(builder_, marker_, null, result_);
return result_;
}
// [paramList]
- private static boolean directiveExpr_5_4_0_1(PsiBuilder builder_, int level_) {
- if (!recursion_guard_(builder_, level_, "directiveExpr_5_4_0_1")) return false;
+ private static boolean directiveExpr_6_4_0_1(PsiBuilder builder_, int level_) {
+ if (!recursion_guard_(builder_, level_, "directiveExpr_6_4_0_1")) return false;
paramList(builder_, level_ + 1);
return true;
}
// [block]
- private static boolean directiveExpr_5_5(PsiBuilder builder_, int level_) {
- if (!recursion_guard_(builder_, level_, "directiveExpr_5_5")) return false;
+ private static boolean directiveExpr_6_5(PsiBuilder builder_, int level_) {
+ if (!recursion_guard_(builder_, level_, "directiveExpr_6_5")) return false;
block(builder_, level_ + 1);
return true;
}
// <> directiveFlag_* block
- private static boolean directiveExpr_6(PsiBuilder builder_, int level_) {
- if (!recursion_guard_(builder_, level_, "directiveExpr_6")) return false;
- boolean result_;
- Marker marker_ = enter_section_(builder_);
- result_ = dirWithBlock(builder_, level_ + 1);
- result_ = result_ && directiveExpr_6_1(builder_, level_ + 1);
- result_ = result_ && block(builder_, level_ + 1);
- exit_section_(builder_, marker_, null, result_);
- return result_;
- }
-
- // directiveFlag_*
- private static boolean directiveExpr_6_1(PsiBuilder builder_, int level_) {
- if (!recursion_guard_(builder_, level_, "directiveExpr_6_1")) return false;
- while (true) {
- int pos_ = current_position_(builder_);
- if (!directiveFlag_(builder_, level_ + 1)) break;
- if (!empty_element_parsed_guard_(builder_, "directiveExpr_6_1", pos_)) break;
- }
- return true;
- }
-
- // <> directiveFlag_* [expr]
private static boolean directiveExpr_7(PsiBuilder builder_, int level_) {
if (!recursion_guard_(builder_, level_, "directiveExpr_7")) return false;
boolean result_;
Marker marker_ = enter_section_(builder_);
- result_ = dirWithOperand(builder_, level_ + 1);
+ result_ = dirWithBlock(builder_, level_ + 1);
result_ = result_ && directiveExpr_7_1(builder_, level_ + 1);
- result_ = result_ && directiveExpr_7_2(builder_, level_ + 1);
+ result_ = result_ && block(builder_, level_ + 1);
exit_section_(builder_, marker_, null, result_);
return result_;
}
@@ -1062,20 +1073,14 @@ public class JaiParser implements PsiParser, LightPsiParser {
return true;
}
- // [expr]
- private static boolean directiveExpr_7_2(PsiBuilder builder_, int level_) {
- if (!recursion_guard_(builder_, level_, "directiveExpr_7_2")) return false;
- expr(builder_, level_ + 1, -1);
- return true;
- }
-
- // <> directiveFlag_*
+ // <> directiveFlag_* [expr]
private static boolean directiveExpr_8(PsiBuilder builder_, int level_) {
if (!recursion_guard_(builder_, level_, "directiveExpr_8")) return false;
boolean result_;
Marker marker_ = enter_section_(builder_);
- result_ = dirPlain(builder_, level_ + 1);
+ result_ = dirWithOperand(builder_, level_ + 1);
result_ = result_ && directiveExpr_8_1(builder_, level_ + 1);
+ result_ = result_ && directiveExpr_8_2(builder_, level_ + 1);
exit_section_(builder_, marker_, null, result_);
return result_;
}
@@ -1091,6 +1096,35 @@ public class JaiParser implements PsiParser, LightPsiParser {
return true;
}
+ // [expr]
+ private static boolean directiveExpr_8_2(PsiBuilder builder_, int level_) {
+ if (!recursion_guard_(builder_, level_, "directiveExpr_8_2")) return false;
+ expr(builder_, level_ + 1, -1);
+ return true;
+ }
+
+ // <> directiveFlag_*
+ private static boolean directiveExpr_9(PsiBuilder builder_, int level_) {
+ if (!recursion_guard_(builder_, level_, "directiveExpr_9")) return false;
+ boolean result_;
+ Marker marker_ = enter_section_(builder_);
+ result_ = dirPlain(builder_, level_ + 1);
+ result_ = result_ && directiveExpr_9_1(builder_, level_ + 1);
+ exit_section_(builder_, marker_, null, result_);
+ return result_;
+ }
+
+ // directiveFlag_*
+ private static boolean directiveExpr_9_1(PsiBuilder builder_, int level_) {
+ if (!recursion_guard_(builder_, level_, "directiveExpr_9_1")) return false;
+ while (true) {
+ int pos_ = current_position_(builder_);
+ if (!directiveFlag_(builder_, level_ + 1)) break;
+ if (!empty_element_parsed_guard_(builder_, "directiveExpr_9_1", pos_)) break;
+ }
+ return true;
+ }
+
/* ********************************************************** */
// <>
static boolean directiveFlag_(PsiBuilder builder_, int level_) {
@@ -1717,16 +1751,19 @@ public class JaiParser implements PsiParser, LightPsiParser {
}
/* ********************************************************** */
- // '+' | '-' | '*' | '/' | '%' | '==' | '!=' | '<' | '>' | '<=' | '>='
+ // '*' '[' ']' | '[' ']' '=' | '[' ']'
+ // | '+' | '-' | '*' | '/' | '%' | '==' | '!=' | '<' | '>' | '<=' | '>='
// | '&' | '|' | '^' | '~' | '<<' | '>>' | '<<<' | '>>>'
// | '+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^='
// | '<<=' | '>>=' | '<<<=' | '>>>='
- // | '*' '[' ']' | '[' ']' '=' | '[' ']'
public static boolean operatorSymbol(PsiBuilder builder_, int level_) {
if (!recursion_guard_(builder_, level_, "operatorSymbol")) return false;
boolean result_;
Marker marker_ = enter_section_(builder_, level_, _NONE_, OPERATOR_SYMBOL, "");
- result_ = consumeToken(builder_, PLUS);
+ result_ = parseTokens(builder_, 0, STAR, LBRACKET, RBRACKET);
+ if (!result_) result_ = parseTokens(builder_, 0, LBRACKET, RBRACKET, EQ);
+ if (!result_) result_ = parseTokens(builder_, 0, LBRACKET, RBRACKET);
+ if (!result_) result_ = consumeToken(builder_, PLUS);
if (!result_) result_ = consumeToken(builder_, MINUS);
if (!result_) result_ = consumeToken(builder_, STAR);
if (!result_) result_ = consumeToken(builder_, SLASH);
@@ -1757,9 +1794,6 @@ 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);
return result_;
}
diff --git a/src/main/gen/dev/hgh/jai/psi/JaiDirectiveExpr.java b/src/main/gen/dev/hgh/jai/psi/JaiDirectiveExpr.java
index ff2915c..488fc02 100644
--- a/src/main/gen/dev/hgh/jai/psi/JaiDirectiveExpr.java
+++ b/src/main/gen/dev/hgh/jai/psi/JaiDirectiveExpr.java
@@ -19,4 +19,7 @@ public interface JaiDirectiveExpr extends JaiExpr {
@NotNull
List getParameterList();
+ @Nullable
+ JaiStatement getStatement();
+
}
diff --git a/src/main/gen/dev/hgh/jai/psi/impl/JaiDirectiveExprImpl.java b/src/main/gen/dev/hgh/jai/psi/impl/JaiDirectiveExprImpl.java
index 2a2e180..8b41feb 100644
--- a/src/main/gen/dev/hgh/jai/psi/impl/JaiDirectiveExprImpl.java
+++ b/src/main/gen/dev/hgh/jai/psi/impl/JaiDirectiveExprImpl.java
@@ -51,4 +51,10 @@ public class JaiDirectiveExprImpl extends JaiExprImpl implements JaiDirectiveExp
return PsiTreeUtil.getChildrenOfTypeAsList(this, JaiParameter.class);
}
+ @Override
+ @Nullable
+ public JaiStatement getStatement() {
+ return findChildByClass(JaiStatement.class);
+ }
+
}
diff --git a/src/main/grammar/Jai.bnf b/src/main/grammar/Jai.bnf
index fa1f6c6..9167719 100644
--- a/src/main/grammar/Jai.bnf
+++ b/src/main/grammar/Jai.bnf
@@ -235,11 +235,13 @@ initializer ::= '---' | expr
// `operator + :: (a: T, b: T) -> T { }`
operatorDeclaration ::= 'operator' operatorSymbol '::' expr semiWithNotes_
-operatorSymbol ::= '+' | '-' | '*' | '/' | '%' | '==' | '!=' | '<' | '>' | '<=' | '>='
+// Longest first: `*` would otherwise match the start of `*[]`, and `[]` the start
+// of `[]=`. Single-token operators cannot collide, since `+=` is one token.
+operatorSymbol ::= '*' '[' ']' | '[' ']' '=' | '[' ']'
+ | '+' | '-' | '*' | '/' | '%' | '==' | '!=' | '<' | '>' | '<=' | '>='
| '&' | '|' | '^' | '~' | '<<' | '>>' | '<<<' | '>>>'
| '+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^='
| '<<=' | '>>=' | '<<<=' | '>>>='
- | '*' '[' ']' | '[' ']' '=' | '[' ']'
// -------------------------------------------------------------------- expressions
@@ -411,6 +413,7 @@ directiveExpr ::= <> directiveFlag_* [expr (',' expr)*] <> directiveFlag_* [<>] [STRING]
| <> directiveFlag_* [<>] [STRING]
| <> directiveFlag_* ['(' [argList] ')'] [block | expr]
+ | <> directiveFlag_* (block | statement)
| <> expr [expr]
| <> '(' [paramList] ')' ['(' [paramList] ')'] [block]
| <> directiveFlag_* block
diff --git a/src/main/kotlin/dev/hgh/jai/parser/JaiParserUtil.kt b/src/main/kotlin/dev/hgh/jai/parser/JaiParserUtil.kt
index 6005d4e..fdd9ac5 100644
--- a/src/main/kotlin/dev/hgh/jai/parser/JaiParserUtil.kt
+++ b/src/main/kotlin/dev/hgh/jai/parser/JaiParserUtil.kt
@@ -237,8 +237,9 @@ object JaiParserUtil : GeneratedParserUtilBase() {
/**
* True if the previous token already closes a construct, so no `;` is needed:
- * `}` ends a block, struct or enum body, and a here-string ends on its own
- * terminator line (`BODY :: #string DONE … DONE`).
+ * `}` ends a block, struct or enum body, a here-string ends on its own terminator
+ * line (`BODY :: #string DONE … DONE`), and a `;` was already taken by a nested
+ * statement (`code :: #code a := 1;`).
*/
@JvmStatic
fun prevEndsConstruct(
@@ -249,7 +250,7 @@ object JaiParserUtil : GeneratedParserUtilBase() {
while (true) {
val t: IElementType = b.rawLookup(i) ?: return false
if (t !== TokenType.WHITE_SPACE && t !== T.LINE_COMMENT && t !== T.BLOCK_COMMENT) {
- return t === T.RBRACE || t === T.HERE_STRING
+ return t === T.RBRACE || t === T.HERE_STRING || t === T.SEMICOLON
}
i--
}
diff --git a/src/test/kotlin/dev/hgh/jai/parser/DebugParseTest.kt b/src/test/kotlin/dev/hgh/jai/parser/DebugParseTest.kt
index 926ea75..04ddfae 100644
--- a/src/test/kotlin/dev/hgh/jai/parser/DebugParseTest.kt
+++ b/src/test/kotlin/dev/hgh/jai/parser/DebugParseTest.kt
@@ -15,24 +15,24 @@ import java.io.File
class DebugParseTest : BasePlatformTestCase() {
private val snippets =
listOf(
- "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",
+ "T :: struct {\n p := .06;\n b: BG;\n b.shape.kind = .ABS;\n b.color = V4.{.00, .10, 1};\n}\n",
+ "T :: struct {\n orientation: enum u8 {\n H :: 0;\n V :: 1;\n }\n\n d: BT;\n}\n",
+ "T :: struct {\n using,except .[\"x\"] orientation: Quaternion;\n}\n",
+ "operator *[] :: (b: *Bucket, index: int) -> *int { return null; }\n",
+ "code :: #code a := Vector3.{1, 1, 1};\n",
)
/** Corpus files to dump the first error of. */
private val corpusFiles =
listOf(
- "modules/Basic/Array.jai",
- "modules/Debug/windows.jai",
- "modules/Simp/backend/gl.jai",
- "modules/GetRect/system/occlusion.jai",
- "modules/POSIX/compare_bindings.jai",
- "modules/Android/Toolchain/apk.jai",
- "modules/Curl/examples/ftp.jai",
- "modules/Objective_C/LightweightRenderingView/module.jai",
+ "modules/GetRect/widgets/color_picker.jai",
+ "modules/GetRect/widgets/slidable_region.jai",
+ "how_to/094_array_operators.jai",
+ "how_to/044_using_advanced/main.jai",
+ "how_to/630_compiler_get_nodes.jai",
+ "modules/Bucket_Array.jai",
+ "modules/Android/Toolchain/adb.jai",
+ "modules/Hash_Table.jai",
)
fun testCorpusFiles() {
@@ -71,14 +71,25 @@ class DebugParseTest : BasePlatformTestCase() {
private fun dumpFile(file: File) {
val text = file.readText()
val psi = PsiFileFactory.getInstance(project).createFileFromText(file.name, JaiLanguage, text)
- val error = firstError(psi)
- if (error == null) {
+ val errors = mutableListOf()
+ psi.accept(
+ object : PsiRecursiveElementWalkingVisitor() {
+ override fun visitErrorElement(element: PsiErrorElement) {
+ errors.add(element)
+ super.visitErrorElement(element)
+ }
+ },
+ )
+ if (errors.isEmpty()) {
println("${file.name}: clean")
return
}
- val line = text.substring(0, error.textOffset).count { it == '\n' } + 1
- println("${file.name}:$line: ${error.errorDescription}")
- println(" at '${text.substring(error.textOffset).take(80).replace("\n", "\\n")}'")
+ println("${file.name}: ${errors.size} error(s)")
+ for (error in errors.take(6)) {
+ val line = text.substring(0, error.textOffset).count { it == '\n' } + 1
+ println(" :$line ${error.errorDescription}")
+ println(" at '${text.substring(error.textOffset).take(70).replace("\n", "\\n")}'")
+ }
}
private fun firstError(file: PsiFile): PsiErrorElement? {
diff --git a/src/test/kotlin/dev/hgh/jai/parser/JaiCorpusParserTest.kt b/src/test/kotlin/dev/hgh/jai/parser/JaiCorpusParserTest.kt
index 1cbded9..f416944 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 = 645
+ const val MIN_CLEAN_FILES = 650
const val REPORTED_ERRORS = 25
const val REPORTED_KINDS = 20
const val GOT_WIDTH = 14