Skip to content

Commit

Permalink
#210 Implemented IfNode.
Browse files Browse the repository at this point in the history
Pending: fix TemplateParser.parse() methods and implement ListNode.
  • Loading branch information
ericbn committed Mar 7, 2015
1 parent 4aedede commit cae6033
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
*
* @author Eric Nielsen
*/
class IfNode extends ParentNode {
class IfNode extends RootNode {
private final Exp exp;

IfNode(Exp exp) {
Expand All @@ -17,6 +17,8 @@ class IfNode extends ParentNode {

@Override
void process(Map values, Appendable appendable) throws IOException {
appendable.append(String.valueOf(exp.resultWith(values)));
if (exp.resultWith(values)) {
super.process(values, appendable);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,11 @@
* BNF rules:
*
* <pre>
* WHITESPACE = Character.isWhitespace()+
* IDENTIFIER = Character.isJavaIdentifierStart() Character.isJavaIdentifierPart()*
* IDENTIFIER_OR_FUNCTION = IDENTIFIER "()"?
* CHAINED_IDENTIFIERS = IDENTIFIER ('.' IDENTIFIER_OR_FUNCTION)*
* LOWER_ALPHA = [a-z]+
* VAR = "%{" CHAINED_IDENTIFIER (WHITESPACE LOWER_ALPHA)? '}'
* VAR = "%{" CHAINED_IDENTIFIER (LOWER_ALPHA)? '}'
*
* CONST = .*
*
Expand All @@ -25,16 +24,15 @@
* FACTOR = COMPARISON | ('!' FACTOR) | ('(' EXP ')')
*
*
* TAG_START = "&lt;#"
* TAG_END = "&lt;/#"
* TAG_START = '&lt' '#'
* TAG_END = '&lt' '/' '#' LOWER_ALPHA '>'
* IF_TAG = TAG_START "if" '(' EXPRESSION ')' '&gt;' LIST_TAG_BODY TAG_END "if" '&gt;'
* </pre>
*
* @author Eric Nielsen
*/
public final class TemplateParser {

private ParentNode parent = new RootNode();
private final String source;
private int index = -1;
private int currentCodePoint;
Expand All @@ -54,6 +52,10 @@ private boolean next() {
}
}

private boolean accept(int codePoint) {
return currentCodePoint == codePoint && next();
}

private boolean __whitespace() { return Character.isWhitespace(currentCodePoint); }

@SuppressWarnings("empty-statement")
Expand All @@ -73,7 +75,7 @@ private boolean _identifier() {
private boolean _identifierOrFunction() {
if (!_identifier()) { return false; }
if (currentCodePoint == '(') {
if (!(next() && currentCodePoint == ')' && next())) { return false; }
if (!(next() && accept(')'))) { return false; }
}
return true;
}
Expand Down Expand Up @@ -104,37 +106,40 @@ private String _lowerAlpha() {
}

private VarNode _var() {
if (!(currentCodePoint == '%' && next() && currentCodePoint == '{' && next())) { return null; }
if (!(accept('%') && accept('{'))) { return null; }
_whitespace(); // optional
Identifiers ident = _chainedIdentifiers();
if (ident == null) { return null; }
BuiltIn builtIn = null;
if (_whitespace()) {
String builtInName = _lowerAlpha();
if (builtInName == null) { return null; }
builtIn = TemplatorConfig.instance().getBuiltIn(builtInName);
if (builtInName != null) {
builtIn = TemplatorConfig.instance().getBuiltIn(builtInName);
_whitespace(); // optional
}
}
if (currentCodePoint != '}') { return null; }
next();
return new VarNode(ident, builtIn);
}

private Op _comparisonOp() {
if (currentCodePoint == '=' && next()) {
return currentCodePoint == '=' && next() ? Op.eq : null;
} else if (currentCodePoint == '>' && next()) {
if (accept('=')) {
return accept('=') ? Op.eq : null;
} else if (accept('>')) {
if (currentCodePoint == '=') {
return next() ? Op.gte : null;
} else {
return Op.gt;
}
} else if (currentCodePoint == '<' && next()) {
} else if (accept('<')) {
if (currentCodePoint == '=') {
return next() ? Op.lte : null;
} else {
return Op.lt;
}
} else {
return currentCodePoint == '!' && next() && currentCodePoint == '=' && next() ? Op.neq : null;
return accept('!') && accept('=') ? Op.neq : null;
}
}

Expand All @@ -155,7 +160,7 @@ private Exp _exp() {
if (leftExp == null) { return null; }
_whitespace(); // optional
if (currentCodePoint == '|') {
if (!(next() && currentCodePoint == '|' && next())) { return null; }
if (!(next() && accept('|'))) { return null; }
_whitespace(); // optional
Exp rightExp = _term();
if (rightExp == null) { return null; }
Expand All @@ -169,7 +174,7 @@ private Exp _term() {
if (leftExp == null) { return null; }
_whitespace(); // optional
if (currentCodePoint == '&') {
if (!(next() && currentCodePoint == '&' && next())) { return null; }
if (!(next() && accept('&'))) { return null; }
_whitespace(); // optional
Exp rightExp = _factor();
if (rightExp == null) { return null; }
Expand All @@ -186,7 +191,7 @@ private Exp _factor() {
Exp exp = _exp();
if (exp == null) { return null; }
_whitespace(); // optional
if (!(currentCodePoint == ')' && next())) { return null; }
if (!accept(')')) { return null; }
return exp;
case '!':
if (!next()) { return null; }
Expand All @@ -198,57 +203,110 @@ private Exp _factor() {
}

private boolean _tagStart() {
return currentCodePoint == '<' && next() && currentCodePoint == '#' && next();
if (!accept('<')) { return false; }
_whitespace(); // optional
if (!accept('#')) { return false; }
return true;
}

private boolean _tagEnd() {
return currentCodePoint == '<' && next() && currentCodePoint == '/' && next() && currentCodePoint == '#' && next();
private String _tagEnd() {
if (!accept('<')) { return null; }
_whitespace(); // optional
if (!accept('/')) { return null; }
_whitespace(); // optional
if (!accept('#')) { return null; }
String tagName = _lowerAlpha();
if (tagName == null) { return null; }
_whitespace(); // optional
if (currentCodePoint != '>') { return null; }
next();
return tagName;
}

private Node _ifTag() {
private Node _ifTagStart() {
if (!_tagStart()) { return null; }
String tagName = _lowerAlpha();
if (!"if".equals(tagName)) { return null; }
_whitespace(); // optional
if (!(currentCodePoint == '(' && next())) { return null; }
if (!accept('(')) { return null; }
_whitespace(); // optional
Exp exp = _exp();
if (exp == null) { return null; }
_whitespace(); // optional
if (!(currentCodePoint == ')' && next())) { return null; }
if (!accept(')')) { return null; }
_whitespace(); // optional
if (currentCodePoint != '>') { return null; }
next();
if (!accept('>')) { return null; }
return new IfNode(exp);
}

Node parse() {
ParentNode root = new RootNode();
//TODO: refactor this
if (next()) {
for (;;) {
int startIndex = index;
Node node = _ifTagStart();
if (node != null) {
addConstEndingAt(startIndex, root);
constStartIndex = index;
if (!"if".equals(parse((ParentNode) node))) {
node = null;
}
} else {
node = _var();
}
if (node == null) {
if (startIndex == index) {
if (!next()) { break; }
}
} else {
addConstEndingAt(startIndex, root);
constStartIndex = index;
root.children.add(node);
}
}
}
addConstEndingAt(source.length(), root);
return root;
}

private String parse(ParentNode root) {
//TODO: refactor this
constStartIndex = index;
if (next()) {
for (;;) {
int startIndex = index;
Node node = _ifTag();
String tagEndName = _tagEnd();
if (tagEndName != null) {
addConstEndingAt(startIndex, root);
constStartIndex = index;
return tagEndName;
}
Node node = _ifTagStart();
if (node == null) {
node = _var();
} else {
if (!"if".equals(parse((ParentNode) node))) {
node = null;
}
}
if (node == null) {
if (startIndex == index) {
if (!next()) { break; }
}
} else {
addConstEndingAt(startIndex);
addConstEndingAt(startIndex, root);
constStartIndex = index;
parent.children.add(node);
root.children.add(node);
}
}
}
addConstEndingAt(source.length());
return parent;
return null;
}

private void addConstEndingAt(int endIndex) {
private void addConstEndingAt(int endIndex, ParentNode root) {
if (endIndex - 1 > constStartIndex) {
parent.children.add(new ConstNode(source.substring(constStartIndex, endIndex)));
root.children.add(new ConstNode(source.substring(constStartIndex, endIndex)));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,26 +48,27 @@ public void shouldParseBuiltIn() throws Exception {

@Test
public void shouldParseExpressionWithEq() throws Exception {
ParentNode node = (ParentNode) new TemplateParser("<#if(left==right)>").parse();
the(node.children.size()).shouldBeEqual(1);
the(process(node, map("left", "help!", "right", "help!"))).shouldBeEqual("true");
the(process(node, map("left", "help!", "right", "help?"))).shouldBeEqual("false");
ParentNode node = (ParentNode) new TemplateParser("<b><<#if(left==right)>tada</#if>></b>").parse();
the(node.children.size()).shouldBeEqual(3);
the(process(node, map("left", "help!", "right", "help!"))).shouldBeEqual("<b><tada></b>");
the(process(node, map("left", "help!", "right", "help?"))).shouldBeEqual("<b><></b>");
}

@Test
public void shouldParseExpressionWithNotNeq() throws Exception {
ParentNode node = (ParentNode) new TemplateParser("<#if(!left!=right)>").parse();
ParentNode node = (ParentNode) new TemplateParser("<#if(!left!=right)>tada</#if>").parse();
the(node.children.size()).shouldBeEqual(1);
the(process(node, map("left", 0, "right", 0))).shouldBeEqual("true");
the(process(node, map("left", 0, "right", 1))).shouldBeEqual("false");
the(process(node, map("left", 0, "right", 0))).shouldBeEqual("tada");
the(process(node, map("left", 0, "right", 1))).shouldBeEqual("");
}

@Test
public void shouldParseExpressionWithAndParensOr() throws Exception {
ParentNode node = (ParentNode) new TemplateParser("<#if (first > second && (first > third || third <= second))>").parse();
ParentNode node = (ParentNode) new TemplateParser(
"<#if (first > second && (first > third || third <= second))><b>%{first}</b></#if>").parse();
the(node.children.size()).shouldBeEqual(1);
the(process(node, map("first", "foo", "second", "bar", "third", "baa"))).shouldBeEqual("true");
the(process(node, map("first", 1, "second", 0, "third", 0))).shouldBeEqual("true");
the(process(node, map("first", 1, "second", 0, "third", 1))).shouldBeEqual("false");
the(process(node, map("first", "foo", "second", "bar", "third", "baa"))).shouldBeEqual("<b>foo</b>");
the(process(node, map("first", 1, "second", 0, "third", 0))).shouldBeEqual("<b>1</b>");
the(process(node, map("first", 1, "second", 0, "third", 1))).shouldBeEqual("");
}
}

0 comments on commit cae6033

Please sign in to comment.