Basic lexer input-output comparison tests.
This commit is contained in:
parent
d099f9514d
commit
e747ac3c32
@ -151,7 +151,8 @@ final List<ToolSpec> toolSpecs = [
|
|||||||
toolSpec('groovyWvc', 'GroovyWvcCompiler'),
|
toolSpec('groovyWvc', 'GroovyWvcCompiler'),
|
||||||
toolSpec('lexer', 'LexerTool'),
|
toolSpec('lexer', 'LexerTool'),
|
||||||
toolSpec('parser', 'ParserTool'),
|
toolSpec('parser', 'ParserTool'),
|
||||||
toolSpec('parseTreeFileMaker', 'ParseTreeFileMakerCli')
|
toolSpec('parseTreeFileMaker', 'ParseTreeFileMakerCli'),
|
||||||
|
toolSpec('tokensFileMaker', 'TokensFileMakerCli')
|
||||||
]
|
]
|
||||||
|
|
||||||
toolSpecs.each { spec ->
|
toolSpecs.each { spec ->
|
||||||
|
@ -190,6 +190,11 @@ class WebViewComponentsTokenStream (private val tokenSource: TokenSource) : Toke
|
|||||||
return this.tokens
|
return this.tokens
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getAllTokensSkipEOF(): List<Token> {
|
||||||
|
this.fill()
|
||||||
|
return this.tokens.filter { it.type != Token.EOF }
|
||||||
|
}
|
||||||
|
|
||||||
private fun fill() {
|
private fun fill() {
|
||||||
val oldIndex = this.currentIndex
|
val oldIndex = this.currentIndex
|
||||||
this.initialize()
|
this.initialize()
|
||||||
|
@ -1,9 +1,17 @@
|
|||||||
package groowt.view.component.web.antlr;
|
package groowt.view.component.web.antlr;
|
||||||
|
|
||||||
|
import groowt.view.component.web.testutil.FileComparisonTestUtil;
|
||||||
|
import groowt.view.component.web.util.ExtensionUtil;
|
||||||
|
import groowt.view.component.web.util.FileUtil;
|
||||||
|
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.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.TestFactory;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static groowt.view.component.web.antlr.WebViewComponentsLexer.*;
|
import static groowt.view.component.web.antlr.WebViewComponentsLexer.*;
|
||||||
@ -48,4 +56,25 @@ public class WebViewComponentsLexerTests {
|
|||||||
assertTokenType(EOF, t4);
|
assertTokenType(EOF, t4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestFactory
|
||||||
|
public Collection<DynamicTest> lexerFileTests() {
|
||||||
|
return FileComparisonTestUtil.getTestsFor(
|
||||||
|
Path.of("src", "test", "lexer"),
|
||||||
|
"*.wvc",
|
||||||
|
Path.of("src", "test", "lexer", "tokens-files"),
|
||||||
|
sourcePath -> {
|
||||||
|
final String nameWithoutExtension = ExtensionUtil.getNameWithoutExtension(sourcePath);
|
||||||
|
return Path.of(nameWithoutExtension + "_tokens.txt");
|
||||||
|
},
|
||||||
|
sourceFile -> {
|
||||||
|
final CharStream input = CharStreams.fromString(FileUtil.readFile(sourceFile));
|
||||||
|
final WebViewComponentsLexer lexer = new WebViewComponentsLexer(input);
|
||||||
|
final WebViewComponentsTokenStream tokenStream = new WebViewComponentsTokenStream(lexer);
|
||||||
|
return tokenStream.getAllTokensSkipEOF().stream()
|
||||||
|
.map(TokenUtil::formatToken)
|
||||||
|
.collect(Collectors.joining("\n"));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
Hello, $target!
|
@ -0,0 +1 @@
|
|||||||
|
Hello, World!
|
@ -0,0 +1,4 @@
|
|||||||
|
RawText[1,0](Hello, )
|
||||||
|
DollarReferenceStart[1,7]($)
|
||||||
|
GroovyCode[1,8](target)
|
||||||
|
RawText[1,14](!\n)
|
@ -0,0 +1 @@
|
|||||||
|
RawText[1,0](Hello, World!\n)
|
@ -1,10 +1,6 @@
|
|||||||
package groowt.view.component.web.tools
|
package groowt.view.component.web.tools
|
||||||
|
|
||||||
import java.util.regex.Pattern
|
abstract class AbstractOutputFileMaker implements SourceFileProcessor {
|
||||||
|
|
||||||
abstract class AbstractTreeFileMaker implements SourceFileProcessor {
|
|
||||||
|
|
||||||
private static final Pattern withoutExtension = ~/(?<name>.*)\.(?<ext>.+)/
|
|
||||||
|
|
||||||
private final Scanner scanner = new Scanner(System.in)
|
private final Scanner scanner = new Scanner(System.in)
|
||||||
|
|
||||||
@ -15,15 +11,6 @@ abstract class AbstractTreeFileMaker implements SourceFileProcessor {
|
|||||||
boolean autoYes
|
boolean autoYes
|
||||||
boolean verbose
|
boolean verbose
|
||||||
|
|
||||||
protected String getNameWithoutExtension(File file) {
|
|
||||||
def m = withoutExtension.matcher(file.name)
|
|
||||||
if (m.matches()) {
|
|
||||||
return m.group('name')
|
|
||||||
} else {
|
|
||||||
throw new IllegalArgumentException("Could not determine file name without extension for ${file}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean getYesNoInput(String prompt, boolean force = false) {
|
protected boolean getYesNoInput(String prompt, boolean force = false) {
|
||||||
if (this.autoYes && !force) {
|
if (this.autoYes && !force) {
|
||||||
return true
|
return true
|
||||||
@ -40,4 +27,20 @@ abstract class AbstractTreeFileMaker implements SourceFileProcessor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void writeToDisk(String name, String formatted) {
|
||||||
|
this.outputDirectory.mkdirs()
|
||||||
|
def out = new File(this.outputDirectory, name + this.suffix + this.extension)
|
||||||
|
if (out.exists()) {
|
||||||
|
if (this.getYesNoInput("${out} already exists. Write over? (y/n)")) {
|
||||||
|
println "Writing to $out..."
|
||||||
|
out.write(formatted)
|
||||||
|
} else {
|
||||||
|
println "Skipping writing to $out."
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println "Writing to $out..."
|
||||||
|
out.write(formatted)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -11,10 +11,11 @@ import groowt.view.component.web.ast.DefaultAstBuilder
|
|||||||
import groowt.view.component.web.ast.DefaultNodeFactory
|
import groowt.view.component.web.ast.DefaultNodeFactory
|
||||||
import groowt.view.component.web.ast.NodeUtil
|
import groowt.view.component.web.ast.NodeUtil
|
||||||
import groowt.view.component.web.ast.node.Node
|
import groowt.view.component.web.ast.node.Node
|
||||||
|
import groowt.view.component.web.util.ExtensionUtil
|
||||||
import org.jetbrains.annotations.Nullable
|
import org.jetbrains.annotations.Nullable
|
||||||
|
|
||||||
@InheritConstructors
|
@InheritConstructors
|
||||||
final class AstFileMaker extends AbstractTreeFileMaker {
|
final class AstFileMaker extends AbstractOutputFileMaker {
|
||||||
|
|
||||||
protected sealed interface BuildResult permits BuildSuccess, BuildFailure {}
|
protected sealed interface BuildResult permits BuildSuccess, BuildFailure {}
|
||||||
|
|
||||||
@ -31,22 +32,6 @@ final class AstFileMaker extends AbstractTreeFileMaker {
|
|||||||
@Nullable String message
|
@Nullable String message
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeFormatted(String name, String formatted) {
|
|
||||||
this.outputDirectory.mkdirs()
|
|
||||||
def outFile = new File(this.outputDirectory, name + this.suffix + this.extension)
|
|
||||||
if (outFile.exists()) {
|
|
||||||
if (this.getYesNoInput("$outFile already exists. Write over? (y/n)")) {
|
|
||||||
println "Writing to $outFile..."
|
|
||||||
outFile.write(formatted)
|
|
||||||
} else {
|
|
||||||
println "Skipping writing to $outFile."
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
println "Writing to $outFile..."
|
|
||||||
outFile.write(formatted)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean onSuccess(String name, BuildSuccess buildSuccess) {
|
private boolean onSuccess(String name, BuildSuccess buildSuccess) {
|
||||||
def formatted = NodeUtil.formatAst(buildSuccess.node, buildSuccess.tokenList)
|
def formatted = NodeUtil.formatAst(buildSuccess.node, buildSuccess.tokenList)
|
||||||
if (!this.autoYes) {
|
if (!this.autoYes) {
|
||||||
@ -54,7 +39,7 @@ final class AstFileMaker extends AbstractTreeFileMaker {
|
|||||||
println formatted
|
println formatted
|
||||||
}
|
}
|
||||||
if (this.getYesNoInput('Would you like to write to disk? (y/n)')) {
|
if (this.getYesNoInput('Would you like to write to disk? (y/n)')) {
|
||||||
this.writeFormatted(name, formatted)
|
this.writeToDisk(name, formatted)
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return !this.getYesNoInput('Do you wish to redo this file? (y/n)')
|
return !this.getYesNoInput('Do you wish to redo this file? (y/n)')
|
||||||
@ -117,7 +102,7 @@ final class AstFileMaker extends AbstractTreeFileMaker {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
void process(File sourceFile) {
|
void process(File sourceFile) {
|
||||||
def name = this.getNameWithoutExtension(sourceFile)
|
def name = ExtensionUtil.getNameWithoutExtension(sourceFile)
|
||||||
println "Processing $name"
|
println "Processing $name"
|
||||||
boolean doneYet = false
|
boolean doneYet = false
|
||||||
while (!doneYet) {
|
while (!doneYet) {
|
||||||
|
@ -3,31 +3,11 @@ package groowt.view.component.web.tools
|
|||||||
import groovy.transform.InheritConstructors
|
import groovy.transform.InheritConstructors
|
||||||
import groowt.view.component.web.antlr.*
|
import groowt.view.component.web.antlr.*
|
||||||
import groowt.view.component.web.antlr.AntlrUtil.ParseErrorCollector
|
import groowt.view.component.web.antlr.AntlrUtil.ParseErrorCollector
|
||||||
|
import groowt.view.component.web.util.ExtensionUtil
|
||||||
import org.antlr.v4.runtime.CharStreams
|
import org.antlr.v4.runtime.CharStreams
|
||||||
|
|
||||||
@InheritConstructors
|
@InheritConstructors
|
||||||
final class ParseTreeFileMaker extends AbstractTreeFileMaker {
|
final class ParseTreeFileMaker extends AbstractOutputFileMaker {
|
||||||
|
|
||||||
private void writeFormatted(
|
|
||||||
String name,
|
|
||||||
WebViewComponentsParser parser,
|
|
||||||
WebViewComponentsParser.CompilationUnitContext cu
|
|
||||||
) {
|
|
||||||
this.outputDirectory.mkdirs()
|
|
||||||
def formatted = ParserUtil.formatTree(parser, cu, false)
|
|
||||||
def out = new File(this.outputDirectory, name + this.suffix + this.extension)
|
|
||||||
if (out.exists()) {
|
|
||||||
if (this.getYesNoInput("${out} already exists. Write over? (y/n)")) {
|
|
||||||
println "Writing to $out..."
|
|
||||||
out.write(formatted)
|
|
||||||
} else {
|
|
||||||
println "Skipping writing to $out."
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
println "Writing to $out..."
|
|
||||||
out.write(formatted)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true if done now, false if not done yet
|
* @return true if done now, false if not done yet
|
||||||
@ -42,7 +22,8 @@ final class ParseTreeFileMaker extends AbstractTreeFileMaker {
|
|||||||
println ParserUtil.formatTree(parser, cu, true)
|
println ParserUtil.formatTree(parser, cu, true)
|
||||||
}
|
}
|
||||||
if (this.getYesNoInput('Write to disk? (y/n)')) {
|
if (this.getYesNoInput('Write to disk? (y/n)')) {
|
||||||
this.writeFormatted(name, parser, cu)
|
def formatted = ParserUtil.formatTree(parser, cu, false)
|
||||||
|
this.writeToDisk(name, formatted)
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return !this.getYesNoInput('Do you wish to redo this file? (y/n)')
|
return !this.getYesNoInput('Do you wish to redo this file? (y/n)')
|
||||||
@ -86,8 +67,8 @@ final class ParseTreeFileMaker extends AbstractTreeFileMaker {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
void process(File sourceFile) {
|
void process(File sourceFile) {
|
||||||
def name = this.getNameWithoutExtension(sourceFile)
|
def name = ExtensionUtil.getNameWithoutExtension(sourceFile)
|
||||||
println "processing: $name"
|
println "Processing: $name"
|
||||||
boolean doneYet = false
|
boolean doneYet = false
|
||||||
while (!doneYet) {
|
while (!doneYet) {
|
||||||
def (parser, cu, errors) = this.parse(sourceFile)
|
def (parser, cu, errors) = this.parse(sourceFile)
|
||||||
|
@ -0,0 +1,57 @@
|
|||||||
|
package groowt.view.component.web.tools
|
||||||
|
|
||||||
|
import groovy.transform.InheritConstructors
|
||||||
|
import groowt.view.component.web.antlr.TokenUtil
|
||||||
|
import groowt.view.component.web.antlr.WebViewComponentsLexer
|
||||||
|
import groowt.view.component.web.antlr.WebViewComponentsTokenStream
|
||||||
|
import groowt.view.component.web.util.ExtensionUtil
|
||||||
|
import org.antlr.v4.runtime.CharStreams
|
||||||
|
import org.antlr.v4.runtime.Token
|
||||||
|
|
||||||
|
@InheritConstructors
|
||||||
|
class TokensFileMaker extends AbstractOutputFileMaker {
|
||||||
|
|
||||||
|
protected boolean onSuccess(String name, List<Token> allTokens) {
|
||||||
|
def formatted = allTokens.collect(TokenUtil.&formatToken).join('\n')
|
||||||
|
if (!this.autoYes) {
|
||||||
|
println 'Please review the following tokens:'
|
||||||
|
println formatted
|
||||||
|
}
|
||||||
|
if (this.getYesNoInput('Write to disk? (y/n)')) {
|
||||||
|
this.writeToDisk(name, formatted)
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return !this.getYesNoInput('Do you wish to redo this file? (y/n)')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean onException(String name, Exception e) {
|
||||||
|
println "There was an exception while tokenizing $name: $e.message"
|
||||||
|
e.printStackTrace()
|
||||||
|
if (this.getYesNoInput('Do you wish to try again? (y/n)', true)) {
|
||||||
|
println "Trying $name again..."
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
println "Skipping $name."
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void process(File sourceFile) {
|
||||||
|
def name = ExtensionUtil.getNameWithoutExtension(sourceFile)
|
||||||
|
println "Processing: $name"
|
||||||
|
boolean doneYet = false
|
||||||
|
while (!doneYet) {
|
||||||
|
try {
|
||||||
|
def input = CharStreams.fromString(sourceFile.getText())
|
||||||
|
def lexer = new WebViewComponentsLexer(input)
|
||||||
|
def tokenStream = new WebViewComponentsTokenStream(lexer)
|
||||||
|
doneYet = this.onSuccess(name, tokenStream.getAllTokensSkipEOF())
|
||||||
|
} catch (Exception e) {
|
||||||
|
doneYet = this.onException(name, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package groowt.view.component.web.tools
|
||||||
|
|
||||||
|
import picocli.CommandLine
|
||||||
|
|
||||||
|
@CommandLine.Command(
|
||||||
|
name = 'tokensFileMaker',
|
||||||
|
description = 'Tokenize given input files and output files containing their tokens.',
|
||||||
|
mixinStandardHelpOptions = true
|
||||||
|
)
|
||||||
|
class TokensFileMakerCli extends SourceFileProcessorSpec {
|
||||||
|
|
||||||
|
static void main(String[] args) {
|
||||||
|
System.exit(new CommandLine(new TokensFileMakerCli()).execute(args))
|
||||||
|
}
|
||||||
|
|
||||||
|
TokensFileMakerCli() {
|
||||||
|
super({ SourceFileProcessorSpec spec ->
|
||||||
|
new TokensFileMaker(
|
||||||
|
dryRun: spec.dryRun,
|
||||||
|
suffix: spec.suffix.orElse('_tokens'),
|
||||||
|
extension: spec.extension,
|
||||||
|
outputDirectory: spec.outputDirectory.orElse(new File('src/test/lexer/tokens-files')),
|
||||||
|
autoYes: spec.autoYes,
|
||||||
|
verbose: spec.verbose
|
||||||
|
)
|
||||||
|
}, 'src/test/lexer')
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user