Saving before switching to Java.
This commit is contained in:
parent
bee1dead98
commit
67cbb11dc3
@ -8,9 +8,6 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// https://mvnrepository.com/artifact/org.apache.groovy/groovy
|
||||
implementation 'org.apache.groovy:groovy:4.0.7'
|
||||
|
||||
// https://mvnrepository.com/artifact/org.apache.groovy/groovy-templates
|
||||
implementation 'org.apache.groovy:groovy-templates:4.0.7'
|
||||
|
||||
@ -19,8 +16,10 @@ dependencies {
|
||||
|
||||
// https://archiva.jessebrault.com/#artifact/com.jessebrault.fsm/groovy-extension/0.1.0-SNAPSHOT
|
||||
implementation 'com.jessebrault.fsm:groovy-extension:0.1.0-SNAPSHOT'
|
||||
|
||||
testRuntimeOnly project(':gcp-impl')
|
||||
}
|
||||
|
||||
jar {
|
||||
archivesBaseName = 'gcp'
|
||||
archivesBaseName = 'gcp-api'
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package com.jessebrault.gcp.component
|
||||
package com.jessebrault.gcp
|
||||
|
||||
interface Component {
|
||||
String render(Map<String, ?> attr, String body)
|
@ -1,4 +1,4 @@
|
||||
package com.jessebrault.gcp.component
|
||||
package com.jessebrault.gcp
|
||||
|
||||
interface ComponentFactory {
|
||||
Component get()
|
@ -1,4 +1,4 @@
|
||||
package com.jessebrault.gcp.component
|
||||
package com.jessebrault.gcp
|
||||
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
@ -1,6 +1,5 @@
|
||||
package com.jessebrault.gcp
|
||||
|
||||
import com.jessebrault.gcp.component.ComponentsContainer
|
||||
import groovy.text.Template
|
||||
|
||||
class GcpTemplate implements Template {
|
@ -1,7 +1,5 @@
|
||||
package com.jessebrault.gcp
|
||||
|
||||
import com.jessebrault.gcp.component.Component
|
||||
import com.jessebrault.gcp.component.ComponentsContainer
|
||||
import groovy.text.Template
|
||||
import groovy.text.TemplateEngine
|
||||
import groovy.transform.TupleConstructor
|
||||
@ -16,6 +14,12 @@ final class GcpTemplateEngine extends TemplateEngine {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(GcpTemplateEngine)
|
||||
|
||||
private static GcpToScriptConverter getConverter() {
|
||||
ServiceLoader.load(GcpToScriptConverter).findFirst().orElseThrow({
|
||||
new NullPointerException('Could not find an implementation of GcpToScriptConverter')
|
||||
})
|
||||
}
|
||||
|
||||
@TupleConstructor(defaults = false)
|
||||
static class Configuration {
|
||||
Supplier<GcpTemplate> ssgTemplateSupplier
|
||||
@ -37,7 +41,7 @@ final class GcpTemplateEngine extends TemplateEngine {
|
||||
Template createTemplate(Reader reader) throws CompilationFailedException, ClassNotFoundException, IOException {
|
||||
def templateSrc = reader.text
|
||||
|
||||
def converter = new TemplateToScriptConverter()
|
||||
def converter = getConverter()
|
||||
def scriptSrc = converter.convert(templateSrc)
|
||||
logger.debug('scriptSrc: {}', scriptSrc)
|
||||
def scriptName = "SsgTemplate${ this.templateCount.getAndIncrement() }.groovy"
|
@ -0,0 +1,5 @@
|
||||
package com.jessebrault.gcp
|
||||
|
||||
interface GcpToScriptConverter {
|
||||
String convert(String gcpSrc)
|
||||
}
|
@ -1,13 +1,12 @@
|
||||
package com.jessebrault.gcp
|
||||
|
||||
|
||||
import com.jessebrault.gcp.component.Component
|
||||
import groovy.text.TemplateEngine
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals
|
||||
|
||||
class GcpTemplateEngineTests {
|
||||
class GcpTemplateEngineIntegrationTests {
|
||||
|
||||
private final TemplateEngine engine = new GcpTemplateEngine(new GcpTemplateEngine.Configuration(
|
||||
{ new GcpTemplate() },
|
@ -1,6 +1,6 @@
|
||||
package components
|
||||
|
||||
import com.jessebrault.gcp.component.Component
|
||||
import com.jessebrault.gcp.Component
|
||||
|
||||
class Greeting implements Component {
|
||||
|
@ -1,6 +1,6 @@
|
||||
package components
|
||||
|
||||
import com.jessebrault.gcp.component.Component
|
||||
import com.jessebrault.gcp.Component
|
||||
|
||||
class Head implements Component {
|
||||
|
25
gcp-impl/build.gradle
Normal file
25
gcp-impl/build.gradle
Normal file
@ -0,0 +1,25 @@
|
||||
plugins {
|
||||
id 'ssg.common'
|
||||
id 'ssg.lib'
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(':gcp-api')
|
||||
|
||||
// https://mvnrepository.com/artifact/org.apache.groovy/groovy-templates
|
||||
implementation 'org.apache.groovy:groovy-templates:4.0.7'
|
||||
|
||||
// https://archiva.jessebrault.com/#artifact/com.jessebrault.fsm/lib/0.1.0-SNAPSHOT
|
||||
implementation 'com.jessebrault.fsm:lib:0.1.0-SNAPSHOT'
|
||||
|
||||
// https://archiva.jessebrault.com/#artifact/com.jessebrault.fsm/groovy-extension/0.1.0-SNAPSHOT
|
||||
implementation 'com.jessebrault.fsm:groovy-extension:0.1.0-SNAPSHOT'
|
||||
}
|
||||
|
||||
jar {
|
||||
archivesBaseName = 'gcp-impl'
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
package com.jessebrault.gcp
|
||||
|
||||
import com.jessebrault.gcp.groovy.BlockScriptletParser
|
||||
import com.jessebrault.gcp.groovy.DollarReferenceParser
|
||||
import com.jessebrault.gcp.groovy.DollarScriptletParser
|
||||
import com.jessebrault.gcp.groovy.ExpressionScriptletParser
|
||||
import com.jessebrault.gcp.node.Document
|
||||
import com.jessebrault.gcp.node.DollarReference
|
||||
import com.jessebrault.gcp.node.DollarScriptlet
|
||||
import com.jessebrault.gcp.node.ExpressionScriptlet
|
||||
import com.jessebrault.gcp.node.Html
|
||||
import com.jessebrault.gcp.node.BlockScriptlet
|
||||
|
||||
import java.util.regex.Matcher
|
||||
import java.util.regex.Pattern
|
||||
|
||||
class GcpParser {
|
||||
|
||||
// private static enum State {
|
||||
// HTML, DOLLAR_GROOVY, SCRIPTLET, EXPRESSION_SCRIPTLET
|
||||
// }
|
||||
//
|
||||
// private static FunctionFsmBuilder<String, State, String> getFsmBuilder() {
|
||||
// new FunctionFsmBuilderImpl<>()
|
||||
// }
|
||||
|
||||
private static final Pattern html = ~/^(?:[\w\W&&[^<$]]|<(?![%\p{Lu}]))+/
|
||||
|
||||
private static final Pattern groovyIdentifier = ~/^[a-zA-Z_\u0024\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u00ff\u0100-\ufff3][a-zA-Z_\u00240-9\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u00ff\u0100-\ufff3]*/
|
||||
|
||||
|
||||
static Document parse(String gcp) {
|
||||
|
||||
}
|
||||
|
||||
private static Document document(String gcp) {
|
||||
def document = new Document()
|
||||
def remaining = gcp
|
||||
while (remaining.length() > 0) {
|
||||
Matcher m
|
||||
DollarReferenceParser.Result dollarReferenceResult
|
||||
DollarScriptletParser.Result dollarScriptletResult
|
||||
BlockScriptletParser.Result blockScriptletResult
|
||||
ExpressionScriptletParser.Result expressionScriptletResult
|
||||
|
||||
String match
|
||||
|
||||
if ((m = html.matcher(remaining)).find()) {
|
||||
match = m.group()
|
||||
document.children << new Html().tap {
|
||||
text = match
|
||||
}
|
||||
} else if (dollarReferenceResult = DollarReferenceParser.parse(remaining)) {
|
||||
match = dollarReferenceResult.fullMatch
|
||||
document.children << new DollarReference().tap {
|
||||
reference = dollarReferenceResult.reference
|
||||
}
|
||||
} else if (dollarScriptletResult = DollarScriptletParser.parseResult(remaining)) {
|
||||
match = dollarScriptletResult.fullMatch
|
||||
document.children << new DollarScriptlet().tap {
|
||||
scriptlet = dollarScriptletResult.scriptlet
|
||||
}
|
||||
} else if (blockScriptletResult = BlockScriptletParser.parseResult(remaining)) {
|
||||
match = blockScriptletResult.fullMatch
|
||||
document.children << new BlockScriptlet().tap {
|
||||
scriptlet = blockScriptletResult.scriptlet
|
||||
}
|
||||
} else if (expressionScriptletResult = ExpressionScriptletParser.parseResult(remaining)) {
|
||||
match = expressionScriptletResult.fullMatch
|
||||
document.children << new ExpressionScriptlet().tap {
|
||||
scriptlet = expressionScriptletResult.scriptlet
|
||||
}
|
||||
}
|
||||
|
||||
remaining = remaining.substring(match.length())
|
||||
}
|
||||
document
|
||||
}
|
||||
|
||||
}
|
@ -2,12 +2,28 @@ package com.jessebrault.gcp
|
||||
|
||||
import com.jessebrault.fsm.stackfunction.StackFunctionFsmBuilder
|
||||
import com.jessebrault.fsm.stackfunction.StackFunctionFsmBuilderImpl
|
||||
import groovy.transform.PackageScope
|
||||
import com.jessebrault.gcp.util.PatternFunction
|
||||
|
||||
import static FsmState.*
|
||||
class GcpToScriptConverterImpl implements GcpToScriptConverter {
|
||||
|
||||
@PackageScope
|
||||
class TemplateToScriptConverter {
|
||||
enum State {
|
||||
HTML,
|
||||
SCRIPTLET,
|
||||
EXPRESSION_SCRIPTLET,
|
||||
DOLLAR,
|
||||
|
||||
COMPONENT,
|
||||
|
||||
COMPONENT_IDENTIFIER,
|
||||
|
||||
COMPONENT_ATTR_KEY,
|
||||
COMPONENT_ATTR_VALUE_OPEN,
|
||||
|
||||
COMPONENT_ATTR_VALUE_STRING,
|
||||
COMPONENT_ATTR_VALUE_STRING_CLOSE,
|
||||
|
||||
COMPONENT_CLOSE
|
||||
}
|
||||
|
||||
private static final PatternFunction html = new PatternFunction(~/^(?:[\w\W&&[^<$]]|<(?!%|\p{Lu})|\$(?!\{))+/)
|
||||
private static final PatternFunction scriptletOpen = new PatternFunction(~/^<%(?!=)/)
|
||||
@ -25,11 +41,11 @@ class TemplateToScriptConverter {
|
||||
private static final PatternFunction attrValueStringContents = new PatternFunction(~/^(?:[\w\W&&[^\\"]]|\\\\|\\")*(?=")/)
|
||||
private static final PatternFunction attrValueStringClose = new PatternFunction(~/["']/)
|
||||
|
||||
private static StackFunctionFsmBuilder<String, FsmState, String> getFsmBuilder() {
|
||||
private static StackFunctionFsmBuilder<String, State, String> getFsmBuilder() {
|
||||
new StackFunctionFsmBuilderImpl<>()
|
||||
}
|
||||
|
||||
@SuppressWarnings('GrMethodMayBeStatic')
|
||||
@Override
|
||||
String convert(String src) {
|
||||
def b = new StringBuilder()
|
||||
def stringAcc = new StringBuilder()
|
||||
@ -37,22 +53,22 @@ class TemplateToScriptConverter {
|
||||
b << 'def getTemplate() {\nreturn { out ->\n'
|
||||
|
||||
def fsm = getFsmBuilder().with {
|
||||
initialState = HTML
|
||||
initialState = State.HTML
|
||||
|
||||
whileIn(HTML) {
|
||||
whileIn(State.HTML) {
|
||||
on html exec {
|
||||
stringAcc << it
|
||||
}
|
||||
on scriptletOpen shiftTo SCRIPTLET exec {
|
||||
on scriptletOpen shiftTo State.SCRIPTLET exec {
|
||||
if (stringAcc.length() > 0) {
|
||||
b << 'out << """' << stringAcc.toString() << '""";\n'
|
||||
stringAcc = new StringBuilder()
|
||||
}
|
||||
}
|
||||
on expressionScriptletOpen shiftTo EXPRESSION_SCRIPTLET exec {
|
||||
on expressionScriptletOpen shiftTo State.EXPRESSION_SCRIPTLET exec {
|
||||
stringAcc << '${'
|
||||
}
|
||||
on componentOpen shiftTo COMPONENT_IDENTIFIER exec {
|
||||
on componentOpen shiftTo State.COMPONENT_IDENTIFIER exec {
|
||||
if (stringAcc.length() > 0) {
|
||||
b << 'out << """' << stringAcc.toString() << '""";\n'
|
||||
stringAcc = new StringBuilder()
|
||||
@ -60,7 +76,7 @@ class TemplateToScriptConverter {
|
||||
}
|
||||
}
|
||||
|
||||
whileIn(SCRIPTLET) {
|
||||
whileIn(State.SCRIPTLET) {
|
||||
on scriptletText exec {
|
||||
b << it
|
||||
}
|
||||
@ -69,7 +85,7 @@ class TemplateToScriptConverter {
|
||||
}
|
||||
}
|
||||
|
||||
whileIn(EXPRESSION_SCRIPTLET) {
|
||||
whileIn(State.EXPRESSION_SCRIPTLET) {
|
||||
on scriptletText exec {
|
||||
stringAcc << it
|
||||
}
|
||||
@ -78,7 +94,7 @@ class TemplateToScriptConverter {
|
||||
}
|
||||
}
|
||||
|
||||
whileIn(COMPONENT) {
|
||||
whileIn(State.COMPONENT) {
|
||||
// tokenize component, figure out body, and tokenize closing component
|
||||
}
|
||||
|
@ -1,16 +1,17 @@
|
||||
package com.jessebrault.gcp.component
|
||||
|
||||
import com.jessebrault.gcp.component.ComponentToken.Type
|
||||
import com.jessebrault.gcp.component.node.ComponentNode
|
||||
import com.jessebrault.gcp.component.node.ComponentRoot
|
||||
import com.jessebrault.gcp.component.node.DollarReferenceValue
|
||||
import com.jessebrault.gcp.component.node.DollarScriptletValue
|
||||
import com.jessebrault.gcp.component.node.ExpressionScriptletValue
|
||||
import com.jessebrault.gcp.component.node.GStringValue
|
||||
import com.jessebrault.gcp.component.node.KeyAndValue
|
||||
import com.jessebrault.gcp.component.node.KeysAndValues
|
||||
import com.jessebrault.gcp.component.node.Node
|
||||
import com.jessebrault.gcp.component.node.ComponentNode
|
||||
import com.jessebrault.gcp.component.node.ScriptletValue
|
||||
import com.jessebrault.gcp.component.node.StringValue
|
||||
import com.jessebrault.gcp.util.PeekBefore
|
||||
import groovy.transform.PackageScope
|
||||
|
||||
import static com.jessebrault.gcp.component.ComponentToken.Type.*
|
||||
@ -24,7 +25,7 @@ class ComponentParser {
|
||||
private Queue<ComponentToken> tokens
|
||||
private String currentIdentifier
|
||||
|
||||
ComponentNode parse(Queue<ComponentToken> tokens) {
|
||||
ComponentRoot parse(Queue<ComponentToken> tokens) {
|
||||
this.tokens = tokens
|
||||
this.selfClosingComponent()
|
||||
}
|
||||
@ -72,22 +73,22 @@ class ComponentParser {
|
||||
t && t.type == type
|
||||
}
|
||||
|
||||
private ComponentNode selfClosingComponent() {
|
||||
private ComponentRoot selfClosingComponent() {
|
||||
this.startOfOpeningOrSelfClosingComponent()
|
||||
def keysAndValues = this.keysAndValues()
|
||||
this.expect(FORWARD_SLASH)
|
||||
this.expect(GT)
|
||||
new ComponentNode().tap {
|
||||
new ComponentRoot().tap {
|
||||
it.identifier = this.currentIdentifier
|
||||
it.children << keysAndValues
|
||||
}
|
||||
}
|
||||
|
||||
private ComponentNode openingComponent() {
|
||||
private ComponentRoot openingComponent() {
|
||||
this.startOfOpeningOrSelfClosingComponent()
|
||||
def keysAndValues = this.keysAndValues()
|
||||
this.expect(GT)
|
||||
new ComponentNode().tap {
|
||||
new ComponentRoot().tap {
|
||||
it.identifier = this.currentIdentifier
|
||||
it.children << keysAndValues
|
||||
}
|
||||
@ -110,7 +111,7 @@ class ComponentParser {
|
||||
}
|
||||
|
||||
private KeysAndValues keysAndValues() {
|
||||
List<Node> children = []
|
||||
List<ComponentNode> children = []
|
||||
while (true) {
|
||||
if (this.peek(KEY)) {
|
||||
def keyAndValue = this.keyAndValue()
|
||||
@ -137,7 +138,7 @@ class ComponentParser {
|
||||
}
|
||||
}
|
||||
|
||||
private Node value() {
|
||||
private ComponentNode value() {
|
||||
if (this.peek(DOUBLE_QUOTE)) {
|
||||
return this.doubleQuoteStringValue()
|
||||
} else if (this.peek(SINGLE_QUOTE)) {
|
@ -1,83 +1,77 @@
|
||||
package com.jessebrault.gcp.component
|
||||
|
||||
import com.jessebrault.gcp.component.node.BooleanValue
|
||||
import com.jessebrault.gcp.component.node.ComponentNode
|
||||
import com.jessebrault.gcp.component.node.ComponentRoot
|
||||
import com.jessebrault.gcp.component.node.DollarReferenceValue
|
||||
import com.jessebrault.gcp.component.node.DollarScriptletValue
|
||||
import com.jessebrault.gcp.component.node.ExpressionScriptletValue
|
||||
import com.jessebrault.gcp.component.node.GStringValue
|
||||
import com.jessebrault.gcp.component.node.KeyAndValue
|
||||
import com.jessebrault.gcp.component.node.KeysAndValues
|
||||
import com.jessebrault.gcp.component.node.NodeVisitor
|
||||
import com.jessebrault.gcp.component.node.ComponentNodeVisitor
|
||||
import com.jessebrault.gcp.component.node.ScriptletValue
|
||||
import com.jessebrault.gcp.component.node.StringValue
|
||||
|
||||
// NOT THREAD SAFE, and must be used exactly once
|
||||
class ComponentToScriptVisitor extends NodeVisitor {
|
||||
// NOT THREAD SAFE
|
||||
class ComponentToClosureVisitor extends ComponentNodeVisitor {
|
||||
|
||||
private final Writer w = new StringWriter()
|
||||
private final IndentPrinter p = new IndentPrinter(this.w, ' ', true, true)
|
||||
private StringBuilder b = new StringBuilder()
|
||||
|
||||
String getResult() {
|
||||
w.toString()
|
||||
b.toString()
|
||||
}
|
||||
|
||||
void visit(ComponentNode componentNode) {
|
||||
p.println('{ attr, bodyOut ->')
|
||||
p.incrementIndent()
|
||||
void reset() {
|
||||
b = new StringBuilder()
|
||||
}
|
||||
|
||||
void visit(ComponentRoot componentNode) {
|
||||
b << '{ '
|
||||
super.visit(componentNode)
|
||||
if (componentNode.body != null) {
|
||||
p.println("bodyOut << ${ componentNode.body };")
|
||||
b << "bodyOut << ${ componentNode.body }; "
|
||||
}
|
||||
p.decrementIndent()
|
||||
p.println('};')
|
||||
b << '};'
|
||||
}
|
||||
|
||||
void visit(KeysAndValues keysAndValues) {
|
||||
p.println('attr {')
|
||||
p.incrementIndent()
|
||||
b << 'attr { '
|
||||
super.visit(keysAndValues)
|
||||
p.decrementIndent()
|
||||
p.println('};')
|
||||
b << '}; '
|
||||
}
|
||||
|
||||
void visit(KeyAndValue keyAndValue) {
|
||||
p.printIndent()
|
||||
p.print("${ keyAndValue.key } = ")
|
||||
b << "${ keyAndValue.key } = "
|
||||
super.visit(keyAndValue)
|
||||
p.print(';\n')
|
||||
b << '; '
|
||||
}
|
||||
|
||||
void visit(GStringValue gStringValue) {
|
||||
p.print("\"${ gStringValue.gString }\"")
|
||||
b << "\"${ gStringValue.gString }\""
|
||||
}
|
||||
|
||||
void visit(StringValue stringValue) {
|
||||
p.print("'${ stringValue.string }'")
|
||||
b << "'${ stringValue.string }'"
|
||||
}
|
||||
|
||||
void visit(DollarReferenceValue dollarReferenceValue) {
|
||||
p.print(dollarReferenceValue.reference)
|
||||
b << dollarReferenceValue.reference
|
||||
}
|
||||
|
||||
void visit(DollarScriptletValue dollarScriptletValue) {
|
||||
p.print(dollarScriptletValue.scriptlet)
|
||||
b << dollarScriptletValue.scriptlet
|
||||
}
|
||||
|
||||
void visit(ScriptletValue scriptletValue) {
|
||||
p.println("render { out ->")
|
||||
p.incrementIndent()
|
||||
p.print(scriptletValue.scriptlet)
|
||||
p.decrementIndent()
|
||||
p.println('}')
|
||||
b << "render { out -> ${ scriptletValue.scriptlet } }"
|
||||
}
|
||||
|
||||
void visit(ExpressionScriptletValue expressionScriptletValue) {
|
||||
p.print(expressionScriptletValue.scriptlet)
|
||||
b << expressionScriptletValue.scriptlet
|
||||
}
|
||||
|
||||
void visit(BooleanValue booleanValue) {
|
||||
p.print(booleanValue.value.toString())
|
||||
b << booleanValue.value.toString()
|
||||
}
|
||||
|
||||
}
|
@ -2,7 +2,8 @@ package com.jessebrault.gcp.component
|
||||
|
||||
import com.jessebrault.fsm.function.FunctionFsmBuilder
|
||||
import com.jessebrault.fsm.function.FunctionFsmBuilderImpl
|
||||
import com.jessebrault.gcp.PatternFunction
|
||||
import com.jessebrault.gcp.groovy.DollarScriptletParser
|
||||
import com.jessebrault.gcp.util.PatternFunction
|
||||
|
||||
import static ComponentToken.Type
|
||||
|
||||
@ -89,7 +90,7 @@ class ComponentTokenizer {
|
||||
tokens << new ComponentToken(Type.GROOVY_IDENTIFIER, s.substring(1)) // skip opening $
|
||||
}
|
||||
//noinspection GroovyAssignabilityCheck // for some reason IntelliJ is confused by this
|
||||
on DollarGroovyParser::parse exec { String s ->
|
||||
on DollarScriptletParser::parse exec { String s ->
|
||||
tokens << new ComponentToken(Type.GROOVY, s.substring(2, s.length() - 1))
|
||||
}
|
||||
on percent shiftTo State.EXPRESSION_SCRIPTLET_GROOVY exec {
|
@ -1,6 +1,6 @@
|
||||
package com.jessebrault.gcp.component.node
|
||||
|
||||
class BooleanValue extends Node {
|
||||
class BooleanValue extends ComponentNode {
|
||||
boolean value
|
||||
|
||||
@Override
|
@ -0,0 +1,5 @@
|
||||
package com.jessebrault.gcp.component.node
|
||||
|
||||
abstract class ComponentNode {
|
||||
List<ComponentNode> children = []
|
||||
}
|
@ -1,12 +1,12 @@
|
||||
package com.jessebrault.gcp.component.node
|
||||
|
||||
abstract class NodeVisitor {
|
||||
abstract class ComponentNodeVisitor {
|
||||
|
||||
void visit(Node node) {
|
||||
void visit(ComponentNode node) {
|
||||
this.visitChildren(node)
|
||||
}
|
||||
|
||||
void visitChildren(Node node) {
|
||||
void visitChildren(ComponentNode node) {
|
||||
node.children.each {
|
||||
this.visit(it)
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
package com.jessebrault.gcp.component.node
|
||||
|
||||
class ComponentNode extends Node {
|
||||
class ComponentRoot extends ComponentNode {
|
||||
|
||||
String identifier
|
||||
String body
|
@ -1,6 +1,6 @@
|
||||
package com.jessebrault.gcp.component.node
|
||||
|
||||
class DollarReferenceValue extends Node {
|
||||
class DollarReferenceValue extends ComponentNode {
|
||||
|
||||
String reference
|
||||
|
@ -1,6 +1,6 @@
|
||||
package com.jessebrault.gcp.component.node
|
||||
|
||||
class DollarScriptletValue extends Node {
|
||||
class DollarScriptletValue extends ComponentNode {
|
||||
|
||||
String scriptlet
|
||||
|
@ -1,6 +1,6 @@
|
||||
package com.jessebrault.gcp.component.node
|
||||
|
||||
class ExpressionScriptletValue extends Node {
|
||||
class ExpressionScriptletValue extends ComponentNode {
|
||||
|
||||
String scriptlet
|
||||
|
@ -1,6 +1,6 @@
|
||||
package com.jessebrault.gcp.component.node
|
||||
|
||||
class GStringValue extends Node {
|
||||
class GStringValue extends ComponentNode {
|
||||
|
||||
String gString
|
||||
|
@ -1,6 +1,6 @@
|
||||
package com.jessebrault.gcp.component.node
|
||||
|
||||
class KeyAndValue extends Node {
|
||||
class KeyAndValue extends ComponentNode {
|
||||
|
||||
String key
|
||||
|
@ -1,6 +1,6 @@
|
||||
package com.jessebrault.gcp.component.node
|
||||
|
||||
class KeysAndValues extends Node {
|
||||
class KeysAndValues extends ComponentNode {
|
||||
|
||||
@Override
|
||||
String toString() {
|
@ -1,6 +1,6 @@
|
||||
package com.jessebrault.gcp.component.node
|
||||
|
||||
class ScriptletValue extends Node {
|
||||
class ScriptletValue extends ComponentNode {
|
||||
|
||||
String scriptlet
|
||||
|
@ -1,6 +1,6 @@
|
||||
package com.jessebrault.gcp.component.node
|
||||
|
||||
class StringValue extends Node {
|
||||
class StringValue extends ComponentNode {
|
||||
|
||||
String string
|
||||
|
@ -0,0 +1,22 @@
|
||||
package com.jessebrault.gcp.groovy
|
||||
|
||||
class BlockScriptletParser {
|
||||
|
||||
static class Result {
|
||||
String fullMatch
|
||||
String scriptlet
|
||||
}
|
||||
|
||||
static String parse(String input) {
|
||||
|
||||
}
|
||||
|
||||
static Result parseResult(String input) {
|
||||
def match = parse(input)
|
||||
match != null ? new Result().tap {
|
||||
fullMatch = match
|
||||
scriptlet = fullMatch.substring(2, fullMatch.length() - 2)
|
||||
} : null
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package com.jessebrault.gcp.groovy
|
||||
|
||||
class DollarReferenceParser {
|
||||
|
||||
static class Result {
|
||||
String fullMatch
|
||||
String reference
|
||||
}
|
||||
|
||||
static Result parse(String input) {
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,11 +1,16 @@
|
||||
package com.jessebrault.gcp.component
|
||||
package com.jessebrault.gcp.groovy
|
||||
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
class DollarGroovyParser {
|
||||
class DollarScriptletParser {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(DollarGroovyParser)
|
||||
private static Logger logger = LoggerFactory.getLogger(DollarScriptletParser)
|
||||
|
||||
static class Result {
|
||||
String fullMatch
|
||||
String scriptlet
|
||||
}
|
||||
|
||||
private enum State {
|
||||
NO_STRING, G_STRING, SINGLE_QUOTE_STRING
|
||||
@ -126,4 +131,16 @@ class DollarGroovyParser {
|
||||
acc.toString()
|
||||
}
|
||||
|
||||
static Result parseResult(String input) {
|
||||
def match = parse(input)
|
||||
if (match) {
|
||||
new Result().tap {
|
||||
fullMatch = match
|
||||
scriptlet = fullMatch.substring(2, fullMatch.length() - 1)
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package com.jessebrault.gcp.groovy
|
||||
|
||||
class ExpressionScriptletParser {
|
||||
|
||||
static class Result {
|
||||
String fullMatch
|
||||
String scriptlet
|
||||
}
|
||||
|
||||
static String parse(String input) {
|
||||
|
||||
}
|
||||
|
||||
static Result parseResult(String input) {
|
||||
def match = parse(input)
|
||||
match != null ? new Result().tap {
|
||||
fullMatch = match
|
||||
scriptlet = fullMatch.substring(3, fullMatch.length() - 2)
|
||||
} : null
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package com.jessebrault.gcp.node
|
||||
|
||||
class BlockScriptlet extends GcpNode {
|
||||
String scriptlet
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package com.jessebrault.gcp.node
|
||||
|
||||
class ComponentInstance extends GcpNode {
|
||||
String opening
|
||||
String closing
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
package com.jessebrault.gcp.node
|
||||
|
||||
class Document extends GcpNode {}
|
@ -0,0 +1,5 @@
|
||||
package com.jessebrault.gcp.node
|
||||
|
||||
class DollarReference extends GcpNode {
|
||||
String reference
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package com.jessebrault.gcp.node
|
||||
|
||||
class DollarScriptlet extends GcpNode {
|
||||
String scriptlet
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package com.jessebrault.gcp.node
|
||||
|
||||
class ExpressionScriptlet extends GcpNode {
|
||||
String scriptlet
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package com.jessebrault.gcp.node
|
||||
|
||||
abstract class GcpNode {
|
||||
List<GcpNode> children = []
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package com.jessebrault.gcp.node
|
||||
|
||||
abstract class GcpNodeVisitor {
|
||||
|
||||
void visit(GcpNode node) {
|
||||
this.visitChildren(node)
|
||||
}
|
||||
|
||||
void visitChildren(GcpNode node) {
|
||||
node.children.each(this.&visit)
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package com.jessebrault.gcp.node
|
||||
|
||||
class Html extends GcpNode {
|
||||
String text
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package com.jessebrault.gcp
|
||||
package com.jessebrault.gcp.util
|
||||
|
||||
import groovy.transform.PackageScope
|
||||
import groovy.transform.TupleConstructor
|
@ -1,5 +1,6 @@
|
||||
package com.jessebrault.gcp.component
|
||||
package com.jessebrault.gcp.util
|
||||
|
||||
import com.jessebrault.gcp.component.ComponentToken
|
||||
|
||||
import java.lang.annotation.Retention
|
||||
import java.lang.annotation.RetentionPolicy
|
@ -0,0 +1 @@
|
||||
com.jessebrault.gcp.GcpToScriptConverterImpl
|
@ -9,6 +9,7 @@ import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
import static com.jessebrault.gcp.component.ComponentToken.Type.*
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue
|
||||
|
||||
@ -16,7 +17,7 @@ class ComponentParserTests {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ComponentParserTests)
|
||||
|
||||
private static class NodeSpec<T extends Node> {
|
||||
private static class NodeSpec<T extends ComponentNode> {
|
||||
|
||||
Class<T> nodeClass
|
||||
Closure<Void> tests
|
||||
@ -31,7 +32,7 @@ class ComponentParserTests {
|
||||
this.tests = tests
|
||||
}
|
||||
|
||||
void test(Node actual) {
|
||||
void test(ComponentNode actual) {
|
||||
logger.debug('actual: {}', actual)
|
||||
assertTrue(nodeClass.isAssignableFrom(actual.class))
|
||||
if (this.tests != null) {
|
||||
@ -60,9 +61,9 @@ class ComponentParserTests {
|
||||
|
||||
private static class NodeTester {
|
||||
|
||||
List<NodeSpec<? extends Node>> childSpecs = []
|
||||
List<NodeSpec<? extends ComponentNode>> childSpecs = []
|
||||
|
||||
def <T extends Node> void expect(
|
||||
def <T extends ComponentNode> void expect(
|
||||
Class<T> childNodeClass,
|
||||
@DelegatesTo(value = NodeTester, strategy = Closure.DELEGATE_FIRST)
|
||||
@ClosureParams(FirstParam.FirstGenericType)
|
||||
@ -88,7 +89,7 @@ class ComponentParserTests {
|
||||
def componentNode = this.parser.parse(tokens)
|
||||
logger.debug('componentNode: {}', componentNode)
|
||||
|
||||
def componentSpec = new NodeSpec(ComponentNode, tests)
|
||||
def componentSpec = new NodeSpec(ComponentRoot, tests)
|
||||
logger.debug('nodeSpec: {}', componentSpec)
|
||||
componentSpec.test(componentNode)
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package com.jessebrault.gcp.component
|
||||
|
||||
import com.jessebrault.gcp.component.node.ComponentRoot
|
||||
import com.jessebrault.gcp.component.node.GStringValue
|
||||
import com.jessebrault.gcp.component.node.KeyAndValue
|
||||
import com.jessebrault.gcp.component.node.KeysAndValues
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals
|
||||
|
||||
class ComponentToClosureVisitorTests {
|
||||
|
||||
@Test
|
||||
void withEmptyKeysAndValues() {
|
||||
def cn = new ComponentRoot().tap {
|
||||
it.children << new KeysAndValues()
|
||||
}
|
||||
def v = new ComponentToClosureVisitor()
|
||||
v.visit(cn)
|
||||
assertEquals('{ attr { }; };', v.result)
|
||||
}
|
||||
|
||||
@Test
|
||||
void withGStringKeyAndValue() {
|
||||
def cn = new ComponentRoot().tap {
|
||||
it.children << new KeysAndValues().tap {
|
||||
it.children << new KeyAndValue().tap {
|
||||
key = 'greeting'
|
||||
it.children << new GStringValue().tap {
|
||||
gString = 'Hello, ${ frontMatter.person }!'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
def v = new ComponentToClosureVisitor()
|
||||
v.visit(cn)
|
||||
assertEquals('{ attr { greeting = "Hello, ${ frontMatter.person }!"; }; };', v.result)
|
||||
}
|
||||
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
package com.jessebrault.gcp.component
|
||||
package com.jessebrault.gcp.groovy
|
||||
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
import static com.jessebrault.gcp.component.DollarGroovyParser.parse
|
||||
import static com.jessebrault.gcp.groovy.DollarScriptletParser.parse
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals
|
||||
|
||||
class DollarGroovyParserTests {
|
||||
class DollarScriptletParserTests {
|
||||
|
||||
@Test
|
||||
void empty() {
|
@ -1,23 +0,0 @@
|
||||
package com.jessebrault.gcp
|
||||
|
||||
import groovy.transform.PackageScope
|
||||
|
||||
@PackageScope
|
||||
enum FsmState {
|
||||
HTML,
|
||||
SCRIPTLET,
|
||||
EXPRESSION_SCRIPTLET,
|
||||
DOLLAR,
|
||||
|
||||
COMPONENT,
|
||||
|
||||
COMPONENT_IDENTIFIER,
|
||||
|
||||
COMPONENT_ATTR_KEY,
|
||||
COMPONENT_ATTR_VALUE_OPEN,
|
||||
|
||||
COMPONENT_ATTR_VALUE_STRING,
|
||||
COMPONENT_ATTR_VALUE_STRING_CLOSE,
|
||||
|
||||
COMPONENT_CLOSE
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
package com.jessebrault.gcp.component.node
|
||||
|
||||
abstract class Node {
|
||||
List<Node> children = []
|
||||
}
|
@ -1,2 +1,2 @@
|
||||
rootProject.name = 'ssg'
|
||||
include 'cli', 'gcp', 'lib'
|
||||
include 'cli', 'gcp-api', 'gcp-impl', 'lib'
|
Loading…
Reference in New Issue
Block a user