Major work on lexer/parser to help intellij plugin.
This commit is contained in:
parent
1b6344dbf2
commit
bfa2ec6cd7
@ -10,6 +10,7 @@ plugins {
|
||||
id 'groovy'
|
||||
id 'org.jetbrains.kotlin.jvm'
|
||||
id 'java-test-fixtures'
|
||||
id 'distribution'
|
||||
}
|
||||
|
||||
repositories {
|
||||
@ -104,6 +105,12 @@ tasks.named('generateWebViewComponentsLexerBase', GroowtAntlrExecTask) { task ->
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named({ String name ->
|
||||
name in ['compileJava', 'compileGroovy', 'compileKotlin']
|
||||
} as Spec<String>).configureEach {
|
||||
dependsOn 'generateAllAntlr'
|
||||
}
|
||||
|
||||
tasks.register('groovyConsole', JavaExec) {
|
||||
group = 'groovy'
|
||||
classpath += sourceSets.main.runtimeClasspath + configurations.groovyConsole
|
||||
@ -112,14 +119,13 @@ tasks.register('groovyConsole', JavaExec) {
|
||||
|
||||
tasks.register('toolsJar', Jar) {
|
||||
group 'tools'
|
||||
archiveBaseName = 'web-tools'
|
||||
archiveBaseName = 'web-view-components-compiler-tools'
|
||||
exclude { FileTreeElement element ->
|
||||
element.file in sourceSets.main.output
|
||||
}
|
||||
from sourceSets.tools.output
|
||||
from sourceSets.tools.runtimeClasspath.filter(File.&exists).collect { it.isDirectory() ? it : zipTree(it) }
|
||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
dependsOn tasks.named('jar')
|
||||
}
|
||||
|
||||
@NullCheck
|
||||
@ -161,9 +167,9 @@ toolSpecs.each { spec ->
|
||||
group = 'tools'
|
||||
outputDir = file('bin')
|
||||
applicationName = spec.name
|
||||
classpath = sourceSets.tools.runtimeClasspath
|
||||
mainClass = spec.fullMainClass
|
||||
unixStartScriptGenerator.template = resources.text.fromFile('src/tools/binTemplate.gst')
|
||||
dependsOn tasks.named('toolsJar')
|
||||
}
|
||||
}
|
||||
|
||||
@ -198,6 +204,12 @@ java {
|
||||
withSourcesJar()
|
||||
}
|
||||
|
||||
tasks.named('sourcesJar', Jar) {
|
||||
from fileTree('src/main/antlr')
|
||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
dependsOn 'generateAllAntlr'
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
create('webViewComponentsCompiler', MavenPublication) {
|
||||
@ -206,4 +218,3 @@ publishing {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@ options {
|
||||
|
||||
tokens {
|
||||
PreambleBreak,
|
||||
ComponentNlws,
|
||||
GroovyCode,
|
||||
GStringAttrValueEnd,
|
||||
JStringAttrValueEnd,
|
||||
@ -15,6 +16,10 @@ tokens {
|
||||
DollarScriptletClose
|
||||
}
|
||||
|
||||
channels {
|
||||
ERROR
|
||||
}
|
||||
|
||||
@header {
|
||||
import java.util.Set;
|
||||
import static groowt.view.component.web.antlr.LexerSemanticPredicates.*;
|
||||
@ -83,6 +88,10 @@ tokens {
|
||||
DollarSlashyStringClosureStart
|
||||
);
|
||||
|
||||
public WebViewComponentsLexerBase() {
|
||||
_interp = new LexerATNSimulator(this,_ATN,_decisionToDFA,_sharedContextCache);
|
||||
}
|
||||
|
||||
private void onPreambleClose() {
|
||||
this.setType(PreambleBreak);
|
||||
this.exitPreamble();
|
||||
@ -112,11 +121,19 @@ PreambleOpen
|
||||
;
|
||||
|
||||
ComponentOpen
|
||||
: LT -> pushMode(TAG_START)
|
||||
: LT { !isAnyOf(this.getNextChar(), '/', '>') }? -> pushMode(TAG_START)
|
||||
;
|
||||
|
||||
ClosingComponentOpen
|
||||
: LT FS -> pushMode(TAG_START)
|
||||
: LT FS { !this.isNext('>') }? -> pushMode(TAG_START)
|
||||
;
|
||||
|
||||
FragmentOpen
|
||||
: LT GT
|
||||
;
|
||||
|
||||
FragmentClose
|
||||
: LT FS GT
|
||||
;
|
||||
|
||||
EqualsScriptletOpen
|
||||
@ -161,10 +178,6 @@ RawText
|
||||
// ----------------------------------------
|
||||
mode TAG_START;
|
||||
|
||||
FragmentClose
|
||||
: GT -> popMode
|
||||
;
|
||||
|
||||
TypedIdentifier
|
||||
: ( PackageIdentifier DOT )* ClassIdentifier ( DOT ClassIdentifier )* -> mode(IN_TAG)
|
||||
;
|
||||
@ -213,6 +226,14 @@ StringIdentifierChar
|
||||
: [-_0-9\p{L}]
|
||||
;
|
||||
|
||||
TagStartNlws
|
||||
: NLWS+ -> type(ComponentNlws), channel(HIDDEN)
|
||||
;
|
||||
|
||||
TagStartError
|
||||
: . -> channel(ERROR)
|
||||
;
|
||||
|
||||
// ----------------------------------------
|
||||
mode IN_TAG;
|
||||
|
||||
@ -255,7 +276,7 @@ JStringAttrValueStart
|
||||
;
|
||||
|
||||
ClosureAttrValueStart
|
||||
: LEFT_CURLY ComponentNlws? { !this.isNext('<') }?
|
||||
: LEFT_CURLY InTagNlws? { !this.isNext('<') }?
|
||||
{
|
||||
this.curlies.push(() -> {
|
||||
this.setType(ClosureAttrValueEnd);
|
||||
@ -267,15 +288,19 @@ ClosureAttrValueStart
|
||||
;
|
||||
|
||||
ComponentAttrValueStart
|
||||
: LEFT_CURLY ComponentNlws? { this.isNext('<') }?
|
||||
: LEFT_CURLY InTagNlws? { this.isNext('<') }?
|
||||
;
|
||||
|
||||
ComponentAttrValueEnd
|
||||
: GT RIGHT_CURLY
|
||||
;
|
||||
|
||||
ComponentNlws
|
||||
: NLWS+
|
||||
InTagNlws
|
||||
: NLWS+ -> type(ComponentNlws), channel(HIDDEN)
|
||||
;
|
||||
|
||||
TagError
|
||||
: . -> channel(ERROR)
|
||||
;
|
||||
|
||||
// ----------------------------------------
|
||||
|
@ -49,21 +49,20 @@ openComponent
|
||||
;
|
||||
|
||||
closingComponent
|
||||
: ClosingComponentOpen ComponentNlws?
|
||||
componentType ComponentNlws?
|
||||
: ClosingComponentOpen
|
||||
componentType
|
||||
ComponentClose
|
||||
;
|
||||
|
||||
fragmentComponent
|
||||
: ComponentOpen ComponentClose
|
||||
body
|
||||
ClosingComponentOpen ComponentClose
|
||||
: FragmentOpen body FragmentClose
|
||||
;
|
||||
|
||||
componentArgs
|
||||
: ComponentNlws? componentType
|
||||
ComponentNlws? componentConstructor?
|
||||
ComponentNlws? ( attr ComponentNlws? )*
|
||||
: componentType
|
||||
( attr )*
|
||||
componentConstructor?
|
||||
( attr )*
|
||||
;
|
||||
|
||||
componentType
|
||||
|
@ -45,8 +45,8 @@ public abstract class AbstractWebViewComponentsLexer extends Lexer {
|
||||
|
||||
protected record StringClosingEndSpec(int type, int popCount) implements GStringPathEndSpec {}
|
||||
|
||||
protected final PairCounter curlies = new SimplePairCounter(this);
|
||||
protected final PairCounter parentheses = new SimplePairCounter(this);
|
||||
protected PairCounter curlies = new SimplePairCounter();
|
||||
protected PairCounter parentheses = new SimplePairCounter();
|
||||
|
||||
private final Logger logger;
|
||||
|
||||
@ -57,6 +57,58 @@ public abstract class AbstractWebViewComponentsLexer extends Lexer {
|
||||
public AbstractWebViewComponentsLexer(CharStream input) {
|
||||
super(input);
|
||||
this.logger = LoggerFactory.getLogger(this.getClass());
|
||||
this.curlies.setLexer(this);
|
||||
this.parentheses.setLexer(this);
|
||||
}
|
||||
|
||||
public AbstractWebViewComponentsLexer() {
|
||||
this.logger = LoggerFactory.getLogger(this.getClass());
|
||||
this.curlies.setLexer(this);
|
||||
this.parentheses.setLexer(this);
|
||||
}
|
||||
|
||||
public PairCounter getCurlies() {
|
||||
return this.curlies;
|
||||
}
|
||||
|
||||
public void setCurlies(PairCounter curlies) {
|
||||
this.curlies = curlies;
|
||||
}
|
||||
|
||||
public PairCounter getParentheses() {
|
||||
return this.parentheses;
|
||||
}
|
||||
|
||||
public void setParentheses(PairCounter parentheses) {
|
||||
this.parentheses = parentheses;
|
||||
}
|
||||
|
||||
public Logger getLogger() {
|
||||
return this.logger;
|
||||
}
|
||||
|
||||
public boolean isCanPreamble() {
|
||||
return this.canPreamble;
|
||||
}
|
||||
|
||||
public void setCanPreamble(boolean canPreamble) {
|
||||
this.canPreamble = canPreamble;
|
||||
}
|
||||
|
||||
public boolean isInPreamble() {
|
||||
return this.inPreamble;
|
||||
}
|
||||
|
||||
public void setInPreamble(boolean inPreamble) {
|
||||
this.inPreamble = inPreamble;
|
||||
}
|
||||
|
||||
public boolean isInConstructor() {
|
||||
return this.inConstructor;
|
||||
}
|
||||
|
||||
public void setInConstructor(boolean inConstructor) {
|
||||
this.inConstructor = inConstructor;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -2,8 +2,15 @@ package groowt.view.component.web.antlr
|
||||
|
||||
import groowt.view.component.web.util.SourcePosition
|
||||
|
||||
data class LexerError(val type: LexerErrorType, val sourcePosition: SourcePosition)
|
||||
data class LexerError(
|
||||
val type: LexerErrorType,
|
||||
val sourcePosition: SourcePosition,
|
||||
val badText: String,
|
||||
val lexerMode: Int
|
||||
)
|
||||
|
||||
fun format(lexerError: LexerError): String {
|
||||
return "At ${lexerError.sourcePosition.toStringLong()}: ${lexerError.type.message}"
|
||||
fun formatLexerError(lexerError: LexerError): String {
|
||||
return "At ${lexerError.sourcePosition.toStringLong()}: ${lexerError.type.message} " +
|
||||
"Bad input: '${escapeChars(lexerError.badText)}'. " +
|
||||
"(mode: ${WebViewComponentsLexer.modeNames[lexerError.lexerMode]})."
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ 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 org.antlr.v4.runtime.misc.Interval
|
||||
import java.util.*
|
||||
|
||||
class LexerErrorListener : ANTLRErrorListener {
|
||||
@ -22,7 +23,12 @@ class LexerErrorListener : ANTLRErrorListener {
|
||||
) {
|
||||
if (e is LexerNoViableAltException) {
|
||||
val sourcePosition = SourcePosition(line, charPositionInLine + 1)
|
||||
val lexerError = LexerError(LexerErrorType.NO_VIABLE_ALTERNATIVE, sourcePosition)
|
||||
val lexerError = LexerError(
|
||||
LexerErrorType.NO_VIABLE_ALTERNATIVE,
|
||||
sourcePosition,
|
||||
e.inputStream.getText(Interval.of(e.startIndex, e.startIndex)),
|
||||
(recognizer as WebViewComponentsLexer)._mode
|
||||
)
|
||||
errors.add(lexerError)
|
||||
} else {
|
||||
throw e
|
||||
|
@ -1,5 +1,5 @@
|
||||
package groowt.view.component.web.antlr
|
||||
|
||||
enum class LexerErrorType(val message: String) {
|
||||
NO_VIABLE_ALTERNATIVE("No viable alternative.")
|
||||
NO_VIABLE_ALTERNATIVE("Lexer has no viable alternative.")
|
||||
}
|
||||
|
@ -1,5 +1,11 @@
|
||||
package groowt.view.component.web.antlr;
|
||||
|
||||
import org.antlr.v4.runtime.Lexer;
|
||||
import org.antlr.v4.runtime.misc.IntegerStack;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Deque;
|
||||
|
||||
public interface PairCounter {
|
||||
|
||||
@FunctionalInterface
|
||||
@ -7,6 +13,17 @@ public interface PairCounter {
|
||||
void after();
|
||||
}
|
||||
|
||||
interface StackEntry {
|
||||
IntegerStack getModes();
|
||||
@Nullable OnPop getOnPop();
|
||||
int get();
|
||||
int increment();
|
||||
int decrement();
|
||||
}
|
||||
|
||||
Deque<StackEntry> getStack();
|
||||
void setLexer(Lexer lexer);
|
||||
|
||||
void push();
|
||||
void push(OnPop onPop);
|
||||
void pop();
|
||||
|
@ -14,7 +14,7 @@ class MismatchedInputParserError(
|
||||
val expectedTokenTypes: Set<Int>
|
||||
) : ParserError(type, offending, context)
|
||||
|
||||
fun format(error: ParserError): String {
|
||||
fun formatParserError(error: ParserError): String {
|
||||
val sb = StringBuilder()
|
||||
val sourcePosition = SourcePosition.fromStartOfToken(error.offending)
|
||||
sb.append("At ")
|
||||
|
@ -1,12 +1,16 @@
|
||||
package groowt.view.component.web.antlr
|
||||
|
||||
import groowt.view.component.web.util.SourcePosition
|
||||
import org.antlr.v4.runtime.*
|
||||
import org.antlr.v4.runtime.misc.Interval
|
||||
|
||||
class ParserErrorListener : BaseErrorListener() {
|
||||
|
||||
private val errors: MutableList<ParserError> = ArrayList()
|
||||
private val lexerErrors: MutableList<LexerError> = ArrayList()
|
||||
private val parserErrors: MutableList<ParserError> = ArrayList()
|
||||
|
||||
fun getErrors(): List<ParserError> = this.errors
|
||||
fun getLexerErrors(): List<LexerError> = this.lexerErrors
|
||||
fun getParserErrors(): List<ParserError> = this.parserErrors
|
||||
|
||||
override fun syntaxError(
|
||||
recognizer: Recognizer<*, *>,
|
||||
@ -18,9 +22,16 @@ class ParserErrorListener : BaseErrorListener() {
|
||||
) {
|
||||
val parser = recognizer as WebViewComponentsParser
|
||||
when (e) {
|
||||
is LexerNoViableAltException -> {
|
||||
val lexer = e.recognizer as WebViewComponentsLexer
|
||||
val sourcePosition = SourcePosition(line, charPositionInLine + 1)
|
||||
val badText = lexer.inputStream.getText(Interval.of(e.startIndex, e.startIndex))
|
||||
val error = LexerError(LexerErrorType.NO_VIABLE_ALTERNATIVE, sourcePosition, badText, lexer._mode)
|
||||
this.lexerErrors.add(error)
|
||||
}
|
||||
is NoViableAltException -> {
|
||||
val error = ParserError(ParserErrorType.NO_VIABLE_ALTERNATIVE, e.offendingToken, parser.context)
|
||||
errors.add(error)
|
||||
parserErrors.add(error)
|
||||
}
|
||||
is InputMismatchException -> {
|
||||
val error = MismatchedInputParserError(
|
||||
@ -29,11 +40,11 @@ class ParserErrorListener : BaseErrorListener() {
|
||||
parser.context,
|
||||
e.expectedTokens.toSet()
|
||||
)
|
||||
errors.add(error)
|
||||
parserErrors.add(error)
|
||||
}
|
||||
is FailedPredicateException -> {
|
||||
val error = ParserError(ParserErrorType.FAILED_PREDICATE, e.offendingToken, parser.context)
|
||||
errors.add(error)
|
||||
parserErrors.add(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +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.")
|
||||
NO_VIABLE_ALTERNATIVE("Parser has no viable alternative."),
|
||||
INPUT_MISMATCH("Parser input mismatch."),
|
||||
FAILED_PREDICATE("Parser input failed predicate.")
|
||||
}
|
||||
|
@ -18,6 +18,8 @@ data class CompilationUnitParseResult(
|
||||
val lexer: WebViewComponentsLexer,
|
||||
val tokenStream: WebViewComponentsTokenStream,
|
||||
val parser: WebViewComponentsParser,
|
||||
val lexerErrors: List<LexerError>,
|
||||
val parserErrors: List<ParserError>,
|
||||
val compilationUnitContext: CompilationUnitContext
|
||||
)
|
||||
|
||||
@ -33,9 +35,21 @@ fun parseCompilationUnit(reader: Reader): CompilationUnitParseResult =
|
||||
fun parseCompilationUnit(charStream: CharStream): CompilationUnitParseResult {
|
||||
val lexer = WebViewComponentsLexer(charStream)
|
||||
val tokenStream = WebViewComponentsTokenStream(lexer)
|
||||
|
||||
val parser = WebViewComponentsParser(tokenStream)
|
||||
parser.removeErrorListener(ConsoleErrorListener.INSTANCE)
|
||||
val parserErrorListener = ParserErrorListener()
|
||||
parser.addErrorListener(parserErrorListener)
|
||||
|
||||
val cu = parser.compilationUnit()
|
||||
return CompilationUnitParseResult(lexer, tokenStream, parser, cu)
|
||||
return CompilationUnitParseResult(
|
||||
lexer,
|
||||
tokenStream,
|
||||
parser,
|
||||
parserErrorListener.getLexerErrors(),
|
||||
parserErrorListener.getParserErrors(),
|
||||
cu
|
||||
)
|
||||
}
|
||||
|
||||
fun parseCompilationUnit(
|
||||
|
@ -1,48 +1,76 @@
|
||||
package groowt.view.component.web.antlr
|
||||
|
||||
import groowt.view.component.web.antlr.PairCounter.StackEntry
|
||||
import org.antlr.v4.runtime.Lexer
|
||||
import org.antlr.v4.runtime.misc.IntegerStack
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.util.*
|
||||
|
||||
class SimplePairCounter(private val lexer: Lexer) : PairCounter {
|
||||
class SimplePairCounter : PairCounter {
|
||||
|
||||
companion object {
|
||||
private val logger: Logger = LoggerFactory.getLogger(SimplePairCounter::class.java)
|
||||
}
|
||||
|
||||
private class StackEntry(val modes: IntegerStack, val onPop: PairCounter.OnPop?) {
|
||||
class SimpleStackEntry(private val modes: IntegerStack, private val onPop: PairCounter.OnPop?) : StackEntry {
|
||||
|
||||
private var count: Int = 0
|
||||
fun get() = this.count
|
||||
fun increment(): Int = ++this.count
|
||||
fun decrement(): Int = --this.count
|
||||
|
||||
override fun getModes() = modes
|
||||
override fun getOnPop() = onPop
|
||||
override fun get() = this.count
|
||||
override fun increment(): Int = ++this.count
|
||||
override fun decrement(): Int = --this.count
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is StackEntry) return false
|
||||
return modes == other.modes
|
||||
&& onPop == other.onPop
|
||||
&& get() == other.get()
|
||||
}
|
||||
|
||||
override fun hashCode() = Objects.hash(modes, onPop, count)
|
||||
|
||||
override fun toString() = "SimpleStackEntry($count)"
|
||||
|
||||
}
|
||||
|
||||
private val stack: Deque<StackEntry> = LinkedList()
|
||||
private val stack: Deque<SimpleStackEntry> = LinkedList()
|
||||
private var lexer: Lexer? = null
|
||||
|
||||
private fun currentEntry(): StackEntry {
|
||||
override fun getStack(): Deque<StackEntry> = LinkedList(stack)
|
||||
|
||||
override fun setLexer(lexer: Lexer?) {
|
||||
this.lexer = lexer
|
||||
}
|
||||
|
||||
private fun currentEntry(): SimpleStackEntry {
|
||||
if (this.stack.isEmpty()) {
|
||||
throw IllegalStateException("Cannot currentEntry() when stack is empty!")
|
||||
}
|
||||
return this.stack.peek()
|
||||
}
|
||||
|
||||
private fun getLexerModes() = IntegerStack(this.lexer._modeStack).also { it.push(this.lexer._mode) }
|
||||
private fun getLexerModes(): IntegerStack {
|
||||
if (lexer == null) {
|
||||
throw IllegalStateException("lexer is null")
|
||||
}
|
||||
return IntegerStack(lexer!!._modeStack).also { it.push(lexer!!._mode) }
|
||||
}
|
||||
|
||||
override fun push() {
|
||||
this.stack.push(StackEntry(this.getLexerModes(), null))
|
||||
this.stack.push(SimpleStackEntry(this.getLexerModes(), null))
|
||||
}
|
||||
|
||||
override fun push(onPop: PairCounter.OnPop?) {
|
||||
this.stack.push(StackEntry(this.getLexerModes(), onPop))
|
||||
this.stack.push(SimpleStackEntry(this.getLexerModes(), onPop))
|
||||
}
|
||||
|
||||
override fun pop() {
|
||||
val entry = this.stack.pop()
|
||||
if (entry.onPop != null) {
|
||||
entry.onPop.after()
|
||||
}
|
||||
entry.onPop?.after()
|
||||
if (logger.isWarnEnabled) {
|
||||
val currentModes = this.getLexerModes()
|
||||
if (entry.modes != currentModes) {
|
||||
@ -80,4 +108,12 @@ class SimplePairCounter(private val lexer: Lexer) : PairCounter {
|
||||
this.stack.clear()
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is PairCounter) return false
|
||||
return stack == other.stack
|
||||
}
|
||||
|
||||
override fun hashCode(): Int = Objects.hash(stack)
|
||||
|
||||
}
|
||||
|
@ -9,6 +9,10 @@ public class WebViewComponentsLexer extends WebViewComponentsLexerBase {
|
||||
this._interp = new PositionAdjustingLexerATNSimulator(this, _ATN, _decisionToDFA, _sharedContextCache);
|
||||
}
|
||||
|
||||
public WebViewComponentsLexer() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PositionAdjustingLexerATNSimulator getPositionAdjustingInterpreter() {
|
||||
return (PositionAdjustingLexerATNSimulator) this.getInterpreter();
|
||||
|
@ -7,7 +7,15 @@ 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,
|
||||
private val ignoreChannels: Set<Int>
|
||||
) : TokenStream {
|
||||
|
||||
constructor(lexer: WebViewComponentsLexer) : this(
|
||||
lexer,
|
||||
setOf(WebViewComponentsLexer.HIDDEN, WebViewComponentsLexer.ERROR)
|
||||
)
|
||||
|
||||
private val tokens: MutableList<Token> = ArrayList()
|
||||
|
||||
@ -67,7 +75,7 @@ class WebViewComponentsTokenStream(private val tokenSource: TokenSource) : Token
|
||||
MergedGroovyCodeToken(groovyTokens, this.tokens.size, this.tokenSource, this.tokenSource.inputStream)
|
||||
)
|
||||
}
|
||||
if (followingToken != null) {
|
||||
if (followingToken != null && followingToken.channel !in this.ignoreChannels) {
|
||||
fetched++
|
||||
if (followingToken is WritableToken) {
|
||||
followingToken.tokenIndex = this.tokens.size
|
||||
@ -191,8 +199,7 @@ class WebViewComponentsTokenStream(private val tokenSource: TokenSource) : Token
|
||||
}
|
||||
|
||||
fun getAllTokensSkipEOF(): List<Token> {
|
||||
this.fill()
|
||||
return this.tokens.filter { it.type != Token.EOF }
|
||||
return this.getAllTokens().filter { it.type != Token.EOF }
|
||||
}
|
||||
|
||||
private fun fill() {
|
||||
|
@ -13,6 +13,7 @@ import org.junit.jupiter.api.TestFactory;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static groowt.view.component.web.antlr.WebViewComponentsLexer.*;
|
||||
@ -70,7 +71,10 @@ public class WebViewComponentsLexerTests {
|
||||
sourceFile -> {
|
||||
final CharStream input = CharStreams.fromString(FileUtil.readFile(sourceFile));
|
||||
final WebViewComponentsLexer lexer = new WebViewComponentsLexer(input);
|
||||
final WebViewComponentsTokenStream tokenStream = new WebViewComponentsTokenStream(lexer);
|
||||
final WebViewComponentsTokenStream tokenStream = new WebViewComponentsTokenStream(
|
||||
lexer,
|
||||
Set.of(WebViewComponentsLexer.HIDDEN) // include bad tokens for testing
|
||||
);
|
||||
final List<Token> allTokens = tokenStream.getAllTokensSkipEOF();
|
||||
final var sb = new StringBuilder();
|
||||
for (int i = 0; i < allTokens.size(); i++) {
|
||||
|
@ -5,6 +5,8 @@ import org.antlr.v4.runtime.CharStreams;
|
||||
import org.junit.jupiter.api.DynamicTest;
|
||||
import org.junit.jupiter.api.TestFactory;
|
||||
import org.junit.jupiter.api.function.Executable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
@ -22,6 +24,8 @@ import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public final class WebViewComponentsParserTests {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(WebViewComponentsParserTests.class);
|
||||
|
||||
private static final String parserFileBase = String.join(File.separator, "src", "test", "parser");
|
||||
private static final String parserTreeFileBase = String.join(File.separator, parserFileBase, "trees");
|
||||
private static final String parseTreeFileSuffix = "_parseTree";
|
||||
|
@ -2,11 +2,19 @@ package groowt.view.component.web.ast;
|
||||
|
||||
import groowt.view.component.web.antlr.ParserUtil;
|
||||
import groowt.view.component.web.antlr.TokenList;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static groowt.view.component.web.antlr.LexerErrorKt.formatLexerError;
|
||||
import static groowt.view.component.web.antlr.ParserErrorKt.formatParserError;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class DefaultAstBuilderTests extends AstBuilderTests {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(DefaultAstBuilderTests.class);
|
||||
|
||||
public DefaultAstBuilderTests() {
|
||||
super(
|
||||
Path.of("src", "test", "ast"),
|
||||
@ -19,6 +27,20 @@ public class DefaultAstBuilderTests extends AstBuilderTests {
|
||||
@Override
|
||||
protected BuildResult buildFromSource(String source) {
|
||||
final var parseResult = ParserUtil.parseCompilationUnit(source);
|
||||
|
||||
if (!parseResult.getLexerErrors().isEmpty()) {
|
||||
parseResult.getLexerErrors().forEach(error -> {
|
||||
logger.error(formatLexerError(error));
|
||||
});
|
||||
fail("There were lexer errors. See log for more information.");
|
||||
}
|
||||
if (!parseResult.getParserErrors().isEmpty()) {
|
||||
parseResult.getParserErrors().forEach(error -> {
|
||||
logger.error(formatParserError(error));
|
||||
});
|
||||
fail("There were parser errors. See log for more information.");
|
||||
}
|
||||
|
||||
final var tokenList = new TokenList(parseResult.getTokenStream());
|
||||
final var b = new DefaultAstBuilder(new DefaultNodeFactory(tokenList));
|
||||
return new BuildResult(b.build(parseResult.getCompilationUnitContext()), tokenList);
|
||||
|
@ -0,0 +1 @@
|
||||
<bad! />
|
1
web-view-components-compiler/src/test/lexer/fragment.wvc
Normal file
1
web-view-components-compiler/src/test/lexer/fragment.wvc
Normal file
@ -0,0 +1 @@
|
||||
<><p>Hello, World!</p></>
|
@ -0,0 +1 @@
|
||||
</
|
@ -0,0 +1,5 @@
|
||||
0: ComponentOpen[1,1](<)
|
||||
1: StringIdentifier[1,2](bad)
|
||||
2: TagError[1,5](!)
|
||||
3: ComponentSelfClose[1,7](/>)
|
||||
4: RawText[1,9](\n)
|
@ -33,51 +33,50 @@
|
||||
32: RawText[11,33](\n )
|
||||
33: ComponentOpen[12,13](<)
|
||||
34: TypedIdentifier[12,14](Case)
|
||||
35: ComponentNlws[12,18]( )
|
||||
36: AttributeIdentifier[12,19](cond)
|
||||
37: Equals[12,23](=)
|
||||
38: ClosureAttrValueStart[12,24]({)
|
||||
39: GroovyCode[12,25](isItTrue())
|
||||
40: ClosureAttrValueEnd[12,35](})
|
||||
41: ComponentClose[12,36](>)
|
||||
42: RawText[12,37](\n )
|
||||
43: ComponentOpen[13,17](<)
|
||||
44: StringIdentifier[13,18](p)
|
||||
45: ComponentClose[13,19](>)
|
||||
46: RawText[13,20](It's true! :))
|
||||
47: ClosingComponentOpen[13,33](</)
|
||||
48: StringIdentifier[13,35](p)
|
||||
49: ComponentClose[13,36](>)
|
||||
50: RawText[13,37](\n )
|
||||
51: ClosingComponentOpen[14,13](</)
|
||||
52: TypedIdentifier[14,15](Case)
|
||||
53: ComponentClose[14,19](>)
|
||||
54: RawText[14,20](\n )
|
||||
55: ComponentOpen[15,13](<)
|
||||
56: TypedIdentifier[15,14](Default)
|
||||
57: ComponentClose[15,21](>)
|
||||
58: RawText[15,22](\n )
|
||||
59: ComponentOpen[16,17](<)
|
||||
60: StringIdentifier[16,18](p)
|
||||
61: ComponentClose[16,19](>)
|
||||
62: RawText[16,20](It's false... :()
|
||||
63: ClosingComponentOpen[16,36](</)
|
||||
64: StringIdentifier[16,38](p)
|
||||
65: ComponentClose[16,39](>)
|
||||
66: RawText[16,40](\n )
|
||||
67: ClosingComponentOpen[17,13](</)
|
||||
68: TypedIdentifier[17,15](Default)
|
||||
69: ComponentClose[17,22](>)
|
||||
70: RawText[17,23](\n )
|
||||
71: ClosingComponentOpen[18,9](</)
|
||||
72: TypedIdentifier[18,11](groowt.view.web.Select)
|
||||
73: ComponentClose[18,33](>)
|
||||
74: RawText[18,34](\n )
|
||||
75: ClosingComponentOpen[19,5](</)
|
||||
76: StringIdentifier[19,7](body)
|
||||
77: ComponentClose[19,11](>)
|
||||
78: RawText[19,12](\n)
|
||||
79: ClosingComponentOpen[20,1](</)
|
||||
80: StringIdentifier[20,3](html)
|
||||
81: ComponentClose[20,7](>)
|
||||
82: RawText[20,8](\n)
|
||||
35: AttributeIdentifier[12,19](cond)
|
||||
36: Equals[12,23](=)
|
||||
37: ClosureAttrValueStart[12,24]({)
|
||||
38: GroovyCode[12,25](isItTrue())
|
||||
39: ClosureAttrValueEnd[12,35](})
|
||||
40: ComponentClose[12,36](>)
|
||||
41: RawText[12,37](\n )
|
||||
42: ComponentOpen[13,17](<)
|
||||
43: StringIdentifier[13,18](p)
|
||||
44: ComponentClose[13,19](>)
|
||||
45: RawText[13,20](It's true! :))
|
||||
46: ClosingComponentOpen[13,33](</)
|
||||
47: StringIdentifier[13,35](p)
|
||||
48: ComponentClose[13,36](>)
|
||||
49: RawText[13,37](\n )
|
||||
50: ClosingComponentOpen[14,13](</)
|
||||
51: TypedIdentifier[14,15](Case)
|
||||
52: ComponentClose[14,19](>)
|
||||
53: RawText[14,20](\n )
|
||||
54: ComponentOpen[15,13](<)
|
||||
55: TypedIdentifier[15,14](Default)
|
||||
56: ComponentClose[15,21](>)
|
||||
57: RawText[15,22](\n )
|
||||
58: ComponentOpen[16,17](<)
|
||||
59: StringIdentifier[16,18](p)
|
||||
60: ComponentClose[16,19](>)
|
||||
61: RawText[16,20](It's false... :()
|
||||
62: ClosingComponentOpen[16,36](</)
|
||||
63: StringIdentifier[16,38](p)
|
||||
64: ComponentClose[16,39](>)
|
||||
65: RawText[16,40](\n )
|
||||
66: ClosingComponentOpen[17,13](</)
|
||||
67: TypedIdentifier[17,15](Default)
|
||||
68: ComponentClose[17,22](>)
|
||||
69: RawText[17,23](\n )
|
||||
70: ClosingComponentOpen[18,9](</)
|
||||
71: TypedIdentifier[18,11](groowt.view.web.Select)
|
||||
72: ComponentClose[18,33](>)
|
||||
73: RawText[18,34](\n )
|
||||
74: ClosingComponentOpen[19,5](</)
|
||||
75: StringIdentifier[19,7](body)
|
||||
76: ComponentClose[19,11](>)
|
||||
77: RawText[19,12](\n)
|
||||
78: ClosingComponentOpen[20,1](</)
|
||||
79: StringIdentifier[20,3](html)
|
||||
80: ComponentClose[20,7](>)
|
||||
81: RawText[20,8](\n)
|
@ -1,5 +1,4 @@
|
||||
0: ComponentOpen[1,1](<)
|
||||
1: StringIdentifier[1,2](data-test)
|
||||
2: ComponentNlws[1,11]( )
|
||||
3: ComponentSelfClose[1,12](/>)
|
||||
4: RawText[1,14](\n)
|
||||
2: ComponentSelfClose[1,12](/>)
|
||||
3: RawText[1,14](\n)
|
@ -0,0 +1,10 @@
|
||||
0: FragmentOpen[1,1](<>)
|
||||
1: ComponentOpen[1,3](<)
|
||||
2: StringIdentifier[1,4](p)
|
||||
3: ComponentClose[1,5](>)
|
||||
4: RawText[1,6](Hello, World!)
|
||||
5: ClosingComponentOpen[1,19](</)
|
||||
6: StringIdentifier[1,21](p)
|
||||
7: ComponentClose[1,22](>)
|
||||
8: FragmentClose[1,23](</>)
|
||||
9: RawText[1,26](\n)
|
@ -0,0 +1 @@
|
||||
0: ClosingComponentOpen[1,1](</)
|
@ -0,0 +1 @@
|
||||
<><p>Hello, World!</p></>
|
@ -90,7 +90,6 @@ compilationUnit[1,1..21,1]
|
||||
componentArgs[12,14..12,35]
|
||||
componentType[12,14..12,14]
|
||||
TypedIdentifier[12,14](Case)
|
||||
ComponentNlws[12,18]( )
|
||||
attr[12,19..12,35]
|
||||
keyValueAttr[12,19..12,35]
|
||||
AttributeIdentifier[12,19](cond)
|
||||
|
@ -0,0 +1,28 @@
|
||||
compilationUnit[1,1..2,1]
|
||||
body[1,1..1,26]
|
||||
component[1,1..1,23]
|
||||
fragmentComponent[1,1..1,23]
|
||||
FragmentOpen[1,1](<>)
|
||||
body[1,3..1,22]
|
||||
component[1,3..1,22]
|
||||
componentWithChildren[1,3..1,22]
|
||||
openComponent[1,3..1,5]
|
||||
ComponentOpen[1,3](<)
|
||||
componentArgs[1,4..1,4]
|
||||
componentType[1,4..1,4]
|
||||
StringIdentifier[1,4](p)
|
||||
ComponentClose[1,5](>)
|
||||
body[1,6..1,6]
|
||||
bodyText[1,6..1,6]
|
||||
jStringBodyText[1,6..1,6]
|
||||
RawText[1,6](Hello, World!)
|
||||
closingComponent[1,19..1,22]
|
||||
ClosingComponentOpen[1,19](</)
|
||||
componentType[1,21..1,21]
|
||||
StringIdentifier[1,21](p)
|
||||
ComponentClose[1,22](>)
|
||||
FragmentClose[1,23](</>)
|
||||
bodyText[1,26..1,26]
|
||||
jStringBodyText[1,26..1,26]
|
||||
RawText[1,26](\n)
|
||||
EOF[2,1](<EOF>)
|
@ -17,12 +17,17 @@ import org.codehaus.groovy.control.CompilePhase;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static groowt.view.component.web.antlr.LexerErrorKt.formatLexerError;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public abstract class GroovyTranspilerTests {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(GroovyTranspilerTests.class);
|
||||
|
||||
protected final GroovyTranspiler transpiler;
|
||||
private final CompilationUnit groovyCompilationUnit;
|
||||
|
||||
@ -36,6 +41,15 @@ public abstract class GroovyTranspilerTests {
|
||||
|
||||
private void doTranspile(String source) {
|
||||
final var parseResult = ParserUtil.parseCompilationUnit(source);
|
||||
|
||||
if (!parseResult.getLexerErrors().isEmpty()) {
|
||||
logger.error("There were lexer errors.");
|
||||
parseResult.getLexerErrors().forEach(error -> {
|
||||
logger.error(formatLexerError(error));
|
||||
});
|
||||
fail("There were lexer errors. See log for more information.");
|
||||
}
|
||||
|
||||
final var tokenList = new TokenList(parseResult.getTokenStream());
|
||||
final var astBuilder = new DefaultAstBuilder(new DefaultNodeFactory(tokenList));
|
||||
final var cuNode = astBuilder.buildCompilationUnit(parseResult.getCompilationUnitContext());
|
||||
|
@ -1,3 +1,8 @@
|
||||
#/usr/bin/env bash
|
||||
|
||||
../gradlew toolsJar && java -cp build/libs/web-tools-0.1.0.jar:build/libs/web-views-0.1.0.jar $mainClassName "\$@"
|
||||
if [ "\$1" == "--debug" ]; then
|
||||
shift
|
||||
gradle -q toolsJar && java -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:8192 -cp build/libs/web-view-components-compiler-tools-0.1.0.jar $mainClassName "\$@"
|
||||
else
|
||||
gradle -q toolsJar && java -cp build/libs/web-view-components-compiler-tools-0.1.0.jar $mainClassName "\$@"
|
||||
fi
|
||||
|
@ -74,13 +74,13 @@ final class ParseTreeFileMaker extends AbstractOutputFileMaker {
|
||||
|
||||
if (!lexerErrorListener.errors.isEmpty()) {
|
||||
println 'There were lexer errors.'
|
||||
lexerErrorListener.errors.each { println LexerErrorKt.format(it) }
|
||||
lexerErrorListener.errors.each { println LexerErrorKt.formatLexerError(it) }
|
||||
return null
|
||||
}
|
||||
|
||||
if (!parserErrorListener.errors.isEmpty()) {
|
||||
if (!parserErrorListener.parserErrors.isEmpty()) {
|
||||
println 'There were parser errors.'
|
||||
parserErrorListener.errors.each { println ParserErrorKt.format(it) }
|
||||
parserErrorListener.parserErrors.each { println ParserErrorKt.formatParserError(it) }
|
||||
return null
|
||||
}
|
||||
|
||||
|
@ -52,7 +52,7 @@ open class TokenizeWvc : AbstractSourceTransformerCli() {
|
||||
|
||||
protected fun onErrors(errors: List<LexerError>): Boolean {
|
||||
println("There were errors during tokenization.")
|
||||
errors.forEach { println(format(it)) }
|
||||
errors.forEach { println(formatLexerError(it)) }
|
||||
return this.getYesNo("Do you wish to try again?", false)
|
||||
}
|
||||
|
||||
@ -99,7 +99,7 @@ open class TokenizeWvc : AbstractSourceTransformerCli() {
|
||||
val errorListener = LexerErrorListener()
|
||||
lexer.addErrorListener(errorListener)
|
||||
|
||||
val tokenStream = WebViewComponentsTokenStream(lexer)
|
||||
val tokenStream = WebViewComponentsTokenStream(lexer, setOf(WebViewComponentsLexer.HIDDEN))
|
||||
val allTokens = tokenStream.getAllTokensSkipEOF()
|
||||
|
||||
val errors = errorListener.getErrors()
|
||||
|
Loading…
Reference in New Issue
Block a user