Lexer identifier refactorings and basic lexer/parser error listeners.

This commit is contained in:
JesseBrault0709 2024-05-21 07:55:39 +02:00
parent e747ac3c32
commit 80b01ec27a
39 changed files with 595 additions and 163 deletions

View File

@ -112,11 +112,11 @@ PreambleOpen
; ;
ComponentOpen ComponentOpen
: LT -> pushMode(IN_TAG) : LT -> pushMode(TAG_START)
; ;
ClosingComponentOpen ClosingComponentOpen
: LT FS -> pushMode(IN_TAG) : LT FS -> pushMode(TAG_START)
; ;
EqualsScriptletOpen EqualsScriptletOpen
@ -159,34 +159,87 @@ RawText
; ;
// ---------------------------------------- // ----------------------------------------
mode IN_TAG; mode TAG_START;
ComponentSelfClose FragmentClose
: FS GT -> popMode : 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 ComponentClose
: GT -> popMode : GT -> popMode
; ;
Identifier ComponentSelfClose
: IdentifierStartChar IdentifierChar* : FS GT -> popMode
;
IdentifierStartChar
: ~[.] { isIdentifierStartChar(this.getCurrentChar()) }?
;
IdentifierChar
: ~[.] { isIdentifierChar(this.getCurrentChar()) }?
; ;
ConstructorOpen ConstructorOpen
: LP { this.enterConstructor(); } : LP { this.enterConstructor(); }
; ;
Dot AttributeIdentifier
: DOT : AttributeIdentifierStartChar AttributeIdentifierChar*
;
fragment
AttributeIdentifierStartChar
: [\p{L}_$]
;
fragment
AttributeIdentifierChar
: [\p{L}_$0-9]
; ;
Equals Equals

View File

@ -67,7 +67,7 @@ componentArgs
; ;
componentType componentType
: Identifier ( Dot Identifier )* : TypedIdentifier | StringIdentifier
; ;
componentConstructor componentConstructor
@ -79,11 +79,11 @@ attr
; ;
keyValueAttr keyValueAttr
: Identifier Equals value : AttributeIdentifier Equals value
; ;
booleanAttr booleanAttr
: Identifier : AttributeIdentifier
; ;
value value

View File

@ -1,17 +1,25 @@
@file:JvmName("MismatchedComponentTypeAnalysis") @file:JvmName("MismatchedComponentTypeAnalysis")
package groowt.view.component.web.analysis 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.ComponentTypeContext
import groowt.view.component.web.antlr.WebViewComponentsParser.ComponentWithChildrenContext import groowt.view.component.web.antlr.WebViewComponentsParser.ComponentWithChildrenContext
import groowt.view.component.web.util.SourcePosition import groowt.view.component.web.util.SourcePosition
import org.antlr.v4.runtime.ParserRuleContext import org.antlr.v4.runtime.ParserRuleContext
import org.antlr.v4.runtime.Token import org.antlr.v4.runtime.Token
import org.antlr.v4.runtime.tree.ParseTree import org.antlr.v4.runtime.tree.ParseTree
import org.antlr.v4.runtime.tree.TerminalNode
private fun getIdentifiers( private fun getIdentifiers(ctx: ComponentTypeContext): Token {
componentTypeContext: ComponentTypeContext val typedIdentifier = ctx.TypedIdentifier()
): List<Token> = componentTypeContext.Identifier().map(TerminalNode::getSymbol) 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( private fun getErrorMessage(
openType: ComponentTypeContext, openType: ComponentTypeContext,
@ -20,20 +28,8 @@ private fun getErrorMessage(
"Found '${openType.text}' at ${SourcePosition.formatStartOfTokenLong(openType.start)} " + "Found '${openType.text}' at ${SourcePosition.formatStartOfTokenLong(openType.start)} " +
"and '${closingType.text}' at ${SourcePosition.formatStartOfTokenLong(closingType.start)}." "and '${closingType.text}' at ${SourcePosition.formatStartOfTokenLong(closingType.start)}."
private fun test( private fun test(openIdentifiers: Token, closingIdentifiers: Token): Boolean =
openIdentifiers: List<Token>, openIdentifiers.text.equals(closingIdentifiers.text)
closingIdentifiers: List<Token>
): 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 doCheck(tree: ParseTree, destination: MutableList<MismatchedComponentTypeError>) { private fun doCheck(tree: ParseTree, destination: MutableList<MismatchedComponentTypeError>) {
if (tree is ParserRuleContext) { if (tree is ParserRuleContext) {
@ -43,9 +39,9 @@ private fun doCheck(tree: ParseTree, destination: MutableList<MismatchedComponen
if (tree is ComponentWithChildrenContext) { if (tree is ComponentWithChildrenContext) {
val openType: ComponentTypeContext = tree.openComponent().componentArgs().componentType() val openType: ComponentTypeContext = tree.openComponent().componentArgs().componentType()
val closingType: ComponentTypeContext = tree.closingComponent().componentType() val closingType: ComponentTypeContext = tree.closingComponent().componentType()
val openTypeIdentifiers = getIdentifiers(openType) val openIdentifier = getIdentifiers(openType)
val closingTypeIdentifiers = getIdentifiers(closingType) val closingIdentifier = getIdentifiers(closingType)
if (!test(openTypeIdentifiers, closingTypeIdentifiers)) { if (!test(openIdentifier, closingIdentifier)) {
destination.add(MismatchedComponentTypeError(tree, getErrorMessage(openType, closingType))) destination.add(MismatchedComponentTypeError(tree, getErrorMessage(openType, closingType)))
} }
} }

View File

@ -0,0 +1,9 @@
package groowt.view.component.web.antlr
import groowt.view.component.web.util.SourcePosition
data class LexerError(val type: LexerErrorType, val sourcePosition: SourcePosition)
fun format(lexerError: LexerError): String {
return "At ${lexerError.sourcePosition.toStringLong()}: ${lexerError.type.message}"
}

View File

@ -0,0 +1,66 @@
package groowt.view.component.web.antlr
import groowt.view.component.web.util.SourcePosition
import org.antlr.v4.runtime.*
import org.antlr.v4.runtime.atn.ATNConfigSet
import org.antlr.v4.runtime.dfa.DFA
import java.util.*
class LexerErrorListener : ANTLRErrorListener {
private val errors: MutableList<LexerError> = ArrayList()
fun getErrors(): List<LexerError> = 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()
}
}

View File

@ -0,0 +1,5 @@
package groowt.view.component.web.antlr
enum class LexerErrorType(val message: String) {
NO_VIABLE_ALTERNATIVE("No viable alternative.")
}

View File

@ -70,7 +70,7 @@ fun isIdentifierStartChar(c: Char): Boolean = Character.isJavaIdentifierStart(c)
fun isIdentifierStartChar(subject: Int) = isIdentifierStartChar(subject.toChar()) 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()) fun isIdentifierChar(subject: Int) = isIdentifierChar(subject.toChar())

View File

@ -4,4 +4,12 @@ package groowt.view.component.web.antlr
import org.antlr.v4.runtime.CharStream import org.antlr.v4.runtime.CharStream
import org.antlr.v4.runtime.Token import org.antlr.v4.runtime.Token
fun runLexerAllTokens(input: CharStream): List<Token> = WebViewComponentsLexer(input).allTokens fun runLexerAllTokensRaw(input: CharStream): List<Token> = WebViewComponentsLexer(input).allTokens
fun runLexerAllTokens(input: CharStream, withEOF: Boolean = false): List<Token> {
val lexer = WebViewComponentsLexer(input)
val stream = WebViewComponentsTokenStream(lexer)
return if (withEOF) stream.getAllTokens() else stream.getAllTokens()
}
fun runLexerAllTokens(input: CharStream) = runLexerAllTokens(input, withEOF = false)

View File

@ -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<Int>
) : 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<Int>): String = expectedTokenTypes.joinToString { getTokenName(it) }

View File

@ -0,0 +1,41 @@
package groowt.view.component.web.antlr
import org.antlr.v4.runtime.*
class ParserErrorListener : BaseErrorListener() {
private val errors: MutableList<ParserError> = ArrayList()
fun getErrors(): List<ParserError> = 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)
}
}
}
}

View File

@ -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.")
}

View File

@ -38,9 +38,6 @@ fun parseCompilationUnit(charStream: CharStream): CompilationUnitParseResult {
return CompilationUnitParseResult(lexer, tokenStream, parser, cu) return CompilationUnitParseResult(lexer, tokenStream, parser, cu)
} }
fun parseCompilationUnit(tokenStream: TokenStream): CompilationUnitContext =
parse(tokenStream, WebViewComponentsParser::compilationUnit)
fun parseCompilationUnit( fun parseCompilationUnit(
tokenStream: TokenStream, tokenStream: TokenStream,
onResult: BiConsumer<CompilationUnitContext, WebViewComponentsParser> onResult: BiConsumer<CompilationUnitContext, WebViewComponentsParser>

View File

@ -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.GStringParts
import groowt.view.component.web.antlr.WebViewComponentsLexer.GroovyTokens import groowt.view.component.web.antlr.WebViewComponentsLexer.GroovyTokens
import groowt.view.component.web.util.SourcePosition
import org.antlr.v4.runtime.Token import org.antlr.v4.runtime.Token
fun isGroovyTokenType(token: Token) = isGroovyTokenType(token.type) fun isGroovyTokenType(token: Token) = isGroovyTokenType(token.type)
fun isGroovyTokenType(type: Int): Boolean = type in GroovyTokens 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 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) fun getTokenName(type: Int): String = WebViewComponentsLexer.VOCABULARY.getDisplayName(type)
@ -25,15 +26,17 @@ fun interface TokenTextFormatter {
} }
fun formatToken(token: Token, textFormatter: TokenTextFormatter): String = fun formatToken(token: Token, textFormatter: TokenTextFormatter): String =
"${getTokenName(token)}[${token.line},${token.charPositionInLine}](${textFormatter.format(token.text)})" getTokenName(token) + "[${formatTokenPositionShort(token)}](${textFormatter.format(token.text)})"
fun shortFormatToken(token: Token): String =
"${getTokenName(token)}[${token.line},${token.charPositionInLine},${token.text.length}]"
fun formatTokenText(text: String): String = excerptTokenParts(escapeTokenPartsToList(text)) fun formatTokenText(text: String): String = excerptTokenParts(escapeTokenPartsToList(text))
fun formatTokenPosition(token: Token): String { fun formatTokenPositionShort(token: Token): String {
return "line ${token.line}, column ${token.charPositionInLine + 1}" 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, "...") 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) return excerptTokenParts(escapeTokenPartsToList(token.text), startLength, endLength, separator)
} }
fun excerptTokenParts( private fun excerptTokenParts(
parts: List<String>, parts: List<String>,
startLength: Int = 30, startLength: Int = 30,
endLength: Int = 7, endLength: Int = 7,

View File

@ -7,7 +7,7 @@ private operator fun Interval.component1(): Int = this.a
private operator fun Interval.component2(): Int = this.b 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<Token> = ArrayList() private val tokens: MutableList<Token> = ArrayList()

View File

@ -1,5 +1,6 @@
package groowt.view.component.web.ast; 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.MergedGroovyCodeToken;
import groowt.view.component.web.antlr.TokenUtil; import groowt.view.component.web.antlr.TokenUtil;
import groowt.view.component.web.antlr.WebViewComponentsParser; import groowt.view.component.web.antlr.WebViewComponentsParser;
@ -219,16 +220,18 @@ public class DefaultAstBuilderVisitor extends WebViewComponentsParserBaseVisitor
@Override @Override
public Node visitComponentType(WebViewComponentsParser.ComponentTypeContext ctx) { public Node visitComponentType(WebViewComponentsParser.ComponentTypeContext ctx) {
final var identifiers = ctx.Identifier(); final var typedIdentifier = ctx.TypedIdentifier();
if (identifiers.size() == 1) { if (typedIdentifier != null) {
final TerminalNode first = identifiers.getFirst(); return this.nodeFactory.classComponentTypeNode(this.getTokenRange(ctx));
if (startsWithLowercaseLetter(first.getText())) { }
final var stringIdentifier = ctx.StringIdentifier();
if (stringIdentifier != null) {
return this.nodeFactory.stringComponentTypeNode( return this.nodeFactory.stringComponentTypeNode(
this.getTokenRange(ctx), first.getSymbol().getTokenIndex() this.getTokenRange(ctx),
stringIdentifier.getSymbol().getTokenIndex()
); );
} }
} throw new WebViewComponentBugError("Could not determine type of " + ctx);
return this.nodeFactory.classComponentTypeNode(this.getTokenRange(ctx));
} }
@Override @Override
@ -253,7 +256,7 @@ public class DefaultAstBuilderVisitor extends WebViewComponentsParserBaseVisitor
@Override @Override
public Node visitKeyValueAttr(WebViewComponentsParser.KeyValueAttrContext ctx) { public Node visitKeyValueAttr(WebViewComponentsParser.KeyValueAttrContext ctx) {
final TerminalNode identifier = ctx.Identifier(); final TerminalNode identifier = ctx.AttributeIdentifier();
final KeyNode keyNode = this.nodeFactory.keyNode(TokenRange.of( final KeyNode keyNode = this.nodeFactory.keyNode(TokenRange.of(
identifier.getSymbol(), identifier.getSymbol(),
ctx.Equals().getSymbol() ctx.Equals().getSymbol()
@ -264,7 +267,7 @@ public class DefaultAstBuilderVisitor extends WebViewComponentsParserBaseVisitor
@Override @Override
public Node visitBooleanAttr(WebViewComponentsParser.BooleanAttrContext ctx) { public Node visitBooleanAttr(WebViewComponentsParser.BooleanAttrContext ctx) {
final Token identifierToken = ctx.Identifier().getSymbol(); final Token identifierToken = ctx.AttributeIdentifier().getSymbol();
final KeyNode keyNode = this.nodeFactory.keyNode( final KeyNode keyNode = this.nodeFactory.keyNode(
TokenRange.of(identifierToken), TokenRange.of(identifierToken),
identifierToken.getTokenIndex() identifierToken.getTokenIndex()

View File

@ -116,7 +116,7 @@ public final class CompilerPipeline {
// build ast // build ast
final var tokenList = new TokenList(parseResult.getTokenStream()); final var tokenList = new TokenList(parseResult.getTokenStream());
final var astBuilder = new DefaultAstBuilder(new DefaultNodeFactory(tokenList)); final var astBuilder = new DefaultAstBuilder(new DefaultNodeFactory(tokenList));
return (CompilationUnitNode) astBuilder.build(parseResult.getCompilationUnitContext()); return astBuilder.buildCompilationUnit(parseResult.getCompilationUnitContext());
} }
private CompilerPipeline() {} private CompilerPipeline() {}

View File

@ -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..<startLength)
val end = s.substring((s.length - endLength)..<s.length)
return start + ellipsis + end
} else {
return s
}
}

View File

@ -1,75 +1,69 @@
CompilationUnitNode(1,1..21,6) CompilationUnitNode(1,1..21,6)
PreambleNode(1,1..6,1) PreambleNode(1,1..6,1)
PreambleBreak[1,0](---\n) PreambleBreak[1,1](---\n)
GroovyCode[2,0](import some.Thing // a comment...World!') GroovyCode[2,1](import some.Thing // a comment...World!')
PreambleBreak[4,30](\n---\n) PreambleBreak[4,31](\n---\n)
BodyNode(6,1..21,1) BodyNode(6,1..21,1)
JStringBodyTextNode(6,1..7,1) JStringBodyTextNode(6,1..7,1)
RawText[6,0](<!DOCTYPE html>\n) RawText[6,1](<!DOCTYPE html>\n)
TypedComponentNode(7,1..20,8) TypedComponentNode(7,1..20,8)
ComponentArgsNode(7,2..7,6) ComponentArgsNode(7,2..7,6)
StringComponentTypeNode(7,2..7,6) StringComponentTypeNode(7,2..7,6)
Identifier[7,1](html) StringIdentifier[7,2](html)
BodyNode(7,7..20,1) BodyNode(7,7..20,1)
TypedComponentNode(8,5..8,18) TypedComponentNode(8,5..8,18)
ComponentArgsNode(8,6..8,10) ComponentArgsNode(8,6..8,10)
StringComponentTypeNode(8,6..8,10) StringComponentTypeNode(8,6..8,10)
Identifier[8,5](head) StringIdentifier[8,6](head)
TypedComponentNode(9,5..19,12) TypedComponentNode(9,5..19,12)
ComponentArgsNode(9,6..9,10) ComponentArgsNode(9,6..9,10)
StringComponentTypeNode(9,6..9,10) StringComponentTypeNode(9,6..9,10)
Identifier[9,5](body) StringIdentifier[9,6](body)
BodyNode(9,11..19,5) BodyNode(9,11..19,5)
TypedComponentNode(10,9..10,29) TypedComponentNode(10,9..10,29)
ComponentArgsNode(10,10..10,12) ComponentArgsNode(10,10..10,12)
StringComponentTypeNode(10,10..10,12) StringComponentTypeNode(10,10..10,12)
Identifier[10,9](h1) StringIdentifier[10,10](h1)
BodyNode(10,13..10,24) BodyNode(10,13..10,24)
GStringBodyTextNode(10,13..10,24) GStringBodyTextNode(10,13..10,24)
DollarScriptletNode(10,13..10,24) DollarScriptletNode(10,13..10,24)
DollarScriptletOpen[10,12](${) DollarScriptletOpen[10,13](${)
GroovyCode[10,14](greeting) GroovyCode[10,15](greeting)
DollarScriptletClose[10,22](}) DollarScriptletClose[10,23](})
TypedComponentNode(11,9..18,34) TypedComponentNode(11,9..18,34)
ComponentArgsNode(11,10..11,32) ComponentArgsNode(11,10..11,32)
ClassComponentTypeNode(11,10..11,32) ClassComponentTypeNode(11,10..11,32)
Identifier[11,9](groowt) TypedIdentifier[11,10](groowt.view.web.Select)
Dot[11,15](.)
Identifier[11,16](view)
Dot[11,20](.)
Identifier[11,21](web)
Dot[11,24](.)
Identifier[11,25](Select)
BodyNode(11,33..18,9) BodyNode(11,33..18,9)
TypedComponentNode(12,13..14,20) TypedComponentNode(12,13..14,20)
ComponentArgsNode(12,14..12,36) ComponentArgsNode(12,14..12,36)
ClassComponentTypeNode(12,14..12,18) ClassComponentTypeNode(12,14..12,18)
Identifier[12,13](Case) TypedIdentifier[12,14](Case)
KeyValueAttrNode(12,19..12,36) KeyValueAttrNode(12,19..12,36)
KeyNode(12,19..12,24) KeyNode(12,19..12,24)
Identifier[12,18](cond) AttributeIdentifier[12,19](cond)
Equals[12,22](=) Equals[12,23](=)
ClosureValueNode(12,24..12,36) ClosureValueNode(12,24..12,36)
ClosureAttrValueStart[12,23]({) ClosureAttrValueStart[12,24]({)
GroovyCode[12,24](isItTrue()) GroovyCode[12,25](isItTrue())
ClosureAttrValueEnd[12,34](}) ClosureAttrValueEnd[12,35](})
BodyNode(12,37..14,13) BodyNode(12,37..14,13)
TypedComponentNode(13,17..13,37) TypedComponentNode(13,17..13,37)
ComponentArgsNode(13,18..13,19) ComponentArgsNode(13,18..13,19)
StringComponentTypeNode(13,18..13,19) StringComponentTypeNode(13,18..13,19)
Identifier[13,17](p) StringIdentifier[13,18](p)
BodyNode(13,20..13,33) BodyNode(13,20..13,33)
JStringBodyTextNode(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) TypedComponentNode(15,13..17,23)
ComponentArgsNode(15,14..15,21) ComponentArgsNode(15,14..15,21)
ClassComponentTypeNode(15,14..15,21) ClassComponentTypeNode(15,14..15,21)
Identifier[15,13](Default) TypedIdentifier[15,14](Default)
BodyNode(15,22..17,13) BodyNode(15,22..17,13)
TypedComponentNode(16,17..16,40) TypedComponentNode(16,17..16,40)
ComponentArgsNode(16,18..16,19) ComponentArgsNode(16,18..16,19)
StringComponentTypeNode(16,18..16,19) StringComponentTypeNode(16,18..16,19)
Identifier[16,17](p) StringIdentifier[16,18](p)
BodyNode(16,20..16,36) BodyNode(16,20..16,36)
JStringBodyTextNode(16,20..16,36) JStringBodyTextNode(16,20..16,36)
RawText[16,19](It's false... :() RawText[16,20](It's false... :()

View File

@ -2,9 +2,9 @@ CompilationUnitNode(1,1..2,6)
BodyNode(1,1..2,1) BodyNode(1,1..2,1)
GStringBodyTextNode(1,1..2,1) GStringBodyTextNode(1,1..2,1)
JStringBodyTextNode(1,1..1,8) JStringBodyTextNode(1,1..1,8)
RawText[1,0](Hello, ) RawText[1,1](Hello, )
DollarReferenceNode(1,8..1,15) DollarReferenceNode(1,8..1,15)
DollarReferenceStart[1,7]($) DollarReferenceStart[1,8]($)
GroovyCode[1,8](target) GroovyCode[1,9](target)
JStringBodyTextNode(1,15..2,1) JStringBodyTextNode(1,15..2,1)
RawText[1,14](!\n) RawText[1,15](!\n)

View File

@ -3,7 +3,7 @@ CompilationUnitNode(1,1..2,6)
TypedComponentNode(1,1..1,35) TypedComponentNode(1,1..1,35)
ComponentArgsNode(1,2..1,10) ComponentArgsNode(1,2..1,10)
ClassComponentTypeNode(1,2..1,10) ClassComponentTypeNode(1,2..1,10)
Identifier[1,1](Greeting) TypedIdentifier[1,2](Greeting)
BodyNode(1,11..1,24) BodyNode(1,11..1,24)
JStringBodyTextNode(1,11..1,24) JStringBodyTextNode(1,11..1,24)
RawText[1,10](to the world!) RawText[1,11](to the world!)

View File

@ -0,0 +1,20 @@
---
import some.Thing // a comment
def greeting = 'Hello, World!'
---
<!DOCTYPE html>
<html>
<head></head>
<body>
<h1>${greeting}</h1>
<groowt.view.web.Select>
<Case cond={isItTrue()}>
<p>It's true! :)</p>
</Case>
<Default>
<p>It's false... :(</p>
</Default>
</groowt.view.web.Select>
</body>
</html>

View File

@ -0,0 +1 @@
<data-test />

View File

@ -0,0 +1 @@
<html></html>

View File

@ -0,0 +1 @@
<groowt.Test></groowt.Test>

View File

@ -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](<!DOCTYPE html>\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](</)
StringIdentifier[8,13](head)
ComponentClose[8,17](>)
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](</)
StringIdentifier[10,26](h1)
ComponentClose[10,28](>)
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](</)
StringIdentifier[13,35](p)
ComponentClose[13,36](>)
RawText[13,37](\n )
ClosingComponentOpen[14,13](</)
TypedIdentifier[14,15](Case)
ComponentClose[14,19](>)
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](</)
StringIdentifier[16,38](p)
ComponentClose[16,39](>)
RawText[16,40](\n )
ClosingComponentOpen[17,13](</)
TypedIdentifier[17,15](Default)
ComponentClose[17,22](>)
RawText[17,23](\n )
ClosingComponentOpen[18,9](</)
TypedIdentifier[18,11](groowt.view.web.Select)
ComponentClose[18,33](>)
RawText[18,34](\n )
ClosingComponentOpen[19,5](</)
StringIdentifier[19,7](body)
ComponentClose[19,11](>)
RawText[19,12](\n)
ClosingComponentOpen[20,1](</)
StringIdentifier[20,3](html)
ComponentClose[20,7](>)
RawText[20,8](\n)

View File

@ -0,0 +1,5 @@
ComponentOpen[1,1](<)
StringIdentifier[1,2](data-test)
ComponentNlws[1,11]( )
ComponentSelfClose[1,12](/>)
RawText[1,14](\n)

View File

@ -0,0 +1,7 @@
ComponentOpen[1,1](<)
StringIdentifier[1,2](html)
ComponentClose[1,6](>)
ClosingComponentOpen[1,7](</)
StringIdentifier[1,9](html)
ComponentClose[1,13](>)
RawText[1,14](\n)

View File

@ -0,0 +1,7 @@
ComponentOpen[1,1](<)
TypedIdentifier[1,2](groowt.Test)
ComponentClose[1,13](>)
ClosingComponentOpen[1,14](</)
TypedIdentifier[1,16](groowt.Test)
ComponentClose[1,27](>)
RawText[1,28](\n)

View File

@ -1,4 +1,4 @@
RawText[1,0](Hello, ) RawText[1,1](Hello, )
DollarReferenceStart[1,7]($) DollarReferenceStart[1,8]($)
GroovyCode[1,8](target) GroovyCode[1,9](target)
RawText[1,14](!\n) RawText[1,15](!\n)

View File

@ -1 +1 @@
RawText[1,0](Hello, World!\n) RawText[1,1](Hello, World!\n)

View File

@ -0,0 +1 @@
<html></html>

View File

@ -13,7 +13,7 @@ compilationUnit[1,1..21,1]
ComponentOpen[7,1](<) ComponentOpen[7,1](<)
componentArgs[7,2..7,2] componentArgs[7,2..7,2]
componentType[7,2..7,2] componentType[7,2..7,2]
Identifier[7,2](html) StringIdentifier[7,2](html)
ComponentClose[7,6](>) ComponentClose[7,6](>)
body[7,7..19,12] body[7,7..19,12]
bodyText[7,7..7,7] bodyText[7,7..7,7]
@ -25,12 +25,12 @@ compilationUnit[1,1..21,1]
ComponentOpen[8,5](<) ComponentOpen[8,5](<)
componentArgs[8,6..8,6] componentArgs[8,6..8,6]
componentType[8,6..8,6] componentType[8,6..8,6]
Identifier[8,6](head) StringIdentifier[8,6](head)
ComponentClose[8,10](>) ComponentClose[8,10](>)
closingComponent[8,11..8,17] closingComponent[8,11..8,17]
ClosingComponentOpen[8,11](</) ClosingComponentOpen[8,11](</)
componentType[8,13..8,13] componentType[8,13..8,13]
Identifier[8,13](head) StringIdentifier[8,13](head)
ComponentClose[8,17](>) ComponentClose[8,17](>)
bodyText[8,18..8,18] bodyText[8,18..8,18]
jStringBodyText[8,18..8,18] jStringBodyText[8,18..8,18]
@ -41,7 +41,7 @@ compilationUnit[1,1..21,1]
ComponentOpen[9,5](<) ComponentOpen[9,5](<)
componentArgs[9,6..9,6] componentArgs[9,6..9,6]
componentType[9,6..9,6] componentType[9,6..9,6]
Identifier[9,6](body) StringIdentifier[9,6](body)
ComponentClose[9,10](>) ComponentClose[9,10](>)
body[9,11..18,34] body[9,11..18,34]
bodyText[9,11..9,11] bodyText[9,11..9,11]
@ -53,7 +53,7 @@ compilationUnit[1,1..21,1]
ComponentOpen[10,9](<) ComponentOpen[10,9](<)
componentArgs[10,10..10,10] componentArgs[10,10..10,10]
componentType[10,10..10,10] componentType[10,10..10,10]
Identifier[10,10](h1) StringIdentifier[10,10](h1)
ComponentClose[10,12](>) ComponentClose[10,12](>)
body[10,13..10,23] body[10,13..10,23]
bodyText[10,13..10,23] bodyText[10,13..10,23]
@ -66,7 +66,7 @@ compilationUnit[1,1..21,1]
closingComponent[10,24..10,28] closingComponent[10,24..10,28]
ClosingComponentOpen[10,24](</) ClosingComponentOpen[10,24](</)
componentType[10,26..10,26] componentType[10,26..10,26]
Identifier[10,26](h1) StringIdentifier[10,26](h1)
ComponentClose[10,28](>) ComponentClose[10,28](>)
bodyText[10,29..10,29] bodyText[10,29..10,29]
jStringBodyText[10,29..10,29] jStringBodyText[10,29..10,29]
@ -75,15 +75,9 @@ compilationUnit[1,1..21,1]
componentWithChildren[11,9..18,33] componentWithChildren[11,9..18,33]
openComponent[11,9..11,32] openComponent[11,9..11,32]
ComponentOpen[11,9](<) ComponentOpen[11,9](<)
componentArgs[11,10..11,26] componentArgs[11,10..11,10]
componentType[11,10..11,26] componentType[11,10..11,10]
Identifier[11,10](groowt) TypedIdentifier[11,10](groowt.view.web.Select)
Dot[11,16](.)
Identifier[11,17](view)
Dot[11,21](.)
Identifier[11,22](web)
Dot[11,25](.)
Identifier[11,26](Select)
ComponentClose[11,32](>) ComponentClose[11,32](>)
body[11,33..17,23] body[11,33..17,23]
bodyText[11,33..11,33] bodyText[11,33..11,33]
@ -95,11 +89,11 @@ compilationUnit[1,1..21,1]
ComponentOpen[12,13](<) ComponentOpen[12,13](<)
componentArgs[12,14..12,35] componentArgs[12,14..12,35]
componentType[12,14..12,14] componentType[12,14..12,14]
Identifier[12,14](Case) TypedIdentifier[12,14](Case)
ComponentNlws[12,18]( ) ComponentNlws[12,18]( )
attr[12,19..12,35] attr[12,19..12,35]
keyValueAttr[12,19..12,35] keyValueAttr[12,19..12,35]
Identifier[12,19](cond) AttributeIdentifier[12,19](cond)
Equals[12,23](=) Equals[12,23](=)
value[12,24..12,35] value[12,24..12,35]
closureAttrValue[12,24..12,35] closureAttrValue[12,24..12,35]
@ -117,7 +111,7 @@ compilationUnit[1,1..21,1]
ComponentOpen[13,17](<) ComponentOpen[13,17](<)
componentArgs[13,18..13,18] componentArgs[13,18..13,18]
componentType[13,18..13,18] componentType[13,18..13,18]
Identifier[13,18](p) StringIdentifier[13,18](p)
ComponentClose[13,19](>) ComponentClose[13,19](>)
body[13,20..13,20] body[13,20..13,20]
bodyText[13,20..13,20] bodyText[13,20..13,20]
@ -126,7 +120,7 @@ compilationUnit[1,1..21,1]
closingComponent[13,33..13,36] closingComponent[13,33..13,36]
ClosingComponentOpen[13,33](</) ClosingComponentOpen[13,33](</)
componentType[13,35..13,35] componentType[13,35..13,35]
Identifier[13,35](p) StringIdentifier[13,35](p)
ComponentClose[13,36](>) ComponentClose[13,36](>)
bodyText[13,37..13,37] bodyText[13,37..13,37]
jStringBodyText[13,37..13,37] jStringBodyText[13,37..13,37]
@ -134,7 +128,7 @@ compilationUnit[1,1..21,1]
closingComponent[14,13..14,19] closingComponent[14,13..14,19]
ClosingComponentOpen[14,13](</) ClosingComponentOpen[14,13](</)
componentType[14,15..14,15] componentType[14,15..14,15]
Identifier[14,15](Case) TypedIdentifier[14,15](Case)
ComponentClose[14,19](>) ComponentClose[14,19](>)
bodyText[14,20..14,20] bodyText[14,20..14,20]
jStringBodyText[14,20..14,20] jStringBodyText[14,20..14,20]
@ -145,7 +139,7 @@ compilationUnit[1,1..21,1]
ComponentOpen[15,13](<) ComponentOpen[15,13](<)
componentArgs[15,14..15,14] componentArgs[15,14..15,14]
componentType[15,14..15,14] componentType[15,14..15,14]
Identifier[15,14](Default) TypedIdentifier[15,14](Default)
ComponentClose[15,21](>) ComponentClose[15,21](>)
body[15,22..16,40] body[15,22..16,40]
bodyText[15,22..15,22] bodyText[15,22..15,22]
@ -157,7 +151,7 @@ compilationUnit[1,1..21,1]
ComponentOpen[16,17](<) ComponentOpen[16,17](<)
componentArgs[16,18..16,18] componentArgs[16,18..16,18]
componentType[16,18..16,18] componentType[16,18..16,18]
Identifier[16,18](p) StringIdentifier[16,18](p)
ComponentClose[16,19](>) ComponentClose[16,19](>)
body[16,20..16,20] body[16,20..16,20]
bodyText[16,20..16,20] bodyText[16,20..16,20]
@ -166,7 +160,7 @@ compilationUnit[1,1..21,1]
closingComponent[16,36..16,39] closingComponent[16,36..16,39]
ClosingComponentOpen[16,36](</) ClosingComponentOpen[16,36](</)
componentType[16,38..16,38] componentType[16,38..16,38]
Identifier[16,38](p) StringIdentifier[16,38](p)
ComponentClose[16,39](>) ComponentClose[16,39](>)
bodyText[16,40..16,40] bodyText[16,40..16,40]
jStringBodyText[16,40..16,40] jStringBodyText[16,40..16,40]
@ -174,21 +168,15 @@ compilationUnit[1,1..21,1]
closingComponent[17,13..17,22] closingComponent[17,13..17,22]
ClosingComponentOpen[17,13](</) ClosingComponentOpen[17,13](</)
componentType[17,15..17,15] componentType[17,15..17,15]
Identifier[17,15](Default) TypedIdentifier[17,15](Default)
ComponentClose[17,22](>) ComponentClose[17,22](>)
bodyText[17,23..17,23] bodyText[17,23..17,23]
jStringBodyText[17,23..17,23] jStringBodyText[17,23..17,23]
RawText[17,23](\n ) RawText[17,23](\n )
closingComponent[18,9..18,33] closingComponent[18,9..18,33]
ClosingComponentOpen[18,9](</) ClosingComponentOpen[18,9](</)
componentType[18,11..18,27] componentType[18,11..18,11]
Identifier[18,11](groowt) TypedIdentifier[18,11](groowt.view.web.Select)
Dot[18,17](.)
Identifier[18,18](view)
Dot[18,22](.)
Identifier[18,23](web)
Dot[18,26](.)
Identifier[18,27](Select)
ComponentClose[18,33](>) ComponentClose[18,33](>)
bodyText[18,34..18,34] bodyText[18,34..18,34]
jStringBodyText[18,34..18,34] jStringBodyText[18,34..18,34]
@ -196,7 +184,7 @@ compilationUnit[1,1..21,1]
closingComponent[19,5..19,11] closingComponent[19,5..19,11]
ClosingComponentOpen[19,5](</) ClosingComponentOpen[19,5](</)
componentType[19,7..19,7] componentType[19,7..19,7]
Identifier[19,7](body) StringIdentifier[19,7](body)
ComponentClose[19,11](>) ComponentClose[19,11](>)
bodyText[19,12..19,12] bodyText[19,12..19,12]
jStringBodyText[19,12..19,12] jStringBodyText[19,12..19,12]
@ -204,7 +192,7 @@ compilationUnit[1,1..21,1]
closingComponent[20,1..20,7] closingComponent[20,1..20,7]
ClosingComponentOpen[20,1](</) ClosingComponentOpen[20,1](</)
componentType[20,3..20,3] componentType[20,3..20,3]
Identifier[20,3](html) StringIdentifier[20,3](html)
ComponentClose[20,7](>) ComponentClose[20,7](>)
bodyText[20,8..20,8] bodyText[20,8..20,8]
jStringBodyText[20,8..20,8] jStringBodyText[20,8..20,8]

View File

@ -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](</)
componentType[1,9..1,9]
StringIdentifier[1,9](html)
ComponentClose[1,13](>)
bodyText[1,14..1,14]
jStringBodyText[1,14..1,14]
RawText[1,14](\n)
EOF[2,1](<EOF>)

View File

@ -5,7 +5,6 @@ import groowt.view.component.web.antlr.TokenList;
import groowt.view.component.web.ast.DefaultAstBuilder; import groowt.view.component.web.ast.DefaultAstBuilder;
import groowt.view.component.web.ast.DefaultNodeFactory; import groowt.view.component.web.ast.DefaultNodeFactory;
import groowt.view.component.web.ast.node.BodyNode; 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.compiler.WebViewComponentTemplateCompileUnit;
import groowt.view.component.web.transpile.BodyTranspiler; import groowt.view.component.web.transpile.BodyTranspiler;
import groowt.view.component.web.transpile.TranspilerConfiguration; import groowt.view.component.web.transpile.TranspilerConfiguration;
@ -39,7 +38,7 @@ public abstract class BodyTranspilerTests {
final var parseResult = ParserUtil.parseCompilationUnit(source); final var parseResult = ParserUtil.parseCompilationUnit(source);
final var tokenList = new TokenList(parseResult.getTokenStream()); final var tokenList = new TokenList(parseResult.getTokenStream());
final var b = new DefaultAstBuilder(new DefaultNodeFactory(tokenList)); 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(); final var bodyNode = cuNode.getBodyNode();
if (bodyNode == null) { if (bodyNode == null) {
fail("No BodyNode was built for source: " + source); fail("No BodyNode was built for source: " + source);

View File

@ -5,7 +5,6 @@ import groowt.view.component.web.antlr.TokenList;
import groowt.view.component.web.ast.DefaultAstBuilder; import groowt.view.component.web.ast.DefaultAstBuilder;
import groowt.view.component.web.ast.DefaultNodeFactory; import groowt.view.component.web.ast.DefaultNodeFactory;
import groowt.view.component.web.ast.node.BodyNode; 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.ast.node.GStringBodyTextNode;
import groowt.view.component.web.transpile.GStringTranspiler; import groowt.view.component.web.transpile.GStringTranspiler;
import org.codehaus.groovy.ast.expr.ClosureExpression; import org.codehaus.groovy.ast.expr.ClosureExpression;
@ -34,7 +33,7 @@ public abstract class GStringTranspilerTests {
final var tokenList = new TokenList(parseResult.getTokenStream()); final var tokenList = new TokenList(parseResult.getTokenStream());
final var nodeFactory = new DefaultNodeFactory(tokenList); final var nodeFactory = new DefaultNodeFactory(tokenList);
final var astBuilder = new DefaultAstBuilder(nodeFactory); 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()); return Objects.requireNonNull(cuNode.getBodyNode());
} }

View File

@ -9,7 +9,6 @@ import groowt.view.component.web.antlr.ParserUtil;
import groowt.view.component.web.antlr.TokenList; import groowt.view.component.web.antlr.TokenList;
import groowt.view.component.web.ast.DefaultAstBuilder; import groowt.view.component.web.ast.DefaultAstBuilder;
import groowt.view.component.web.ast.DefaultNodeFactory; 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.AnonymousWebViewComponent;
import groowt.view.component.web.compiler.DefaultWebViewComponentTemplateCompileUnit; import groowt.view.component.web.compiler.DefaultWebViewComponentTemplateCompileUnit;
import groowt.view.component.web.transpile.GroovyTranspiler; import groowt.view.component.web.transpile.GroovyTranspiler;
@ -39,7 +38,7 @@ public abstract class GroovyTranspilerTests {
final var parseResult = ParserUtil.parseCompilationUnit(source); final var parseResult = ParserUtil.parseCompilationUnit(source);
final var tokenList = new TokenList(parseResult.getTokenStream()); final var tokenList = new TokenList(parseResult.getTokenStream());
final var astBuilder = new DefaultAstBuilder(new DefaultNodeFactory(tokenList)); final var astBuilder = new DefaultAstBuilder(new DefaultNodeFactory(tokenList));
final var cuNode = (CompilationUnitNode) astBuilder.build(parseResult.getCompilationUnitContext()); final var cuNode = astBuilder.buildCompilationUnit(parseResult.getCompilationUnitContext());
try { try {
this.transpiler.transpile( this.transpiler.transpile(
new DefaultComponentTemplateCompilerConfiguration(), new DefaultComponentTemplateCompilerConfiguration(),

View File

@ -3,8 +3,10 @@ package groowt.view.component.web.tools
import groovy.transform.InheritConstructors import groovy.transform.InheritConstructors
import groowt.view.component.web.antlr.* import groowt.view.component.web.antlr.*
import groowt.view.component.web.antlr.AntlrUtil.ParseErrorCollector import groowt.view.component.web.antlr.AntlrUtil.ParseErrorCollector
import groowt.view.component.web.antlr.WebViewComponentsParser.CompilationUnitContext
import groowt.view.component.web.util.ExtensionUtil import groowt.view.component.web.util.ExtensionUtil
import org.antlr.v4.runtime.CharStreams import org.antlr.v4.runtime.CharStreams
import org.antlr.v4.runtime.ConsoleErrorListener
@InheritConstructors @InheritConstructors
final class ParseTreeFileMaker extends AbstractOutputFileMaker { final class ParseTreeFileMaker extends AbstractOutputFileMaker {
@ -36,7 +38,7 @@ final class ParseTreeFileMaker extends AbstractOutputFileMaker {
private boolean onErrors( private boolean onErrors(
String name, String name,
WebViewComponentsParser parser, WebViewComponentsParser parser,
WebViewComponentsParser.CompilationUnitContext cu, CompilationUnitContext cu,
ParseErrorCollector errors ParseErrorCollector errors
) { ) {
def errorCount = errors.errorCount def errorCount = errors.errorCount
@ -53,14 +55,35 @@ final class ParseTreeFileMaker extends AbstractOutputFileMaker {
} }
} }
private Tuple3<WebViewComponentsParser, WebViewComponentsParser.CompilationUnitContext, ParseErrorCollector> parse( private Tuple3<WebViewComponentsParser, CompilationUnitContext, ParseErrorCollector> parse(File sourceFile) {
File sourceFile
) {
def input = CharStreams.fromFileName(sourceFile.toString()) def input = CharStreams.fromFileName(sourceFile.toString())
def lexer = new WebViewComponentsLexer(input) def lexer = new WebViewComponentsLexer(input)
def lexerErrorListener = new LexerErrorListener()
lexer.removeErrorListener(ConsoleErrorListener.INSTANCE)
lexer.addErrorListener(lexerErrorListener)
def tokenStream = new WebViewComponentsTokenStream(lexer) def tokenStream = new WebViewComponentsTokenStream(lexer)
def parser = new WebViewComponentsParser(tokenStream) def parser = new WebViewComponentsParser(tokenStream)
def parserErrorListener = new ParserErrorListener()
parser.removeErrorListener(ConsoleErrorListener.INSTANCE)
parser.addErrorListener(parserErrorListener)
def cu = parser.compilationUnit() 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) def errors = AntlrUtil.findErrorNodes(cu)
new Tuple3<>(parser, cu, errors) new Tuple3<>(parser, cu, errors)
} }
@ -71,12 +94,28 @@ final class ParseTreeFileMaker extends AbstractOutputFileMaker {
println "Processing: $name" println "Processing: $name"
boolean doneYet = false boolean doneYet = false
while (!doneYet) { while (!doneYet) {
def (parser, cu, errors) = this.parse(sourceFile) 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()) { if (errors.isEmpty()) {
doneYet = this.onSuccess(name, parser, cu) doneYet = this.onSuccess(name, parser, cu)
} else { } else {
doneYet = this.onErrors(name, parser, cu, errors) 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)
}
} }
} }

View File

@ -1,11 +1,10 @@
package groowt.view.component.web.tools package groowt.view.component.web.tools
import groovy.transform.InheritConstructors import groovy.transform.InheritConstructors
import groowt.view.component.web.antlr.TokenUtil import groowt.view.component.web.antlr.*
import groowt.view.component.web.antlr.WebViewComponentsLexer
import groowt.view.component.web.antlr.WebViewComponentsTokenStream
import groowt.view.component.web.util.ExtensionUtil import groowt.view.component.web.util.ExtensionUtil
import org.antlr.v4.runtime.CharStreams import org.antlr.v4.runtime.CharStreams
import org.antlr.v4.runtime.ConsoleErrorListener
import org.antlr.v4.runtime.Token import org.antlr.v4.runtime.Token
@InheritConstructors @InheritConstructors
@ -25,6 +24,18 @@ class TokensFileMaker extends AbstractOutputFileMaker {
} }
} }
protected boolean onLexerErrors(String name, List<LexerError> 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) { protected boolean onException(String name, Exception e) {
println "There was an exception while tokenizing $name: $e.message" println "There was an exception while tokenizing $name: $e.message"
e.printStackTrace() e.printStackTrace()
@ -46,8 +57,16 @@ class TokensFileMaker extends AbstractOutputFileMaker {
try { try {
def input = CharStreams.fromString(sourceFile.getText()) def input = CharStreams.fromString(sourceFile.getText())
def lexer = new WebViewComponentsLexer(input) def lexer = new WebViewComponentsLexer(input)
lexer.removeErrorListener(ConsoleErrorListener.INSTANCE)
def lexerErrorListener = new LexerErrorListener()
lexer.addErrorListener(lexerErrorListener)
def tokenStream = new WebViewComponentsTokenStream(lexer) 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) { } catch (Exception e) {
doneYet = this.onException(name, e) doneYet = this.onException(name, e)
} }

View File

@ -2,11 +2,11 @@
package groowt.view.component.web.tools package groowt.view.component.web.tools
import groowt.view.component.web.antlr.formatToken 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<String>) { fun main(args: Array<String>) {
val options = processArgs(args) val options = processArgs(args)
configureLog(options.logLevel) configureLog(options.logLevel)
val input = getInput(options.source) val input = getInput(options.source)
runLexerAllTokens(input).forEachIndexed { i, t -> println("$i: ${formatToken(t)}") } runLexerAllTokensRaw(input).forEachIndexed { i, t -> println("$i: ${formatToken(t)}") }
} }