Lots of refactoring lexer, parser, and tools.
This commit is contained in:
parent
bfa2ec6cd7
commit
58d7641ece
@ -3,6 +3,7 @@ package groowt.gradle.antlr;
|
|||||||
import org.gradle.api.Plugin;
|
import org.gradle.api.Plugin;
|
||||||
import org.gradle.api.Project;
|
import org.gradle.api.Project;
|
||||||
import org.gradle.api.plugins.JavaPluginExtension;
|
import org.gradle.api.plugins.JavaPluginExtension;
|
||||||
|
import org.gradle.api.tasks.Delete;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
@ -39,7 +40,7 @@ public final class GroowtAntlrPlugin implements Plugin<Project> {
|
|||||||
|
|
||||||
sourceSet.getJava().srcDir(baseOutputDir);
|
sourceSet.getJava().srcDir(baseOutputDir);
|
||||||
|
|
||||||
final var sourceSetTasks = antlrSourceDirectorySet.getFiles().stream()
|
final var generateTasks = antlrSourceDirectorySet.getFiles().stream()
|
||||||
.filter(GroowtAntlrUtil::isAntlrFile)
|
.filter(GroowtAntlrUtil::isAntlrFile)
|
||||||
.map(file -> {
|
.map(file -> {
|
||||||
final var taskProvider = project.getTasks().register(
|
final var taskProvider = project.getTasks().register(
|
||||||
@ -64,10 +65,21 @@ public final class GroowtAntlrPlugin implements Plugin<Project> {
|
|||||||
})
|
})
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
if (!sourceSetTasks.isEmpty()) {
|
if (!generateTasks.isEmpty()) {
|
||||||
|
final var cleanAntlr = project.getTasks().register(
|
||||||
|
sourceSet.getTaskName("clean", "Antlr"),
|
||||||
|
Delete.class,
|
||||||
|
deleteTask -> {
|
||||||
|
deleteTask.setGroup(GROOWT_ANTLR);
|
||||||
|
deleteTask.delete(baseOutputDir);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
project.getTasks().register(sourceSet.getTaskName("generate", "AllAntlr"), task -> {
|
project.getTasks().register(sourceSet.getTaskName("generate", "AllAntlr"), task -> {
|
||||||
task.dependsOn(sourceSetTasks);
|
|
||||||
task.setGroup(GROOWT_ANTLR);
|
task.setGroup(GROOWT_ANTLR);
|
||||||
|
//noinspection ResultOfMethodCallIgnored
|
||||||
|
task.doFirst(first -> baseOutputDir.get().getAsFile().delete());
|
||||||
|
task.dependsOn(generateTasks);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -152,14 +152,15 @@ def toolSpec = { String name, String mainClass ->
|
|||||||
}
|
}
|
||||||
|
|
||||||
final List<ToolSpec> toolSpecs = [
|
final List<ToolSpec> toolSpecs = [
|
||||||
toolSpec('astFileMaker', 'AstFileMakerCli'),
|
toolSpec('astFileMaker', 'AstFileMakerCli'), // deprecated
|
||||||
toolSpec('convertToGroovy', 'ConvertToGroovy'),
|
toolSpec('convertToGroovy', 'ConvertToGroovy'),
|
||||||
toolSpec('groovyWvc', 'GroovyWvcCompiler'),
|
toolSpec('groovyWvc', 'GroovyWvcCompiler'),
|
||||||
toolSpec('lexer', 'LexerTool'),
|
toolSpec('lexer', 'LexerTool'), // deprecated
|
||||||
toolSpec('parser', 'ParserTool'),
|
toolSpec('parser', 'ParserTool'), // deprecated
|
||||||
toolSpec('parseTreeFileMaker', 'ParseTreeFileMakerCli'),
|
toolSpec('parseTreeFileMaker', 'ParseTreeFileMakerCli'), // deprecated
|
||||||
|
toolSpec('parseWvc', 'ParseWvc'),
|
||||||
toolSpec('tokenizeWvc', 'TokenizeWvc'),
|
toolSpec('tokenizeWvc', 'TokenizeWvc'),
|
||||||
toolSpec('tokensFileMaker', 'TokensFileMakerCli')
|
toolSpec('tokensFileMaker', 'TokensFileMakerCli') // deprecated
|
||||||
]
|
]
|
||||||
|
|
||||||
toolSpecs.each { spec ->
|
toolSpecs.each { spec ->
|
||||||
|
10
web-view-components-compiler/makeLexerTest
Executable file
10
web-view-components-compiler/makeLexerTest
Executable file
@ -0,0 +1,10 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
ARGS="-v -d src/test/lexer/tokens-files -s _tokens -e .txt"
|
||||||
|
|
||||||
|
if [ "$1" == "--debug" ]; then
|
||||||
|
shift
|
||||||
|
bin/tokenizeWvc --debug $ARGS "$@"
|
||||||
|
else
|
||||||
|
bin/tokenizeWvc $ARGS "$@"
|
||||||
|
fi
|
10
web-view-components-compiler/makeParserTest
Executable file
10
web-view-components-compiler/makeParserTest
Executable file
@ -0,0 +1,10 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
ARGS="-v -d src/test/parser/parse-tree-files -s _parseTree -e .txt"
|
||||||
|
|
||||||
|
if [ "$1" == "--debug" ]; then
|
||||||
|
shift
|
||||||
|
bin/parseWvc --debug $ARGS "$@"
|
||||||
|
else
|
||||||
|
bin/parseWvc $ARGS "$@"
|
||||||
|
fi
|
@ -8,12 +8,13 @@ options {
|
|||||||
|
|
||||||
tokens {
|
tokens {
|
||||||
PreambleBreak,
|
PreambleBreak,
|
||||||
ComponentNlws,
|
|
||||||
GroovyCode,
|
GroovyCode,
|
||||||
GStringAttrValueEnd,
|
GStringAttrValueEnd,
|
||||||
JStringAttrValueEnd,
|
JStringAttrValueEnd,
|
||||||
ClosureAttrValueEnd,
|
ClosureAttrValueEnd,
|
||||||
DollarScriptletClose
|
DollarScriptletClose,
|
||||||
|
Nlws,
|
||||||
|
ErrorChar
|
||||||
}
|
}
|
||||||
|
|
||||||
channels {
|
channels {
|
||||||
@ -88,14 +89,12 @@ channels {
|
|||||||
DollarSlashyStringClosureStart
|
DollarSlashyStringClosureStart
|
||||||
);
|
);
|
||||||
|
|
||||||
public WebViewComponentsLexerBase() {
|
public WebViewComponentsLexerBase() {}
|
||||||
_interp = new LexerATNSimulator(this,_ATN,_decisionToDFA,_sharedContextCache);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onPreambleClose() {
|
private void onPreambleClose() {
|
||||||
this.setType(PreambleBreak);
|
this.setType(PreambleBreak);
|
||||||
this.exitPreamble();
|
this.exitPreamble();
|
||||||
this.popMode();
|
this.mode(MAIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onGStringClosure() {
|
private void onGStringClosure() {
|
||||||
@ -116,10 +115,21 @@ channels {
|
|||||||
// DEFAULT_MODE
|
// DEFAULT_MODE
|
||||||
|
|
||||||
PreambleOpen
|
PreambleOpen
|
||||||
: THREE_DASH ( NL | WS+ )? { this.canPreamble() }? { this.enterPreamble(); }
|
: THREE_DASH ( NL | WS+ )? { this.enterPreamble(); }
|
||||||
-> type(PreambleBreak), pushMode(GROOVY_CODE)
|
-> type(PreambleBreak), pushMode(GROOVY_CODE)
|
||||||
;
|
;
|
||||||
|
|
||||||
|
NotPreambleOpen
|
||||||
|
: .
|
||||||
|
{
|
||||||
|
this.rollbackOne();
|
||||||
|
this.setCanPreamble(false);
|
||||||
|
} -> skip, mode(MAIN)
|
||||||
|
;
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
mode MAIN;
|
||||||
|
|
||||||
ComponentOpen
|
ComponentOpen
|
||||||
: LT { !isAnyOf(this.getNextChar(), '/', '>') }? -> pushMode(TAG_START)
|
: LT { !isAnyOf(this.getNextChar(), '/', '>') }? -> pushMode(TAG_START)
|
||||||
;
|
;
|
||||||
@ -156,25 +166,39 @@ DollarScriptletOpen
|
|||||||
;
|
;
|
||||||
|
|
||||||
DollarReferenceStart
|
DollarReferenceStart
|
||||||
: DOLLAR { isIdentifierStartChar(this.getNextChar()) }? -> pushMode(IN_G_STRING_PATH)
|
: DOLLAR { isGStringIdentifierStartChar(this.getNextChar()) }? -> pushMode(IN_G_STRING_PATH)
|
||||||
;
|
;
|
||||||
|
|
||||||
QuestionTag
|
QuestionTagOpen
|
||||||
: LT QUESTION .*? QUESTION GT
|
: LT QUESTION
|
||||||
;
|
;
|
||||||
|
|
||||||
HtmlComment
|
QuestionTagClose
|
||||||
: LT BANG TWO_DASH .*? TWO_DASH GT
|
: QUESTION GT
|
||||||
|
;
|
||||||
|
|
||||||
|
HtmlCommentOpen
|
||||||
|
: LT BANG TWO_DASH
|
||||||
|
;
|
||||||
|
|
||||||
|
HtmlCommentClose
|
||||||
|
: TWO_DASH GT
|
||||||
;
|
;
|
||||||
|
|
||||||
RawText
|
RawText
|
||||||
: ( ~[-<$] // n.b.: LT cannot be escaped, only via <
|
: ( ~[-<$?]
|
||||||
| MINUS { !this.isNext("--") }?
|
| MINUS { !this.isNext("->") }?
|
||||||
| DOLLAR { !this.isNext('{') && !isIdentifierStartChar(this.getNextChar()) }?
|
| LT { canFollowLessThan(this.getNextCharAsString()) }?
|
||||||
| LT BANG { !this.isNext("--") }?
|
| LT BANG { !this.isNext("--") }?
|
||||||
|
| DOLLAR { !(this.isNext('{') || isIdentifierStartChar(this.getNextChar())) }?
|
||||||
|
| QUESTION { !this.isNext('>') }?
|
||||||
)+
|
)+
|
||||||
;
|
;
|
||||||
|
|
||||||
|
MainError
|
||||||
|
: . -> type(ErrorChar), channel(ERROR)
|
||||||
|
;
|
||||||
|
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
mode TAG_START;
|
mode TAG_START;
|
||||||
|
|
||||||
@ -227,11 +251,11 @@ StringIdentifierChar
|
|||||||
;
|
;
|
||||||
|
|
||||||
TagStartNlws
|
TagStartNlws
|
||||||
: NLWS+ -> type(ComponentNlws), channel(HIDDEN)
|
: NLWS+ -> type(Nlws), channel(HIDDEN)
|
||||||
;
|
;
|
||||||
|
|
||||||
TagStartError
|
TagStartError
|
||||||
: . -> channel(ERROR)
|
: . -> type(ErrorChar), channel(ERROR)
|
||||||
;
|
;
|
||||||
|
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
@ -276,7 +300,7 @@ JStringAttrValueStart
|
|||||||
;
|
;
|
||||||
|
|
||||||
ClosureAttrValueStart
|
ClosureAttrValueStart
|
||||||
: LEFT_CURLY InTagNlws? { !this.isNext('<') }?
|
: LEFT_CURLY { !this.isNextIgnoreNlws('<') }?
|
||||||
{
|
{
|
||||||
this.curlies.push(() -> {
|
this.curlies.push(() -> {
|
||||||
this.setType(ClosureAttrValueEnd);
|
this.setType(ClosureAttrValueEnd);
|
||||||
@ -296,7 +320,7 @@ ComponentAttrValueEnd
|
|||||||
;
|
;
|
||||||
|
|
||||||
InTagNlws
|
InTagNlws
|
||||||
: NLWS+ -> type(ComponentNlws), channel(HIDDEN)
|
: NLWS+ -> type(Nlws), channel(HIDDEN)
|
||||||
;
|
;
|
||||||
|
|
||||||
TagError
|
TagError
|
||||||
@ -307,10 +331,7 @@ TagError
|
|||||||
mode GROOVY_CODE;
|
mode GROOVY_CODE;
|
||||||
|
|
||||||
PreambleClose
|
PreambleClose
|
||||||
: NL? THREE_DASH ( NL | WS+ )? { this.inPreamble() }?
|
: THREE_DASH { this.inPreamble() && this.getCharPositionInLine() == 3 }? { this.onPreambleClose(); }
|
||||||
{
|
|
||||||
this.onPreambleClose();
|
|
||||||
}
|
|
||||||
;
|
;
|
||||||
|
|
||||||
ScriptletClose
|
ScriptletClose
|
||||||
@ -318,10 +339,9 @@ ScriptletClose
|
|||||||
;
|
;
|
||||||
|
|
||||||
GroovyCodeChars
|
GroovyCodeChars
|
||||||
: ( ~[/\n\r\-$%(){}'"]
|
: ( ~[-/$%(){}'"]
|
||||||
| FS { !isAnyOf(this.getNextChar(), '/', '*') }?
|
|
||||||
| NL { !this.inPreamble() || !this.isNext("---") }?
|
|
||||||
| MINUS { !(this.getCharPositionInLine() == 1 && this.isNext("--")) }?
|
| MINUS { !(this.getCharPositionInLine() == 1 && this.isNext("--")) }?
|
||||||
|
| FS { !isAnyOf(this.getNextChar(), '/', '*') }?
|
||||||
| DOLLAR { !this.isNext('/') }?
|
| DOLLAR { !this.isNext('/') }?
|
||||||
| PERCENT { !this.isNext('>') }?
|
| PERCENT { !this.isNext('>') }?
|
||||||
)+
|
)+
|
||||||
@ -418,7 +438,7 @@ LineCommentEnd
|
|||||||
mode IN_STAR_COMMENT;
|
mode IN_STAR_COMMENT;
|
||||||
|
|
||||||
StarCommentChars
|
StarCommentChars
|
||||||
: ~'*'+
|
: ~'*' | ( '*' { !this.isNext('/') }? )
|
||||||
;
|
;
|
||||||
|
|
||||||
StarCommentEnd
|
StarCommentEnd
|
||||||
@ -449,11 +469,12 @@ GStringText
|
|||||||
: ( ~[\n\r"$]
|
: ( ~[\n\r"$]
|
||||||
| BS DQ
|
| BS DQ
|
||||||
| BS DOLLAR
|
| BS DOLLAR
|
||||||
|
| DOLLAR { !isGStringIdentifierStartChar(this.getNextChar()) }?
|
||||||
)+
|
)+
|
||||||
;
|
;
|
||||||
|
|
||||||
GStringDollarValueStart
|
GStringDollarValueStart
|
||||||
: DOLLAR { !this.isNext('{') }? -> pushMode(IN_G_STRING_PATH)
|
: DOLLAR { !this.isNext('{') && isGStringIdentifierStartChar(this.getNextChar()) }? -> pushMode(IN_G_STRING_PATH)
|
||||||
;
|
;
|
||||||
|
|
||||||
GStringClosureStart
|
GStringClosureStart
|
||||||
@ -521,13 +542,14 @@ GStringPathEnd
|
|||||||
yield new StringContinueEndSpec();
|
yield new StringContinueEndSpec();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case DEFAULT_MODE -> new StringContinueEndSpec();
|
case MAIN -> new StringContinueEndSpec();
|
||||||
default -> throw new IllegalStateException("not a valid gString context: " + this.getModeName(this.peekMode(1)));
|
default -> throw new IllegalStateException("not a valid gString context: " + this.getModeName(this.peekMode(1)));
|
||||||
};
|
};
|
||||||
switch (endSpec) {
|
switch (endSpec) {
|
||||||
case StringContinueEndSpec ignored -> {
|
case StringContinueEndSpec ignored -> {
|
||||||
this.popMode();
|
this.popMode();
|
||||||
this.rollbackOne();
|
this.rollbackOne(true);
|
||||||
|
this.skip();
|
||||||
}
|
}
|
||||||
case StringClosingEndSpec closingEndSpec -> {
|
case StringClosingEndSpec closingEndSpec -> {
|
||||||
this.setType(closingEndSpec.type());
|
this.setType(closingEndSpec.type());
|
||||||
@ -550,21 +572,21 @@ GStringIdentifierChar
|
|||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
mode IN_TRIPLE_J_STRING;
|
mode IN_TRIPLE_J_STRING;
|
||||||
|
|
||||||
// TODO: check for unescaped SQ, I think groovy allows them
|
|
||||||
TripleJStringContent
|
TripleJStringContent
|
||||||
: ( ~['] | BS SQ )+
|
: ( ~[']
|
||||||
|
| SQ { !(this.isNext("''") && this._input.LA(3) != '\'') }?
|
||||||
|
)+
|
||||||
;
|
;
|
||||||
|
|
||||||
TripleJStringEnd
|
TripleJStringEnd
|
||||||
: SQ SQ SQ -> popMode
|
: SQ SQ SQ { !this.isNext('\'') }? -> popMode
|
||||||
;
|
;
|
||||||
|
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
mode IN_TRIPLE_G_STRING;
|
mode IN_TRIPLE_G_STRING;
|
||||||
|
|
||||||
// TODO: check for unescaped DQ, I think groovy allows them
|
|
||||||
TripleGStringDollarValueStart
|
TripleGStringDollarValueStart
|
||||||
: DOLLAR { !this.isNext('{') }? -> pushMode(IN_G_STRING_PATH)
|
: DOLLAR { isGStringIdentifierStartChar(this.getNextChar()) }? -> pushMode(IN_G_STRING_PATH)
|
||||||
;
|
;
|
||||||
|
|
||||||
TripleGStringClosureStart
|
TripleGStringClosureStart
|
||||||
@ -572,11 +594,15 @@ TripleGStringClosureStart
|
|||||||
;
|
;
|
||||||
|
|
||||||
TripleGStringText
|
TripleGStringText
|
||||||
: ( ~["$] | BS DQ | BS DOLLAR )+
|
: ( ~["$]
|
||||||
|
| DQ { !(this.isNext("\"\"") && this._input.LA(3) != '"') }?
|
||||||
|
| BS DOLLAR
|
||||||
|
| DOLLAR { !isGStringIdentifierStartChar(this.getNextChar()) }?
|
||||||
|
)+
|
||||||
;
|
;
|
||||||
|
|
||||||
TripleGStringEnd
|
TripleGStringEnd
|
||||||
: DQ DQ DQ -> popMode
|
: DQ DQ DQ { !this.isNext('"') }? -> popMode
|
||||||
;
|
;
|
||||||
|
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
@ -587,8 +613,9 @@ ParenthesesSlashyStringText
|
|||||||
;
|
;
|
||||||
|
|
||||||
ParenthesesSlashyStringDollarValueStart
|
ParenthesesSlashyStringDollarValueStart
|
||||||
: DOLLAR { !this.isNext('{') }? -> pushMode(IN_G_STRING_PATH)
|
: DOLLAR { isGStringIdentifierStartChar(this.getNextChar()) }? -> pushMode(IN_G_STRING_PATH)
|
||||||
;
|
;
|
||||||
|
|
||||||
ParenthesesSlashyStringClosureStart
|
ParenthesesSlashyStringClosureStart
|
||||||
: DOLLAR LEFT_CURLY { this.onGStringClosure(); }
|
: DOLLAR LEFT_CURLY { this.onGStringClosure(); }
|
||||||
;
|
;
|
||||||
@ -603,13 +630,14 @@ mode IN_DOLLAR_SLASHY_STRING;
|
|||||||
DollarSlashyStringText
|
DollarSlashyStringText
|
||||||
: ( ~[$/]
|
: ( ~[$/]
|
||||||
| DOLLAR DOLLAR
|
| DOLLAR DOLLAR
|
||||||
| DOLLAR FS
|
| DOLLAR { !isGStringIdentifierStartChar(this.getNextChar()) }?
|
||||||
| FS DOLLAR DOLLAR
|
| FS { !this.isNext('$') }?
|
||||||
)+
|
)+
|
||||||
;
|
;
|
||||||
|
|
||||||
DollarSlashyStringDollarValueStart
|
DollarSlashyStringDollarValueStart
|
||||||
: DOLLAR { !isAnyOf(this.getNextChar(), '/', '$', '{') }? -> pushMode(IN_G_STRING_PATH)
|
: DOLLAR { isGStringIdentifierStartChar(this.getNextChar()) }?
|
||||||
|
-> pushMode(IN_G_STRING_PATH)
|
||||||
;
|
;
|
||||||
|
|
||||||
DollarSlashyStringClosureStart
|
DollarSlashyStringClosureStart
|
||||||
|
@ -17,18 +17,22 @@ body
|
|||||||
;
|
;
|
||||||
|
|
||||||
bodyText
|
bodyText
|
||||||
: gStringBodyText | jStringBodyText
|
: ( questionTag | htmlComment | text | bodyTextGroovyElement )+
|
||||||
;
|
;
|
||||||
|
|
||||||
gStringBodyText
|
questionTag
|
||||||
: jStringBodyText? ( gStringBodyTextGroovyElement jStringBodyText? )+
|
: QuestionTagOpen ( text | bodyTextGroovyElement )* QuestionTagClose
|
||||||
;
|
;
|
||||||
|
|
||||||
jStringBodyText
|
htmlComment
|
||||||
: ( QuestionTag | HtmlComment | RawText )+
|
: HtmlCommentOpen ( text | bodyTextGroovyElement )* HtmlCommentClose
|
||||||
;
|
;
|
||||||
|
|
||||||
gStringBodyTextGroovyElement
|
text
|
||||||
|
: RawText
|
||||||
|
;
|
||||||
|
|
||||||
|
bodyTextGroovyElement
|
||||||
: plainScriptlet | equalsScriptlet | dollarScriptlet | dollarReference
|
: plainScriptlet | equalsScriptlet | dollarScriptlet | dollarReference
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package groowt.view.component.web.antlr;
|
package groowt.view.component.web.antlr;
|
||||||
|
|
||||||
|
import groovyjarjarantlr4.runtime.Token;
|
||||||
import org.antlr.v4.runtime.CharStream;
|
import org.antlr.v4.runtime.CharStream;
|
||||||
import org.antlr.v4.runtime.Lexer;
|
import org.antlr.v4.runtime.Lexer;
|
||||||
import org.antlr.v4.runtime.atn.ATN;
|
import org.antlr.v4.runtime.atn.ATN;
|
||||||
@ -15,6 +16,7 @@ import java.util.LinkedList;
|
|||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static groowt.view.component.web.antlr.LexerSemanticPredicates.isAnyOf;
|
||||||
import static groowt.view.component.web.antlr.TokenUtil.escapeChars;
|
import static groowt.view.component.web.antlr.TokenUtil.escapeChars;
|
||||||
|
|
||||||
public abstract class AbstractWebViewComponentsLexer extends Lexer {
|
public abstract class AbstractWebViewComponentsLexer extends Lexer {
|
||||||
@ -30,11 +32,13 @@ public abstract class AbstractWebViewComponentsLexer extends Lexer {
|
|||||||
super(recognizer, atn, decisionToDFA, sharedContextCache);
|
super(recognizer, atn, decisionToDFA, sharedContextCache);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void resetAcceptPosition(CharStream input, int index, int line, int charPositionInLine) {
|
public void resetAcceptPosition(CharStream input, int index, int line, int charPositionInLine, boolean consume) {
|
||||||
input.seek(index);
|
input.seek(index);
|
||||||
this.line = line;
|
this.line = line;
|
||||||
this.charPositionInLine = charPositionInLine;
|
this.charPositionInLine = charPositionInLine;
|
||||||
this.consume(input);
|
if (consume) {
|
||||||
|
this.consume(input);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -87,7 +91,7 @@ public abstract class AbstractWebViewComponentsLexer extends Lexer {
|
|||||||
return this.logger;
|
return this.logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isCanPreamble() {
|
public boolean canPreamble() {
|
||||||
return this.canPreamble;
|
return this.canPreamble;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,7 +99,7 @@ public abstract class AbstractWebViewComponentsLexer extends Lexer {
|
|||||||
this.canPreamble = canPreamble;
|
this.canPreamble = canPreamble;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isInPreamble() {
|
public boolean inPreamble() {
|
||||||
return this.inPreamble;
|
return this.inPreamble;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,7 +107,7 @@ public abstract class AbstractWebViewComponentsLexer extends Lexer {
|
|||||||
this.inPreamble = inPreamble;
|
this.inPreamble = inPreamble;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isInConstructor() {
|
public boolean inConstructor() {
|
||||||
return this.inConstructor;
|
return this.inConstructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,19 +180,11 @@ public abstract class AbstractWebViewComponentsLexer extends Lexer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean canPreamble() {
|
|
||||||
return this.canPreamble;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void enterPreamble() {
|
protected void enterPreamble() {
|
||||||
this.inPreamble = true;
|
this.inPreamble = true;
|
||||||
this.canPreamble = false;
|
this.canPreamble = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean inPreamble() {
|
|
||||||
return this.inPreamble;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void exitPreamble() {
|
protected void exitPreamble() {
|
||||||
this.inPreamble = false;
|
this.inPreamble = false;
|
||||||
}
|
}
|
||||||
@ -199,10 +195,6 @@ public abstract class AbstractWebViewComponentsLexer extends Lexer {
|
|||||||
this.inConstructor = true;
|
this.inConstructor = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean inConstructor() {
|
|
||||||
return this.inConstructor;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean canExitConstructor() {
|
protected boolean canExitConstructor() {
|
||||||
return this.inConstructor && this.parentheses.getStackSize() == 1 && this.parentheses.isLast();
|
return this.inConstructor && this.parentheses.getStackSize() == 1 && this.parentheses.isLast();
|
||||||
}
|
}
|
||||||
@ -220,6 +212,10 @@ public abstract class AbstractWebViewComponentsLexer extends Lexer {
|
|||||||
return b.toString();
|
return b.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected String getNextCharAsString() {
|
||||||
|
return Character.toString((char) this.getNextChar());
|
||||||
|
}
|
||||||
|
|
||||||
protected int getCurrentChar() {
|
protected int getCurrentChar() {
|
||||||
return this._input.LA(-1);
|
return this._input.LA(-1);
|
||||||
}
|
}
|
||||||
@ -236,6 +232,16 @@ public abstract class AbstractWebViewComponentsLexer extends Lexer {
|
|||||||
return this.getNextCharsAsString(test.length()).equals(test);
|
return this.getNextCharsAsString(test.length()).equals(test);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected boolean isNextIgnoreNlws(char test) {
|
||||||
|
for (int i = 1; this._input.LA(i) != Token.EOF; i++) {
|
||||||
|
final char subject = (char) this._input.LA(i);
|
||||||
|
if (!isAnyOf(subject, ' ', '\t', '\n', '\r')) {
|
||||||
|
return subject == test;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final LexerATNSimulator getInterpreter() {
|
public final LexerATNSimulator getInterpreter() {
|
||||||
return this._interp;
|
return this._interp;
|
||||||
@ -244,11 +250,16 @@ public abstract class AbstractWebViewComponentsLexer extends Lexer {
|
|||||||
protected abstract PositionAdjustingLexerATNSimulator getPositionAdjustingInterpreter();
|
protected abstract PositionAdjustingLexerATNSimulator getPositionAdjustingInterpreter();
|
||||||
|
|
||||||
protected void rollbackOne() {
|
protected void rollbackOne() {
|
||||||
|
this.rollbackOne(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void rollbackOne(boolean consume) {
|
||||||
this.getPositionAdjustingInterpreter().resetAcceptPosition(
|
this.getPositionAdjustingInterpreter().resetAcceptPosition(
|
||||||
this._input,
|
this._input,
|
||||||
this._tokenStartCharIndex - 1,
|
Math.max(this._tokenStartCharIndex - 1, 0),
|
||||||
this._tokenStartLine,
|
this._tokenStartLine,
|
||||||
this._tokenStartCharIndex - 1
|
Math.max(this._tokenStartCharIndex - 1, 0),
|
||||||
|
consume
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ package groowt.view.component.web.antlr
|
|||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import java.lang.invoke.MethodHandles
|
import java.lang.invoke.MethodHandles
|
||||||
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
private val logger: Logger = LoggerFactory.getLogger(
|
private val logger: Logger = LoggerFactory.getLogger(
|
||||||
MethodHandles.lookup().lookupClass()
|
MethodHandles.lookup().lookupClass()
|
||||||
@ -66,6 +67,13 @@ fun canFollowGStringOpening(nextTwo: String): Boolean {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val notAllowedAfterLessThan = Pattern.compile("[\\p{L}%/>]")
|
||||||
|
|
||||||
|
fun canFollowLessThan(subject: String): Boolean {
|
||||||
|
val m = notAllowedAfterLessThan.matcher(subject)
|
||||||
|
return !m.matches()
|
||||||
|
}
|
||||||
|
|
||||||
fun isIdentifierStartChar(c: Char): Boolean = Character.isJavaIdentifierStart(c)
|
fun isIdentifierStartChar(c: Char): Boolean = Character.isJavaIdentifierStart(c)
|
||||||
|
|
||||||
fun isIdentifierStartChar(subject: Int) = isIdentifierStartChar(subject.toChar())
|
fun isIdentifierStartChar(subject: Int) = isIdentifierStartChar(subject.toChar())
|
||||||
|
@ -5,25 +5,73 @@ import groowt.view.component.web.util.excerpt
|
|||||||
import org.antlr.v4.runtime.ParserRuleContext
|
import org.antlr.v4.runtime.ParserRuleContext
|
||||||
import org.antlr.v4.runtime.Token
|
import org.antlr.v4.runtime.Token
|
||||||
|
|
||||||
open class ParserError(val type: ParserErrorType, val offending: Token, val context: ParserRuleContext)
|
open class ParserError {
|
||||||
|
|
||||||
|
val type: ParserErrorType
|
||||||
|
val offendingToken: Token?
|
||||||
|
val offendingSymbol: Any?
|
||||||
|
val msg: String?
|
||||||
|
val sourcePosition: SourcePosition
|
||||||
|
val context: ParserRuleContext
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
type: ParserErrorType,
|
||||||
|
offendingToken: Token?,
|
||||||
|
sourcePosition: SourcePosition,
|
||||||
|
context: ParserRuleContext
|
||||||
|
) {
|
||||||
|
this.type = type
|
||||||
|
this.offendingToken = offendingToken
|
||||||
|
this.offendingSymbol = offendingToken
|
||||||
|
this.msg = null;
|
||||||
|
this.sourcePosition = sourcePosition
|
||||||
|
this.context = context
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
type: ParserErrorType,
|
||||||
|
offendingSymbol: Any?,
|
||||||
|
msg: String,
|
||||||
|
sourcePosition: SourcePosition,
|
||||||
|
context: ParserRuleContext
|
||||||
|
) {
|
||||||
|
this.type = type
|
||||||
|
this.offendingToken = null
|
||||||
|
this.offendingSymbol = offendingSymbol
|
||||||
|
this.msg = msg
|
||||||
|
this.sourcePosition = sourcePosition
|
||||||
|
this.context = context
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
class MismatchedInputParserError(
|
class MismatchedInputParserError(
|
||||||
type: ParserErrorType,
|
type: ParserErrorType,
|
||||||
offending: Token,
|
offendingToken: Token,
|
||||||
|
sourcePosition: SourcePosition,
|
||||||
context: ParserRuleContext,
|
context: ParserRuleContext,
|
||||||
val expectedTokenTypes: Set<Int>
|
val expectedTokenTypes: Set<Int>
|
||||||
) : ParserError(type, offending, context)
|
) : ParserError(type, offendingToken, sourcePosition, context)
|
||||||
|
|
||||||
fun formatParserError(error: ParserError): String {
|
fun formatParserError(error: ParserError): String {
|
||||||
val sb = StringBuilder()
|
val sb = StringBuilder()
|
||||||
val sourcePosition = SourcePosition.fromStartOfToken(error.offending)
|
|
||||||
sb.append("At ")
|
sb.append("At ")
|
||||||
.append(sourcePosition.toStringLong())
|
.append(error.sourcePosition.toStringLong())
|
||||||
.append(": ")
|
.append(": ")
|
||||||
.append(error.type.message)
|
if (error.type != ParserErrorType.UNKNOWN) {
|
||||||
.append(" Offending token: ")
|
sb.append(error.type.message)
|
||||||
.append(formatTokenForError(error.offending))
|
} else if (error.msg != null) {
|
||||||
.append(". ")
|
sb.append(error.msg + ".")
|
||||||
|
} else {
|
||||||
|
sb.append("Parser unknown error.")
|
||||||
|
}
|
||||||
|
if (error.offendingToken != null) {
|
||||||
|
sb.append(" Offending token: ")
|
||||||
|
.append(formatTokenForError(error.offendingToken))
|
||||||
|
.append(". ")
|
||||||
|
} else if (error.offendingSymbol != null) {
|
||||||
|
sb.append(" Offending symbol: ${error.offendingSymbol}. ")
|
||||||
|
}
|
||||||
if (error is MismatchedInputParserError) {
|
if (error is MismatchedInputParserError) {
|
||||||
sb.append("Expected any of: ")
|
sb.append("Expected any of: ")
|
||||||
.append(formatExpected(error.expectedTokenTypes))
|
.append(formatExpected(error.expectedTokenTypes))
|
||||||
|
@ -18,7 +18,7 @@ class ParserErrorListener : BaseErrorListener() {
|
|||||||
line: Int,
|
line: Int,
|
||||||
charPositionInLine: Int,
|
charPositionInLine: Int,
|
||||||
msg: String,
|
msg: String,
|
||||||
e: RecognitionException
|
e: RecognitionException?
|
||||||
) {
|
) {
|
||||||
val parser = recognizer as WebViewComponentsParser
|
val parser = recognizer as WebViewComponentsParser
|
||||||
when (e) {
|
when (e) {
|
||||||
@ -30,20 +30,41 @@ class ParserErrorListener : BaseErrorListener() {
|
|||||||
this.lexerErrors.add(error)
|
this.lexerErrors.add(error)
|
||||||
}
|
}
|
||||||
is NoViableAltException -> {
|
is NoViableAltException -> {
|
||||||
val error = ParserError(ParserErrorType.NO_VIABLE_ALTERNATIVE, e.offendingToken, parser.context)
|
val error = ParserError(
|
||||||
|
ParserErrorType.NO_VIABLE_ALTERNATIVE,
|
||||||
|
e.offendingToken,
|
||||||
|
SourcePosition.fromStartOfToken(e.offendingToken),
|
||||||
|
parser.context
|
||||||
|
)
|
||||||
parserErrors.add(error)
|
parserErrors.add(error)
|
||||||
}
|
}
|
||||||
is InputMismatchException -> {
|
is InputMismatchException -> {
|
||||||
val error = MismatchedInputParserError(
|
val error = MismatchedInputParserError(
|
||||||
ParserErrorType.INPUT_MISMATCH,
|
ParserErrorType.INPUT_MISMATCH,
|
||||||
e.offendingToken,
|
e.offendingToken,
|
||||||
|
SourcePosition.fromStartOfToken(e.offendingToken),
|
||||||
parser.context,
|
parser.context,
|
||||||
e.expectedTokens.toSet()
|
e.expectedTokens.toSet()
|
||||||
)
|
)
|
||||||
parserErrors.add(error)
|
parserErrors.add(error)
|
||||||
}
|
}
|
||||||
is FailedPredicateException -> {
|
is FailedPredicateException -> {
|
||||||
val error = ParserError(ParserErrorType.FAILED_PREDICATE, e.offendingToken, parser.context)
|
val error = ParserError(
|
||||||
|
ParserErrorType.FAILED_PREDICATE,
|
||||||
|
e.offendingToken,
|
||||||
|
SourcePosition.fromStartOfToken(e.offendingToken),
|
||||||
|
parser.context
|
||||||
|
)
|
||||||
|
parserErrors.add(error)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
val error = ParserError(
|
||||||
|
ParserErrorType.UNKNOWN,
|
||||||
|
offendingSymbol,
|
||||||
|
msg,
|
||||||
|
SourcePosition(line, charPositionInLine + 1),
|
||||||
|
parser.context
|
||||||
|
)
|
||||||
parserErrors.add(error)
|
parserErrors.add(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,5 +3,6 @@ package groowt.view.component.web.antlr
|
|||||||
enum class ParserErrorType(val message: String) {
|
enum class ParserErrorType(val message: String) {
|
||||||
NO_VIABLE_ALTERNATIVE("Parser has no viable alternative."),
|
NO_VIABLE_ALTERNATIVE("Parser has no viable alternative."),
|
||||||
INPUT_MISMATCH("Parser input mismatch."),
|
INPUT_MISMATCH("Parser input mismatch."),
|
||||||
FAILED_PREDICATE("Parser input failed predicate.")
|
FAILED_PREDICATE("Parser input failed predicate."),
|
||||||
|
UNKNOWN("Parser unknown error.")
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
package groowt.view.component.web.antlr
|
package groowt.view.component.web.antlr
|
||||||
|
|
||||||
import groowt.view.component.web.antlr.WebViewComponentsParser.CompilationUnitContext
|
import groowt.view.component.web.antlr.WebViewComponentsParser.CompilationUnitContext
|
||||||
|
import groowt.view.component.web.util.SourcePosition
|
||||||
import org.antlr.v4.runtime.*
|
import org.antlr.v4.runtime.*
|
||||||
import org.antlr.v4.runtime.tree.ErrorNode
|
import org.antlr.v4.runtime.tree.ErrorNode
|
||||||
import org.antlr.v4.runtime.tree.ParseTree
|
import org.antlr.v4.runtime.tree.ParseTree
|
||||||
@ -85,7 +86,7 @@ private fun formatForError(parser: Parser, tree: ParseTree): String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun formatTree(parser: Parser, tree: Tree, colors: Boolean, consumer: Consumer<String>): Unit =
|
fun formatTree(parser: Parser, tree: Tree, colors: Boolean, consumer: Consumer<String>): Unit =
|
||||||
consumer.accept(formatTree(parser, tree, colors).toString())
|
consumer.accept(formatTree(parser, tree, colors))
|
||||||
|
|
||||||
fun formatTree(parser: Parser, tree: Tree, colors: Boolean = true): String =
|
fun formatTree(parser: Parser, tree: Tree, colors: Boolean = true): String =
|
||||||
doFormatTree(parser, tree, colors, 0, " ", StringBuilder()).toString()
|
doFormatTree(parser, tree, colors, 0, " ", StringBuilder()).toString()
|
||||||
@ -109,12 +110,10 @@ private fun formatBasicInfo(parser: Parser, tree: Tree, sb: StringBuilder) {
|
|||||||
when (tree) {
|
when (tree) {
|
||||||
is ParserRuleContext -> {
|
is ParserRuleContext -> {
|
||||||
sb.append(parser.ruleNames[tree.ruleIndex])
|
sb.append(parser.ruleNames[tree.ruleIndex])
|
||||||
.append(
|
val start = SourcePosition.fromStartOfToken(tree.start)
|
||||||
"[${tree.start.line},${tree.start.charPositionInLine + 1}.."
|
val end = SourcePosition.fromEndOfToken(tree.stop)
|
||||||
+ "${tree.stop.line},${tree.stop.charPositionInLine + 1}]"
|
sb.append("[${start.line},${start.column}..${end.line},${end.column}]")
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
is TerminalNode -> {
|
is TerminalNode -> {
|
||||||
sb.append(parser.vocabulary.getDisplayName(tree.symbol.type))
|
sb.append(parser.vocabulary.getDisplayName(tree.symbol.type))
|
||||||
.append("[${tree.symbol.line},${tree.symbol.charPositionInLine + 1}]")
|
.append("[${tree.symbol.line},${tree.symbol.charPositionInLine + 1}]")
|
||||||
@ -139,14 +138,14 @@ private fun doFormatTree(
|
|||||||
sb.append(ansi().fgRed())
|
sb.append(ansi().fgRed())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
formatBasicInfo (parser, tree, sb)
|
formatBasicInfo(parser, tree, sb)
|
||||||
if (e != null) {
|
if (e != null) {
|
||||||
sb.append(": Exception: ${e.javaClass.simpleName}(${escapeChars(tree.text)})")
|
sb.append(": Exception: ${e.javaClass.simpleName}(${escapeChars(tree.text)})")
|
||||||
if (colors) {
|
if (colors) {
|
||||||
sb.append(ansi().reset())
|
sb.append(ansi().reset())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sb . append ("\n")
|
sb.append ("\n")
|
||||||
var i = 0
|
var i = 0
|
||||||
while (i < tree.childCount) {
|
while (i < tree.childCount) {
|
||||||
doFormatTree(parser, tree.getChild(i), colors, indentTimes + 1, indentText, sb)
|
doFormatTree(parser, tree.getChild(i), colors, indentTimes + 1, indentText, sb)
|
||||||
|
@ -11,6 +11,7 @@ public class WebViewComponentsLexer extends WebViewComponentsLexerBase {
|
|||||||
|
|
||||||
public WebViewComponentsLexer() {
|
public WebViewComponentsLexer() {
|
||||||
super();
|
super();
|
||||||
|
this._interp = new PositionAdjustingLexerATNSimulator(this, _ATN, _decisionToDFA, _sharedContextCache);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -14,7 +14,7 @@ class WebViewComponentsTokenStream(
|
|||||||
|
|
||||||
constructor(lexer: WebViewComponentsLexer) : this(
|
constructor(lexer: WebViewComponentsLexer) : this(
|
||||||
lexer,
|
lexer,
|
||||||
setOf(WebViewComponentsLexer.HIDDEN, WebViewComponentsLexer.ERROR)
|
setOf(WebViewComponentsLexer.HIDDEN)
|
||||||
)
|
)
|
||||||
|
|
||||||
private val tokens: MutableList<Token> = ArrayList()
|
private val tokens: MutableList<Token> = ArrayList()
|
||||||
|
@ -4,6 +4,7 @@ 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;
|
||||||
|
import groowt.view.component.web.antlr.WebViewComponentsParser.BodyTextContext;
|
||||||
import groowt.view.component.web.antlr.WebViewComponentsParserBaseVisitor;
|
import groowt.view.component.web.antlr.WebViewComponentsParserBaseVisitor;
|
||||||
import groowt.view.component.web.ast.node.*;
|
import groowt.view.component.web.ast.node.*;
|
||||||
import groowt.view.component.web.util.TokenRange;
|
import groowt.view.component.web.util.TokenRange;
|
||||||
@ -18,7 +19,6 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
public class DefaultAstBuilderVisitor extends WebViewComponentsParserBaseVisitor<Node> {
|
public class DefaultAstBuilderVisitor extends WebViewComponentsParserBaseVisitor<Node> {
|
||||||
|
|
||||||
@ -73,18 +73,10 @@ public class DefaultAstBuilderVisitor extends WebViewComponentsParserBaseVisitor
|
|||||||
return ctx.getChild(0).accept(this);
|
return ctx.getChild(0).accept(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected <R extends Node> R getSingleChildAs(ParserRuleContext ctx, Class<R> type) {
|
|
||||||
return type.cast(this.getSingleChild(ctx));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected TokenRange getTokenRange(ParserRuleContext ctx) {
|
protected TokenRange getTokenRange(ParserRuleContext ctx) {
|
||||||
return TokenRange.of(ctx.start, ctx.stop);
|
return TokenRange.of(ctx.start, ctx.stop);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected TerminalNode getSingleChildTerminalNode(WebViewComponentsParser.JStringBodyTextContext ctx) {
|
|
||||||
return ctx.getChild(TerminalNode.class, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node visitCompilationUnit(WebViewComponentsParser.CompilationUnitContext ctx) {
|
public Node visitCompilationUnit(WebViewComponentsParser.CompilationUnitContext ctx) {
|
||||||
final PreambleNode preamble = this.getSingleAs(ctx.preamble(), PreambleNode.class);
|
final PreambleNode preamble = this.getSingleAs(ctx.preamble(), PreambleNode.class);
|
||||||
@ -119,37 +111,57 @@ public class DefaultAstBuilderVisitor extends WebViewComponentsParserBaseVisitor
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node visitBodyText(WebViewComponentsParser.BodyTextContext ctx) {
|
public @Nullable Node visitBodyText(BodyTextContext ctx) {
|
||||||
return this.getSingleChild(ctx);
|
final List<BodyTextChild> children = new ArrayList<>();
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Node visitGStringBodyText(WebViewComponentsParser.GStringBodyTextContext ctx) {
|
|
||||||
final List<Node> children = new ArrayList<>();
|
|
||||||
for (final var child : ctx.children) {
|
for (final var child : ctx.children) {
|
||||||
final @Nullable Node childResult = child.accept(this);
|
final @Nullable Node childResult = child.accept(this);
|
||||||
if (childResult != null) {
|
if (childResult != null) {
|
||||||
children.add(childResult);
|
children.add((BodyTextChild) childResult);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this.nodeFactory.gStringBodyTextNode(this.getTokenRange(ctx), children);
|
if (children.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return this.nodeFactory.bodyTextNode(this.getTokenRange(ctx), children);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable Node visitJStringBodyText(WebViewComponentsParser.JStringBodyTextContext ctx) {
|
public @Nullable Node visitQuestionTag(WebViewComponentsParser.QuestionTagContext ctx) {
|
||||||
final String text = ctx.getText();
|
final List<QuestionTagChild> children = new ArrayList<>();
|
||||||
if (isNotBlankNotEmpty(text)) {
|
for (final var child : ctx.children) {
|
||||||
return this.nodeFactory.jStringBodyTextNode(
|
final @Nullable Node childResult = child.accept(this);
|
||||||
this.getTokenRange(ctx),
|
if (childResult != null) {
|
||||||
text
|
children.add((QuestionTagChild) childResult);
|
||||||
);
|
}
|
||||||
|
}
|
||||||
|
return this.nodeFactory.questionTagNode(this.getTokenRange(ctx), children);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Node visitHtmlComment(WebViewComponentsParser.HtmlCommentContext ctx) {
|
||||||
|
final List<HtmlCommentChild> children = new ArrayList<>();
|
||||||
|
for (final var child : ctx.children) {
|
||||||
|
final @Nullable Node childResult = child.accept(this);
|
||||||
|
if (childResult != null) {
|
||||||
|
children.add((HtmlCommentChild) childResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.nodeFactory.htmlCommentNode(this.getTokenRange(ctx), children);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Node visitText(WebViewComponentsParser.TextContext ctx) {
|
||||||
|
final String content = ctx.getText();
|
||||||
|
if (isNotBlankNotEmpty(content)) {
|
||||||
|
return this.nodeFactory.textNode(this.getTokenRange(ctx), content);
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node visitGStringBodyTextGroovyElement(WebViewComponentsParser.GStringBodyTextGroovyElementContext ctx) {
|
public Node visitBodyTextGroovyElement(WebViewComponentsParser.BodyTextGroovyElementContext ctx) {
|
||||||
return this.getSingleChild(ctx);
|
return this.getSingleChild(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -208,16 +220,6 @@ public class DefaultAstBuilderVisitor extends WebViewComponentsParserBaseVisitor
|
|||||||
return this.nodeFactory.componentArgsNode(this.getTokenRange(ctx), typeNode, constructorNode, attrNodes);
|
return this.nodeFactory.componentArgsNode(this.getTokenRange(ctx), typeNode, constructorNode, attrNodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Pattern lowercaseLetterPattern = Pattern.compile("\\p{Ll}");
|
|
||||||
|
|
||||||
protected boolean startsWithLowercaseLetter(String subject) {
|
|
||||||
if (subject.isEmpty()) {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"Cannot test for starting lowercase letter when the subject length is 0; given: " + subject);
|
|
||||||
}
|
|
||||||
return lowercaseLetterPattern.matcher(subject.substring(0, 1)).matches();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node visitComponentType(WebViewComponentsParser.ComponentTypeContext ctx) {
|
public Node visitComponentType(WebViewComponentsParser.ComponentTypeContext ctx) {
|
||||||
final var typedIdentifier = ctx.TypedIdentifier();
|
final var typedIdentifier = ctx.TypedIdentifier();
|
||||||
@ -291,7 +293,6 @@ public class DefaultAstBuilderVisitor extends WebViewComponentsParserBaseVisitor
|
|||||||
if (groovyCode != null) {
|
if (groovyCode != null) {
|
||||||
final MergedGroovyCodeToken groovyCodeToken = (MergedGroovyCodeToken) groovyCode.getSymbol();
|
final MergedGroovyCodeToken groovyCodeToken = (MergedGroovyCodeToken) groovyCode.getSymbol();
|
||||||
if (canBeGString(groovyCodeToken.getOriginals())) {
|
if (canBeGString(groovyCodeToken.getOriginals())) {
|
||||||
// TODO: we need to set the appropriate type: slashy, dollar slashy, etc.
|
|
||||||
return this.nodeFactory.gStringValueNode(ctxTokenRange, groovyCodeToken.getTokenIndex());
|
return this.nodeFactory.gStringValueNode(ctxTokenRange, groovyCodeToken.getTokenIndex());
|
||||||
} else {
|
} else {
|
||||||
return this.nodeFactory.jStringValueNode(ctxTokenRange, groovyCode.getText());
|
return this.nodeFactory.jStringValueNode(ctxTokenRange, groovyCode.getText());
|
||||||
@ -337,7 +338,7 @@ public class DefaultAstBuilderVisitor extends WebViewComponentsParserBaseVisitor
|
|||||||
public @Nullable Node visitEqualsScriptlet(WebViewComponentsParser.EqualsScriptletContext ctx) {
|
public @Nullable Node visitEqualsScriptlet(WebViewComponentsParser.EqualsScriptletContext ctx) {
|
||||||
final TerminalNode groovyCode = ctx.GroovyCode();
|
final TerminalNode groovyCode = ctx.GroovyCode();
|
||||||
if (groovyCode != null) {
|
if (groovyCode != null) {
|
||||||
return this.nodeFactory.dollarScriptletNode(
|
return this.nodeFactory.equalsScriptletNode(
|
||||||
this.getTokenRange(ctx),
|
this.getTokenRange(ctx),
|
||||||
ctx.GroovyCode().getSymbol().getTokenIndex()
|
ctx.GroovyCode().getSymbol().getTokenIndex()
|
||||||
);
|
);
|
||||||
@ -384,12 +385,12 @@ public class DefaultAstBuilderVisitor extends WebViewComponentsParserBaseVisitor
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node visitTerminal(TerminalNode node) {
|
public Node visitTerminal(TerminalNode node) {
|
||||||
throw new UnsupportedOperationException();
|
throw new WebViewComponentBugError("Should not be visiting terminal nodes.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node visitErrorNode(ErrorNode node) {
|
public Node visitErrorNode(ErrorNode node) {
|
||||||
throw new IllegalStateException("Found an ErrorNode: " + node);
|
throw new WebViewComponentBugError("Should not have found an ErrorNode by this point: " + node);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,8 +22,10 @@ public class DefaultNodeFactory implements NodeFactory {
|
|||||||
CompilationUnitNode.class,
|
CompilationUnitNode.class,
|
||||||
PreambleNode.class,
|
PreambleNode.class,
|
||||||
BodyNode.class,
|
BodyNode.class,
|
||||||
GStringBodyTextNode.class,
|
BodyTextNode.class,
|
||||||
JStringBodyTextNode.class,
|
QuestionNode.class,
|
||||||
|
HtmlCommentNode.class,
|
||||||
|
TextNode.class,
|
||||||
TypedComponentNode.class,
|
TypedComponentNode.class,
|
||||||
FragmentComponentNode.class,
|
FragmentComponentNode.class,
|
||||||
ComponentArgsNode.class,
|
ComponentArgsNode.class,
|
||||||
@ -102,13 +104,23 @@ public class DefaultNodeFactory implements NodeFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GStringBodyTextNode gStringBodyTextNode(TokenRange tokenRange, List<? extends Node> children) {
|
public BodyTextNode bodyTextNode(TokenRange tokenRange, List<? extends BodyTextChild> children) {
|
||||||
return this.objectFactory.get(GStringBodyTextNode.class, tokenRange, children);
|
return this.objectFactory.get(BodyTextNode.class, tokenRange, children);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JStringBodyTextNode jStringBodyTextNode(TokenRange tokenRange, String content) {
|
public QuestionNode questionTagNode(TokenRange tokenRange, List<? extends QuestionTagChild> children) {
|
||||||
return this.objectFactory.get(JStringBodyTextNode.class, tokenRange, content);
|
return this.objectFactory.get(QuestionNode.class, tokenRange, children);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HtmlCommentNode htmlCommentNode(TokenRange tokenRange, List<? extends HtmlCommentChild> children) {
|
||||||
|
return this.objectFactory.get(HtmlCommentNode.class, tokenRange, children);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TextNode textNode(TokenRange tokenRange, String content) {
|
||||||
|
return this.objectFactory.get(TextNode.class, tokenRange, content);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -196,6 +208,11 @@ public class DefaultNodeFactory implements NodeFactory {
|
|||||||
return this.objectFactory.get(ComponentValueNode.class, tokenRange, componentNode);
|
return this.objectFactory.get(ComponentValueNode.class, tokenRange, componentNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EqualsScriptletNode equalsScriptletNode(TokenRange tokenRange, int groovyIndex) {
|
||||||
|
return this.objectFactory.get(EqualsScriptletNode.class, tokenRange, groovyIndex);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PlainScriptletNode plainScriptletNode(TokenRange tokenRange, int groovyIndex) {
|
public PlainScriptletNode plainScriptletNode(TokenRange tokenRange, int groovyIndex) {
|
||||||
return this.objectFactory.get(PlainScriptletNode.class, tokenRange, groovyIndex);
|
return this.objectFactory.get(PlainScriptletNode.class, tokenRange, groovyIndex);
|
||||||
|
@ -18,9 +18,13 @@ public interface NodeFactory {
|
|||||||
|
|
||||||
BodyNode bodyNode(TokenRange tokenRange, List<? extends BodyChildNode> children);
|
BodyNode bodyNode(TokenRange tokenRange, List<? extends BodyChildNode> children);
|
||||||
|
|
||||||
GStringBodyTextNode gStringBodyTextNode(TokenRange tokenRange, List<? extends Node> children);
|
BodyTextNode bodyTextNode(TokenRange tokenRange, List<? extends BodyTextChild> children);
|
||||||
|
|
||||||
JStringBodyTextNode jStringBodyTextNode(TokenRange tokenRange, String content);
|
QuestionNode questionTagNode(TokenRange tokenRange, List<? extends QuestionTagChild> children);
|
||||||
|
|
||||||
|
HtmlCommentNode htmlCommentNode(TokenRange tokenRange, List<? extends HtmlCommentChild> children);
|
||||||
|
|
||||||
|
TextNode textNode(TokenRange tokenRange, String content);
|
||||||
|
|
||||||
TypedComponentNode typedComponentNode(
|
TypedComponentNode typedComponentNode(
|
||||||
TokenRange tokenRange,
|
TokenRange tokenRange,
|
||||||
@ -59,6 +63,8 @@ public interface NodeFactory {
|
|||||||
|
|
||||||
ComponentValueNode componentValueNode(TokenRange tokenRange, ComponentNode componentNode);
|
ComponentValueNode componentValueNode(TokenRange tokenRange, ComponentNode componentNode);
|
||||||
|
|
||||||
|
EqualsScriptletNode equalsScriptletNode(TokenRange tokenRange, int groovyIndex);
|
||||||
|
|
||||||
PlainScriptletNode plainScriptletNode(TokenRange tokenRange, int groovyIndex);
|
PlainScriptletNode plainScriptletNode(TokenRange tokenRange, int groovyIndex);
|
||||||
|
|
||||||
DollarScriptletNode dollarScriptletNode(TokenRange tokenRange, int groovyIndex);
|
DollarScriptletNode dollarScriptletNode(TokenRange tokenRange, int groovyIndex);
|
||||||
|
@ -21,7 +21,11 @@ public class BodyNode extends AbstractTreeNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public BodyNode(NodeExtensionContainer extensionContainer, @Given TokenRange tokenRange, @Given List<? extends BodyChildNode> children) {
|
public BodyNode(
|
||||||
|
NodeExtensionContainer extensionContainer,
|
||||||
|
@Given TokenRange tokenRange,
|
||||||
|
@Given List<? extends BodyChildNode> children
|
||||||
|
) {
|
||||||
super(tokenRange, extensionContainer, childrenAsNodes(checkChildren(children)));
|
super(tokenRange, extensionContainer, childrenAsNodes(checkChildren(children)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
package groowt.view.component.web.ast.node;
|
||||||
|
|
||||||
|
public interface BodyTextChild {
|
||||||
|
|
||||||
|
default Node asNode() {
|
||||||
|
return (Node) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package groowt.view.component.web.ast.node;
|
||||||
|
|
||||||
|
import groowt.util.di.annotation.Given;
|
||||||
|
import groowt.view.component.web.ast.extension.NodeExtensionContainer;
|
||||||
|
import groowt.view.component.web.util.TokenRange;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class BodyTextNode extends AbstractTreeNode implements BodyChildNode {
|
||||||
|
|
||||||
|
protected static List<? extends BodyTextChild> checkChildren(List<? extends BodyTextChild> children) {
|
||||||
|
if (children.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("A valid BodyTextNode must have at least one child BodyTextChildNode.");
|
||||||
|
}
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static List<Node> childrenAsNodes(List<? extends BodyTextChild> children) {
|
||||||
|
return children.stream().map(BodyTextChild::asNode).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public BodyTextNode(
|
||||||
|
NodeExtensionContainer extensionContainer,
|
||||||
|
@Given TokenRange tokenRange,
|
||||||
|
@Given List<? extends BodyTextChild> children
|
||||||
|
) {
|
||||||
|
super(tokenRange, extensionContainer, childrenAsNodes(checkChildren(children)));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -6,9 +6,8 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public sealed abstract class ComponentNode extends AbstractTreeNode implements BodyChildNode permits
|
public sealed abstract class ComponentNode extends AbstractTreeNode implements BodyChildNode
|
||||||
FragmentComponentNode,
|
permits FragmentComponentNode, TypedComponentNode {
|
||||||
TypedComponentNode {
|
|
||||||
|
|
||||||
private final BodyNode bodyNode;
|
private final BodyNode bodyNode;
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ import groowt.view.component.web.ast.extension.NodeExtensionContainer;
|
|||||||
import groowt.view.component.web.util.TokenRange;
|
import groowt.view.component.web.util.TokenRange;
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
|
|
||||||
public class DollarReferenceNode extends AbstractLeafNode {
|
public class DollarReferenceNode extends AbstractLeafNode implements GroovyBodyNode {
|
||||||
|
|
||||||
private final int groovyTokenIndex;
|
private final int groovyTokenIndex;
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ import groowt.view.component.web.ast.extension.NodeExtensionContainer;
|
|||||||
import groowt.view.component.web.util.TokenRange;
|
import groowt.view.component.web.util.TokenRange;
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
|
|
||||||
public class DollarScriptletNode extends AbstractLeafNode {
|
public class DollarScriptletNode extends AbstractLeafNode implements GroovyBodyNode {
|
||||||
|
|
||||||
private final GStringScriptletExtension gStringScriptlet;
|
private final GStringScriptletExtension gStringScriptlet;
|
||||||
|
|
||||||
|
@ -0,0 +1,50 @@
|
|||||||
|
package groowt.view.component.web.ast.node;
|
||||||
|
|
||||||
|
import groowt.util.di.annotation.Given;
|
||||||
|
import groowt.view.component.web.antlr.TokenList;
|
||||||
|
import groowt.view.component.web.ast.extension.GroovyCodeNodeExtension;
|
||||||
|
import groowt.view.component.web.ast.extension.NodeExtensionContainer;
|
||||||
|
import groowt.view.component.web.util.TokenRange;
|
||||||
|
import org.antlr.v4.runtime.Token;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class EqualsScriptletNode extends AbstractLeafNode implements GroovyBodyNode {
|
||||||
|
|
||||||
|
private final int groovyIndex;
|
||||||
|
private final GroovyCodeNodeExtension groovyCode;
|
||||||
|
|
||||||
|
public EqualsScriptletNode(
|
||||||
|
TokenList tokenList,
|
||||||
|
NodeExtensionContainer extensionContainer,
|
||||||
|
@Given TokenRange tokenRange,
|
||||||
|
@Given int groovyIndex
|
||||||
|
) {
|
||||||
|
super(tokenRange, extensionContainer);
|
||||||
|
this.groovyIndex = groovyIndex;
|
||||||
|
this.groovyCode = this.createGroovyCode(tokenList);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected GroovyCodeNodeExtension createGroovyCode(TokenList tokenList) {
|
||||||
|
return this.createExtension(
|
||||||
|
GroovyCodeNodeExtension.class,
|
||||||
|
TokenRange.fromIndex(tokenList, this.groovyIndex),
|
||||||
|
(Function<? super List<Token>, String>) this::toValidGroovyCode
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String toValidGroovyCode(List<Token> groovyTokens) {
|
||||||
|
return "{ -> " + groovyTokens.stream().map(Token::getText).collect(Collectors.joining()) + " }";
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getGroovyIndex() {
|
||||||
|
return this.groovyIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GroovyCodeNodeExtension getGroovyCode() {
|
||||||
|
return this.groovyCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -8,6 +8,7 @@ import jakarta.inject.Inject;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public class GStringBodyTextNode extends AbstractTreeNode implements BodyChildNode {
|
public class GStringBodyTextNode extends AbstractTreeNode implements BodyChildNode {
|
||||||
|
|
||||||
protected static List<? extends Node> checkChildren(List<? extends Node> children) {
|
protected static List<? extends Node> checkChildren(List<? extends Node> children) {
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
package groowt.view.component.web.ast.node;
|
||||||
|
|
||||||
|
public interface GroovyBodyNode extends BodyTextChild, HtmlCommentChild, QuestionTagChild {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default Node asNode() {
|
||||||
|
return (Node) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package groowt.view.component.web.ast.node;
|
||||||
|
|
||||||
|
public interface HtmlCommentChild {
|
||||||
|
|
||||||
|
default Node asNode() {
|
||||||
|
return (Node) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package groowt.view.component.web.ast.node;
|
||||||
|
|
||||||
|
import groowt.util.di.annotation.Given;
|
||||||
|
import groowt.view.component.web.ast.extension.NodeExtensionContainer;
|
||||||
|
import groowt.view.component.web.util.TokenRange;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class HtmlCommentNode extends AbstractTreeNode implements BodyTextChild {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public HtmlCommentNode(
|
||||||
|
NodeExtensionContainer extensionContainer,
|
||||||
|
@Given TokenRange tokenRange,
|
||||||
|
@Given List<? extends HtmlCommentChild> children
|
||||||
|
) {
|
||||||
|
super(tokenRange, extensionContainer, children.stream().map(HtmlCommentChild::asNode).toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -5,6 +5,7 @@ import groowt.view.component.web.ast.extension.NodeExtensionContainer;
|
|||||||
import groowt.view.component.web.util.TokenRange;
|
import groowt.view.component.web.util.TokenRange;
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public class JStringBodyTextNode extends AbstractLeafNode implements BodyChildNode {
|
public class JStringBodyTextNode extends AbstractLeafNode implements BodyChildNode {
|
||||||
|
|
||||||
private final String content;
|
private final String content;
|
||||||
|
@ -12,7 +12,7 @@ import java.util.List;
|
|||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class PlainScriptletNode extends AbstractLeafNode implements BodyChildNode {
|
public class PlainScriptletNode extends AbstractLeafNode implements GroovyBodyNode {
|
||||||
|
|
||||||
private final int groovyIndex;
|
private final int groovyIndex;
|
||||||
private final GroovyCodeNodeExtension groovyCode;
|
private final GroovyCodeNodeExtension groovyCode;
|
||||||
@ -38,7 +38,7 @@ public class PlainScriptletNode extends AbstractLeafNode implements BodyChildNod
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected String toValidGroovyCode(List<Token> groovyTokens) {
|
protected String toValidGroovyCode(List<Token> groovyTokens) {
|
||||||
return "{ Writer out ->\n" + groovyTokens.stream().map(Token::getText).collect(Collectors.joining()) + "\n}";
|
return "{ Writer out -> " + groovyTokens.stream().map(Token::getText).collect(Collectors.joining()) + " }";
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getGroovyIndex() {
|
public int getGroovyIndex() {
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
package groowt.view.component.web.ast.node;
|
||||||
|
|
||||||
|
import groowt.util.di.annotation.Given;
|
||||||
|
import groowt.view.component.web.ast.extension.NodeExtensionContainer;
|
||||||
|
import groowt.view.component.web.util.TokenRange;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class QuestionNode extends AbstractTreeNode implements BodyTextChild {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public QuestionNode(
|
||||||
|
NodeExtensionContainer extensionContainer,
|
||||||
|
@Given TokenRange tokenRange,
|
||||||
|
@Given List<? extends QuestionTagChild> children
|
||||||
|
) {
|
||||||
|
super(tokenRange, extensionContainer, children.stream().map(QuestionTagChild::asNode).toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package groowt.view.component.web.ast.node;
|
||||||
|
|
||||||
|
public interface QuestionTagChild {
|
||||||
|
|
||||||
|
default Node asNode() {
|
||||||
|
return (Node) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
package groowt.view.component.web.ast.node;
|
||||||
|
|
||||||
|
import groowt.util.di.annotation.Given;
|
||||||
|
import groowt.view.component.web.ast.extension.NodeExtensionContainer;
|
||||||
|
import groowt.view.component.web.util.TokenRange;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
|
||||||
|
public class TextNode extends AbstractLeafNode implements BodyTextChild, HtmlCommentChild, QuestionTagChild {
|
||||||
|
|
||||||
|
private final String content;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public TextNode(
|
||||||
|
NodeExtensionContainer extensionContainer,
|
||||||
|
@Given TokenRange tokenRange,
|
||||||
|
@Given String content
|
||||||
|
) {
|
||||||
|
super(tokenRange, extensionContainer);
|
||||||
|
this.content = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Node asNode() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getContent() {
|
||||||
|
return this.content;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -20,30 +20,34 @@ public record SourcePosition(int line, int column) {
|
|||||||
|
|
||||||
public static SourcePosition fromEndOfToken(Token token) {
|
public static SourcePosition fromEndOfToken(Token token) {
|
||||||
final var text = token.getText();
|
final var text = token.getText();
|
||||||
int line = token.getLine();
|
if (token.getType() == Token.EOF || text.length() == 1) {
|
||||||
int col = token.getCharPositionInLine() + 1;
|
return new SourcePosition(token.getLine(), token.getCharPositionInLine() + 1);
|
||||||
int i = 0;
|
} else {
|
||||||
while (i < text.length()) {
|
int line = token.getLine();
|
||||||
final char c0 = text.charAt(i);
|
int col = token.getCharPositionInLine() + 1;
|
||||||
if (c0 == '\r') {
|
int i = 0;
|
||||||
line++;
|
while (i < text.length()) {
|
||||||
col = 1;
|
final char c0 = text.charAt(i);
|
||||||
final char c1 = text.charAt(i + 1);
|
if (c0 == '\r') {
|
||||||
if (c1 == '\n') {
|
line++;
|
||||||
i += 2;
|
col = 1;
|
||||||
|
final char c1 = text.charAt(i + 1);
|
||||||
|
if (c1 == '\n') {
|
||||||
|
i += 2;
|
||||||
|
} else {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
} else if (c0 == '\n') {
|
||||||
|
line++;
|
||||||
|
col = 1;
|
||||||
|
i++;
|
||||||
} else {
|
} else {
|
||||||
|
col++;
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
} else if (c0 == '\n') {
|
|
||||||
line++;
|
|
||||||
col = 1;
|
|
||||||
i++;
|
|
||||||
} else {
|
|
||||||
col++;
|
|
||||||
i++;
|
|
||||||
}
|
}
|
||||||
|
return new SourcePosition(line, col);
|
||||||
}
|
}
|
||||||
return new SourcePosition(line, col);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toStringShort() {
|
public String toStringShort() {
|
||||||
|
@ -1,69 +0,0 @@
|
|||||||
CompilationUnitNode(1,1..21,6)
|
|
||||||
PreambleNode(1,1..6,1)
|
|
||||||
PreambleBreak[1,1](---\n)
|
|
||||||
GroovyCode[2,1](import some.Thing // a comment...World!')
|
|
||||||
PreambleBreak[4,31](\n---\n)
|
|
||||||
BodyNode(6,1..21,1)
|
|
||||||
JStringBodyTextNode(6,1..7,1)
|
|
||||||
RawText[6,1](<!DOCTYPE html>\n)
|
|
||||||
TypedComponentNode(7,1..20,8)
|
|
||||||
ComponentArgsNode(7,2..7,6)
|
|
||||||
StringComponentTypeNode(7,2..7,6)
|
|
||||||
StringIdentifier[7,2](html)
|
|
||||||
BodyNode(7,7..20,1)
|
|
||||||
TypedComponentNode(8,5..8,18)
|
|
||||||
ComponentArgsNode(8,6..8,10)
|
|
||||||
StringComponentTypeNode(8,6..8,10)
|
|
||||||
StringIdentifier[8,6](head)
|
|
||||||
TypedComponentNode(9,5..19,12)
|
|
||||||
ComponentArgsNode(9,6..9,10)
|
|
||||||
StringComponentTypeNode(9,6..9,10)
|
|
||||||
StringIdentifier[9,6](body)
|
|
||||||
BodyNode(9,11..19,5)
|
|
||||||
TypedComponentNode(10,9..10,29)
|
|
||||||
ComponentArgsNode(10,10..10,12)
|
|
||||||
StringComponentTypeNode(10,10..10,12)
|
|
||||||
StringIdentifier[10,10](h1)
|
|
||||||
BodyNode(10,13..10,24)
|
|
||||||
GStringBodyTextNode(10,13..10,24)
|
|
||||||
DollarScriptletNode(10,13..10,24)
|
|
||||||
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)
|
|
||||||
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)
|
|
||||||
TypedIdentifier[12,14](Case)
|
|
||||||
KeyValueAttrNode(12,19..12,36)
|
|
||||||
KeyNode(12,19..12,24)
|
|
||||||
AttributeIdentifier[12,19](cond)
|
|
||||||
Equals[12,23](=)
|
|
||||||
ClosureValueNode(12,24..12,36)
|
|
||||||
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)
|
|
||||||
StringIdentifier[13,18](p)
|
|
||||||
BodyNode(13,20..13,33)
|
|
||||||
JStringBodyTextNode(13,20..13,33)
|
|
||||||
RawText[13,20](It's true! :))
|
|
||||||
TypedComponentNode(15,13..17,23)
|
|
||||||
ComponentArgsNode(15,14..15,21)
|
|
||||||
ClassComponentTypeNode(15,14..15,21)
|
|
||||||
TypedIdentifier[15,14](Default)
|
|
||||||
BodyNode(15,22..17,13)
|
|
||||||
TypedComponentNode(16,17..16,40)
|
|
||||||
ComponentArgsNode(16,18..16,19)
|
|
||||||
StringComponentTypeNode(16,18..16,19)
|
|
||||||
StringIdentifier[16,18](p)
|
|
||||||
BodyNode(16,20..16,36)
|
|
||||||
JStringBodyTextNode(16,20..16,36)
|
|
||||||
RawText[16,20](It's false... :()
|
|
@ -1,10 +0,0 @@
|
|||||||
CompilationUnitNode(1,1..2,6)
|
|
||||||
BodyNode(1,1..2,1)
|
|
||||||
GStringBodyTextNode(1,1..2,1)
|
|
||||||
JStringBodyTextNode(1,1..1,8)
|
|
||||||
RawText[1,1](Hello, )
|
|
||||||
DollarReferenceNode(1,8..1,15)
|
|
||||||
DollarReferenceStart[1,8]($)
|
|
||||||
GroovyCode[1,9](target)
|
|
||||||
JStringBodyTextNode(1,15..2,1)
|
|
||||||
RawText[1,15](!\n)
|
|
@ -1,9 +0,0 @@
|
|||||||
CompilationUnitNode(1,1..2,6)
|
|
||||||
BodyNode(1,1..2,1)
|
|
||||||
TypedComponentNode(1,1..1,35)
|
|
||||||
ComponentArgsNode(1,2..1,10)
|
|
||||||
ClassComponentTypeNode(1,2..1,10)
|
|
||||||
TypedIdentifier[1,2](Greeting)
|
|
||||||
BodyNode(1,11..1,24)
|
|
||||||
JStringBodyTextNode(1,11..1,24)
|
|
||||||
RawText[1,11](to the world!)
|
|
@ -10,6 +10,7 @@ import org.junit.jupiter.api.Test
|
|||||||
import static groowt.view.component.web.antlr.TokenUtil.getTokenName
|
import static groowt.view.component.web.antlr.TokenUtil.getTokenName
|
||||||
import static groowt.view.component.web.antlr.WebViewComponentsLexer.GroovyCode
|
import static groowt.view.component.web.antlr.WebViewComponentsLexer.GroovyCode
|
||||||
import static groowt.view.component.web.antlr.WebViewComponentsLexer.PreambleBreak
|
import static groowt.view.component.web.antlr.WebViewComponentsLexer.PreambleBreak
|
||||||
|
import static groowt.view.component.web.antlr.WebViewComponentsLexerBase.RawText
|
||||||
import static org.antlr.v4.runtime.Recognizer.EOF
|
import static org.antlr.v4.runtime.Recognizer.EOF
|
||||||
import static org.junit.jupiter.api.Assertions.*
|
import static org.junit.jupiter.api.Assertions.*
|
||||||
|
|
||||||
@ -50,11 +51,11 @@ class WebViewComponentsTokenStreamTests {
|
|||||||
def lexer = new WebViewComponentsLexer(input)
|
def lexer = new WebViewComponentsLexer(input)
|
||||||
def tokenStream = new WebViewComponentsTokenStream(lexer)
|
def tokenStream = new WebViewComponentsTokenStream(lexer)
|
||||||
def tokens = tokenStream.allTokens
|
def tokens = tokenStream.allTokens
|
||||||
assertTypes([PreambleBreak, GroovyCode, PreambleBreak, EOF], tokens)
|
assertTypes([PreambleBreak, GroovyCode, PreambleBreak, RawText, EOF], tokens)
|
||||||
assertMergedGroovyCodeToken(tokens[1]) {
|
assertMergedGroovyCodeToken(tokens[1]) {
|
||||||
assertEquals('println \'Hello, World!\' // comment\n', it.text)
|
assertEquals('println \'Hello, World!\' // comment\n', it.text)
|
||||||
}
|
}
|
||||||
assertIterableEquals(0..3, tokens*.tokenIndex)
|
assertIterableEquals(0..4, tokens*.tokenIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -7,57 +7,15 @@ import org.antlr.v4.runtime.CharStream;
|
|||||||
import org.antlr.v4.runtime.CharStreams;
|
import org.antlr.v4.runtime.CharStreams;
|
||||||
import org.antlr.v4.runtime.Token;
|
import org.antlr.v4.runtime.Token;
|
||||||
import org.junit.jupiter.api.DynamicTest;
|
import org.junit.jupiter.api.DynamicTest;
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.junit.jupiter.api.TestFactory;
|
import org.junit.jupiter.api.TestFactory;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import static groowt.view.component.web.antlr.WebViewComponentsLexer.*;
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
public class WebViewComponentsLexerTests {
|
public class WebViewComponentsLexerTests {
|
||||||
|
|
||||||
private static void assertTokenType(int type, Token token) {
|
|
||||||
assertEquals(
|
|
||||||
type,
|
|
||||||
token.getType(),
|
|
||||||
() -> "Expected " + VOCABULARY.getDisplayName(type)
|
|
||||||
+ " but got " + VOCABULARY.getDisplayName(token.getType())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void helloTarget() {
|
|
||||||
final var input = CharStreams.fromString("Hello, $target!");
|
|
||||||
final var lexer = new WebViewComponentsLexer(input);
|
|
||||||
final var tokenStream = new WebViewComponentsTokenStream(lexer);
|
|
||||||
final var allTokens = tokenStream.getAllTokens();
|
|
||||||
assertEquals(5, allTokens.size(), () -> {
|
|
||||||
return "Wrong number of tokens; tokens: " + allTokens.stream()
|
|
||||||
.map(Token::toString)
|
|
||||||
.collect(Collectors.joining(", "));
|
|
||||||
});
|
|
||||||
final var t0 = allTokens.get(0);
|
|
||||||
final var t1 = allTokens.get(1);
|
|
||||||
final var t2 = allTokens.get(2);
|
|
||||||
final var t3 = allTokens.get(3);
|
|
||||||
final var t4 = allTokens.get(4);
|
|
||||||
assertEquals("Hello, ", t0.getText());
|
|
||||||
assertTokenType(RawText, t0);
|
|
||||||
assertEquals("$", t1.getText());
|
|
||||||
assertTokenType(DollarReferenceStart, t1);
|
|
||||||
assertEquals("target", t2.getText());
|
|
||||||
assertTokenType(GroovyCode, t2);
|
|
||||||
assertInstanceOf(MergedGroovyCodeToken.class, t2);
|
|
||||||
assertEquals("!", t3.getText());
|
|
||||||
assertTokenType(RawText, t3);
|
|
||||||
assertTokenType(EOF, t4);
|
|
||||||
}
|
|
||||||
|
|
||||||
@TestFactory
|
@TestFactory
|
||||||
public Collection<DynamicTest> lexerFileTests() {
|
public Collection<DynamicTest> lexerFileTests() {
|
||||||
return FileComparisonTestUtil.getTestsFor(
|
return FileComparisonTestUtil.getTestsFor(
|
||||||
@ -71,10 +29,8 @@ public class WebViewComponentsLexerTests {
|
|||||||
sourceFile -> {
|
sourceFile -> {
|
||||||
final CharStream input = CharStreams.fromString(FileUtil.readFile(sourceFile));
|
final CharStream input = CharStreams.fromString(FileUtil.readFile(sourceFile));
|
||||||
final WebViewComponentsLexer lexer = new WebViewComponentsLexer(input);
|
final WebViewComponentsLexer lexer = new WebViewComponentsLexer(input);
|
||||||
final WebViewComponentsTokenStream tokenStream = new WebViewComponentsTokenStream(
|
// include all (!) (non-skipped) tokens for testing via Set.of()
|
||||||
lexer,
|
final WebViewComponentsTokenStream tokenStream = new WebViewComponentsTokenStream(lexer, Set.of());
|
||||||
Set.of(WebViewComponentsLexer.HIDDEN) // include bad tokens for testing
|
|
||||||
);
|
|
||||||
final List<Token> allTokens = tokenStream.getAllTokensSkipEOF();
|
final List<Token> allTokens = tokenStream.getAllTokensSkipEOF();
|
||||||
final var sb = new StringBuilder();
|
final var sb = new StringBuilder();
|
||||||
for (int i = 0; i < allTokens.size(); i++) {
|
for (int i = 0; i < allTokens.size(); i++) {
|
||||||
|
@ -27,7 +27,7 @@ public final class WebViewComponentsParserTests {
|
|||||||
private static final Logger logger = LoggerFactory.getLogger(WebViewComponentsParserTests.class);
|
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 parserFileBase = String.join(File.separator, "src", "test", "parser");
|
||||||
private static final String parserTreeFileBase = String.join(File.separator, parserFileBase, "trees");
|
private static final String parserTreeFileBase = String.join(File.separator, parserFileBase, "parse-tree-files");
|
||||||
private static final String parseTreeFileSuffix = "_parseTree";
|
private static final String parseTreeFileSuffix = "_parseTree";
|
||||||
private static final String parseTreeFileExtension = ".txt";
|
private static final String parseTreeFileExtension = ".txt";
|
||||||
private static final Set<String> parserFileGlobs = Set.of(
|
private static final Set<String> parserFileGlobs = Set.of(
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
<!DOCTYPE html>
|
@ -0,0 +1 @@
|
|||||||
|
<></>
|
@ -0,0 +1,3 @@
|
|||||||
|
<Test
|
||||||
|
someAttr
|
||||||
|
/>
|
@ -0,0 +1 @@
|
|||||||
|
Hello!
|
@ -0,0 +1,2 @@
|
|||||||
|
---
|
||||||
|
----
|
@ -0,0 +1,2 @@
|
|||||||
|
----
|
||||||
|
---
|
@ -0,0 +1,2 @@
|
|||||||
|
---
|
||||||
|
------
|
@ -1,7 +1,3 @@
|
|||||||
---
|
---
|
||||||
|
// ---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
def regex = $/match --- me $ $test ${test('---')} $$ $/ / /$
|
||||||
|
---
|
@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
def test = "---$test${test('---')}"
|
||||||
|
---
|
@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
def test = '---'
|
||||||
|
---
|
@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
def regex = (/match --- \/ me/)
|
||||||
|
---
|
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
def test = """
|
||||||
|
--- $test ${test("---")} " \$
|
||||||
|
"""
|
||||||
|
---
|
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
def test = '''
|
||||||
|
---
|
||||||
|
'''
|
||||||
|
---
|
@ -0,0 +1 @@
|
|||||||
|
<Test />
|
@ -0,0 +1 @@
|
|||||||
|
---
|
@ -1,5 +1,6 @@
|
|||||||
0: ComponentOpen[1,1](<)
|
0: ComponentOpen[1,1](<)
|
||||||
1: StringIdentifier[1,2](bad)
|
1: StringIdentifier[1,2](bad)
|
||||||
2: TagError[1,5](!)
|
2: TagError[1,5](!)
|
||||||
3: ComponentSelfClose[1,7](/>)
|
3: Nlws[1,6]( )
|
||||||
4: RawText[1,9](\n)
|
4: ComponentSelfClose[1,7](/>)
|
||||||
|
5: RawText[1,9](\n)
|
@ -1,7 +1,7 @@
|
|||||||
0: PreambleBreak[1,1](---\n)
|
0: PreambleBreak[1,1](---\n)
|
||||||
1: GroovyCode[2,1](import some.Thing // a comment...World!')
|
1: GroovyCode[2,1](import some.Thing // a comment...rld!'\n)
|
||||||
2: PreambleBreak[4,31](\n---\n)
|
2: PreambleBreak[5,1](---)
|
||||||
3: RawText[6,1](<!DOCTYPE html>\n)
|
3: RawText[5,4](\n<!DOCTYPE html>\n)
|
||||||
4: ComponentOpen[7,1](<)
|
4: ComponentOpen[7,1](<)
|
||||||
5: StringIdentifier[7,2](html)
|
5: StringIdentifier[7,2](html)
|
||||||
6: ComponentClose[7,6](>)
|
6: ComponentClose[7,6](>)
|
||||||
@ -33,50 +33,51 @@
|
|||||||
32: RawText[11,33](\n )
|
32: RawText[11,33](\n )
|
||||||
33: ComponentOpen[12,13](<)
|
33: ComponentOpen[12,13](<)
|
||||||
34: TypedIdentifier[12,14](Case)
|
34: TypedIdentifier[12,14](Case)
|
||||||
35: AttributeIdentifier[12,19](cond)
|
35: Nlws[12,18]( )
|
||||||
36: Equals[12,23](=)
|
36: AttributeIdentifier[12,19](cond)
|
||||||
37: ClosureAttrValueStart[12,24]({)
|
37: Equals[12,23](=)
|
||||||
38: GroovyCode[12,25](isItTrue())
|
38: ClosureAttrValueStart[12,24]({)
|
||||||
39: ClosureAttrValueEnd[12,35](})
|
39: GroovyCode[12,25](isItTrue())
|
||||||
40: ComponentClose[12,36](>)
|
40: ClosureAttrValueEnd[12,35](})
|
||||||
41: RawText[12,37](\n )
|
41: ComponentClose[12,36](>)
|
||||||
42: ComponentOpen[13,17](<)
|
42: RawText[12,37](\n )
|
||||||
43: StringIdentifier[13,18](p)
|
43: ComponentOpen[13,17](<)
|
||||||
44: ComponentClose[13,19](>)
|
44: StringIdentifier[13,18](p)
|
||||||
45: RawText[13,20](It's true! :))
|
45: ComponentClose[13,19](>)
|
||||||
46: ClosingComponentOpen[13,33](</)
|
46: RawText[13,20](It's true! :))
|
||||||
47: StringIdentifier[13,35](p)
|
47: ClosingComponentOpen[13,33](</)
|
||||||
48: ComponentClose[13,36](>)
|
48: StringIdentifier[13,35](p)
|
||||||
49: RawText[13,37](\n )
|
49: ComponentClose[13,36](>)
|
||||||
50: ClosingComponentOpen[14,13](</)
|
50: RawText[13,37](\n )
|
||||||
51: TypedIdentifier[14,15](Case)
|
51: ClosingComponentOpen[14,13](</)
|
||||||
52: ComponentClose[14,19](>)
|
52: TypedIdentifier[14,15](Case)
|
||||||
53: RawText[14,20](\n )
|
53: ComponentClose[14,19](>)
|
||||||
54: ComponentOpen[15,13](<)
|
54: RawText[14,20](\n )
|
||||||
55: TypedIdentifier[15,14](Default)
|
55: ComponentOpen[15,13](<)
|
||||||
56: ComponentClose[15,21](>)
|
56: TypedIdentifier[15,14](Default)
|
||||||
57: RawText[15,22](\n )
|
57: ComponentClose[15,21](>)
|
||||||
58: ComponentOpen[16,17](<)
|
58: RawText[15,22](\n )
|
||||||
59: StringIdentifier[16,18](p)
|
59: ComponentOpen[16,17](<)
|
||||||
60: ComponentClose[16,19](>)
|
60: StringIdentifier[16,18](p)
|
||||||
61: RawText[16,20](It's false... :()
|
61: ComponentClose[16,19](>)
|
||||||
62: ClosingComponentOpen[16,36](</)
|
62: RawText[16,20](It's false... :()
|
||||||
63: StringIdentifier[16,38](p)
|
63: ClosingComponentOpen[16,36](</)
|
||||||
64: ComponentClose[16,39](>)
|
64: StringIdentifier[16,38](p)
|
||||||
65: RawText[16,40](\n )
|
65: ComponentClose[16,39](>)
|
||||||
66: ClosingComponentOpen[17,13](</)
|
66: RawText[16,40](\n )
|
||||||
67: TypedIdentifier[17,15](Default)
|
67: ClosingComponentOpen[17,13](</)
|
||||||
68: ComponentClose[17,22](>)
|
68: TypedIdentifier[17,15](Default)
|
||||||
69: RawText[17,23](\n )
|
69: ComponentClose[17,22](>)
|
||||||
70: ClosingComponentOpen[18,9](</)
|
70: RawText[17,23](\n )
|
||||||
71: TypedIdentifier[18,11](groowt.view.web.Select)
|
71: ClosingComponentOpen[18,9](</)
|
||||||
72: ComponentClose[18,33](>)
|
72: TypedIdentifier[18,11](groowt.view.web.Select)
|
||||||
73: RawText[18,34](\n )
|
73: ComponentClose[18,33](>)
|
||||||
74: ClosingComponentOpen[19,5](</)
|
74: RawText[18,34](\n )
|
||||||
75: StringIdentifier[19,7](body)
|
75: ClosingComponentOpen[19,5](</)
|
||||||
76: ComponentClose[19,11](>)
|
76: StringIdentifier[19,7](body)
|
||||||
77: RawText[19,12](\n)
|
77: ComponentClose[19,11](>)
|
||||||
78: ClosingComponentOpen[20,1](</)
|
78: RawText[19,12](\n)
|
||||||
79: StringIdentifier[20,3](html)
|
79: ClosingComponentOpen[20,1](</)
|
||||||
80: ComponentClose[20,7](>)
|
80: StringIdentifier[20,3](html)
|
||||||
81: RawText[20,8](\n)
|
81: ComponentClose[20,7](>)
|
||||||
|
82: RawText[20,8](\n)
|
@ -1,4 +1,5 @@
|
|||||||
0: ComponentOpen[1,1](<)
|
0: ComponentOpen[1,1](<)
|
||||||
1: StringIdentifier[1,2](data-test)
|
1: StringIdentifier[1,2](data-test)
|
||||||
2: ComponentSelfClose[1,12](/>)
|
2: Nlws[1,11]( )
|
||||||
3: RawText[1,14](\n)
|
3: ComponentSelfClose[1,12](/>)
|
||||||
|
4: RawText[1,14](\n)
|
@ -0,0 +1 @@
|
|||||||
|
0: RawText[1,1](<!DOCTYPE html>\n)
|
@ -0,0 +1,3 @@
|
|||||||
|
0: FragmentOpen[1,1](<>)
|
||||||
|
1: FragmentClose[1,3](</>)
|
||||||
|
2: RawText[1,6](\n)
|
@ -0,0 +1,7 @@
|
|||||||
|
0: ComponentOpen[1,1](<)
|
||||||
|
1: TypedIdentifier[1,2](Test)
|
||||||
|
2: Nlws[1,6](\n )
|
||||||
|
3: AttributeIdentifier[2,5](someAttr)
|
||||||
|
4: Nlws[2,13](\n)
|
||||||
|
5: ComponentSelfClose[3,1](/>)
|
||||||
|
6: RawText[3,3](\n)
|
@ -0,0 +1 @@
|
|||||||
|
0: RawText[1,1](Hello!\n)
|
@ -0,0 +1,3 @@
|
|||||||
|
0: PreambleBreak[1,1](---\n)
|
||||||
|
1: PreambleBreak[2,1](---)
|
||||||
|
2: RawText[2,4](-\n)
|
@ -0,0 +1,4 @@
|
|||||||
|
0: PreambleBreak[1,1](---)
|
||||||
|
1: GroovyCode[1,4](-\n)
|
||||||
|
2: PreambleBreak[2,1](---)
|
||||||
|
3: RawText[2,4](\n)
|
@ -0,0 +1,3 @@
|
|||||||
|
0: PreambleBreak[1,1](---\n)
|
||||||
|
1: PreambleBreak[2,1](---)
|
||||||
|
2: RawText[2,4](---\n)
|
@ -0,0 +1,4 @@
|
|||||||
|
0: PreambleBreak[1,1](---\n)
|
||||||
|
1: GroovyCode[2,1](// ---\n)
|
||||||
|
2: PreambleBreak[3,1](---)
|
||||||
|
3: RawText[3,4](\n)
|
@ -0,0 +1,4 @@
|
|||||||
|
0: PreambleBreak[1,1](---\n)
|
||||||
|
1: GroovyCode[2,1](def regex = $/match --- me $ $... / /$\n)
|
||||||
|
2: PreambleBreak[3,1](---)
|
||||||
|
3: RawText[3,4](\n)
|
@ -0,0 +1,4 @@
|
|||||||
|
0: PreambleBreak[1,1](---\n)
|
||||||
|
1: GroovyCode[2,1](def test = "---$test${test('---')}"\n)
|
||||||
|
2: PreambleBreak[3,1](---)
|
||||||
|
3: RawText[3,4](\n)
|
@ -0,0 +1,4 @@
|
|||||||
|
0: PreambleBreak[1,1](---\n)
|
||||||
|
1: GroovyCode[2,1](def test = '---'\n)
|
||||||
|
2: PreambleBreak[3,1](---)
|
||||||
|
3: RawText[3,4](\n)
|
@ -0,0 +1,4 @@
|
|||||||
|
0: PreambleBreak[1,1](---\n)
|
||||||
|
1: GroovyCode[2,1](def regex = (/match --- \/ me/)\n)
|
||||||
|
2: PreambleBreak[3,1](---)
|
||||||
|
3: RawText[3,4](\n)
|
@ -0,0 +1,4 @@
|
|||||||
|
0: PreambleBreak[1,1](---\n)
|
||||||
|
1: GroovyCode[2,1](def test = """\n--- $test ${te...\n"""\n)
|
||||||
|
2: PreambleBreak[5,1](---)
|
||||||
|
3: RawText[5,4](\n)
|
@ -0,0 +1,4 @@
|
|||||||
|
0: PreambleBreak[1,1](---\n)
|
||||||
|
1: GroovyCode[2,1](def test = '''\n---\n'''\n)
|
||||||
|
2: PreambleBreak[5,1](---)
|
||||||
|
3: RawText[5,4](\n)
|
@ -0,0 +1,5 @@
|
|||||||
|
0: ComponentOpen[1,1](<)
|
||||||
|
1: TypedIdentifier[1,2](Test)
|
||||||
|
2: Nlws[1,6]( )
|
||||||
|
3: ComponentSelfClose[1,7](/>)
|
||||||
|
4: RawText[1,9](\n)
|
@ -1 +1,2 @@
|
|||||||
0: ClosingComponentOpen[1,1](</)
|
0: ClosingComponentOpen[1,1](</)
|
||||||
|
1: Nlws[1,3](\n)
|
@ -0,0 +1 @@
|
|||||||
|
0: RawText[1,1]( ---\n)
|
@ -0,0 +1,4 @@
|
|||||||
|
0: PreambleBreak[1,1](---\n)
|
||||||
|
1: GroovyCode[2,1](package test\n)
|
||||||
|
2: PreambleBreak[3,1](---)
|
||||||
|
3: RawText[3,4](\n)
|
@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
package test
|
||||||
|
---
|
@ -1,2 +0,0 @@
|
|||||||
---
|
|
||||||
---
|
|
@ -1 +1 @@
|
|||||||
<><p>Hello, World!</p></>
|
<>Hello, World!</>
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
Hello, World!
|
@ -1,94 +1,93 @@
|
|||||||
compilationUnit[1,1..21,1]
|
compilationUnit[1,1..21,1]
|
||||||
preamble[1,1..4,31]
|
preamble[1,1..5,4]
|
||||||
PreambleBreak[1,1](---\n)
|
PreambleBreak[1,1](---\n)
|
||||||
GroovyCode[2,1](import some.Thing // a comment\n\ndef greeting = 'Hello, World!')
|
GroovyCode[2,1](import some.Thing // a comment\n\ndef greeting = 'Hello, World!'\n)
|
||||||
PreambleBreak[4,31](\n---\n)
|
PreambleBreak[5,1](---)
|
||||||
body[6,1..20,8]
|
body[5,4..20,8]
|
||||||
bodyText[6,1..6,1]
|
bodyText[5,4..7,1]
|
||||||
jStringBodyText[6,1..6,1]
|
text[5,4..7,1]
|
||||||
RawText[6,1](<!DOCTYPE html>\n)
|
RawText[5,4](\n<!DOCTYPE html>\n)
|
||||||
component[7,1..20,7]
|
component[7,1..20,7]
|
||||||
componentWithChildren[7,1..20,7]
|
componentWithChildren[7,1..20,7]
|
||||||
openComponent[7,1..7,6]
|
openComponent[7,1..7,6]
|
||||||
ComponentOpen[7,1](<)
|
ComponentOpen[7,1](<)
|
||||||
componentArgs[7,2..7,2]
|
componentArgs[7,2..7,6]
|
||||||
componentType[7,2..7,2]
|
componentType[7,2..7,6]
|
||||||
StringIdentifier[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..8,5]
|
||||||
jStringBodyText[7,7..7,7]
|
text[7,7..8,5]
|
||||||
RawText[7,7](\n )
|
RawText[7,7](\n )
|
||||||
component[8,5..8,17]
|
component[8,5..8,17]
|
||||||
componentWithChildren[8,5..8,17]
|
componentWithChildren[8,5..8,17]
|
||||||
openComponent[8,5..8,10]
|
openComponent[8,5..8,10]
|
||||||
ComponentOpen[8,5](<)
|
ComponentOpen[8,5](<)
|
||||||
componentArgs[8,6..8,6]
|
componentArgs[8,6..8,10]
|
||||||
componentType[8,6..8,6]
|
componentType[8,6..8,10]
|
||||||
StringIdentifier[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,17]
|
||||||
StringIdentifier[8,13](head)
|
StringIdentifier[8,13](head)
|
||||||
ComponentClose[8,17](>)
|
ComponentClose[8,17](>)
|
||||||
bodyText[8,18..8,18]
|
bodyText[8,18..9,5]
|
||||||
jStringBodyText[8,18..8,18]
|
text[8,18..9,5]
|
||||||
RawText[8,18](\n )
|
RawText[8,18](\n )
|
||||||
component[9,5..19,11]
|
component[9,5..19,11]
|
||||||
componentWithChildren[9,5..19,11]
|
componentWithChildren[9,5..19,11]
|
||||||
openComponent[9,5..9,10]
|
openComponent[9,5..9,10]
|
||||||
ComponentOpen[9,5](<)
|
ComponentOpen[9,5](<)
|
||||||
componentArgs[9,6..9,6]
|
componentArgs[9,6..9,10]
|
||||||
componentType[9,6..9,6]
|
componentType[9,6..9,10]
|
||||||
StringIdentifier[9,6](body)
|
StringIdentifier[9,6](body)
|
||||||
ComponentClose[9,10](>)
|
ComponentClose[9,10](>)
|
||||||
body[9,11..18,34]
|
body[9,11..19,5]
|
||||||
bodyText[9,11..9,11]
|
bodyText[9,11..10,9]
|
||||||
jStringBodyText[9,11..9,11]
|
text[9,11..10,9]
|
||||||
RawText[9,11](\n )
|
RawText[9,11](\n )
|
||||||
component[10,9..10,28]
|
component[10,9..10,28]
|
||||||
componentWithChildren[10,9..10,28]
|
componentWithChildren[10,9..10,28]
|
||||||
openComponent[10,9..10,12]
|
openComponent[10,9..10,12]
|
||||||
ComponentOpen[10,9](<)
|
ComponentOpen[10,9](<)
|
||||||
componentArgs[10,10..10,10]
|
componentArgs[10,10..10,12]
|
||||||
componentType[10,10..10,10]
|
componentType[10,10..10,12]
|
||||||
StringIdentifier[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]
|
||||||
gStringBodyText[10,13..10,23]
|
bodyTextGroovyElement[10,13..10,23]
|
||||||
gStringBodyTextGroovyElement[10,13..10,23]
|
dollarScriptlet[10,13..10,23]
|
||||||
dollarScriptlet[10,13..10,23]
|
DollarScriptletOpen[10,13](${)
|
||||||
DollarScriptletOpen[10,13](${)
|
GroovyCode[10,15](greeting)
|
||||||
GroovyCode[10,15](greeting)
|
DollarScriptletClose[10,23](})
|
||||||
DollarScriptletClose[10,23](})
|
|
||||||
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,28]
|
||||||
StringIdentifier[10,26](h1)
|
StringIdentifier[10,26](h1)
|
||||||
ComponentClose[10,28](>)
|
ComponentClose[10,28](>)
|
||||||
bodyText[10,29..10,29]
|
bodyText[10,29..11,9]
|
||||||
jStringBodyText[10,29..10,29]
|
text[10,29..11,9]
|
||||||
RawText[10,29](\n )
|
RawText[10,29](\n )
|
||||||
component[11,9..18,33]
|
component[11,9..18,33]
|
||||||
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,10]
|
componentArgs[11,10..11,32]
|
||||||
componentType[11,10..11,10]
|
componentType[11,10..11,32]
|
||||||
TypedIdentifier[11,10](groowt.view.web.Select)
|
TypedIdentifier[11,10](groowt.view.web.Select)
|
||||||
ComponentClose[11,32](>)
|
ComponentClose[11,32](>)
|
||||||
body[11,33..17,23]
|
body[11,33..18,9]
|
||||||
bodyText[11,33..11,33]
|
bodyText[11,33..12,13]
|
||||||
jStringBodyText[11,33..11,33]
|
text[11,33..12,13]
|
||||||
RawText[11,33](\n )
|
RawText[11,33](\n )
|
||||||
component[12,13..14,19]
|
component[12,13..14,19]
|
||||||
componentWithChildren[12,13..14,19]
|
componentWithChildren[12,13..14,19]
|
||||||
openComponent[12,13..12,36]
|
openComponent[12,13..12,36]
|
||||||
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,18]
|
||||||
TypedIdentifier[12,14](Case)
|
TypedIdentifier[12,14](Case)
|
||||||
attr[12,19..12,35]
|
attr[12,19..12,35]
|
||||||
keyValueAttr[12,19..12,35]
|
keyValueAttr[12,19..12,35]
|
||||||
@ -100,9 +99,9 @@ compilationUnit[1,1..21,1]
|
|||||||
GroovyCode[12,25](isItTrue())
|
GroovyCode[12,25](isItTrue())
|
||||||
ClosureAttrValueEnd[12,35](})
|
ClosureAttrValueEnd[12,35](})
|
||||||
ComponentClose[12,36](>)
|
ComponentClose[12,36](>)
|
||||||
body[12,37..13,37]
|
body[12,37..14,13]
|
||||||
bodyText[12,37..12,37]
|
bodyText[12,37..13,17]
|
||||||
jStringBodyText[12,37..12,37]
|
text[12,37..13,17]
|
||||||
RawText[12,37](\n )
|
RawText[12,37](\n )
|
||||||
component[13,17..13,36]
|
component[13,17..13,36]
|
||||||
componentWithChildren[13,17..13,36]
|
componentWithChildren[13,17..13,36]
|
||||||
@ -112,37 +111,37 @@ compilationUnit[1,1..21,1]
|
|||||||
componentType[13,18..13,18]
|
componentType[13,18..13,18]
|
||||||
StringIdentifier[13,18](p)
|
StringIdentifier[13,18](p)
|
||||||
ComponentClose[13,19](>)
|
ComponentClose[13,19](>)
|
||||||
body[13,20..13,20]
|
body[13,20..13,33]
|
||||||
bodyText[13,20..13,20]
|
bodyText[13,20..13,33]
|
||||||
jStringBodyText[13,20..13,20]
|
text[13,20..13,33]
|
||||||
RawText[13,20](It's true! :))
|
RawText[13,20](It's true! :))
|
||||||
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]
|
||||||
StringIdentifier[13,35](p)
|
StringIdentifier[13,35](p)
|
||||||
ComponentClose[13,36](>)
|
ComponentClose[13,36](>)
|
||||||
bodyText[13,37..13,37]
|
bodyText[13,37..14,13]
|
||||||
jStringBodyText[13,37..13,37]
|
text[13,37..14,13]
|
||||||
RawText[13,37](\n )
|
RawText[13,37](\n )
|
||||||
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,19]
|
||||||
TypedIdentifier[14,15](Case)
|
TypedIdentifier[14,15](Case)
|
||||||
ComponentClose[14,19](>)
|
ComponentClose[14,19](>)
|
||||||
bodyText[14,20..14,20]
|
bodyText[14,20..15,13]
|
||||||
jStringBodyText[14,20..14,20]
|
text[14,20..15,13]
|
||||||
RawText[14,20](\n )
|
RawText[14,20](\n )
|
||||||
component[15,13..17,22]
|
component[15,13..17,22]
|
||||||
componentWithChildren[15,13..17,22]
|
componentWithChildren[15,13..17,22]
|
||||||
openComponent[15,13..15,21]
|
openComponent[15,13..15,21]
|
||||||
ComponentOpen[15,13](<)
|
ComponentOpen[15,13](<)
|
||||||
componentArgs[15,14..15,14]
|
componentArgs[15,14..15,21]
|
||||||
componentType[15,14..15,14]
|
componentType[15,14..15,21]
|
||||||
TypedIdentifier[15,14](Default)
|
TypedIdentifier[15,14](Default)
|
||||||
ComponentClose[15,21](>)
|
ComponentClose[15,21](>)
|
||||||
body[15,22..16,40]
|
body[15,22..17,13]
|
||||||
bodyText[15,22..15,22]
|
bodyText[15,22..16,17]
|
||||||
jStringBodyText[15,22..15,22]
|
text[15,22..16,17]
|
||||||
RawText[15,22](\n )
|
RawText[15,22](\n )
|
||||||
component[16,17..16,39]
|
component[16,17..16,39]
|
||||||
componentWithChildren[16,17..16,39]
|
componentWithChildren[16,17..16,39]
|
||||||
@ -152,48 +151,48 @@ compilationUnit[1,1..21,1]
|
|||||||
componentType[16,18..16,18]
|
componentType[16,18..16,18]
|
||||||
StringIdentifier[16,18](p)
|
StringIdentifier[16,18](p)
|
||||||
ComponentClose[16,19](>)
|
ComponentClose[16,19](>)
|
||||||
body[16,20..16,20]
|
body[16,20..16,36]
|
||||||
bodyText[16,20..16,20]
|
bodyText[16,20..16,36]
|
||||||
jStringBodyText[16,20..16,20]
|
text[16,20..16,36]
|
||||||
RawText[16,20](It's false... :()
|
RawText[16,20](It's false... :()
|
||||||
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]
|
||||||
StringIdentifier[16,38](p)
|
StringIdentifier[16,38](p)
|
||||||
ComponentClose[16,39](>)
|
ComponentClose[16,39](>)
|
||||||
bodyText[16,40..16,40]
|
bodyText[16,40..17,13]
|
||||||
jStringBodyText[16,40..16,40]
|
text[16,40..17,13]
|
||||||
RawText[16,40](\n )
|
RawText[16,40](\n )
|
||||||
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,22]
|
||||||
TypedIdentifier[17,15](Default)
|
TypedIdentifier[17,15](Default)
|
||||||
ComponentClose[17,22](>)
|
ComponentClose[17,22](>)
|
||||||
bodyText[17,23..17,23]
|
bodyText[17,23..18,9]
|
||||||
jStringBodyText[17,23..17,23]
|
text[17,23..18,9]
|
||||||
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,11]
|
componentType[18,11..18,33]
|
||||||
TypedIdentifier[18,11](groowt.view.web.Select)
|
TypedIdentifier[18,11](groowt.view.web.Select)
|
||||||
ComponentClose[18,33](>)
|
ComponentClose[18,33](>)
|
||||||
bodyText[18,34..18,34]
|
bodyText[18,34..19,5]
|
||||||
jStringBodyText[18,34..18,34]
|
text[18,34..19,5]
|
||||||
RawText[18,34](\n )
|
RawText[18,34](\n )
|
||||||
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,11]
|
||||||
StringIdentifier[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]
|
text[19,12..19,12]
|
||||||
RawText[19,12](\n)
|
RawText[19,12](\n)
|
||||||
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,7]
|
||||||
StringIdentifier[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]
|
text[20,8..20,8]
|
||||||
RawText[20,8](\n)
|
RawText[20,8](\n)
|
||||||
EOF[21,1](<EOF>)
|
EOF[21,1](<EOF>)
|
@ -4,16 +4,16 @@ compilationUnit[1,1..2,1]
|
|||||||
componentWithChildren[1,1..1,13]
|
componentWithChildren[1,1..1,13]
|
||||||
openComponent[1,1..1,6]
|
openComponent[1,1..1,6]
|
||||||
ComponentOpen[1,1](<)
|
ComponentOpen[1,1](<)
|
||||||
componentArgs[1,2..1,2]
|
componentArgs[1,2..1,6]
|
||||||
componentType[1,2..1,2]
|
componentType[1,2..1,6]
|
||||||
StringIdentifier[1,2](html)
|
StringIdentifier[1,2](html)
|
||||||
ComponentClose[1,6](>)
|
ComponentClose[1,6](>)
|
||||||
closingComponent[1,7..1,13]
|
closingComponent[1,7..1,13]
|
||||||
ClosingComponentOpen[1,7](</)
|
ClosingComponentOpen[1,7](</)
|
||||||
componentType[1,9..1,9]
|
componentType[1,9..1,13]
|
||||||
StringIdentifier[1,9](html)
|
StringIdentifier[1,9](html)
|
||||||
ComponentClose[1,13](>)
|
ComponentClose[1,13](>)
|
||||||
bodyText[1,14..1,14]
|
bodyText[1,14..1,14]
|
||||||
jStringBodyText[1,14..1,14]
|
text[1,14..1,14]
|
||||||
RawText[1,14](\n)
|
RawText[1,14](\n)
|
||||||
EOF[2,1](<EOF>)
|
EOF[2,1](<EOF>)
|
@ -0,0 +1,14 @@
|
|||||||
|
compilationUnit[1,1..2,1]
|
||||||
|
body[1,1..1,19]
|
||||||
|
component[1,1..1,19]
|
||||||
|
fragmentComponent[1,1..1,19]
|
||||||
|
FragmentOpen[1,1](<>)
|
||||||
|
body[1,3..1,16]
|
||||||
|
bodyText[1,3..1,16]
|
||||||
|
text[1,3..1,16]
|
||||||
|
RawText[1,3](Hello, World!)
|
||||||
|
FragmentClose[1,16](</>)
|
||||||
|
bodyText[1,19..1,19]
|
||||||
|
text[1,19..1,19]
|
||||||
|
RawText[1,19](\n)
|
||||||
|
EOF[2,1](<EOF>)
|
@ -0,0 +1,12 @@
|
|||||||
|
compilationUnit[1,1..1,16]
|
||||||
|
body[1,1..1,15]
|
||||||
|
bodyText[1,1..1,15]
|
||||||
|
text[1,1..1,8]
|
||||||
|
RawText[1,1](Hello, )
|
||||||
|
bodyTextGroovyElement[1,8..1,15]
|
||||||
|
dollarReference[1,8..1,15]
|
||||||
|
DollarReferenceStart[1,8]($)
|
||||||
|
GroovyCode[1,9](target)
|
||||||
|
text[1,15..1,15]
|
||||||
|
RawText[1,15](!)
|
||||||
|
EOF[1,16](<EOF>)
|
@ -0,0 +1,6 @@
|
|||||||
|
compilationUnit[1,1..2,1]
|
||||||
|
body[1,1..2,1]
|
||||||
|
bodyText[1,1..2,1]
|
||||||
|
text[1,1..2,1]
|
||||||
|
RawText[1,1](Hello, World!\n)
|
||||||
|
EOF[2,1](<EOF>)
|
@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
class Greeting {
|
|
||||||
String myGreeting
|
|
||||||
}
|
|
||||||
---
|
|
@ -1,5 +0,0 @@
|
|||||||
compilationUnit[1,1..3,1]
|
|
||||||
preamble[1,1..2,1]
|
|
||||||
PreambleBreak[1,1](---\n)
|
|
||||||
PreambleBreak[2,1](---\n)
|
|
||||||
EOF[3,1](<EOF>)
|
|
@ -1,6 +0,0 @@
|
|||||||
compilationUnit[1,1..8,1]
|
|
||||||
preamble[1,1..6,1]
|
|
||||||
PreambleBreak[1,1](---\n)
|
|
||||||
GroovyCode[2,1](\n\n\n\n)
|
|
||||||
PreambleBreak[6,1](\n---\n)
|
|
||||||
EOF[8,1](<EOF>)
|
|
@ -1,28 +0,0 @@
|
|||||||
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>)
|
|
@ -1,13 +0,0 @@
|
|||||||
compilationUnit[1,1..1,16]
|
|
||||||
body[1,1..1,15]
|
|
||||||
bodyText[1,1..1,15]
|
|
||||||
gStringBodyText[1,1..1,15]
|
|
||||||
jStringBodyText[1,1..1,1]
|
|
||||||
RawText[1,1](Hello, )
|
|
||||||
gStringBodyTextGroovyElement[1,8..1,9]
|
|
||||||
dollarReference[1,8..1,9]
|
|
||||||
DollarReferenceStart[1,8]($)
|
|
||||||
GroovyCode[1,9](target)
|
|
||||||
jStringBodyText[1,15..1,15]
|
|
||||||
RawText[1,15](!)
|
|
||||||
EOF[1,16](<EOF>)
|
|
@ -1,6 +0,0 @@
|
|||||||
compilationUnit[1,1..6,1]
|
|
||||||
preamble[1,1..4,2]
|
|
||||||
PreambleBreak[1,1](---\n)
|
|
||||||
GroovyCode[2,1](class Greeting {\n String myGreeting\n})
|
|
||||||
PreambleBreak[4,2](\n---\n)
|
|
||||||
EOF[6,1](<EOF>)
|
|
@ -1,6 +1,5 @@
|
|||||||
package groowt.view.component.web.ast;
|
package groowt.view.component.web.ast;
|
||||||
|
|
||||||
import groowt.view.component.web.ast.extension.GStringNodeExtension;
|
|
||||||
import groowt.view.component.web.ast.node.*;
|
import groowt.view.component.web.ast.node.*;
|
||||||
import groowt.view.component.web.util.TokenRange;
|
import groowt.view.component.web.util.TokenRange;
|
||||||
import org.antlr.v4.runtime.Token;
|
import org.antlr.v4.runtime.Token;
|
||||||
@ -75,14 +74,26 @@ public abstract class NodeFactoryTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void gStringBodyTextNode(@Mock LeafNode child) {
|
public void bodyTextNode(@Mock BodyTextChild child, @Mock TreeNode childAsNode) {
|
||||||
when(child.hasExtension(GStringNodeExtension.class)).thenReturn(true);
|
when(child.asNode()).thenReturn(childAsNode);
|
||||||
assertNotNull(this.nodeFactory.gStringBodyTextNode(this.getTokenRange(), List.of(child)));
|
assertNotNull(this.nodeFactory.bodyTextNode(this.getTokenRange(), List.of(child)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void jStringBodyTextNode() {
|
public void questionTagNode(@Mock QuestionTagChild child, @Mock TreeNode childAsNode) {
|
||||||
assertNotNull(this.nodeFactory.jStringBodyTextNode(this.getTokenRange(), "Hello!"));
|
when(child.asNode()).thenReturn(childAsNode);
|
||||||
|
assertNotNull(this.nodeFactory.questionTagNode(this.getTokenRange(), List.of(child)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void htmlCommentNode(@Mock HtmlCommentChild child, @Mock TreeNode childAsNode) {
|
||||||
|
when(child.asNode()).thenReturn(childAsNode);
|
||||||
|
assertNotNull(this.nodeFactory.htmlCommentNode(this.getTokenRange(), List.of(child)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void textNode() {
|
||||||
|
assertNotNull(this.nodeFactory.textNode(this.getTokenRange(), "Hello, World!"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -16,36 +16,36 @@ abstract class AbstractSourceTransformerCli : Callable<Int> {
|
|||||||
protected lateinit var targets: List<Path>
|
protected lateinit var targets: List<Path>
|
||||||
|
|
||||||
@Option(
|
@Option(
|
||||||
names = ["--n", "--dry-run"],
|
names = ["-i", "--interactive"],
|
||||||
description = ["Do a dry run; do not output files to disk."]
|
description = ["Allow interactive recovery from errors. If false, implies -W."]
|
||||||
)
|
)
|
||||||
protected var dryRun: Boolean = false
|
protected var interactive = false
|
||||||
|
|
||||||
@Option(
|
@Option(
|
||||||
names = ["-y", "--yes"],
|
names = ["-P", "--print-only"],
|
||||||
description = ["Automatically write output files if there are no processing errors."]
|
description = ["Only print result(s) to stdout; do not write files to disk."]
|
||||||
)
|
)
|
||||||
protected var autoYes: Boolean = false
|
protected var printOnly = false
|
||||||
|
|
||||||
@Option(
|
@Option(
|
||||||
names = ["-W", "--write-over"],
|
names = ["-W", "--write-over"],
|
||||||
description = ["If an output file already exists, write over it without asking."]
|
description = ["If an output file already exists, write over it without asking."]
|
||||||
)
|
)
|
||||||
protected var autoWriteOver: Boolean = false
|
protected var autoWriteOver = false
|
||||||
|
|
||||||
@Option(
|
@Option(
|
||||||
names = ["-v", "--verbose"],
|
names = ["-v", "--verbose"],
|
||||||
description = ["Log verbosely to the console."]
|
description = ["Log exceptions and errors verbosely to stderr."]
|
||||||
)
|
)
|
||||||
protected var verbose: Boolean = false
|
protected var verbose = false
|
||||||
|
|
||||||
private val scanner = Scanner(System.`in`)
|
private val scanner = Scanner(System.`in`)
|
||||||
|
|
||||||
protected fun getYesNo(prompt: String, allowAuto: Boolean = true): Boolean {
|
protected open fun getYesNo(prompt: String, fallback: Boolean): Boolean {
|
||||||
if (this.autoYes && allowAuto) {
|
if (!interactive) {
|
||||||
return true
|
return fallback
|
||||||
} else {
|
} else {
|
||||||
print("$prompt (y/n) ")
|
print("$prompt (y/n): ")
|
||||||
while (true) {
|
while (true) {
|
||||||
if (this.scanner.hasNextLine()) {
|
if (this.scanner.hasNextLine()) {
|
||||||
val input = this.scanner.nextLine()
|
val input = this.scanner.nextLine()
|
||||||
@ -60,24 +60,21 @@ abstract class AbstractSourceTransformerCli : Callable<Int> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun doWrite(resolvedTarget: Path, text: String) {
|
private fun doWrite(resolvedTarget: Path, text: String) {
|
||||||
if (this.dryRun) {
|
if (!this.printOnly) {
|
||||||
println("Dry-run: would write to $resolvedTarget")
|
|
||||||
} else {
|
|
||||||
println("Writing to $resolvedTarget...")
|
|
||||||
Files.writeString(resolvedTarget, text)
|
Files.writeString(resolvedTarget, text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun writeToDisk(target: Path, text: String) {
|
protected open fun writeToDisk(target: Path, text: String) {
|
||||||
val outputDir = getOutputDir()
|
val outputDir = getOutputDir()
|
||||||
if (outputDir != null) {
|
if (outputDir != null) {
|
||||||
Files.createDirectories(outputDir)
|
Files.createDirectories(outputDir)
|
||||||
}
|
}
|
||||||
val resolvedTarget = outputDir?.resolve(target) ?: target
|
val resolvedTarget = outputDir?.resolve(target) ?: target
|
||||||
if (Files.exists(resolvedTarget) && !autoWriteOver) {
|
if (Files.exists(resolvedTarget) && !autoWriteOver) {
|
||||||
if (getYesNo("$resolvedTarget already exists. Write over?")) {
|
if (getYesNo("$resolvedTarget already exists. Write over?", true)) {
|
||||||
doWrite(resolvedTarget, text)
|
doWrite(resolvedTarget, text)
|
||||||
} else {
|
} else if (interactive) {
|
||||||
println("Skipping writing to $resolvedTarget")
|
println("Skipping writing to $resolvedTarget")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -0,0 +1,161 @@
|
|||||||
|
package groowt.view.component.web.tools
|
||||||
|
|
||||||
|
import groowt.view.component.web.antlr.*
|
||||||
|
import groowt.view.component.web.antlr.WebViewComponentsLexerBase.ERROR
|
||||||
|
import groowt.view.component.web.antlr.WebViewComponentsLexerBase.HIDDEN
|
||||||
|
import groowt.view.component.web.antlr.WebViewComponentsParser.CompilationUnitContext
|
||||||
|
import org.antlr.v4.runtime.CharStreams
|
||||||
|
import org.antlr.v4.runtime.ConsoleErrorListener
|
||||||
|
import picocli.CommandLine
|
||||||
|
import picocli.CommandLine.Option
|
||||||
|
import java.nio.file.Path
|
||||||
|
import kotlin.io.path.nameWithoutExtension
|
||||||
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
|
open class ParseWvc : AbstractSourceTransformerCli() {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
exitProcess(CommandLine(ParseWvc()).execute(*args))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Option(
|
||||||
|
names = ["-s", "--suffix"],
|
||||||
|
description = ["The suffix (not extension!) to append to the output file."]
|
||||||
|
)
|
||||||
|
protected var suffix: String? = null
|
||||||
|
|
||||||
|
@Option(
|
||||||
|
names = ["-e", "--extension"],
|
||||||
|
description = ["The extension for output files."],
|
||||||
|
defaultValue = ".txt"
|
||||||
|
)
|
||||||
|
protected lateinit var extension: String
|
||||||
|
|
||||||
|
@Option(
|
||||||
|
names = ["-d", "--output-dir"],
|
||||||
|
description = ["The output directory."],
|
||||||
|
defaultValue = ".",
|
||||||
|
paramLabel = "outputDir"
|
||||||
|
)
|
||||||
|
protected lateinit var myOutputDir: Path
|
||||||
|
|
||||||
|
@Option(
|
||||||
|
names = ["--strict"],
|
||||||
|
description = ["If true, do not recover from syntax errors during parsing."],
|
||||||
|
negatable = true
|
||||||
|
)
|
||||||
|
protected var strict = true
|
||||||
|
|
||||||
|
protected open fun onLexerErrors(errors: List<LexerError>): Boolean {
|
||||||
|
System.err.println("There were lexer errors.")
|
||||||
|
errors.forEach { System.err.println(formatLexerError(it)) }
|
||||||
|
return this.getYesNo("Do you wish to try again?", false)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open fun onParserErrors(errors: List<ParserError>): Boolean {
|
||||||
|
System.err.println("There were parser errors.")
|
||||||
|
errors.forEach { System.err.println(formatParserError(it)) }
|
||||||
|
return this.getYesNo("Do you wish to try again?", false)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open fun getOutputPath(target: Path): Path =
|
||||||
|
Path.of(target.nameWithoutExtension + (suffix ?: "") + extension)
|
||||||
|
|
||||||
|
protected open fun onSuccess(
|
||||||
|
target: Path,
|
||||||
|
parser: WebViewComponentsParser,
|
||||||
|
cuContext: CompilationUnitContext
|
||||||
|
): Boolean {
|
||||||
|
val formatted = formatTree(
|
||||||
|
parser = parser,
|
||||||
|
tree = cuContext,
|
||||||
|
colors = false
|
||||||
|
)
|
||||||
|
if (this.interactive) {
|
||||||
|
println("Please review the following parse tree:\n$formatted")
|
||||||
|
} else {
|
||||||
|
println(formatted)
|
||||||
|
}
|
||||||
|
if (this.getYesNo("Write to disk?", true)) {
|
||||||
|
this.writeToDisk(getOutputPath(target), formatted)
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
return this.getYesNo("Do you wish to redo this file?", false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open fun onException(e: Exception): Boolean {
|
||||||
|
System.err.println("There was an exception during parsing: $e")
|
||||||
|
if (this.verbose) {
|
||||||
|
e.printStackTrace(System.err)
|
||||||
|
}
|
||||||
|
return this.getYesNo("Do you wish to try again?", false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getOutputDir() = myOutputDir
|
||||||
|
|
||||||
|
override fun transform(target: Path): Int {
|
||||||
|
if (interactive) {
|
||||||
|
println("Parsing $target")
|
||||||
|
}
|
||||||
|
var code = 0
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
val input = CharStreams.fromPath(target)
|
||||||
|
|
||||||
|
val lexer = WebViewComponentsLexer(input)
|
||||||
|
lexer.removeErrorListener(ConsoleErrorListener.INSTANCE)
|
||||||
|
val lexerErrorListener = LexerErrorListener()
|
||||||
|
lexer.addErrorListener(lexerErrorListener)
|
||||||
|
|
||||||
|
val tokenStream = if (strict) {
|
||||||
|
WebViewComponentsTokenStream(lexer) // only ignore hidden (default)
|
||||||
|
} else {
|
||||||
|
WebViewComponentsTokenStream(lexer, setOf(HIDDEN, ERROR)) // ignore hidden and error
|
||||||
|
}
|
||||||
|
|
||||||
|
val parser = WebViewComponentsParser(tokenStream)
|
||||||
|
parser.removeErrorListener(ConsoleErrorListener.INSTANCE)
|
||||||
|
val parserErrorListener = ParserErrorListener()
|
||||||
|
parser.addErrorListener(parserErrorListener)
|
||||||
|
|
||||||
|
val cuContext = parser.compilationUnit()
|
||||||
|
|
||||||
|
val lexerErrors = lexerErrorListener.getErrors() + parserErrorListener.getLexerErrors()
|
||||||
|
val parserErrors = parserErrorListener.getParserErrors()
|
||||||
|
|
||||||
|
if (lexerErrors.isNotEmpty()) {
|
||||||
|
val recover = this.onLexerErrors(lexerErrors)
|
||||||
|
if (!recover) {
|
||||||
|
code = 1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else if (parserErrors.isNotEmpty()) {
|
||||||
|
val recover = this.onParserErrors(parserErrors)
|
||||||
|
if (!recover) {
|
||||||
|
code = 1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val redo = this.onSuccess(target, parser, cuContext)
|
||||||
|
if (!redo) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
val recover = this.onException(e)
|
||||||
|
if (!recover) {
|
||||||
|
code = 1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -8,7 +8,6 @@ import picocli.CommandLine
|
|||||||
import picocli.CommandLine.Command
|
import picocli.CommandLine.Command
|
||||||
import picocli.CommandLine.Option
|
import picocli.CommandLine.Option
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.util.*
|
|
||||||
import kotlin.io.path.nameWithoutExtension
|
import kotlin.io.path.nameWithoutExtension
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
@ -31,9 +30,9 @@ open class TokenizeWvc : AbstractSourceTransformerCli() {
|
|||||||
|
|
||||||
@Option(
|
@Option(
|
||||||
names = ["-s", "--suffix"],
|
names = ["-s", "--suffix"],
|
||||||
description = ["The suffix (not extension!) to append to the output file names."]
|
description = ["The suffix (not extension!) to append to output file names."]
|
||||||
)
|
)
|
||||||
protected lateinit var suffix: Optional<String>
|
protected var suffix: String? = null
|
||||||
|
|
||||||
@Option(
|
@Option(
|
||||||
names = ["-e", "--extension"],
|
names = ["-e", "--extension"],
|
||||||
@ -48,47 +47,46 @@ open class TokenizeWvc : AbstractSourceTransformerCli() {
|
|||||||
defaultValue = ".",
|
defaultValue = ".",
|
||||||
paramLabel = "outputDir"
|
paramLabel = "outputDir"
|
||||||
)
|
)
|
||||||
private var myOutputDir: Path? = null
|
protected lateinit var myOutputDir: Path
|
||||||
|
|
||||||
protected fun onErrors(errors: List<LexerError>): Boolean {
|
protected fun onErrors(errors: List<LexerError>): Boolean {
|
||||||
println("There were errors during tokenization.")
|
System.err.println("There were errors during tokenization.")
|
||||||
errors.forEach { println(formatLexerError(it)) }
|
errors.forEach { System.err.println(formatLexerError(it)) }
|
||||||
return this.getYesNo("Do you wish to try again?", false)
|
return this.getYesNo("Do you wish to try again?", false)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getOutputPath(target: Path): Path =
|
protected fun getOutputPath(target: Path): Path =
|
||||||
Path.of(target.nameWithoutExtension + suffix.orElse("") + extension)
|
Path.of(target.nameWithoutExtension + (suffix ?: "") + extension)
|
||||||
|
|
||||||
protected fun onSuccess(target: Path, allTokens: List<Token>): Boolean {
|
protected fun onSuccess(target: Path, allTokens: List<Token>): Boolean {
|
||||||
val formatted = allTokens.mapIndexed { index, token ->
|
val formatted = allTokens.mapIndexed { index, token ->
|
||||||
"$index: ${formatToken(token)}"
|
"$index: ${formatToken(token)}"
|
||||||
}.joinToString(separator = "\n")
|
}.joinToString(separator = "\n")
|
||||||
if (!this.autoYes) {
|
if (interactive) {
|
||||||
println("Please review the following tokens:\n$formatted")
|
println("Please review the following tokens:\n$formatted")
|
||||||
if (this.getYesNo("Write to disk?")) {
|
}
|
||||||
this.writeToDisk(getOutputPath(target), formatted)
|
if (this.getYesNo("Write to disk?", true)) {
|
||||||
return false
|
|
||||||
} else {
|
|
||||||
return this.getYesNo("Do you wish to redo this file?", false)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.writeToDisk(getOutputPath(target), formatted)
|
this.writeToDisk(getOutputPath(target), formatted)
|
||||||
return false
|
return false
|
||||||
|
} else {
|
||||||
|
return this.getYesNo("Do you wish to redo this file?", false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun onException(e: Exception): Boolean {
|
protected fun onException(e: Exception): Boolean {
|
||||||
println("There was an exception during processing: $e")
|
System.err.println("There was an exception during processing: $e")
|
||||||
if (this.verbose) {
|
if (this.verbose) {
|
||||||
e.printStackTrace()
|
e.printStackTrace(System.err)
|
||||||
}
|
}
|
||||||
return this.getYesNo("Do you wish to try again?", false)
|
return this.getYesNo("Do you wish to try again?", false)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getOutputDir(): Path? = myOutputDir
|
override fun getOutputDir() = myOutputDir
|
||||||
|
|
||||||
override fun transform(target: Path): Int {
|
override fun transform(target: Path): Int {
|
||||||
println("Processing $target")
|
if (interactive) {
|
||||||
|
println("Tokenizing $target")
|
||||||
|
}
|
||||||
var code = 0
|
var code = 0
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
@ -99,7 +97,7 @@ open class TokenizeWvc : AbstractSourceTransformerCli() {
|
|||||||
val errorListener = LexerErrorListener()
|
val errorListener = LexerErrorListener()
|
||||||
lexer.addErrorListener(errorListener)
|
lexer.addErrorListener(errorListener)
|
||||||
|
|
||||||
val tokenStream = WebViewComponentsTokenStream(lexer, setOf(WebViewComponentsLexer.HIDDEN))
|
val tokenStream = WebViewComponentsTokenStream(lexer, setOf()) // include everything (ignore nothing)
|
||||||
val allTokens = tokenStream.getAllTokensSkipEOF()
|
val allTokens = tokenStream.getAllTokensSkipEOF()
|
||||||
|
|
||||||
val errors = errorListener.getErrors()
|
val errors = errorListener.getErrors()
|
||||||
|
Loading…
Reference in New Issue
Block a user