diff --git a/web-view-components-compiler/src/main/antlr/WebViewComponentsLexerBase.g4 b/web-view-components-compiler/src/main/antlr/WebViewComponentsLexerBase.g4 index ec068ba..a9f56dc 100644 --- a/web-view-components-compiler/src/main/antlr/WebViewComponentsLexerBase.g4 +++ b/web-view-components-compiler/src/main/antlr/WebViewComponentsLexerBase.g4 @@ -112,11 +112,11 @@ PreambleOpen ; ComponentOpen - : LT -> pushMode(IN_TAG) + : LT -> pushMode(TAG_START) ; ClosingComponentOpen - : LT FS -> pushMode(IN_TAG) + : LT FS -> pushMode(TAG_START) ; EqualsScriptletOpen @@ -159,34 +159,87 @@ RawText ; // ---------------------------------------- -mode IN_TAG; +mode TAG_START; -ComponentSelfClose - : FS GT -> popMode +FragmentClose + : GT -> popMode ; +TypedIdentifier + : ( PackageIdentifier DOT )* ClassIdentifier ( DOT ClassIdentifier )* -> mode(IN_TAG) + ; + +fragment +PackageIdentifier + : PackageIdentifierStartChar PackageIdentifierChar* + ; + +fragment +PackageIdentifierStartChar + : [\p{Ll}] + ; + +fragment +PackageIdentifierChar + : [\p{L}_0-9] + ; + +fragment +ClassIdentifier + : ClassIdentifierStartChar ClassIdentifierChar* + ; + +fragment +ClassIdentifierStartChar + : [\p{Lu}] + ; + +fragment +ClassIdentifierChar + : [\p{L}_0-9] + ; + +StringIdentifier + : StringIdentifierStartChar StringIdentifierChar* -> mode(IN_TAG) + ; + +fragment +StringIdentifierStartChar + : [\p{Ll}] + ; + +fragment +StringIdentifierChar + : [-_0-9\p{L}] + ; + +// ---------------------------------------- +mode IN_TAG; + ComponentClose : GT -> popMode ; -Identifier - : IdentifierStartChar IdentifierChar* - ; - -IdentifierStartChar - : ~[.] { isIdentifierStartChar(this.getCurrentChar()) }? - ; - -IdentifierChar - : ~[.] { isIdentifierChar(this.getCurrentChar()) }? +ComponentSelfClose + : FS GT -> popMode ; ConstructorOpen : LP { this.enterConstructor(); } ; -Dot - : DOT +AttributeIdentifier + : AttributeIdentifierStartChar AttributeIdentifierChar* + ; + +fragment +AttributeIdentifierStartChar + : [\p{L}_$] + ; + +fragment +AttributeIdentifierChar + : [\p{L}_$0-9] ; Equals diff --git a/web-view-components-compiler/src/main/antlr/WebViewComponentsParser.g4 b/web-view-components-compiler/src/main/antlr/WebViewComponentsParser.g4 index 1442da3..32fe70c 100644 --- a/web-view-components-compiler/src/main/antlr/WebViewComponentsParser.g4 +++ b/web-view-components-compiler/src/main/antlr/WebViewComponentsParser.g4 @@ -67,7 +67,7 @@ componentArgs ; componentType - : Identifier ( Dot Identifier )* + : TypedIdentifier | StringIdentifier ; componentConstructor @@ -79,11 +79,11 @@ attr ; keyValueAttr - : Identifier Equals value + : AttributeIdentifier Equals value ; booleanAttr - : Identifier + : AttributeIdentifier ; value diff --git a/web-view-components-compiler/src/main/java/groowt/view/component/web/analysis/MismatchedComponentTypeAnalysis.kt b/web-view-components-compiler/src/main/java/groowt/view/component/web/analysis/MismatchedComponentTypeAnalysis.kt index 9e62556..9e6d778 100644 --- a/web-view-components-compiler/src/main/java/groowt/view/component/web/analysis/MismatchedComponentTypeAnalysis.kt +++ b/web-view-components-compiler/src/main/java/groowt/view/component/web/analysis/MismatchedComponentTypeAnalysis.kt @@ -1,17 +1,25 @@ @file:JvmName("MismatchedComponentTypeAnalysis") package groowt.view.component.web.analysis +import groowt.view.component.web.WebViewComponentBugError import groowt.view.component.web.antlr.WebViewComponentsParser.ComponentTypeContext import groowt.view.component.web.antlr.WebViewComponentsParser.ComponentWithChildrenContext import groowt.view.component.web.util.SourcePosition import org.antlr.v4.runtime.ParserRuleContext import org.antlr.v4.runtime.Token import org.antlr.v4.runtime.tree.ParseTree -import org.antlr.v4.runtime.tree.TerminalNode -private fun getIdentifiers( - componentTypeContext: ComponentTypeContext -): List = componentTypeContext.Identifier().map(TerminalNode::getSymbol) +private fun getIdentifiers(ctx: ComponentTypeContext): Token { + val typedIdentifier = ctx.TypedIdentifier() + if (typedIdentifier != null) { + return typedIdentifier.symbol + } + val stringIdentifier = ctx.StringIdentifier() + if (stringIdentifier != null) { + return stringIdentifier.symbol + } + throw WebViewComponentBugError("Could not determine identifier type: $ctx") +} private fun getErrorMessage( openType: ComponentTypeContext, @@ -20,20 +28,8 @@ private fun getErrorMessage( "Found '${openType.text}' at ${SourcePosition.formatStartOfTokenLong(openType.start)} " + "and '${closingType.text}' at ${SourcePosition.formatStartOfTokenLong(closingType.start)}." -private fun test( - openIdentifiers: List, - closingIdentifiers: List -): Boolean { - if (openIdentifiers.size != closingIdentifiers.size) { - return false - } - openIdentifiers.zip(closingIdentifiers).forEach { (openIdentifier, closingIdentifier) -> - if (!openIdentifier.text.equals(closingIdentifier.text)) { - return false - } - } - return true -} +private fun test(openIdentifiers: Token, closingIdentifiers: Token): Boolean = + openIdentifiers.text.equals(closingIdentifiers.text) private fun doCheck(tree: ParseTree, destination: MutableList) { if (tree is ParserRuleContext) { @@ -43,9 +39,9 @@ private fun doCheck(tree: ParseTree, destination: MutableList = ArrayList() + + fun getErrors(): List = this.errors + + override fun syntaxError( + recognizer: Recognizer<*, *>, + offendingSymbol: Any?, + line: Int, + charPositionInLine: Int, + msg: String, + e: RecognitionException + ) { + if (e is LexerNoViableAltException) { + val sourcePosition = SourcePosition(line, charPositionInLine + 1) + val lexerError = LexerError(LexerErrorType.NO_VIABLE_ALTERNATIVE, sourcePosition) + errors.add(lexerError) + } else { + throw e + } + } + + override fun reportAmbiguity( + recognizer: Parser?, + dfa: DFA?, + startIndex: Int, + stopIndex: Int, + exact: Boolean, + ambigAlts: BitSet?, + configs: ATNConfigSet? + ) { + throw UnsupportedOperationException() + } + + override fun reportAttemptingFullContext( + recognizer: Parser?, + dfa: DFA?, + startIndex: Int, + stopIndex: Int, + conflictingAlts: BitSet?, + configs: ATNConfigSet? + ) { + throw UnsupportedOperationException() + } + + override fun reportContextSensitivity( + recognizer: Parser?, + dfa: DFA?, + startIndex: Int, + stopIndex: Int, + prediction: Int, + configs: ATNConfigSet? + ) { + throw UnsupportedOperationException() + } + +} diff --git a/web-view-components-compiler/src/main/java/groowt/view/component/web/antlr/LexerErrorType.kt b/web-view-components-compiler/src/main/java/groowt/view/component/web/antlr/LexerErrorType.kt new file mode 100644 index 0000000..85a3683 --- /dev/null +++ b/web-view-components-compiler/src/main/java/groowt/view/component/web/antlr/LexerErrorType.kt @@ -0,0 +1,5 @@ +package groowt.view.component.web.antlr + +enum class LexerErrorType(val message: String) { + NO_VIABLE_ALTERNATIVE("No viable alternative.") +} diff --git a/web-view-components-compiler/src/main/java/groowt/view/component/web/antlr/LexerSemanticPredicates.kt b/web-view-components-compiler/src/main/java/groowt/view/component/web/antlr/LexerSemanticPredicates.kt index fba4806..40c06ca 100644 --- a/web-view-components-compiler/src/main/java/groowt/view/component/web/antlr/LexerSemanticPredicates.kt +++ b/web-view-components-compiler/src/main/java/groowt/view/component/web/antlr/LexerSemanticPredicates.kt @@ -70,7 +70,7 @@ fun isIdentifierStartChar(c: Char): Boolean = Character.isJavaIdentifierStart(c) fun isIdentifierStartChar(subject: Int) = isIdentifierStartChar(subject.toChar()) -fun isIdentifierChar(c: Char): Boolean = Character.isJavaIdentifierPart(c) +fun isIdentifierChar(c: Char): Boolean = Character.isJavaIdentifierPart(c) || c == '-' fun isIdentifierChar(subject: Int) = isIdentifierChar(subject.toChar()) diff --git a/web-view-components-compiler/src/main/java/groowt/view/component/web/antlr/LexerUtil.kt b/web-view-components-compiler/src/main/java/groowt/view/component/web/antlr/LexerUtil.kt index ed3ab26..7439b7d 100644 --- a/web-view-components-compiler/src/main/java/groowt/view/component/web/antlr/LexerUtil.kt +++ b/web-view-components-compiler/src/main/java/groowt/view/component/web/antlr/LexerUtil.kt @@ -4,4 +4,12 @@ package groowt.view.component.web.antlr import org.antlr.v4.runtime.CharStream import org.antlr.v4.runtime.Token -fun runLexerAllTokens(input: CharStream): List = WebViewComponentsLexer(input).allTokens +fun runLexerAllTokensRaw(input: CharStream): List = WebViewComponentsLexer(input).allTokens + +fun runLexerAllTokens(input: CharStream, withEOF: Boolean = false): List { + val lexer = WebViewComponentsLexer(input) + val stream = WebViewComponentsTokenStream(lexer) + return if (withEOF) stream.getAllTokens() else stream.getAllTokens() +} + +fun runLexerAllTokens(input: CharStream) = runLexerAllTokens(input, withEOF = false) diff --git a/web-view-components-compiler/src/main/java/groowt/view/component/web/antlr/ParserError.kt b/web-view-components-compiler/src/main/java/groowt/view/component/web/antlr/ParserError.kt new file mode 100644 index 0000000..2d95cf0 --- /dev/null +++ b/web-view-components-compiler/src/main/java/groowt/view/component/web/antlr/ParserError.kt @@ -0,0 +1,50 @@ +package groowt.view.component.web.antlr + +import groowt.view.component.web.util.SourcePosition +import groowt.view.component.web.util.excerpt +import org.antlr.v4.runtime.ParserRuleContext +import org.antlr.v4.runtime.Token + +open class ParserError(val type: ParserErrorType, val offending: Token, val context: ParserRuleContext) + +class MismatchedInputParserError( + type: ParserErrorType, + offending: Token, + context: ParserRuleContext, + val expectedTokenTypes: Set +) : ParserError(type, offending, context) + +fun format(error: ParserError): String { + val sb = StringBuilder() + val sourcePosition = SourcePosition.fromStartOfToken(error.offending) + sb.append("At ") + .append(sourcePosition.toStringLong()) + .append(": ") + .append(error.type.message) + .append(" Offending token: ") + .append(formatTokenForError(error.offending)) + .append(". ") + if (error is MismatchedInputParserError) { + sb.append("Expected any of: ") + .append(formatExpected(error.expectedTokenTypes)) + .append(". ") + } + sb.append("(" + formatContext(error.context) + ").") + return sb.toString() +} + +private fun formatTokenForError(token: Token): String { + return "'${token.text}' (${getTokenName(token)})" +} + +private fun formatContext(context: ParserRuleContext): String { + val sb = StringBuilder() + sb.append("context: ${context.javaClass.simpleName}") + if (context.text.isNotEmpty()) { + sb.append(", text: ") + .append(escapeChars(excerpt(context.text))) + } + return sb.toString() +} + +private fun formatExpected(expectedTokenTypes: Set): String = expectedTokenTypes.joinToString { getTokenName(it) } diff --git a/web-view-components-compiler/src/main/java/groowt/view/component/web/antlr/ParserErrorListener.kt b/web-view-components-compiler/src/main/java/groowt/view/component/web/antlr/ParserErrorListener.kt new file mode 100644 index 0000000..1c8e024 --- /dev/null +++ b/web-view-components-compiler/src/main/java/groowt/view/component/web/antlr/ParserErrorListener.kt @@ -0,0 +1,41 @@ +package groowt.view.component.web.antlr + +import org.antlr.v4.runtime.* + +class ParserErrorListener : BaseErrorListener() { + + private val errors: MutableList = ArrayList() + + fun getErrors(): List = this.errors + + override fun syntaxError( + recognizer: Recognizer<*, *>, + offendingSymbol: Any, + line: Int, + charPositionInLine: Int, + msg: String, + e: RecognitionException + ) { + val parser = recognizer as WebViewComponentsParser + when (e) { + is NoViableAltException -> { + val error = ParserError(ParserErrorType.NO_VIABLE_ALTERNATIVE, e.offendingToken, parser.context) + errors.add(error) + } + is InputMismatchException -> { + val error = MismatchedInputParserError( + ParserErrorType.INPUT_MISMATCH, + e.offendingToken, + parser.context, + e.expectedTokens.toSet() + ) + errors.add(error) + } + is FailedPredicateException -> { + val error = ParserError(ParserErrorType.FAILED_PREDICATE, e.offendingToken, parser.context) + errors.add(error) + } + } + } + +} diff --git a/web-view-components-compiler/src/main/java/groowt/view/component/web/antlr/ParserErrorType.kt b/web-view-components-compiler/src/main/java/groowt/view/component/web/antlr/ParserErrorType.kt new file mode 100644 index 0000000..8470b69 --- /dev/null +++ b/web-view-components-compiler/src/main/java/groowt/view/component/web/antlr/ParserErrorType.kt @@ -0,0 +1,7 @@ +package groowt.view.component.web.antlr + +enum class ParserErrorType(val message: String) { + NO_VIABLE_ALTERNATIVE("No viable alternative."), + INPUT_MISMATCH("Input mismatch."), + FAILED_PREDICATE("Input failed predicate.") +} diff --git a/web-view-components-compiler/src/main/java/groowt/view/component/web/antlr/ParserUtil.kt b/web-view-components-compiler/src/main/java/groowt/view/component/web/antlr/ParserUtil.kt index 29290be..36cb4e5 100644 --- a/web-view-components-compiler/src/main/java/groowt/view/component/web/antlr/ParserUtil.kt +++ b/web-view-components-compiler/src/main/java/groowt/view/component/web/antlr/ParserUtil.kt @@ -38,9 +38,6 @@ fun parseCompilationUnit(charStream: CharStream): CompilationUnitParseResult { return CompilationUnitParseResult(lexer, tokenStream, parser, cu) } -fun parseCompilationUnit(tokenStream: TokenStream): CompilationUnitContext = - parse(tokenStream, WebViewComponentsParser::compilationUnit) - fun parseCompilationUnit( tokenStream: TokenStream, onResult: BiConsumer diff --git a/web-view-components-compiler/src/main/java/groowt/view/component/web/antlr/TokenUtil.kt b/web-view-components-compiler/src/main/java/groowt/view/component/web/antlr/TokenUtil.kt index fdf700b..6df6191 100644 --- a/web-view-components-compiler/src/main/java/groowt/view/component/web/antlr/TokenUtil.kt +++ b/web-view-components-compiler/src/main/java/groowt/view/component/web/antlr/TokenUtil.kt @@ -3,17 +3,18 @@ package groowt.view.component.web.antlr import groowt.view.component.web.antlr.WebViewComponentsLexer.GStringParts import groowt.view.component.web.antlr.WebViewComponentsLexer.GroovyTokens +import groowt.view.component.web.util.SourcePosition import org.antlr.v4.runtime.Token fun isGroovyTokenType(token: Token) = isGroovyTokenType(token.type) fun isGroovyTokenType(type: Int): Boolean = type in GroovyTokens -fun isGStringPart (token: Token) = isGStringPart(token.type) +fun isGStringPart(token: Token) = isGStringPart(token.type) fun isGStringPart(type: Int): Boolean = type in GStringParts -fun getTokenName (token: Token) = getTokenName(token.type) +fun getTokenName(token: Token) = getTokenName(token.type) fun getTokenName(type: Int): String = WebViewComponentsLexer.VOCABULARY.getDisplayName(type) @@ -25,15 +26,17 @@ fun interface TokenTextFormatter { } fun formatToken(token: Token, textFormatter: TokenTextFormatter): String = - "${getTokenName(token)}[${token.line},${token.charPositionInLine}](${textFormatter.format(token.text)})" - -fun shortFormatToken(token: Token): String = - "${getTokenName(token)}[${token.line},${token.charPositionInLine},${token.text.length}]" + getTokenName(token) + "[${formatTokenPositionShort(token)}](${textFormatter.format(token.text)})" fun formatTokenText(text: String): String = excerptTokenParts(escapeTokenPartsToList(text)) -fun formatTokenPosition(token: Token): String { - return "line ${token.line}, column ${token.charPositionInLine + 1}" +fun formatTokenPositionShort(token: Token): String { + val sourcePosition = getTokenSourcePosition(token) + return "${sourcePosition.line},${sourcePosition.column}" +} + +fun getTokenSourcePosition(token: Token): SourcePosition { + return SourcePosition(token.line, token.charPositionInLine + 1) } fun excerptToken(token: Token) = excerptToken(token, 30, 7, "...") @@ -42,7 +45,7 @@ fun excerptToken(token: Token, startLength: Int = 30, endLength: Int = 7, separa return excerptTokenParts(escapeTokenPartsToList(token.text), startLength, endLength, separator) } -fun excerptTokenParts( +private fun excerptTokenParts( parts: List, startLength: Int = 30, endLength: Int = 7, diff --git a/web-view-components-compiler/src/main/java/groowt/view/component/web/antlr/WebViewComponentsTokenStream.kt b/web-view-components-compiler/src/main/java/groowt/view/component/web/antlr/WebViewComponentsTokenStream.kt index d5dfa96..be3bb2e 100644 --- a/web-view-components-compiler/src/main/java/groowt/view/component/web/antlr/WebViewComponentsTokenStream.kt +++ b/web-view-components-compiler/src/main/java/groowt/view/component/web/antlr/WebViewComponentsTokenStream.kt @@ -7,7 +7,7 @@ private operator fun Interval.component1(): Int = this.a private operator fun Interval.component2(): Int = this.b -class WebViewComponentsTokenStream (private val tokenSource: TokenSource) : TokenStream { +class WebViewComponentsTokenStream(private val tokenSource: TokenSource) : TokenStream { private val tokens: MutableList = ArrayList() diff --git a/web-view-components-compiler/src/main/java/groowt/view/component/web/ast/DefaultAstBuilderVisitor.java b/web-view-components-compiler/src/main/java/groowt/view/component/web/ast/DefaultAstBuilderVisitor.java index 6b61491..aea976d 100644 --- a/web-view-components-compiler/src/main/java/groowt/view/component/web/ast/DefaultAstBuilderVisitor.java +++ b/web-view-components-compiler/src/main/java/groowt/view/component/web/ast/DefaultAstBuilderVisitor.java @@ -1,5 +1,6 @@ package groowt.view.component.web.ast; +import groowt.view.component.web.WebViewComponentBugError; import groowt.view.component.web.antlr.MergedGroovyCodeToken; import groowt.view.component.web.antlr.TokenUtil; import groowt.view.component.web.antlr.WebViewComponentsParser; @@ -219,16 +220,18 @@ public class DefaultAstBuilderVisitor extends WebViewComponentsParserBaseVisitor @Override public Node visitComponentType(WebViewComponentsParser.ComponentTypeContext ctx) { - final var identifiers = ctx.Identifier(); - if (identifiers.size() == 1) { - final TerminalNode first = identifiers.getFirst(); - if (startsWithLowercaseLetter(first.getText())) { - return this.nodeFactory.stringComponentTypeNode( - this.getTokenRange(ctx), first.getSymbol().getTokenIndex() - ); - } + final var typedIdentifier = ctx.TypedIdentifier(); + if (typedIdentifier != null) { + return this.nodeFactory.classComponentTypeNode(this.getTokenRange(ctx)); } - return this.nodeFactory.classComponentTypeNode(this.getTokenRange(ctx)); + final var stringIdentifier = ctx.StringIdentifier(); + if (stringIdentifier != null) { + return this.nodeFactory.stringComponentTypeNode( + this.getTokenRange(ctx), + stringIdentifier.getSymbol().getTokenIndex() + ); + } + throw new WebViewComponentBugError("Could not determine type of " + ctx); } @Override @@ -253,7 +256,7 @@ public class DefaultAstBuilderVisitor extends WebViewComponentsParserBaseVisitor @Override public Node visitKeyValueAttr(WebViewComponentsParser.KeyValueAttrContext ctx) { - final TerminalNode identifier = ctx.Identifier(); + final TerminalNode identifier = ctx.AttributeIdentifier(); final KeyNode keyNode = this.nodeFactory.keyNode(TokenRange.of( identifier.getSymbol(), ctx.Equals().getSymbol() @@ -264,7 +267,7 @@ public class DefaultAstBuilderVisitor extends WebViewComponentsParserBaseVisitor @Override public Node visitBooleanAttr(WebViewComponentsParser.BooleanAttrContext ctx) { - final Token identifierToken = ctx.Identifier().getSymbol(); + final Token identifierToken = ctx.AttributeIdentifier().getSymbol(); final KeyNode keyNode = this.nodeFactory.keyNode( TokenRange.of(identifierToken), identifierToken.getTokenIndex() diff --git a/web-view-components-compiler/src/main/java/groowt/view/component/web/compiler/CompilerPipeline.java b/web-view-components-compiler/src/main/java/groowt/view/component/web/compiler/CompilerPipeline.java index c0df674..ee8fc3f 100644 --- a/web-view-components-compiler/src/main/java/groowt/view/component/web/compiler/CompilerPipeline.java +++ b/web-view-components-compiler/src/main/java/groowt/view/component/web/compiler/CompilerPipeline.java @@ -116,7 +116,7 @@ public final class CompilerPipeline { // build ast final var tokenList = new TokenList(parseResult.getTokenStream()); final var astBuilder = new DefaultAstBuilder(new DefaultNodeFactory(tokenList)); - return (CompilationUnitNode) astBuilder.build(parseResult.getCompilationUnitContext()); + return astBuilder.buildCompilationUnit(parseResult.getCompilationUnitContext()); } private CompilerPipeline() {} diff --git a/web-view-components-compiler/src/main/java/groowt/view/component/web/util/TextUtil.kt b/web-view-components-compiler/src/main/java/groowt/view/component/web/util/TextUtil.kt new file mode 100644 index 0000000..5796e9f --- /dev/null +++ b/web-view-components-compiler/src/main/java/groowt/view/component/web/util/TextUtil.kt @@ -0,0 +1,12 @@ +@file:JvmName("TextUtil") +package groowt.view.component.web.util + +fun excerpt(s: String, startLength: Int = 30, endLength: Int = 7, ellipsis: String = "..."): String { + if (s.length > startLength + endLength + ellipsis.length) { + val start = s.substring(0..\n) + RawText[6,1](\n) TypedComponentNode(7,1..20,8) ComponentArgsNode(7,2..7,6) StringComponentTypeNode(7,2..7,6) - Identifier[7,1](html) + StringIdentifier[7,2](html) BodyNode(7,7..20,1) TypedComponentNode(8,5..8,18) ComponentArgsNode(8,6..8,10) StringComponentTypeNode(8,6..8,10) - Identifier[8,5](head) + StringIdentifier[8,6](head) TypedComponentNode(9,5..19,12) ComponentArgsNode(9,6..9,10) StringComponentTypeNode(9,6..9,10) - Identifier[9,5](body) + StringIdentifier[9,6](body) BodyNode(9,11..19,5) TypedComponentNode(10,9..10,29) ComponentArgsNode(10,10..10,12) StringComponentTypeNode(10,10..10,12) - Identifier[10,9](h1) + StringIdentifier[10,10](h1) BodyNode(10,13..10,24) GStringBodyTextNode(10,13..10,24) DollarScriptletNode(10,13..10,24) - DollarScriptletOpen[10,12](${) - GroovyCode[10,14](greeting) - DollarScriptletClose[10,22](}) + DollarScriptletOpen[10,13](${) + GroovyCode[10,15](greeting) + DollarScriptletClose[10,23](}) TypedComponentNode(11,9..18,34) ComponentArgsNode(11,10..11,32) ClassComponentTypeNode(11,10..11,32) - Identifier[11,9](groowt) - Dot[11,15](.) - Identifier[11,16](view) - Dot[11,20](.) - Identifier[11,21](web) - Dot[11,24](.) - Identifier[11,25](Select) + TypedIdentifier[11,10](groowt.view.web.Select) BodyNode(11,33..18,9) TypedComponentNode(12,13..14,20) ComponentArgsNode(12,14..12,36) ClassComponentTypeNode(12,14..12,18) - Identifier[12,13](Case) + TypedIdentifier[12,14](Case) KeyValueAttrNode(12,19..12,36) KeyNode(12,19..12,24) - Identifier[12,18](cond) - Equals[12,22](=) + AttributeIdentifier[12,19](cond) + Equals[12,23](=) ClosureValueNode(12,24..12,36) - ClosureAttrValueStart[12,23]({) - GroovyCode[12,24](isItTrue()) - ClosureAttrValueEnd[12,34](}) + ClosureAttrValueStart[12,24]({) + GroovyCode[12,25](isItTrue()) + ClosureAttrValueEnd[12,35](}) BodyNode(12,37..14,13) TypedComponentNode(13,17..13,37) ComponentArgsNode(13,18..13,19) StringComponentTypeNode(13,18..13,19) - Identifier[13,17](p) + StringIdentifier[13,18](p) BodyNode(13,20..13,33) JStringBodyTextNode(13,20..13,33) - RawText[13,19](It's true! :)) + RawText[13,20](It's true! :)) TypedComponentNode(15,13..17,23) ComponentArgsNode(15,14..15,21) ClassComponentTypeNode(15,14..15,21) - Identifier[15,13](Default) + TypedIdentifier[15,14](Default) BodyNode(15,22..17,13) TypedComponentNode(16,17..16,40) ComponentArgsNode(16,18..16,19) StringComponentTypeNode(16,18..16,19) - Identifier[16,17](p) + StringIdentifier[16,18](p) BodyNode(16,20..16,36) JStringBodyTextNode(16,20..16,36) - RawText[16,19](It's false... :() + RawText[16,20](It's false... :() diff --git a/web-view-components-compiler/src/test/ast/trees/helloTarget_ast.txt b/web-view-components-compiler/src/test/ast/trees/helloTarget_ast.txt index 7d0a83d..c151ad9 100644 --- a/web-view-components-compiler/src/test/ast/trees/helloTarget_ast.txt +++ b/web-view-components-compiler/src/test/ast/trees/helloTarget_ast.txt @@ -2,9 +2,9 @@ CompilationUnitNode(1,1..2,6) BodyNode(1,1..2,1) GStringBodyTextNode(1,1..2,1) JStringBodyTextNode(1,1..1,8) - RawText[1,0](Hello, ) + RawText[1,1](Hello, ) DollarReferenceNode(1,8..1,15) - DollarReferenceStart[1,7]($) - GroovyCode[1,8](target) + DollarReferenceStart[1,8]($) + GroovyCode[1,9](target) JStringBodyTextNode(1,15..2,1) - RawText[1,14](!\n) + RawText[1,15](!\n) diff --git a/web-view-components-compiler/src/test/ast/trees/simpleComponentWithBody_ast.txt b/web-view-components-compiler/src/test/ast/trees/simpleComponentWithBody_ast.txt index 129cdfb..5caad41 100644 --- a/web-view-components-compiler/src/test/ast/trees/simpleComponentWithBody_ast.txt +++ b/web-view-components-compiler/src/test/ast/trees/simpleComponentWithBody_ast.txt @@ -3,7 +3,7 @@ CompilationUnitNode(1,1..2,6) TypedComponentNode(1,1..1,35) ComponentArgsNode(1,2..1,10) ClassComponentTypeNode(1,2..1,10) - Identifier[1,1](Greeting) + TypedIdentifier[1,2](Greeting) BodyNode(1,11..1,24) JStringBodyTextNode(1,11..1,24) - RawText[1,10](to the world!) + RawText[1,11](to the world!) diff --git a/web-view-components-compiler/src/test/lexer/complicated.wvc b/web-view-components-compiler/src/test/lexer/complicated.wvc new file mode 100644 index 0000000..0a37f78 --- /dev/null +++ b/web-view-components-compiler/src/test/lexer/complicated.wvc @@ -0,0 +1,20 @@ +--- +import some.Thing // a comment + +def greeting = 'Hello, World!' +--- + + + + +

${greeting}

+ + +

It's true! :)

+
+ +

It's false... :(

+
+
+ + diff --git a/web-view-components-compiler/src/test/lexer/dataComponent.wvc b/web-view-components-compiler/src/test/lexer/dataComponent.wvc new file mode 100644 index 0000000..b13c00f --- /dev/null +++ b/web-view-components-compiler/src/test/lexer/dataComponent.wvc @@ -0,0 +1 @@ + diff --git a/web-view-components-compiler/src/test/lexer/emptyHtml.wvc b/web-view-components-compiler/src/test/lexer/emptyHtml.wvc new file mode 100644 index 0000000..18ecdcb --- /dev/null +++ b/web-view-components-compiler/src/test/lexer/emptyHtml.wvc @@ -0,0 +1 @@ + diff --git a/web-view-components-compiler/src/test/lexer/emptyTypedWithPackage.wvc b/web-view-components-compiler/src/test/lexer/emptyTypedWithPackage.wvc new file mode 100644 index 0000000..409050a --- /dev/null +++ b/web-view-components-compiler/src/test/lexer/emptyTypedWithPackage.wvc @@ -0,0 +1 @@ + diff --git a/web-view-components-compiler/src/test/lexer/tokens-files/complicated_tokens.txt b/web-view-components-compiler/src/test/lexer/tokens-files/complicated_tokens.txt new file mode 100644 index 0000000..07e1c23 --- /dev/null +++ b/web-view-components-compiler/src/test/lexer/tokens-files/complicated_tokens.txt @@ -0,0 +1,83 @@ +PreambleBreak[1,1](---\n) +GroovyCode[2,1](import some.Thing // a comment...World!') +PreambleBreak[4,31](\n---\n) +RawText[6,1](\n) +ComponentOpen[7,1](<) +StringIdentifier[7,2](html) +ComponentClose[7,6](>) +RawText[7,7](\n ) +ComponentOpen[8,5](<) +StringIdentifier[8,6](head) +ComponentClose[8,10](>) +ClosingComponentOpen[8,11]() +RawText[8,18](\n ) +ComponentOpen[9,5](<) +StringIdentifier[9,6](body) +ComponentClose[9,10](>) +RawText[9,11](\n ) +ComponentOpen[10,9](<) +StringIdentifier[10,10](h1) +ComponentClose[10,12](>) +DollarScriptletOpen[10,13](${) +GroovyCode[10,15](greeting) +DollarScriptletClose[10,23](}) +ClosingComponentOpen[10,24]() +RawText[10,29](\n ) +ComponentOpen[11,9](<) +TypedIdentifier[11,10](groowt.view.web.Select) +ComponentClose[11,32](>) +RawText[11,33](\n ) +ComponentOpen[12,13](<) +TypedIdentifier[12,14](Case) +ComponentNlws[12,18]( ) +AttributeIdentifier[12,19](cond) +Equals[12,23](=) +ClosureAttrValueStart[12,24]({) +GroovyCode[12,25](isItTrue()) +ClosureAttrValueEnd[12,35](}) +ComponentClose[12,36](>) +RawText[12,37](\n ) +ComponentOpen[13,17](<) +StringIdentifier[13,18](p) +ComponentClose[13,19](>) +RawText[13,20](It's true! :)) +ClosingComponentOpen[13,33]() +RawText[13,37](\n ) +ClosingComponentOpen[14,13]() +RawText[14,20](\n ) +ComponentOpen[15,13](<) +TypedIdentifier[15,14](Default) +ComponentClose[15,21](>) +RawText[15,22](\n ) +ComponentOpen[16,17](<) +StringIdentifier[16,18](p) +ComponentClose[16,19](>) +RawText[16,20](It's false... :() +ClosingComponentOpen[16,36]() +RawText[16,40](\n ) +ClosingComponentOpen[17,13]() +RawText[17,23](\n ) +ClosingComponentOpen[18,9]() +RawText[18,34](\n ) +ClosingComponentOpen[19,5]() +RawText[19,12](\n) +ClosingComponentOpen[20,1]() +RawText[20,8](\n) \ No newline at end of file diff --git a/web-view-components-compiler/src/test/lexer/tokens-files/dataComponent_tokens.txt b/web-view-components-compiler/src/test/lexer/tokens-files/dataComponent_tokens.txt new file mode 100644 index 0000000..23711ba --- /dev/null +++ b/web-view-components-compiler/src/test/lexer/tokens-files/dataComponent_tokens.txt @@ -0,0 +1,5 @@ +ComponentOpen[1,1](<) +StringIdentifier[1,2](data-test) +ComponentNlws[1,11]( ) +ComponentSelfClose[1,12](/>) +RawText[1,14](\n) \ No newline at end of file diff --git a/web-view-components-compiler/src/test/lexer/tokens-files/emptyHtml_tokens.txt b/web-view-components-compiler/src/test/lexer/tokens-files/emptyHtml_tokens.txt new file mode 100644 index 0000000..e2a03f0 --- /dev/null +++ b/web-view-components-compiler/src/test/lexer/tokens-files/emptyHtml_tokens.txt @@ -0,0 +1,7 @@ +ComponentOpen[1,1](<) +StringIdentifier[1,2](html) +ComponentClose[1,6](>) +ClosingComponentOpen[1,7]() +RawText[1,14](\n) \ No newline at end of file diff --git a/web-view-components-compiler/src/test/lexer/tokens-files/emptyTypedWithPackage_tokens.txt b/web-view-components-compiler/src/test/lexer/tokens-files/emptyTypedWithPackage_tokens.txt new file mode 100644 index 0000000..21fb391 --- /dev/null +++ b/web-view-components-compiler/src/test/lexer/tokens-files/emptyTypedWithPackage_tokens.txt @@ -0,0 +1,7 @@ +ComponentOpen[1,1](<) +TypedIdentifier[1,2](groowt.Test) +ComponentClose[1,13](>) +ClosingComponentOpen[1,14]() +RawText[1,28](\n) \ No newline at end of file diff --git a/web-view-components-compiler/src/test/lexer/tokens-files/helloTarget_tokens.txt b/web-view-components-compiler/src/test/lexer/tokens-files/helloTarget_tokens.txt index 3128441..f4ec876 100644 --- a/web-view-components-compiler/src/test/lexer/tokens-files/helloTarget_tokens.txt +++ b/web-view-components-compiler/src/test/lexer/tokens-files/helloTarget_tokens.txt @@ -1,4 +1,4 @@ -RawText[1,0](Hello, ) -DollarReferenceStart[1,7]($) -GroovyCode[1,8](target) -RawText[1,14](!\n) \ No newline at end of file +RawText[1,1](Hello, ) +DollarReferenceStart[1,8]($) +GroovyCode[1,9](target) +RawText[1,15](!\n) \ No newline at end of file diff --git a/web-view-components-compiler/src/test/lexer/tokens-files/helloWorld_tokens.txt b/web-view-components-compiler/src/test/lexer/tokens-files/helloWorld_tokens.txt index d28150d..3a7aeeb 100644 --- a/web-view-components-compiler/src/test/lexer/tokens-files/helloWorld_tokens.txt +++ b/web-view-components-compiler/src/test/lexer/tokens-files/helloWorld_tokens.txt @@ -1 +1 @@ -RawText[1,0](Hello, World!\n) \ No newline at end of file +RawText[1,1](Hello, World!\n) \ No newline at end of file diff --git a/web-view-components-compiler/src/test/parser/emptyHtml.wvc b/web-view-components-compiler/src/test/parser/emptyHtml.wvc new file mode 100644 index 0000000..18ecdcb --- /dev/null +++ b/web-view-components-compiler/src/test/parser/emptyHtml.wvc @@ -0,0 +1 @@ + diff --git a/web-view-components-compiler/src/test/parser/trees/complicated_parseTree.txt b/web-view-components-compiler/src/test/parser/trees/complicated_parseTree.txt index 51ca430..b3adfad 100644 --- a/web-view-components-compiler/src/test/parser/trees/complicated_parseTree.txt +++ b/web-view-components-compiler/src/test/parser/trees/complicated_parseTree.txt @@ -13,7 +13,7 @@ compilationUnit[1,1..21,1] ComponentOpen[7,1](<) componentArgs[7,2..7,2] componentType[7,2..7,2] - Identifier[7,2](html) + StringIdentifier[7,2](html) ComponentClose[7,6](>) body[7,7..19,12] bodyText[7,7..7,7] @@ -25,12 +25,12 @@ compilationUnit[1,1..21,1] ComponentOpen[8,5](<) componentArgs[8,6..8,6] componentType[8,6..8,6] - Identifier[8,6](head) + StringIdentifier[8,6](head) ComponentClose[8,10](>) closingComponent[8,11..8,17] ClosingComponentOpen[8,11]() bodyText[8,18..8,18] jStringBodyText[8,18..8,18] @@ -41,7 +41,7 @@ compilationUnit[1,1..21,1] ComponentOpen[9,5](<) componentArgs[9,6..9,6] componentType[9,6..9,6] - Identifier[9,6](body) + StringIdentifier[9,6](body) ComponentClose[9,10](>) body[9,11..18,34] bodyText[9,11..9,11] @@ -53,7 +53,7 @@ compilationUnit[1,1..21,1] ComponentOpen[10,9](<) componentArgs[10,10..10,10] componentType[10,10..10,10] - Identifier[10,10](h1) + StringIdentifier[10,10](h1) ComponentClose[10,12](>) body[10,13..10,23] bodyText[10,13..10,23] @@ -66,7 +66,7 @@ compilationUnit[1,1..21,1] closingComponent[10,24..10,28] ClosingComponentOpen[10,24]() bodyText[10,29..10,29] jStringBodyText[10,29..10,29] @@ -75,15 +75,9 @@ compilationUnit[1,1..21,1] componentWithChildren[11,9..18,33] openComponent[11,9..11,32] ComponentOpen[11,9](<) - componentArgs[11,10..11,26] - componentType[11,10..11,26] - Identifier[11,10](groowt) - Dot[11,16](.) - Identifier[11,17](view) - Dot[11,21](.) - Identifier[11,22](web) - Dot[11,25](.) - Identifier[11,26](Select) + componentArgs[11,10..11,10] + componentType[11,10..11,10] + TypedIdentifier[11,10](groowt.view.web.Select) ComponentClose[11,32](>) body[11,33..17,23] bodyText[11,33..11,33] @@ -95,11 +89,11 @@ compilationUnit[1,1..21,1] ComponentOpen[12,13](<) componentArgs[12,14..12,35] componentType[12,14..12,14] - Identifier[12,14](Case) + TypedIdentifier[12,14](Case) ComponentNlws[12,18]( ) attr[12,19..12,35] keyValueAttr[12,19..12,35] - Identifier[12,19](cond) + AttributeIdentifier[12,19](cond) Equals[12,23](=) value[12,24..12,35] closureAttrValue[12,24..12,35] @@ -117,7 +111,7 @@ compilationUnit[1,1..21,1] ComponentOpen[13,17](<) componentArgs[13,18..13,18] componentType[13,18..13,18] - Identifier[13,18](p) + StringIdentifier[13,18](p) ComponentClose[13,19](>) body[13,20..13,20] bodyText[13,20..13,20] @@ -126,7 +120,7 @@ compilationUnit[1,1..21,1] closingComponent[13,33..13,36] ClosingComponentOpen[13,33]() bodyText[13,37..13,37] jStringBodyText[13,37..13,37] @@ -134,7 +128,7 @@ compilationUnit[1,1..21,1] closingComponent[14,13..14,19] ClosingComponentOpen[14,13]() bodyText[14,20..14,20] jStringBodyText[14,20..14,20] @@ -145,7 +139,7 @@ compilationUnit[1,1..21,1] ComponentOpen[15,13](<) componentArgs[15,14..15,14] componentType[15,14..15,14] - Identifier[15,14](Default) + TypedIdentifier[15,14](Default) ComponentClose[15,21](>) body[15,22..16,40] bodyText[15,22..15,22] @@ -157,7 +151,7 @@ compilationUnit[1,1..21,1] ComponentOpen[16,17](<) componentArgs[16,18..16,18] componentType[16,18..16,18] - Identifier[16,18](p) + StringIdentifier[16,18](p) ComponentClose[16,19](>) body[16,20..16,20] bodyText[16,20..16,20] @@ -166,7 +160,7 @@ compilationUnit[1,1..21,1] closingComponent[16,36..16,39] ClosingComponentOpen[16,36]() bodyText[16,40..16,40] jStringBodyText[16,40..16,40] @@ -174,21 +168,15 @@ compilationUnit[1,1..21,1] closingComponent[17,13..17,22] ClosingComponentOpen[17,13]() bodyText[17,23..17,23] jStringBodyText[17,23..17,23] RawText[17,23](\n ) closingComponent[18,9..18,33] ClosingComponentOpen[18,9]() bodyText[18,34..18,34] jStringBodyText[18,34..18,34] @@ -196,7 +184,7 @@ compilationUnit[1,1..21,1] closingComponent[19,5..19,11] ClosingComponentOpen[19,5]() bodyText[19,12..19,12] jStringBodyText[19,12..19,12] @@ -204,7 +192,7 @@ compilationUnit[1,1..21,1] closingComponent[20,1..20,7] ClosingComponentOpen[20,1]() bodyText[20,8..20,8] jStringBodyText[20,8..20,8] diff --git a/web-view-components-compiler/src/test/parser/trees/emptyHtml_parseTree.txt b/web-view-components-compiler/src/test/parser/trees/emptyHtml_parseTree.txt new file mode 100644 index 0000000..832245a --- /dev/null +++ b/web-view-components-compiler/src/test/parser/trees/emptyHtml_parseTree.txt @@ -0,0 +1,19 @@ +compilationUnit[1,1..2,1] + body[1,1..1,14] + component[1,1..1,13] + componentWithChildren[1,1..1,13] + openComponent[1,1..1,6] + ComponentOpen[1,1](<) + componentArgs[1,2..1,2] + componentType[1,2..1,2] + StringIdentifier[1,2](html) + ComponentClose[1,6](>) + closingComponent[1,7..1,13] + ClosingComponentOpen[1,7]() + bodyText[1,14..1,14] + jStringBodyText[1,14..1,14] + RawText[1,14](\n) + EOF[2,1]() diff --git a/web-view-components-compiler/src/testFixtures/java/groowt/view/component/web/transpiler/BodyTranspilerTests.java b/web-view-components-compiler/src/testFixtures/java/groowt/view/component/web/transpiler/BodyTranspilerTests.java index cb04bfb..7ab2ffa 100644 --- a/web-view-components-compiler/src/testFixtures/java/groowt/view/component/web/transpiler/BodyTranspilerTests.java +++ b/web-view-components-compiler/src/testFixtures/java/groowt/view/component/web/transpiler/BodyTranspilerTests.java @@ -5,7 +5,6 @@ import groowt.view.component.web.antlr.TokenList; import groowt.view.component.web.ast.DefaultAstBuilder; import groowt.view.component.web.ast.DefaultNodeFactory; import groowt.view.component.web.ast.node.BodyNode; -import groowt.view.component.web.ast.node.CompilationUnitNode; import groowt.view.component.web.compiler.WebViewComponentTemplateCompileUnit; import groowt.view.component.web.transpile.BodyTranspiler; import groowt.view.component.web.transpile.TranspilerConfiguration; @@ -39,7 +38,7 @@ public abstract class BodyTranspilerTests { final var parseResult = ParserUtil.parseCompilationUnit(source); final var tokenList = new TokenList(parseResult.getTokenStream()); final var b = new DefaultAstBuilder(new DefaultNodeFactory(tokenList)); - final var cuNode = (CompilationUnitNode) b.build(parseResult.getCompilationUnitContext()); + final var cuNode = b.buildCompilationUnit(parseResult.getCompilationUnitContext()); final var bodyNode = cuNode.getBodyNode(); if (bodyNode == null) { fail("No BodyNode was built for source: " + source); diff --git a/web-view-components-compiler/src/testFixtures/java/groowt/view/component/web/transpiler/GStringTranspilerTests.java b/web-view-components-compiler/src/testFixtures/java/groowt/view/component/web/transpiler/GStringTranspilerTests.java index 0b22cfc..da156ef 100644 --- a/web-view-components-compiler/src/testFixtures/java/groowt/view/component/web/transpiler/GStringTranspilerTests.java +++ b/web-view-components-compiler/src/testFixtures/java/groowt/view/component/web/transpiler/GStringTranspilerTests.java @@ -5,7 +5,6 @@ import groowt.view.component.web.antlr.TokenList; import groowt.view.component.web.ast.DefaultAstBuilder; import groowt.view.component.web.ast.DefaultNodeFactory; import groowt.view.component.web.ast.node.BodyNode; -import groowt.view.component.web.ast.node.CompilationUnitNode; import groowt.view.component.web.ast.node.GStringBodyTextNode; import groowt.view.component.web.transpile.GStringTranspiler; import org.codehaus.groovy.ast.expr.ClosureExpression; @@ -34,7 +33,7 @@ public abstract class GStringTranspilerTests { final var tokenList = new TokenList(parseResult.getTokenStream()); final var nodeFactory = new DefaultNodeFactory(tokenList); final var astBuilder = new DefaultAstBuilder(nodeFactory); - final var cuNode = (CompilationUnitNode) astBuilder.build(parseResult.getCompilationUnitContext()); + final var cuNode = astBuilder.buildCompilationUnit(parseResult.getCompilationUnitContext()); return Objects.requireNonNull(cuNode.getBodyNode()); } diff --git a/web-view-components-compiler/src/testFixtures/java/groowt/view/component/web/transpiler/GroovyTranspilerTests.java b/web-view-components-compiler/src/testFixtures/java/groowt/view/component/web/transpiler/GroovyTranspilerTests.java index 443b4b6..7e91138 100644 --- a/web-view-components-compiler/src/testFixtures/java/groowt/view/component/web/transpiler/GroovyTranspilerTests.java +++ b/web-view-components-compiler/src/testFixtures/java/groowt/view/component/web/transpiler/GroovyTranspilerTests.java @@ -9,7 +9,6 @@ import groowt.view.component.web.antlr.ParserUtil; import groowt.view.component.web.antlr.TokenList; import groowt.view.component.web.ast.DefaultAstBuilder; import groowt.view.component.web.ast.DefaultNodeFactory; -import groowt.view.component.web.ast.node.CompilationUnitNode; import groowt.view.component.web.compiler.AnonymousWebViewComponent; import groowt.view.component.web.compiler.DefaultWebViewComponentTemplateCompileUnit; import groowt.view.component.web.transpile.GroovyTranspiler; @@ -39,7 +38,7 @@ public abstract class GroovyTranspilerTests { final var parseResult = ParserUtil.parseCompilationUnit(source); final var tokenList = new TokenList(parseResult.getTokenStream()); final var astBuilder = new DefaultAstBuilder(new DefaultNodeFactory(tokenList)); - final var cuNode = (CompilationUnitNode) astBuilder.build(parseResult.getCompilationUnitContext()); + final var cuNode = astBuilder.buildCompilationUnit(parseResult.getCompilationUnitContext()); try { this.transpiler.transpile( new DefaultComponentTemplateCompilerConfiguration(), diff --git a/web-view-components-compiler/src/tools/groovy/groowt/view/component/web/tools/ParseTreeFileMaker.groovy b/web-view-components-compiler/src/tools/groovy/groowt/view/component/web/tools/ParseTreeFileMaker.groovy index 7e81b34..0e641eb 100644 --- a/web-view-components-compiler/src/tools/groovy/groowt/view/component/web/tools/ParseTreeFileMaker.groovy +++ b/web-view-components-compiler/src/tools/groovy/groowt/view/component/web/tools/ParseTreeFileMaker.groovy @@ -3,8 +3,10 @@ package groowt.view.component.web.tools import groovy.transform.InheritConstructors import groowt.view.component.web.antlr.* import groowt.view.component.web.antlr.AntlrUtil.ParseErrorCollector +import groowt.view.component.web.antlr.WebViewComponentsParser.CompilationUnitContext import groowt.view.component.web.util.ExtensionUtil import org.antlr.v4.runtime.CharStreams +import org.antlr.v4.runtime.ConsoleErrorListener @InheritConstructors final class ParseTreeFileMaker extends AbstractOutputFileMaker { @@ -36,7 +38,7 @@ final class ParseTreeFileMaker extends AbstractOutputFileMaker { private boolean onErrors( String name, WebViewComponentsParser parser, - WebViewComponentsParser.CompilationUnitContext cu, + CompilationUnitContext cu, ParseErrorCollector errors ) { def errorCount = errors.errorCount @@ -53,14 +55,35 @@ final class ParseTreeFileMaker extends AbstractOutputFileMaker { } } - private Tuple3 parse( - File sourceFile - ) { + private Tuple3 parse(File sourceFile) { def input = CharStreams.fromFileName(sourceFile.toString()) + def lexer = new WebViewComponentsLexer(input) + def lexerErrorListener = new LexerErrorListener() + lexer.removeErrorListener(ConsoleErrorListener.INSTANCE) + lexer.addErrorListener(lexerErrorListener) + def tokenStream = new WebViewComponentsTokenStream(lexer) + def parser = new WebViewComponentsParser(tokenStream) + def parserErrorListener = new ParserErrorListener() + parser.removeErrorListener(ConsoleErrorListener.INSTANCE) + parser.addErrorListener(parserErrorListener) + def cu = parser.compilationUnit() + + if (!lexerErrorListener.errors.isEmpty()) { + println 'There were lexer errors.' + lexerErrorListener.errors.each { println LexerErrorKt.format(it) } + return null + } + + if (!parserErrorListener.errors.isEmpty()) { + println 'There were parser errors.' + parserErrorListener.errors.each { println ParserErrorKt.format(it) } + return null + } + def errors = AntlrUtil.findErrorNodes(cu) new Tuple3<>(parser, cu, errors) } @@ -71,11 +94,27 @@ final class ParseTreeFileMaker extends AbstractOutputFileMaker { println "Processing: $name" boolean doneYet = false while (!doneYet) { - def (parser, cu, errors) = this.parse(sourceFile) - if (errors.isEmpty()) { - doneYet = this.onSuccess(name, parser, cu) - } else { - doneYet = this.onErrors(name, parser, cu, errors) + final WebViewComponentsParser parser + final CompilationUnitContext cu + final ParseErrorCollector errors + try { + def result = this.parse(sourceFile) + if (result != null) { + (parser, cu, errors) = result + if (errors.isEmpty()) { + doneYet = this.onSuccess(name, parser, cu) + } else { + doneYet = this.onErrors(name, parser, cu, errors) + } + } else { + doneYet = !this.getYesNoInput('Would you like to try again? (y/n)', true) + } + } catch (Exception e) { + println "There was an exception: $e" + if (this.verbose) { + e.printStackTrace() + } + doneYet = !this.getYesNoInput('Would you like to try again? (y/n)', true) } } } diff --git a/web-view-components-compiler/src/tools/groovy/groowt/view/component/web/tools/TokensFileMaker.groovy b/web-view-components-compiler/src/tools/groovy/groowt/view/component/web/tools/TokensFileMaker.groovy index fc7fe93..7d9751b 100644 --- a/web-view-components-compiler/src/tools/groovy/groowt/view/component/web/tools/TokensFileMaker.groovy +++ b/web-view-components-compiler/src/tools/groovy/groowt/view/component/web/tools/TokensFileMaker.groovy @@ -1,11 +1,10 @@ package groowt.view.component.web.tools import groovy.transform.InheritConstructors -import groowt.view.component.web.antlr.TokenUtil -import groowt.view.component.web.antlr.WebViewComponentsLexer -import groowt.view.component.web.antlr.WebViewComponentsTokenStream +import groowt.view.component.web.antlr.* import groowt.view.component.web.util.ExtensionUtil import org.antlr.v4.runtime.CharStreams +import org.antlr.v4.runtime.ConsoleErrorListener import org.antlr.v4.runtime.Token @InheritConstructors @@ -25,6 +24,18 @@ class TokensFileMaker extends AbstractOutputFileMaker { } } + protected boolean onLexerErrors(String name, List errors) { + println "There were lexer errors in $name." + errors.each { println LexerError.format(it) } + if (this.getYesNoInput('Do you wish to try again? (y/n)', true)) { + println "Trying $name again..." + return false + } else { + println "Skipping $name..." + return true + } + } + protected boolean onException(String name, Exception e) { println "There was an exception while tokenizing $name: $e.message" e.printStackTrace() @@ -46,8 +57,16 @@ class TokensFileMaker extends AbstractOutputFileMaker { try { def input = CharStreams.fromString(sourceFile.getText()) def lexer = new WebViewComponentsLexer(input) + lexer.removeErrorListener(ConsoleErrorListener.INSTANCE) + def lexerErrorListener = new LexerErrorListener() + lexer.addErrorListener(lexerErrorListener) def tokenStream = new WebViewComponentsTokenStream(lexer) - doneYet = this.onSuccess(name, tokenStream.getAllTokensSkipEOF()) + def allTokens = tokenStream.getAllTokensSkipEOF() + if (!lexerErrorListener.errors.isEmpty()) { + + } else { + doneYet = this.onSuccess(name, allTokens) + } } catch (Exception e) { doneYet = this.onException(name, e) } diff --git a/web-view-components-compiler/src/tools/kotlin/groowt/view/component/web/tools/LexerTool.kt b/web-view-components-compiler/src/tools/kotlin/groowt/view/component/web/tools/LexerTool.kt index 6b43aba..819b285 100644 --- a/web-view-components-compiler/src/tools/kotlin/groowt/view/component/web/tools/LexerTool.kt +++ b/web-view-components-compiler/src/tools/kotlin/groowt/view/component/web/tools/LexerTool.kt @@ -2,11 +2,11 @@ package groowt.view.component.web.tools import groowt.view.component.web.antlr.formatToken -import groowt.view.component.web.antlr.runLexerAllTokens +import groowt.view.component.web.antlr.runLexerAllTokensRaw fun main(args: Array) { val options = processArgs(args) configureLog(options.logLevel) val input = getInput(options.source) - runLexerAllTokens(input).forEachIndexed { i, t -> println("$i: ${formatToken(t)}") } + runLexerAllTokensRaw(input).forEachIndexed { i, t -> println("$i: ${formatToken(t)}") } }