diff --git a/pom.xml b/pom.xml
index 57094da..7c5546f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
ir.ac.sbu
PGen
- 2.0
+ 2.1
UTF-8
diff --git a/src/main/java/ir/ac/sbu/controller/EdgePropertiesController.java b/src/main/java/ir/ac/sbu/controller/EdgePropertiesController.java
index c0f73e1..2b0365b 100644
--- a/src/main/java/ir/ac/sbu/controller/EdgePropertiesController.java
+++ b/src/main/java/ir/ac/sbu/controller/EdgePropertiesController.java
@@ -1,5 +1,6 @@
package ir.ac.sbu.controller;
+import ir.ac.sbu.utility.CheckUtility;
import ir.ac.sbu.utility.DialogUtility;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
@@ -34,12 +35,14 @@ public void init(EdgeModel edge) {
}
public void apply(ActionEvent actionEvent) {
- if (tokenText.getText().trim().isEmpty()) {
- DialogUtility.showErrorDialog("Token can not be empty");
- } else {
+ try {
+ CheckUtility.checkTokenName(tokenText.getText());
+ CheckUtility.checkFunctionName(funcText.getText());
Stage stage = (Stage) applyBtn.getScene().getWindow();
CommandManager.getInstance().applyCommand(new ChangeEdgeCmd(edge, tokenText.getText(), funcText.getText(), graphChk.isSelected(), globalChk.isSelected()));
stage.close();
+ } catch (IllegalArgumentException e) {
+ DialogUtility.showErrorDialog(e.getMessage());
}
}
}
diff --git a/src/main/java/ir/ac/sbu/controller/MainController.java b/src/main/java/ir/ac/sbu/controller/MainController.java
index 265394a..2a5f32f 100644
--- a/src/main/java/ir/ac/sbu/controller/MainController.java
+++ b/src/main/java/ir/ac/sbu/controller/MainController.java
@@ -7,6 +7,7 @@
import ir.ac.sbu.parser.LLParserGenerator;
import ir.ac.sbu.service.ExportService;
import ir.ac.sbu.service.SaveLoadService;
+import ir.ac.sbu.utility.CheckUtility;
import ir.ac.sbu.utility.DialogUtility;
import ir.ac.sbu.utility.GenerateUID;
import ir.ac.sbu.utility.ResourceUtility;
@@ -103,22 +104,24 @@ protected void updateItem(GraphModel item, boolean empty) {
renameBtn.setOnAction(event -> {
Optional newValue = DialogUtility.showInputDialog(cell.getItem().getName(), "Rename Graph");
if (newValue.isPresent()) {
- if (!newValue.get().trim().isEmpty()) {
+ try {
+ CheckUtility.checkGraphName(newValue.get());
cell.getItem().setName(newValue.get());
- } else {
- DialogUtility.showErrorDialog("Name of graph can not be empty");
+ } catch (IllegalArgumentException e) {
+ DialogUtility.showErrorDialog(e.getMessage());
}
}
});
duplicateBtn.setOnAction(event -> {
Optional result = DialogUtility.showInputDialog(cell.getItem().getName(), "New Graph");
if (result.isPresent()) {
- if (!result.get().trim().isEmpty()) {
+ try {
+ CheckUtility.checkGraphName(result.get());
GraphModel currentGraph = cell.getItem();
GraphModel duplicateGraph = currentGraph.createCopy(result.get());
graphs.add(duplicateGraph);
- } else {
- DialogUtility.showErrorDialog("Name of graph can not be empty");
+ } catch (IllegalArgumentException e) {
+ DialogUtility.showErrorDialog(e.getMessage());
}
}
});
@@ -172,9 +175,9 @@ public void exportFullParser(ActionEvent actionEvent) {
File directorySelected = DialogUtility.showDirectoryDialog(pane.getScene().getWindow());
try {
if (directorySelected != null) {
- InputStream parserSource = ResourceUtility.getResourceAsStream("parser/Parser.code");
- InputStream lexicalSource = ResourceUtility.getResourceAsStream("parser/Lexical.code");
- InputStream codeGeneratorSource = ResourceUtility.getResourceAsStream("parser/CodeGenerator.code");
+ InputStream parserSource = ResourceUtility.getResourceAsStream("parser/Parser.java");
+ InputStream lexicalSource = ResourceUtility.getResourceAsStream("parser/Lexical.java");
+ InputStream codeGeneratorSource = ResourceUtility.getResourceAsStream("parser/CodeGenerator.java");
Path destination = Paths.get(directorySelected.getPath());
LLParserGenerator parser = new LLParserGenerator(graphs);
diff --git a/src/main/java/ir/ac/sbu/parser/LLParserGenerator.java b/src/main/java/ir/ac/sbu/parser/LLParserGenerator.java
index 5e89635..2782ef3 100644
--- a/src/main/java/ir/ac/sbu/parser/LLParserGenerator.java
+++ b/src/main/java/ir/ac/sbu/parser/LLParserGenerator.java
@@ -1,12 +1,16 @@
package ir.ac.sbu.parser;
+import ir.ac.sbu.command.ChangeEdgeCmd;
+import ir.ac.sbu.command.CommandManager;
import ir.ac.sbu.exception.TableException;
import ir.ac.sbu.parser.builder.Action;
import ir.ac.sbu.parser.builder.LLCell;
+import ir.ac.sbu.utility.CheckUtility;
import ir.ac.sbu.utility.DialogUtility;
import ir.ac.sbu.wagu.Block;
import ir.ac.sbu.wagu.Board;
import ir.ac.sbu.wagu.Table;
+import javafx.stage.Stage;
import javafx.util.Pair;
import ir.ac.sbu.model.EdgeModel;
import ir.ac.sbu.model.GraphModel;
@@ -51,7 +55,7 @@ public LLParserGenerator(List graphs) throws TableException {
checkGraphs(graphs);
checkTokens();
- variableGraph = graphs.stream().collect(Collectors.toMap(GraphModel::getName, Function.identity(), (o, o2) -> o));
+ variableGraph = graphs.stream().collect(Collectors.toMap(GraphModel::getName, Function.identity(), (o, v) -> o));
allNodes = graphs.stream().flatMap(graphModel -> graphModel.getNodes().stream())
.collect(Collectors.toList());
allEdges = graphs.stream()
@@ -68,11 +72,25 @@ public LLParserGenerator(List graphs) throws TableException {
}
private void checkEdges() throws TableException {
+ List messages = new ArrayList<>();
+
+ for (EdgeModel edge : allEdges) {
+ try {
+ CheckUtility.checkFunctionName(edge.getFunction());
+ } catch (IllegalArgumentException e) {
+ messages.add(e.getMessage());
+ }
+ }
+
if (allEdges.stream()
.filter(EdgeModel::isGraph)
.map(EdgeModel::getToken)
.anyMatch(token -> token.equals("MAIN"))) {
- throw new TableException(Collections.singletonList("Graph MAIN should not used in graphs"));
+ messages.add("Graph MAIN should not used in graphs.");
+ }
+
+ if (!messages.isEmpty()) {
+ throw new TableException(messages);
}
}
@@ -82,18 +100,25 @@ private void checkTokens() throws TableException {
tokenAsInt.put("$", 0);
int tokenUID = 1;
for (String token : tokens) {
- if (token.startsWith("$")) {
- messages.add("All string starting with $ are predefined tokens");
+ try {
+ CheckUtility.checkTokenName(token);
+ tokenAsInt.put(token, tokenUID);
+ tokenUID++;
+ } catch (IllegalArgumentException e) {
+ messages.add(e.getMessage());
}
- tokenAsInt.put(token, tokenUID);
- tokenUID++;
}
for (String variable : variables) {
- if (tokenAsInt.put(variable, tokenUID) != null) {
- messages.add(String.format("%s Should be either a token or a graph", variable));
+ try {
+ CheckUtility.checkGraphName(variable);
+ if (tokenAsInt.put(variable, tokenUID) != null) {
+ messages.add(String.format("%s Should be either a token or a graph.", variable));
+ }
+ tokenUID++;
+ } catch (IllegalArgumentException e) {
+ messages.add(e.getMessage());
}
- tokenUID++;
}
tokensSortedById = tokenAsInt.keySet().stream()
@@ -119,10 +144,15 @@ private void checkGraphs(List graphs) throws TableException {
.forEach(s -> messages.add(String.format("Duplicate graph %s exist", s)));
List graphWithNoFinalNode = graphs.stream().
- filter(graph -> graph.getNodes().stream().noneMatch(NodeModel::isFinalNode)).collect(Collectors.toList());
- graphWithNoFinalNode.forEach(graph -> messages.add(String.format("Graph %s doesn't have final node", graph.getName())));
- List graphWithNoStartNode = graphs.stream().filter(graph -> graph.getStart() == null).collect(Collectors.toList());
- graphWithNoStartNode.forEach(graph -> messages.add(String.format("Graph %s doesn't have start node", graph.getName())));
+ filter(graph -> graph.getNodes().stream().noneMatch(NodeModel::isFinalNode))
+ .collect(Collectors.toList());
+ graphWithNoFinalNode.forEach(graph ->
+ messages.add(String.format("Graph %s doesn't have final node", graph.getName())));
+ List graphWithNoStartNode = graphs.stream()
+ .filter(graph -> graph.getStart() == null)
+ .collect(Collectors.toList());
+ graphWithNoStartNode.forEach(graph ->
+ messages.add(String.format("Graph %s doesn't have start node", graph.getName())));
if (!messages.isEmpty()) {
throw new TableException(messages);
@@ -213,11 +243,10 @@ public void buildTable(File file) throws TableException {
writer.println(allNodes.size() + " " + table[0].length);
writer.println(startNode);
- writer.println(String.join(" ", tokensSortedById));
+ writer.println(String.join(CheckUtility.DELIMITER, tokensSortedById));
for (LLCell[] cellOfNode : table) {
- for (LLCell cell : cellOfNode) {
- writer.print(cell + " ");
- }
+ writer.print(Arrays.stream(cellOfNode).map(LLCell::toString)
+ .collect(Collectors.joining(CheckUtility.DELIMITER)));
writer.println();
}
writer.println();
diff --git a/src/main/java/ir/ac/sbu/utility/CheckUtility.java b/src/main/java/ir/ac/sbu/utility/CheckUtility.java
new file mode 100644
index 0000000..a4a4755
--- /dev/null
+++ b/src/main/java/ir/ac/sbu/utility/CheckUtility.java
@@ -0,0 +1,34 @@
+package ir.ac.sbu.utility;
+
+public class CheckUtility {
+ public static String DELIMITER = ",";
+
+ private CheckUtility() {
+ }
+
+ public static void checkGraphName(String graphName) {
+ if (graphName.trim().isEmpty()) {
+ throw new IllegalArgumentException("Graph name can not be empty.");
+ } else if (graphName.contains(DELIMITER)) {
+ throw new IllegalArgumentException("Graph can not contain '" + DELIMITER + "' character.");
+ }
+ }
+
+ public static void checkTokenName(String tokenName) {
+ if (tokenName.startsWith("$")) {
+ throw new IllegalArgumentException("All string starting with $ are predefined tokens.");
+ } else if (tokenName.trim().isEmpty()) {
+ throw new IllegalArgumentException("Token can not be empty.");
+ } else if (tokenName.contains(DELIMITER)) {
+ throw new IllegalArgumentException("Token can not contain '" + DELIMITER + "' character.");
+ }
+ }
+
+ public static void checkFunctionName(String functionName) {
+ if (functionName.contains(DELIMITER)) {
+ throw new IllegalArgumentException("Token can not contain '" + DELIMITER + "' character.");
+ } else if (functionName.contains(" ")) {
+ throw new IllegalArgumentException("Token can not contain ' ' character.");
+ }
+ }
+}
diff --git a/src/main/resources/parser/CodeGenerator.code b/src/main/resources/parser/CodeGenerator.java
similarity index 100%
rename from src/main/resources/parser/CodeGenerator.code
rename to src/main/resources/parser/CodeGenerator.java
diff --git a/src/main/resources/parser/Lexical.code b/src/main/resources/parser/Lexical.java
similarity index 100%
rename from src/main/resources/parser/Lexical.code
rename to src/main/resources/parser/Lexical.java
diff --git a/src/main/resources/parser/Parser.code b/src/main/resources/parser/Parser.java
similarity index 60%
rename from src/main/resources/parser/Parser.code
rename to src/main/resources/parser/Parser.java
index 61ba20c..f84374b 100644
--- a/src/main/resources/parser/Parser.code
+++ b/src/main/resources/parser/Parser.java
@@ -2,6 +2,7 @@
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
+import java.util.stream.Collectors;
enum Action {
ERROR, SHIFT, GOTO, PUSH_GOTO, REDUCE, ACCEPT
@@ -10,12 +11,12 @@ enum Action {
class LLCell {
private Action action;
private int target;
- private String function;
+ private List functions;
- public LLCell(Action action, int target, String function) {
+ public LLCell(Action action, int target, List functions) {
this.action = action;
this.target = target;
- this.function = function;
+ this.functions = functions;
}
public Action getAction() {
@@ -26,15 +27,18 @@ public int getTarget() {
return target;
}
- public String getFunction() {
- return function;
+ public List getFunction() {
+ return functions;
}
}
public class Parser {
+ public static String TABLE_DELIMITER = ",";
+
private Lexical lexical;
private CodeGenerator codeGenerator;
+ private boolean debugMode;
private String[] symbols;
private LLCell[][] parseTable;
@@ -43,6 +47,11 @@ public class Parser {
private List recoveryState;
+ public Parser(Lexical lexical, CodeGenerator codeGenerator, String nptPath, boolean debugMode) {
+ this(lexical, codeGenerator, nptPath);
+ this.debugMode = debugMode;
+ }
+
public Parser(Lexical lexical, CodeGenerator codeGenerator, String nptPath) {
this.lexical = lexical;
this.codeGenerator = codeGenerator;
@@ -55,28 +64,40 @@ public Parser(Lexical lexical, CodeGenerator codeGenerator, String nptPath) {
try {
Scanner in = new Scanner(new FileInputStream(nptPath));
String[] tmpArr = in.nextLine().trim().split(" ");
- int rowSize = Integer.valueOf(tmpArr[0]);
- int colSize = Integer.valueOf(tmpArr[1]);
- startNode = Integer.valueOf(in.nextLine());
- symbols = in.nextLine().trim().split(" ");
+ int rowSize = Integer.parseInt(tmpArr[0]);
+ int colSize = Integer.parseInt(tmpArr[1]);
+ startNode = Integer.parseInt(in.nextLine());
+ symbols = in.nextLine().trim().split(TABLE_DELIMITER);
parseTable = new LLCell[rowSize][colSize];
for (int i = 0; i < rowSize; i++) {
- tmpArr = in.nextLine().trim().split(" ");
- if (tmpArr.length != colSize * 3) {
- throw new RuntimeException("Invalid .npt file");
+ tmpArr = in.nextLine().trim().split(TABLE_DELIMITER);
+ if (tmpArr.length != colSize) {
+ throw new RuntimeException("Invalid .npt file: File contains rows with length" +
+ " bigger than column size.");
}
for (int j = 0; j < colSize; j++) {
- parseTable[i][j] = new LLCell(Action.values()[Integer.parseInt((tmpArr[j * 3]))],
- Integer.parseInt(tmpArr[j * 3 + 1]),
- tmpArr[j * 3 + 2]);
+ String[] cellParts = tmpArr[j].split(" ");
+ if (cellParts.length != 3) {
+ throw new RuntimeException("Invalid .npt file: Parser cells must have extactly 3 values.");
+ }
+ Action action = Action.values()[Integer.parseInt(cellParts[0])];
+ int target = Integer.parseInt(cellParts[1]);
+ List allFunctions;
+ if (cellParts[2].equals("NoSem")) {
+ allFunctions = new ArrayList<>();
+ } else {
+ allFunctions = Arrays.stream(cellParts[2].substring(1).split("[;]"))
+ .filter(s -> !s.isEmpty()).collect(Collectors.toList());
+ }
+ parseTable[i][j] = new LLCell(action, target, allFunctions);
}
}
} catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
- throw new RuntimeException("Invalid .npt file");
+ throw new RuntimeException("Invalid .npt file.");
} catch (FileNotFoundException e) {
- throw new RuntimeException("Unable to load .npt file", e);
+ throw new RuntimeException("Unable to load .npt file.", e);
}
}
@@ -87,10 +108,19 @@ public void parse() {
while (!accepted) {
String tokenText = symbols[tokenID];
LLCell cell = parseTable[currentNode][tokenID];
+ if (debugMode) {
+ System.out.println("Current token: text='" + symbols[tokenID] + "' id=" + tokenID);
+ System.out.println("Current node: " + currentNode);
+ System.out.println("Current cell of parser table: " +
+ "target-node=" + cell.getTarget() +
+ " action=" + cell.getAction() +
+ " function=" + cell.getFunction());
+ System.out.println(String.join("", Collections.nCopies(50, "-")));
+ }
switch (cell.getAction()) {
case ERROR:
updateRecoveryState(currentNode, tokenText);
- generateError("Unable to parse input");
+ generateError("Unable to parse input.");
case SHIFT:
doSemantics(cell.getFunction());
tokenID = nextTokenID();
@@ -125,8 +155,10 @@ public void parse() {
}
private void generateError(String message) {
+ System.out.flush();
+ System.out.println("Error happened while parsing ...");
for (String state : recoveryState) {
- System.err.println(state);
+ System.out.println(state);
}
throw new RuntimeException(message);
}
@@ -145,23 +177,19 @@ private void updateRecoveryState(int currentNode, String token) {
private int nextTokenID() {
String token = lexical.nextToken();
for (int i = 0; i < symbols.length; i++) {
- if (symbols[i].equalsIgnoreCase(token)) {
+ if (symbols[i].equals(token)) {
return i;
}
}
throw new RuntimeException("Undefined token: " + token);
}
- private void doSemantics(String semantics) {
- if (semantics.equals("NoSem")) {
- return;
+ private void doSemantics(List functions) {
+ if (debugMode) {
+ System.out.println("Execute semantic codes: " + functions);
}
- semantics = semantics.substring(1);
- String[] allFunctions = semantics.split("[;]");
- for (String function : allFunctions) {
- if (function.length() > 0) {
- codeGenerator.doSemantic(function);
- }
+ for (String function : functions) {
+ codeGenerator.doSemantic(function);
}
}
}