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]() +29: ClosingComponentOpen[1,70]() +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]() + closingComponent[1,70..1,74] + ClosingComponentOpen[1,70]() + 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

' + ) + } + +}