Made DollarGroovyParser.
This commit is contained in:
parent
a4bfea92ef
commit
82bb229eb8
@ -94,6 +94,9 @@ class ComponentTokenizer {
|
||||
on dollarOpen shiftTo State.DOLLAR_GROOVY exec { String s ->
|
||||
tokens << new ComponentToken(Type.DOLLAR, s[0])
|
||||
tokens << new ComponentToken(Type.CURLY_OPEN, s[1])
|
||||
}
|
||||
on DollarGroovyParser.&parse exec {
|
||||
|
||||
}
|
||||
on percent shiftTo State.EXPRESSION_SCRIPTLET_GROOVY exec {
|
||||
tokens << new ComponentToken(Type.PERCENT, it)
|
||||
@ -131,27 +134,7 @@ class ComponentTokenizer {
|
||||
}
|
||||
|
||||
whileIn(State.DOLLAR_GROOVY) {
|
||||
on { String input ->
|
||||
def b = new StringBuilder()
|
||||
def openCurlyCount = 1
|
||||
def iterator = input.iterator() as Iterator<String>
|
||||
while (iterator.hasNext()) {
|
||||
def c0 = iterator.next()
|
||||
if (c0 == '{') {
|
||||
b << c0
|
||||
openCurlyCount++
|
||||
} else if (c0 == '}') {
|
||||
b << c0
|
||||
openCurlyCount--
|
||||
if (openCurlyCount == 0) {
|
||||
break
|
||||
}
|
||||
} else {
|
||||
b << c0
|
||||
}
|
||||
}
|
||||
b.toString()
|
||||
} shiftTo State.KEYS_AND_VALUES exec { String s ->
|
||||
on DollarGroovyParser.&parse shiftTo State.KEYS_AND_VALUES exec { String s ->
|
||||
tokens << new ComponentToken(Type.GROOVY, s.substring(0, s.length() - 1))
|
||||
tokens << new ComponentToken(Type.CURLY_CLOSE, '}')
|
||||
}
|
||||
|
@ -0,0 +1,129 @@
|
||||
package com.jessebrault.ssg.template.gspe.component
|
||||
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
class DollarGroovyParser {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(DollarGroovyParser)
|
||||
|
||||
private enum State {
|
||||
NO_STRING, G_STRING, SINGLE_QUOTE_STRING
|
||||
}
|
||||
|
||||
private static class Counter {
|
||||
|
||||
int count = 0
|
||||
|
||||
void increment() {
|
||||
this.count++
|
||||
}
|
||||
|
||||
void decrement() {
|
||||
this.count--
|
||||
}
|
||||
|
||||
void next() {
|
||||
this.increment()
|
||||
}
|
||||
|
||||
void previous() {
|
||||
this.decrement()
|
||||
}
|
||||
|
||||
boolean isZero() {
|
||||
this.count == 0
|
||||
}
|
||||
|
||||
@Override
|
||||
String toString() {
|
||||
"Counter(${ this.count })"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static String parse(String input) {
|
||||
def acc = new StringBuilder()
|
||||
def stateStack = new LinkedList<State>([State.NO_STRING])
|
||||
def counterStack = new LinkedList<Counter>([new Counter()])
|
||||
def iterator = input.iterator() as Iterator<String>
|
||||
|
||||
if (!iterator.hasNext() || iterator.next() != '$') {
|
||||
return null
|
||||
} else {
|
||||
acc << '$'
|
||||
counterStack.peek()++
|
||||
}
|
||||
|
||||
if (!iterator.hasNext() || iterator.next() != '{') {
|
||||
return null
|
||||
} else {
|
||||
acc << '{'
|
||||
}
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
assert counterStack.size() > 0
|
||||
assert stateStack.size() > 0
|
||||
|
||||
def c0 = iterator.next()
|
||||
acc << c0
|
||||
logger.debug('----')
|
||||
logger.debug('c0: {}', c0)
|
||||
logger.debug('acc: {}', acc)
|
||||
if (stateStack.peek() == State.NO_STRING) {
|
||||
if (c0 == '{') {
|
||||
counterStack.peek()++
|
||||
} else if (c0 == '}') {
|
||||
counterStack.peek()--
|
||||
if (counterStack.peek().isZero()) {
|
||||
if (counterStack.size() == 1) {
|
||||
logger.debug('single Counter is zero; breaking while loop')
|
||||
break // escape while loop
|
||||
} else {
|
||||
logger.debug('counterStack.size() is greater than zero and top Counter is zero; ' +
|
||||
'popping state and counter stacks')
|
||||
counterStack.pop()
|
||||
stateStack.pop()
|
||||
}
|
||||
}
|
||||
} else if (c0 == '"') {
|
||||
stateStack.push(State.G_STRING)
|
||||
} else if (c0 == "'") {
|
||||
stateStack.push(State.SINGLE_QUOTE_STRING)
|
||||
}
|
||||
} else if (stateStack.peek() == State.G_STRING) {
|
||||
if (c0 == '\\') {
|
||||
if (iterator.hasNext()) {
|
||||
acc << iterator.next()
|
||||
} else {
|
||||
throw new IllegalArgumentException('Ill-formed dollar groovy')
|
||||
}
|
||||
} else if (c0 == '$') {
|
||||
if (iterator.hasNext()) {
|
||||
def c1 = iterator.next()
|
||||
acc << c1
|
||||
if (c1 == '{') {
|
||||
stateStack.push(State.NO_STRING)
|
||||
counterStack.push(new Counter())
|
||||
counterStack.peek()++
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException('Ill-formed dollar groovy')
|
||||
}
|
||||
} else if (c0 == '"') {
|
||||
logger.debug('popping G_STRING state')
|
||||
stateStack.pop()
|
||||
}
|
||||
} else if (stateStack.peek() == State.SINGLE_QUOTE_STRING) {
|
||||
if (c0 == "'") {
|
||||
logger.debug('popping SINGLE_QUOTE_STRING state')
|
||||
stateStack.pop()
|
||||
}
|
||||
}
|
||||
logger.debug('stateStack: {}', stateStack)
|
||||
logger.debug('counterStack: {}', counterStack)
|
||||
}
|
||||
acc.toString()
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
package com.jessebrault.ssg.template.gspe.component
|
||||
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
import static com.jessebrault.ssg.template.gspe.component.DollarGroovyParser.parse
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals
|
||||
|
||||
class DollarGroovyParserTests {
|
||||
|
||||
@Test
|
||||
void empty() {
|
||||
assertEquals('${}', parse('${}'))
|
||||
}
|
||||
|
||||
@Test
|
||||
void simple() {
|
||||
assertEquals('${ 1 + 2 }', parse('${ 1 + 2 }'))
|
||||
}
|
||||
|
||||
@Test
|
||||
void nestedString() {
|
||||
assertEquals('${ "myString" }', parse('${ "myString" }'))
|
||||
}
|
||||
|
||||
@Test
|
||||
void nestedCurlyBraces() {
|
||||
assertEquals(
|
||||
'${ [1, 2, 3].collect { it + 1 }.size() }',
|
||||
parse('${ [1, 2, 3].collect { it + 1 }.size() }')
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
void nestedSingleQuoteString() {
|
||||
assertEquals(
|
||||
'${ \'abc\' }',
|
||||
parse('${ \'abc\' }')
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
void nestedGString() {
|
||||
assertEquals(
|
||||
'${ "abc" }',
|
||||
parse('${ "abc" }')
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
void nestedGStringWithClosure() {
|
||||
assertEquals(
|
||||
'${ "abc${ it }" }',
|
||||
parse('${ "abc${ it }" }')
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
void takesOnlyAsNeeded() {
|
||||
assertEquals(
|
||||
'${ 1 + 2 }',
|
||||
parse('${ 1 + 2 } someOther=${ 3 + 4 }')
|
||||
)
|
||||
}
|
||||
|
||||
}
|
6
template/src/test/resources/test.gcp
Normal file
6
template/src/test/resources/test.gcp
Normal file
@ -0,0 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<Head val=${ frontMatter.test } val=$test val="Hello!" val='Hello!'>
|
||||
<meta val=${ binding.someVar } />
|
||||
</Head>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user