From 131fbaf1555a83195392f58164e16c4ad6669cc8 Mon Sep 17 00:00:00 2001
From: JesseBrault0709 <62299747+JesseBrault0709@users.noreply.github.com>
Date: Fri, 31 May 2024 16:50:30 +0200
Subject: [PATCH] 1+ nested components in component attr values working; Each
tests.
---
.../main/antlr/WebViewComponentsLexerBase.g4 | 19 +++--
.../antlr/AbstractWebViewComponentsLexer.java | 65 ++++++++++-------
.../component/web/ast/DefaultNodeFactory.java | 1 +
.../src/test/lexer/componentValueNestedLi.wvc | 1 +
.../componentValueNestedLi_tokens.txt | 36 ++++++++++
.../test/parser/componentValueNestedLi.wvc | 1 +
.../componentValueNestedLi_parseTree.txt | 72 +++++++++++++++++++
.../web/DefaultWebViewComponentScope.groovy | 2 +
.../view/component/web/lib/EachTests.groovy | 20 ++++++
9 files changed, 186 insertions(+), 31 deletions(-)
create mode 100644 web-view-components-compiler/src/test/lexer/componentValueNestedLi.wvc
create mode 100644 web-view-components-compiler/src/test/lexer/tokens-files/componentValueNestedLi_tokens.txt
create mode 100644 web-view-components-compiler/src/test/parser/componentValueNestedLi.wvc
create mode 100644 web-view-components-compiler/src/test/parser/parse-tree-files/componentValueNestedLi_parseTree.txt
create mode 100644 web-view-components/src/test/groovy/groowt/view/component/web/lib/EachTests.groovy
diff --git a/web-view-components-compiler/src/main/antlr/WebViewComponentsLexerBase.g4 b/web-view-components-compiler/src/main/antlr/WebViewComponentsLexerBase.g4
index 5cf9db7..fff06ea 100644
--- a/web-view-components-compiler/src/main/antlr/WebViewComponentsLexerBase.g4
+++ b/web-view-components-compiler/src/main/antlr/WebViewComponentsLexerBase.g4
@@ -110,7 +110,8 @@ channels {
this.pushMode(GROOVY_CODE);
}
- private boolean inAttrComponent() {
+ @Override
+ protected boolean inAttrComponent() {
return this.peekMode(1) == COMPONENT_ATTR_VALUE;
}
@@ -269,7 +270,7 @@ mode IN_TAG;
ComponentClose
: GT
{
- if (this.inAttrComponent() && this.attrComponentFinished()) {
+ if (this.inAttrComponent() && this.isAttrComponentFinished()) {
this.popMode();
this.popMode();
} else {
@@ -283,7 +284,7 @@ ComponentSelfClose
{
if (this.inAttrComponent()) {
this.exitAttrComponent();
- if (this.attrComponentFinished()) {
+ if (this.isAttrComponentFinished()) {
this.popMode();
this.popMode();
}
@@ -338,7 +339,7 @@ ClosureAttrValueStart
ComponentAttrValueStart
: LEFT_CURLY InTagNlws? { this.isNext('<') }?
{
- this.enterAttrComponent();
+ this.pushAttrComponent();
this.pushMode(COMPONENT_ATTR_VALUE);
}
;
@@ -359,7 +360,10 @@ TagError
mode COMPONENT_ATTR_VALUE;
AttrComponentOpen
- : LT { !isAnyOf(this.getNextChar(), '/', '>') }? -> type(ComponentOpen), pushMode(TAG_START)
+ : LT { !isAnyOf(this.getNextChar(), '/', '>') }?
+ {
+ this.enterAttrComponent();
+ } -> type(ComponentOpen), pushMode(TAG_START)
;
AttrClosingComponentOpen
@@ -370,7 +374,10 @@ AttrClosingComponentOpen
;
AttrFragmentOpen
- : LT GT -> type(FragmentOpen)
+ : LT GT
+ {
+ this.enterAttrComponent();
+ } -> type(FragmentOpen)
;
AttrFragmentClose
diff --git a/web-view-components-compiler/src/main/java/groowt/view/component/web/antlr/AbstractWebViewComponentsLexer.java b/web-view-components-compiler/src/main/java/groowt/view/component/web/antlr/AbstractWebViewComponentsLexer.java
index 62b795d..45125ea 100644
--- a/web-view-components-compiler/src/main/java/groowt/view/component/web/antlr/AbstractWebViewComponentsLexer.java
+++ b/web-view-components-compiler/src/main/java/groowt/view/component/web/antlr/AbstractWebViewComponentsLexer.java
@@ -14,7 +14,8 @@ import org.slf4j.LoggerFactory;
import java.util.Arrays;
import java.util.Deque;
import java.util.LinkedList;
-import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.NoSuchElementException;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -59,7 +60,7 @@ public abstract class AbstractWebViewComponentsLexer extends Lexer {
private boolean canPreamble = true;
private boolean inPreamble;
private boolean inConstructor;
- private Deque attrComponentFinishedStack = new LinkedList<>();
+ private Deque attrComponentCountStack = new LinkedList<>();
public AbstractWebViewComponentsLexer(CharStream input) {
@@ -119,12 +120,12 @@ public abstract class AbstractWebViewComponentsLexer extends Lexer {
this.inConstructor = inConstructor;
}
- public Deque getAttrComponentFinishedStack() {
- return this.attrComponentFinishedStack;
+ public Deque getAttrComponentCountStack() {
+ return this.attrComponentCountStack;
}
- public void setAttrComponentFinishedStack(Deque attrComponentFinishedStack) {
- this.attrComponentFinishedStack = attrComponentFinishedStack;
+ public void setAttrComponentCountStack(Deque attrComponentCountStack) {
+ this.attrComponentCountStack = attrComponentCountStack;
}
@Override
@@ -134,7 +135,7 @@ public abstract class AbstractWebViewComponentsLexer extends Lexer {
this.canPreamble = true;
this.inPreamble = false;
this.inConstructor = false;
- this.attrComponentFinishedStack = new LinkedList<>();
+ this.attrComponentCountStack = new LinkedList<>();
super.reset();
}
@@ -217,28 +218,42 @@ public abstract class AbstractWebViewComponentsLexer extends Lexer {
this.inConstructor = false;
}
- protected void enterAttrComponent() {
- this.attrComponentFinishedStack.push(new AtomicBoolean());
- }
+ protected abstract boolean inAttrComponent();
- protected void exitAttrComponent() {
- final AtomicBoolean attrComponentFinished = this.attrComponentFinishedStack.peek();
- if (attrComponentFinished == null) {
- throw new WebViewComponentBugError(new IllegalStateException());
- }
- attrComponentFinished.set(true);
- }
-
- protected boolean attrComponentFinished() {
- final AtomicBoolean attrComponentFinished = this.attrComponentFinishedStack.peek();
- if (attrComponentFinished == null) {
- throw new WebViewComponentBugError(new IllegalStateException());
- }
- return attrComponentFinished.get();
+ protected void pushAttrComponent() {
+ this.attrComponentCountStack.push(new AtomicInteger());
}
protected void popAttrComponent() {
- this.attrComponentFinishedStack.pop();
+ try {
+ this.attrComponentCountStack.pop();
+ } catch (NoSuchElementException e) {
+ throw new WebViewComponentBugError("attrComponentCountStack was empty.", e);
+ }
+ }
+
+ protected boolean isAttrComponentFinished() {
+ final AtomicInteger attrComponentFinished = this.attrComponentCountStack.peek();
+ if (attrComponentFinished == null) {
+ throw new WebViewComponentBugError(new IllegalStateException());
+ }
+ return attrComponentFinished.get() == 0;
+ }
+
+ protected void enterAttrComponent() {
+ final AtomicInteger attrComponentCount = this.attrComponentCountStack.peek();
+ if (attrComponentCount == null) {
+ throw new WebViewComponentBugError(new IllegalStateException("attrComponentCountStack is empty."));
+ }
+ attrComponentCount.incrementAndGet();
+ }
+
+ protected void exitAttrComponent() {
+ final AtomicInteger attrComponentCount = this.attrComponentCountStack.peek();
+ if (attrComponentCount == null) {
+ throw new WebViewComponentBugError(new IllegalStateException("attrComponentCountStack is empty."));
+ }
+ attrComponentCount.decrementAndGet();
}
protected String getNextCharsAsString(int numberOfChars) {
diff --git a/web-view-components-compiler/src/main/java/groowt/view/component/web/ast/DefaultNodeFactory.java b/web-view-components-compiler/src/main/java/groowt/view/component/web/ast/DefaultNodeFactory.java
index 197aeed..9192199 100644
--- a/web-view-components-compiler/src/main/java/groowt/view/component/web/ast/DefaultNodeFactory.java
+++ b/web-view-components-compiler/src/main/java/groowt/view/component/web/ast/DefaultNodeFactory.java
@@ -39,6 +39,7 @@ public class DefaultNodeFactory implements NodeFactory {
GStringValueNode.class,
JStringValueNode.class,
ClosureValueNode.class,
+ EmptyClosureValueNode.class,
ComponentValueNode.class,
PlainScriptletNode.class,
EqualsScriptletNode.class,
diff --git a/web-view-components-compiler/src/test/lexer/componentValueNestedLi.wvc b/web-view-components-compiler/src/test/lexer/componentValueNestedLi.wvc
new file mode 100644
index 0000000..f61cce1
--- /dev/null
+++ b/web-view-components-compiler/src/test/lexer/componentValueNestedLi.wvc
@@ -0,0 +1 @@
+$it.name} />
diff --git a/web-view-components-compiler/src/test/lexer/tokens-files/componentValueNestedLi_tokens.txt b/web-view-components-compiler/src/test/lexer/tokens-files/componentValueNestedLi_tokens.txt
new file mode 100644
index 0000000..f9ba6b4
--- /dev/null
+++ b/web-view-components-compiler/src/test/lexer/tokens-files/componentValueNestedLi_tokens.txt
@@ -0,0 +1,36 @@
+0: ComponentOpen[1,1](<)
+1: TypedIdentifier[1,2](Each)
+2: Nlws[1,6]( )
+3: AttributeIdentifier[1,7](items)
+4: Equals[1,12](=)
+5: ClosureAttrValueStart[1,13]({)
+6: GroovyCode[1,14](menuItems)
+7: ClosureAttrValueEnd[1,23](})
+8: Nlws[1,24]( )
+9: AttributeIdentifier[1,25](transform)
+10: Equals[1,34](=)
+11: ComponentAttrValueStart[1,35]({)
+12: ComponentOpen[1,36](<)
+13: StringIdentifier[1,37](li)
+14: ComponentClose[1,39](>)
+15: ComponentOpen[1,40](<)
+16: StringIdentifier[1,41](a)
+17: Nlws[1,42]( )
+18: AttributeIdentifier[1,43](href)
+19: Equals[1,47](=)
+20: ClosureAttrValueStart[1,48]({)
+21: GroovyCode[1,49](it.path)
+22: ClosureAttrValueEnd[1,56](})
+23: ComponentClose[1,57](>)
+24: DollarReferenceStart[1,58]($)
+25: GroovyCode[1,59](it.name)
+26: ClosingComponentOpen[1,66]()
+27: StringIdentifier[1,68](a)
+28: ComponentClose[1,69](>)
+29: ClosingComponentOpen[1,70]()
+30: StringIdentifier[1,72](li)
+31: ComponentClose[1,74](>)
+32: ComponentAttrValueEnd[1,75](})
+33: Nlws[1,76]( )
+34: ComponentSelfClose[1,77](/>)
+35: RawText[1,79](\n)
\ No newline at end of file
diff --git a/web-view-components-compiler/src/test/parser/componentValueNestedLi.wvc b/web-view-components-compiler/src/test/parser/componentValueNestedLi.wvc
new file mode 100644
index 0000000..f61cce1
--- /dev/null
+++ b/web-view-components-compiler/src/test/parser/componentValueNestedLi.wvc
@@ -0,0 +1 @@
+$it.name} />
diff --git a/web-view-components-compiler/src/test/parser/parse-tree-files/componentValueNestedLi_parseTree.txt b/web-view-components-compiler/src/test/parser/parse-tree-files/componentValueNestedLi_parseTree.txt
new file mode 100644
index 0000000..a0f4e94
--- /dev/null
+++ b/web-view-components-compiler/src/test/parser/parse-tree-files/componentValueNestedLi_parseTree.txt
@@ -0,0 +1,72 @@
+compilationUnit[1,1..2,1]
+ body[1,1..1,79]
+ component[1,1..1,79]
+ selfClosingComponent[1,1..1,79]
+ ComponentOpen[1,1](<)
+ componentArgs[1,2..1,75]
+ componentType[1,2..1,6]
+ TypedIdentifier[1,2](Each)
+ attr[1,7..1,23]
+ keyValueAttr[1,7..1,23]
+ AttributeIdentifier[1,7](items)
+ Equals[1,12](=)
+ value[1,13..1,23]
+ closureAttrValue[1,13..1,23]
+ ClosureAttrValueStart[1,13]({)
+ GroovyCode[1,14](menuItems)
+ ClosureAttrValueEnd[1,23](})
+ attr[1,25..1,75]
+ keyValueAttr[1,25..1,75]
+ AttributeIdentifier[1,25](transform)
+ Equals[1,34](=)
+ value[1,35..1,75]
+ componentAttrValue[1,35..1,75]
+ ComponentAttrValueStart[1,35]({)
+ component[1,36..1,74]
+ componentWithChildren[1,36..1,74]
+ openComponent[1,36..1,39]
+ ComponentOpen[1,36](<)
+ componentArgs[1,37..1,39]
+ componentType[1,37..1,39]
+ StringIdentifier[1,37](li)
+ ComponentClose[1,39](>)
+ body[1,40..1,69]
+ component[1,40..1,69]
+ componentWithChildren[1,40..1,69]
+ openComponent[1,40..1,57]
+ ComponentOpen[1,40](<)
+ componentArgs[1,41..1,56]
+ componentType[1,41..1,41]
+ StringIdentifier[1,41](a)
+ attr[1,43..1,56]
+ keyValueAttr[1,43..1,56]
+ AttributeIdentifier[1,43](href)
+ Equals[1,47](=)
+ value[1,48..1,56]
+ closureAttrValue[1,48..1,56]
+ ClosureAttrValueStart[1,48]({)
+ GroovyCode[1,49](it.path)
+ ClosureAttrValueEnd[1,56](})
+ ComponentClose[1,57](>)
+ body[1,58..1,66]
+ bodyText[1,58..1,66]
+ bodyTextGroovyElement[1,58..1,66]
+ dollarReference[1,58..1,66]
+ DollarReferenceStart[1,58]($)
+ GroovyCode[1,59](it.name)
+ closingComponent[1,66..1,69]
+ ClosingComponentOpen[1,66]()
+ componentType[1,68..1,68]
+ StringIdentifier[1,68](a)
+ ComponentClose[1,69](>)
+ closingComponent[1,70..1,74]
+ ClosingComponentOpen[1,70]()
+ componentType[1,72..1,74]
+ StringIdentifier[1,72](li)
+ ComponentClose[1,74](>)
+ ComponentAttrValueEnd[1,75](})
+ ComponentSelfClose[1,77](/>)
+ bodyText[1,79..1,79]
+ text[1,79..1,79]
+ RawText[1,79](\n)
+ EOF[2,1]()
diff --git a/web-view-components/src/main/groovy/groowt/view/component/web/DefaultWebViewComponentScope.groovy b/web-view-components/src/main/groovy/groowt/view/component/web/DefaultWebViewComponentScope.groovy
index e73bc43..7452546 100644
--- a/web-view-components/src/main/groovy/groowt/view/component/web/DefaultWebViewComponentScope.groovy
+++ b/web-view-components/src/main/groovy/groowt/view/component/web/DefaultWebViewComponentScope.groovy
@@ -1,6 +1,7 @@
package groowt.view.component.web
import groowt.view.component.context.DefaultComponentScope
+import groowt.view.component.web.lib.Each
import groowt.view.component.web.lib.Echo
import groowt.view.component.web.lib.IntrinsicHtml
import org.codehaus.groovy.runtime.InvokerHelper
@@ -12,6 +13,7 @@ class DefaultWebViewComponentScope extends DefaultComponentScope implements WebV
static DefaultWebViewComponentScope getDefaultRootScope() {
new DefaultWebViewComponentScope().tap {
addWithAttr(Echo)
+ addWithAttr(Each)
}
}
diff --git a/web-view-components/src/test/groovy/groowt/view/component/web/lib/EachTests.groovy b/web-view-components/src/test/groovy/groowt/view/component/web/lib/EachTests.groovy
new file mode 100644
index 0000000..21bab20
--- /dev/null
+++ b/web-view-components/src/test/groovy/groowt/view/component/web/lib/EachTests.groovy
@@ -0,0 +1,20 @@
+package groowt.view.component.web.lib
+
+import org.junit.jupiter.api.Test
+
+class EachTests extends AbstractWebViewComponentTests {
+
+ @Test
+ void simple() {
+ this.doTest('', '012')
+ }
+
+ @Test
+ void withTransform() {
+ this.doTest(
+ '$it
} />',
+ '0
1
2
'
+ )
+ }
+
+}