Skip to content

Commit

Permalink
#217 Implemented for loop in speclang2
Browse files Browse the repository at this point in the history
  • Loading branch information
ishubin committed May 5, 2015
1 parent 0dea5f3 commit c04266f
Show file tree
Hide file tree
Showing 13 changed files with 349 additions and 12 deletions.
Expand Up @@ -52,7 +52,7 @@ public List<ColorRange> read(StringCharReader reader) {


Range range = expectRange.read(reader); Range range = expectRange.read(reader);


String colorText = reader.readUntilSymbol(',').trim(); String colorText = reader.readSafeUntilSymbol(',').trim();


if (colorText.isEmpty()) { if (colorText.isEmpty()) {
throw new SyntaxException("No color defined"); throw new SyntaxException("No color defined");
Expand Down
Expand Up @@ -40,7 +40,7 @@ public List<Pair<String, String>> read(StringCharReader charReader) {
} }
} }
else { else {
final String value = charReader.readUntilSymbol(',').trim(); final String value = charReader.readSafeUntilSymbol(',').trim();
currentParam.setValue(value); currentParam.setValue(value);
currentParam = null; currentParam = null;
} }
Expand Down
Expand Up @@ -113,7 +113,7 @@ public static Expectation<String> doubleQuotedText() {
public String read(StringCharReader charReader) { public String read(StringCharReader charReader) {
char firstNonWhiteSpaceSymbol = charReader.firstNonWhiteSpaceSymbol(); char firstNonWhiteSpaceSymbol = charReader.firstNonWhiteSpaceSymbol();
if (firstNonWhiteSpaceSymbol == '"') { if (firstNonWhiteSpaceSymbol == '"') {
charReader.readUntilSymbol('"'); charReader.readSafeUntilSymbol('"');
return new ExpectString().read(charReader); return new ExpectString().read(charReader);
} else { } else {
throw new SyntaxException("Expected \" symbol, got: " + firstNonWhiteSpaceSymbol); throw new SyntaxException("Expected \" symbol, got: " + firstNonWhiteSpaceSymbol);
Expand Down
Expand Up @@ -39,7 +39,7 @@ public String parse(String template, String initialValue) {
text.append(mathSymbol); text.append(mathSymbol);
} }
else if (nextCh == '{') { else if (nextCh == '{') {
String expression = reader.readUntilSymbol('}').replace(" ", ""); String expression = reader.readSafeUntilSymbol('}').replace(" ", "");
if (expression.length() < 2) { if (expression.length() < 2) {
throw new SyntaxException("Can't parse expression: " + expression); throw new SyntaxException("Can't parse expression: " + expression);
} }
Expand Down
@@ -0,0 +1,137 @@
/*******************************************************************************
* Copyright 2015 Ivan Shubin http://mindengine.net
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package net.mindengine.galen.speclang2.reader.pagespec;

import net.mindengine.galen.parser.ProcessedStructNode;
import net.mindengine.galen.parser.StructNode;
import net.mindengine.galen.parser.SyntaxException;
import net.mindengine.galen.specs.reader.StringCharReader;
import net.mindengine.galen.suite.reader.Line;

import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Pattern;

import static net.mindengine.galen.suite.reader.Line.UNKNOWN_LINE;

public class ForLoop {

public static final String INDEX_DEFAULT_NAME = "index";
private List<String> sequence;
private String indexName;

public ForLoop(List<String> sequence, String indexName) {
this.sequence = sequence;
this.indexName = indexName;
}

public static ForLoop read(StringCharReader reader, StructNode originNode) {
try {
String emptyness = reader.readUntilSymbol('[').trim();
if (!emptyness.isEmpty()) {
throw originNode.createSyntaxException("Unexpected token: " + emptyness);
}

String parameterizations = reader.readUntilSymbol(']');
List<String> sequence = readSequence(parameterizations);
String indexName = INDEX_DEFAULT_NAME;

if (reader.hasMoreNormalSymbols()) {
String as = reader.readWord();
if (as.equals("as")) {

} else {
throw new SyntaxException("Invalid token: " + as);
}

indexName = reader.readWord();

if (indexName.isEmpty()) {
throw new SyntaxException("Missing index");
}

if (reader.hasMoreNormalSymbols()) {
throw new SyntaxException("Unknown statement: " + reader.getTheRest().trim());
}
}
return new ForLoop(sequence, indexName);
} catch (SyntaxException ex) {
ex.setLine(new Line(originNode.getSource(), originNode.getFileLineNumber()));
throw ex;
}
}

private static List<String> readSequence(String sequenceText) {
sequenceText = sequenceText.replace(" ", "");
sequenceText = sequenceText.replace("\t", "");
Pattern sequencePattern = Pattern.compile(".*\\-.*");
try {
String[] values = sequenceText.split(",");

List<String> sequence = new LinkedList<String>();

for (String value : values) {
if (sequencePattern.matcher(value).matches()) {
sequence.addAll(createSequence(value));
}
else {
sequence.add(value);
}
}

return sequence;
}
catch (Exception ex) {
throw new SyntaxException(UNKNOWN_LINE, "Incorrect sequence syntax: " + sequenceText, ex);
}
}

private static List<String> createSequence(String value) {
int dashIndex = value.indexOf('-');

int rangeA = Integer.parseInt(value.substring(0, dashIndex));
int rangeB = Integer.parseInt(value.substring(dashIndex + 1));

return createSequence(rangeA, rangeB);
}

private static List<String> createSequence(int min, int max) {
if (max >= min) {
List<String> parameters = new LinkedList<String>();
for (int i = min; i <= max; i++) {
parameters.add(Integer.toString(i));
}
return parameters;
}
else {
return Collections.emptyList();
}
}

public List<ProcessedStructNode> apply(LoopVisitor loopVisitor) {
List<ProcessedStructNode> resultingNodes = new LinkedList<ProcessedStructNode>();

for (final String sequenceValue : sequence) {
resultingNodes.addAll(loopVisitor.visitLoop(new HashMap<String, String>(){{
put(indexName, sequenceValue);
}}));
}

return resultingNodes;
}
}
@@ -0,0 +1,89 @@
/*******************************************************************************
* Copyright 2015 Ivan Shubin http://mindengine.net
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package net.mindengine.galen.speclang2.reader.pagespec;

import net.mindengine.galen.parser.ProcessedStructNode;
import net.mindengine.galen.parser.StructNode;
import net.mindengine.galen.specs.reader.StringCharReader;

import java.util.*;

public class LogicProcessor {
private final PageSpecProcessor pageSpecProcessor;

public LogicProcessor(PageSpecProcessor pageSpecProcessor) {
this.pageSpecProcessor = pageSpecProcessor;
}

public List<ProcessedStructNode> process(List<StructNode> nodes) {
List<ProcessedStructNode> resultingNodes = new LinkedList<ProcessedStructNode>();

for (StructNode node : nodes) {
ProcessedStructNode processedNode = pageSpecProcessor.processExpressionsIn(node);

if (isLogicStatement(processedNode.getName())) {
resultingNodes.addAll(processLogicStatement(processedNode));
} else {
resultingNodes.add(processNonLogicStatement(processedNode));
}
}

return resultingNodes;
}

private ProcessedStructNode processNonLogicStatement(ProcessedStructNode processedNode) {
if (processedNode.getChildNodes() != null) {
ProcessedStructNode fullyProcessed = new ProcessedStructNode(processedNode.getName(), processedNode);

fullyProcessed.setChildNodes(convertList(process(processedNode.getChildNodes())));
return fullyProcessed;
} else {
return processedNode;
}
}

private List<StructNode> convertList(List<ProcessedStructNode> processed) {
List<StructNode> list = new LinkedList<StructNode>();
for (ProcessedStructNode item: processed) {
list.add(item);
}
return list;
}


private List<ProcessedStructNode> processLogicStatement(final ProcessedStructNode logicStatementNode) {
StringCharReader reader = new StringCharReader(logicStatementNode.getName());
String firstWord = reader.readWord();
if (firstWord.equals("@for")) {
ForLoop forLoop = ForLoop.read(reader, logicStatementNode);

return forLoop.apply(new LoopVisitor() {
@Override
public List<ProcessedStructNode> visitLoop(Map<String, String> variables) {
pageSpecProcessor.setGlobalVariables(variables, logicStatementNode);
return process(logicStatementNode.getChildNodes());
}
});

} else {
throw logicStatementNode.createSyntaxException("Invalid statement: " + firstWord);
}
}

private boolean isLogicStatement(String name) {
return name.startsWith("@");
}
}
@@ -0,0 +1,25 @@
/*******************************************************************************
* Copyright 2015 Ivan Shubin http://mindengine.net
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package net.mindengine.galen.speclang2.reader.pagespec;

import net.mindengine.galen.parser.ProcessedStructNode;

import java.util.List;
import java.util.Map;

public interface LoopVisitor {
List<ProcessedStructNode> visitLoop(Map<String, String> variables);
}
Expand Up @@ -21,6 +21,7 @@
import net.mindengine.galen.specs.page.PageSection; import net.mindengine.galen.specs.page.PageSection;


import java.io.IOException; import java.io.IOException;
import java.util.List;


public class PageSectionProcessor { public class PageSectionProcessor {
private final PageSpecProcessor pageSpecProcessor; private final PageSpecProcessor pageSpecProcessor;
Expand All @@ -37,22 +38,23 @@ public PageSectionProcessor(PageSpecProcessor pageSpecProcessor, PageSection par
} }




public void process(ProcessedStructNode structNode, String contextPath) throws IOException { public void process(ProcessedStructNode sectionNode, String contextPath) throws IOException {
PageSection section = new PageSection(); PageSection section = new PageSection();
section.setName(structNode.getName().substring(1, structNode.getName().length() - 1).trim()); section.setName(sectionNode.getName().substring(1, sectionNode.getName().length() - 1).trim());


if (structNode.getChildNodes() != null) { if (sectionNode.getChildNodes() != null) {
for (StructNode childNode : structNode.getChildNodes()) { List<ProcessedStructNode> allProcessedChildNodes = new LogicProcessor(pageSpecProcessor).process(sectionNode.getChildNodes());

for (ProcessedStructNode processedChildNode : allProcessedChildNodes) {


ProcessedStructNode processedChildNode = pageSpecProcessor.processExpressionsIn(childNode);
String childLine = processedChildNode.getName(); String childLine = processedChildNode.getName();


if (isSectionDefinition(childLine)) { if (isSectionDefinition(childLine)) {
new PageSectionProcessor(pageSpecProcessor, section).process(processedChildNode, contextPath); new PageSectionProcessor(pageSpecProcessor, section).process(processedChildNode, contextPath);
} else if (isObject(childLine)) { } else if (isObject(childLine)) {
processObject(section, processedChildNode, contextPath); processObject(section, processedChildNode, contextPath);
} else { } else {
throw childNode.createSyntaxException("Unknown statement: " + childLine); throw processedChildNode.createSyntaxException("Unknown statement: " + childLine);
} }
} }
} }
Expand Down
Expand Up @@ -26,6 +26,7 @@
import net.mindengine.galen.suite.reader.Context; import net.mindengine.galen.suite.reader.Context;
import net.mindengine.galen.suite.reader.Line; import net.mindengine.galen.suite.reader.Line;


import java.util.Map;
import java.util.Properties; import java.util.Properties;




Expand Down Expand Up @@ -133,4 +134,10 @@ public ProcessedStructNode processExpressionsIn(StructNode structNode) {
String result = getVarsParser().parse(structNode.getName()); String result = getVarsParser().parse(structNode.getName());
return new ProcessedStructNode(result, structNode); return new ProcessedStructNode(result, structNode);
} }

public void setGlobalVariables(Map<String, String> variables, ProcessedStructNode originNode) {
for(Map.Entry<String, String> variable : variables.entrySet()) {
setGlobalVariable(variable.getKey(), variable.getValue(), originNode);
}
}
} }
Expand Up @@ -16,6 +16,7 @@
package net.mindengine.galen.specs.reader; package net.mindengine.galen.specs.reader;


import net.mindengine.galen.parser.Expectations; import net.mindengine.galen.parser.Expectations;
import net.mindengine.galen.parser.SyntaxException;


public class StringCharReader { public class StringCharReader {


Expand Down Expand Up @@ -73,7 +74,15 @@ public char firstNonWhiteSpaceSymbol() {
return 0; return 0;
} }


public String readUntilSymbol(char breakingSymbol) { public String readUntilSymbol(char breakingSymbol) {
return readUntilSymbol(breakingSymbol, true);
}

public String readSafeUntilSymbol(char breakingSymbol) {
return readUntilSymbol(breakingSymbol, false);
}

private String readUntilSymbol(char breakingSymbol, boolean failIfSymbolNotFound) {
StringBuffer buffer = new StringBuffer(); StringBuffer buffer = new StringBuffer();


while(hasMore()) { while(hasMore()) {
Expand All @@ -85,6 +94,10 @@ public String readUntilSymbol(char breakingSymbol) {
buffer.append(ch); buffer.append(ch);
} }
} }

if (failIfSymbolNotFound) {
throw new SyntaxException("Missing symbol: " + breakingSymbol);
}


return buffer.toString(); return buffer.toString();
} }
Expand All @@ -104,4 +117,5 @@ public int currentCursorPosition() {
public void moveCursorTo(int position) { public void moveCursorTo(int position) {
cursor = position; cursor = position;
} }

} }
Expand Up @@ -102,7 +102,7 @@ else if (firstWord.equals("set")) {
setVariables(varsContext.process(reader.getTheRest().trim())); setVariables(varsContext.process(reader.getTheRest().trim()));
} }
else if (firstWord.equals("rule")) { else if (firstWord.equals("rule")) {
if (!reader.readUntilSymbol(':').trim().isEmpty()) { if (!reader.readSafeUntilSymbol(':').trim().isEmpty()) {
throw new SyntaxException("Incorrect rule declaration. ':' is in the wrong place"); throw new SyntaxException("Incorrect rule declaration. ':' is in the wrong place");
} }


Expand Down

0 comments on commit c04266f

Please sign in to comment.