From 9a54d05f1279b570248bd1082042eeae4f04d711 Mon Sep 17 00:00:00 2001 From: XudongXie Date: Tue, 17 Sep 2024 14:28:30 +0800 Subject: [PATCH] add procedure --- sqlTranslate/.idea/workspace.xml | 39 +-- .../main/java/Document/Procedure in Oracle | 17 +- sqlTranslate/src/main/java/Document/Todolist | 4 +- .../java/Generator/OpenGaussGenerator.java | 55 ++++ .../src/main/java/Lexer/OracleLexer.java | 5 + sqlTranslate/src/main/java/Main.java | 61 +++-- .../AST/Exception/ExceptionEndNode.java | 5 +- .../AST/Procedure/ProcedureColumnNode.java | 5 +- .../Procedure/ProcedurePLStatementNode.java | 1 + .../AST/Procedure/ProcedureRetDefNode.java | 1 + .../java/Parser/AST/Update/UpdateObjNode.java | 2 +- .../java/Parser/AST/Update/UpdateSetNode.java | 2 +- .../src/main/java/Parser/OracleParser.java | 255 ++++++++++++++++++ 13 files changed, 408 insertions(+), 44 deletions(-) diff --git a/sqlTranslate/.idea/workspace.xml b/sqlTranslate/.idea/workspace.xml index d2dacd75d..c849afc30 100644 --- a/sqlTranslate/.idea/workspace.xml +++ b/sqlTranslate/.idea/workspace.xml @@ -4,17 +4,18 @@ - @@ -495,7 +495,8 @@ - diff --git a/sqlTranslate/src/main/java/Document/Procedure in Oracle b/sqlTranslate/src/main/java/Document/Procedure in Oracle index 4495b34c1..dc73b7ead 100644 --- a/sqlTranslate/src/main/java/Document/Procedure in Oracle +++ b/sqlTranslate/src/main/java/Document/Procedure in Oracle @@ -6,15 +6,30 @@ BEGIN -- Exception handling END [procedure_name]; -- [procedure_name] should be removed in OpenGauss -Example: CREATE OR REPLACE PROCEDURE update_salary( +Example: CREATE OR REPLACE PROCEDURE update_salary ( employee_id IN NUMBER, new_salary IN OUT NUMBER ) IS + v_employee employees%ROWTYPE; BEGIN + SELECT * INTO v_employee FROM employees WHERE employee_id = employee_id; + IF new_salary < 3000 THEN new_salary := new_salary * 1.1; ELSE new_salary := new_salary * 1.05; END IF; + + UPDATE employees SET salary = new_salary WHERE employee_id = employee_id; + COMMIT; + + DBMS_OUTPUT.PUT_LINE('Updated Salary: ' || new_salary); + EXCEPTION + WHEN NO_DATA_FOUND THEN + DBMS_OUTPUT.PUT_LINE('Employee not found.'); + WHEN TOO_MANY_ROWS THEN + DBMS_OUTPUT.PUT_LINE('Multiple employees found.'); + WHEN OTHERS THEN + DBMS_OUTPUT.PUT_LINE('An error occurred: ' || SQLERRM); END update_salary; / \ No newline at end of file diff --git a/sqlTranslate/src/main/java/Document/Todolist b/sqlTranslate/src/main/java/Document/Todolist index 1dfaaa171..25611b338 100644 --- a/sqlTranslate/src/main/java/Document/Todolist +++ b/sqlTranslate/src/main/java/Document/Todolist @@ -15,7 +15,7 @@ Operation  continue、exit 1  return ***  exception 1 - commit、rollback、execute **** + commit、rollback、execute ***  alter table 1  create table 1  drop table 1 @@ -24,4 +24,4 @@ Object  view 1  function ***  procedure *** - trigger ***** \ No newline at end of file + trigger **** \ No newline at end of file diff --git a/sqlTranslate/src/main/java/Generator/OpenGaussGenerator.java b/sqlTranslate/src/main/java/Generator/OpenGaussGenerator.java index 1014f31f1..a00185ee6 100644 --- a/sqlTranslate/src/main/java/Generator/OpenGaussGenerator.java +++ b/sqlTranslate/src/main/java/Generator/OpenGaussGenerator.java @@ -22,6 +22,10 @@ import Parser.AST.Loop.ForNode; import Parser.AST.Loop.LoopBodyNode; import Parser.AST.Loop.LoopNode; import Parser.AST.Loop.WhileNode; +import Parser.AST.Procedure.ProcedureColumnNode; +import Parser.AST.Procedure.ProcedureEndNode; +import Parser.AST.Procedure.ProcedureNode; +import Parser.AST.Procedure.ProcedureRetDefNode; import Parser.AST.Select.SelectNode; import Parser.AST.Update.UpdateNode; import Parser.AST.View.ViewCreateNode; @@ -75,6 +79,9 @@ public class OpenGaussGenerator { else if (node instanceof ExceptionNode) { return GenExceptionSQL(node); } + else if (node instanceof ProcedureNode) { + return GenProcedureSQL(node); + } else { try { throw new GenerateFailedException("Root node:" + node.getClass() + "(Unsupported node type!)"); @@ -145,6 +152,12 @@ public class OpenGaussGenerator { return node.toQueryString(); } + private String GenProcedureSQL(ASTNode node) { + visitPL(node); + System.out.println(node.getASTString()); + return node.toQueryString(); + } + private void visitCrt(ASTNode node) { if (node instanceof ColumnNode) { ColumnTypeConvert((ColumnNode) node); @@ -240,6 +253,24 @@ public class OpenGaussGenerator { visitCreateView(child); } } + + private void visitPL(ASTNode node) { + if (node instanceof ProcedureRetDefNode) { + PLConvert(node); + } + else if (node instanceof ProcedureColumnNode) { + ColumnTypeConvert((ProcedureColumnNode) node); + } + else if (node instanceof ProcedureEndNode) { + PLConvert(node); + } + else if (node instanceof ExceptionNode) { + visitException(node); + } + for (ASTNode child : node.getChildren()) { + visitPL(child); + } + } private void PLConvert(ASTNode node) { if (node.checkExistsByRegex("(?i)DBMS_OUTPUT.PUT_LINE\\(.*?\\)")) { String printObj = ""; @@ -304,6 +335,30 @@ public class OpenGaussGenerator { } } + if (node instanceof ProcedureRetDefNode) { + List tokens = new ArrayList<>(); + tokens.add(new Token(Token.TokenType.KEYWORD, "AS")); + tokens.add(new Token(Token.TokenType.KEYWORD, "$$")); + for (Token token: node.getTokens()) { + if (token.hasType(Token.TokenType.KEYWORD) && token.getValue().equalsIgnoreCase("IS")) { + tokens.add(new Token(Token.TokenType.KEYWORD, "DECLARE")); + } + else { + tokens.add(token); + } + } + node.setTokens(tokens); + } + if (node instanceof ProcedureEndNode) { + List tokens = new ArrayList<>(); + tokens.add(new Token(Token.TokenType.KEYWORD, "END")); + tokens.add(new Token(Token.TokenType.SYMBOL, ";")); + tokens.add(new Token(Token.TokenType.KEYWORD, "$$")); + tokens.add(new Token(Token.TokenType.KEYWORD, "LANGUAGE")); + tokens.add(new Token(Token.TokenType.KEYWORD, "plpgsql")); + tokens.add(new Token(Token.TokenType.SYMBOL, ";")); + node.setTokens(tokens); + } } diff --git a/sqlTranslate/src/main/java/Lexer/OracleLexer.java b/sqlTranslate/src/main/java/Lexer/OracleLexer.java index ac4d1a0bb..cda8db868 100644 --- a/sqlTranslate/src/main/java/Lexer/OracleLexer.java +++ b/sqlTranslate/src/main/java/Lexer/OracleLexer.java @@ -35,6 +35,8 @@ public class OracleLexer { , "LOOP", "EXIT", "WHILE", "FOR", "IN", "CONTINUE" // Exception , "EXCEPTION", "SQLERRM", "ZERO_DIVIDE", "INVALID_NUMBER", "OTHERS" + // Procedure + , "PROCEDURE", "IS", "IN", "OUT", "$$" }; private static final Pattern TOKEN_PATTERN = Pattern.compile( "(NUMBER\\(.*?\\))|" + // NUMBER() function @@ -78,6 +80,7 @@ public class OracleLexer { "(:=)|" + "(\\.\\.)|" + + "(\\b[A-Za-z_][A-Za-z0-9_]*(%[A-Za-z_][A-Za-z0-9_]*)*\\b)|" + // Keywords and identifiers "(\\b[A-Za-z_][A-Za-z0-9_]*(\\.[A-Za-z_][A-Za-z0-9_]*)*\\b)|" + // Keywords and identifiers "(\\b[A-Za-z_][A-Za-z0-9_]*\\b)|" + // Keywords and identifiers "(\\d+\\.?\\d*)|" + // Numbers (integer or decimal) @@ -178,6 +181,8 @@ public class OracleLexer { else if (isKeyword(tokenValue)) { return new Token(Token.TokenType.KEYWORD, tokenValue); + } else if (tokenValue.matches("[A-Za-z_][A-Za-z0-9_]*(%[A-Za-z_][A-Za-z0-9_]*)*")) { + return new Token(Token.TokenType.IDENTIFIER, tokenValue); } else if (tokenValue.matches("[A-Za-z_][A-Za-z0-9_]*(\\.[A-Za-z_][A-Za-z0-9_]*)*")) { return new Token(Token.TokenType.IDENTIFIER, tokenValue); } else if (tokenValue.matches("[a-zA-Z_][a-zA-Z0-9_]*")) { diff --git a/sqlTranslate/src/main/java/Main.java b/sqlTranslate/src/main/java/Main.java index 3d7016c09..36ee86875 100644 --- a/sqlTranslate/src/main/java/Main.java +++ b/sqlTranslate/src/main/java/Main.java @@ -22,11 +22,11 @@ public class Main { // String sql = "DROP TABLE employees CASCADE CONSTRAINTS;"; // String sql = "SELECT e.first_name, d.department_name FROM employees e JOIN departments d Using e.department_id = d.department_id;"; // String sql = "employees e JOIN departments d ON e.department_id = d.department_id;"; -// String sql = "UPDATE employees e\n" + -// "JOIN departments d using e.department_id = d.department_id\n" + -// "SET e.salary = e.salary * 1.10,\n" + -// " d.budget = d.budget * 1.10\n" + -// "WHERE d.department_name = 'Sales';"; + String sql = "UPDATE employees e\n" + + "JOIN departments d using e.department_id = d.department_id\n" + + "SET e.salary = e.salary * 1.10,\n" + + " d.budget = d.budget * 1.10\n" + + "WHERE d.department_name = 'Sales';"; // String sql = "DELETE FROM employees e\n" + // "WHERE e.department_id IN (\n" + // " SELECT d.department_id\n" + @@ -51,20 +51,47 @@ public class Main { // " v_counter := v_counter + 1;\n" + // " EXIT WHEN v_counter > 10;\n" + // " END LOOP;"; - String sql = "EXCEPTION\n" + - " WHEN e_custom_exception THEN\n" + - " DBMS_OUTPUT.PUT_LINE('Caught an exception: Custom exception raised');\n" + - " WHEN ZERO_DIVIDE THEN\n" + - " DBMS_OUTPUT.PUT_LINE('Caught an exception: Division by zero');\n" + - " WHEN INVALID_NUMBER THEN\n" + - " DBMS_OUTPUT.PUT_LINE('Caught an exception: Invalid number');\n" + - " WHEN OTHERS THEN\n" + - " DBMS_OUTPUT.PUT_LINE('Caught an exception: ' || SQLERRM);"; +// String sql = "EXCEPTION\n" + +// " WHEN e_custom_exception THEN\n" + +// " DBMS_OUTPUT.PUT_LINE('Caught an exception: Custom exception raised');\n" + +// " WHEN ZERO_DIVIDE THEN\n" + +// " DBMS_OUTPUT.PUT_LINE('Caught an exception: Division by zero');\n" + +// " WHEN INVALID_NUMBER THEN\n" + +// " DBMS_OUTPUT.PUT_LINE('Caught an exception: Invalid number');\n" + +// " WHEN OTHERS THEN\n" + +// " DBMS_OUTPUT.PUT_LINE('Caught an exception: ' || SQLERRM);"; +// String sql = "CREATE OR REPLACE PROCEDURE update_salary (\n" + +// " employee_id IN NUMBER,\n" + +// " new_salary IN OUT NUMBER\n" + +// " ) IS\n" + +// " v_employee employees%ROWTYPE;\n" + +// " BEGIN\n" + +// " SELECT * INTO v_employee FROM employees WHERE employee_id = employee_id;\n" + +// "\n" + +// " IF new_salary < 3000 THEN\n" + +// " new_salary := new_salary * 1.1;\n" + +// " ELSE\n" + +// " new_salary := new_salary * 1.05;\n" + +// " END IF;\n" + +// "\n" + +// " UPDATE employees SET salary = new_salary WHERE employee_id = employee_id;\n" + +// " COMMIT;\n" + +// "\n" + +// " DBMS_OUTPUT.PUT_LINE('Updated Salary: ' || new_salary);\n" + +// " EXCEPTION\n" + +// " WHEN NO_DATA_FOUND THEN\n" + +// " DBMS_OUTPUT.PUT_LINE('Employee not found.');\n" + +// " WHEN TOO_MANY_ROWS THEN\n" + +// " DBMS_OUTPUT.PUT_LINE('Multiple employees found.');\n" + +// " WHEN OTHERS THEN\n" + +// " DBMS_OUTPUT.PUT_LINE('An error occurred: ' || SQLERRM);\n" + +// " END update_salary;\n" + +// " /"; OracleLexer lexer = new OracleLexer(sql); lexer.printTokens(); -// OracleParser parser = new OracleParser(lexer); -// ASTNode root = parser.parse(); - ASTNode root = OracleParser.parseException(lexer.getTokens()); + OracleParser parser = new OracleParser(lexer); + ASTNode root = parser.parse(); +// ASTNode root = OracleParser.parseException(lexer.getTokens()); // ASTNode root = OracleParser.parseLoop(lexer.getTokens()); // ASTNode root = OracleParser.parseIFELSE(lexer.getTokens()); // ASTNode root = OracleParser.parseCaseWhen(lexer.getTokens()); diff --git a/sqlTranslate/src/main/java/Parser/AST/Exception/ExceptionEndNode.java b/sqlTranslate/src/main/java/Parser/AST/Exception/ExceptionEndNode.java index a1b829638..42c3dd4ba 100644 --- a/sqlTranslate/src/main/java/Parser/AST/Exception/ExceptionEndNode.java +++ b/sqlTranslate/src/main/java/Parser/AST/Exception/ExceptionEndNode.java @@ -22,6 +22,9 @@ public class ExceptionEndNode extends ASTNode { @Override public void visit(ASTNode node, StringBuilder queryString) { - + queryString.append(toString()); + for (ASTNode child : node.getChildren()) { + child.visit(child, queryString); + } } } diff --git a/sqlTranslate/src/main/java/Parser/AST/Procedure/ProcedureColumnNode.java b/sqlTranslate/src/main/java/Parser/AST/Procedure/ProcedureColumnNode.java index f495fbff9..e158fe8a1 100644 --- a/sqlTranslate/src/main/java/Parser/AST/Procedure/ProcedureColumnNode.java +++ b/sqlTranslate/src/main/java/Parser/AST/Procedure/ProcedureColumnNode.java @@ -16,6 +16,7 @@ public class ProcedureColumnNode extends ASTNode implements ColumnType { public ProcedureColumnNode() { super(); setTokens(new ArrayList<>()); + setInOut(new ArrayList<>()); } public ProcedureColumnNode(ASTNode node) @@ -31,9 +32,9 @@ public class ProcedureColumnNode extends ASTNode implements ColumnType { @Override public void visit(ASTNode node, StringBuilder queryString) { - if (node.hasChild() && !(node.getChildren().get(0) instanceof ProcedureColumnNode) ) + if (node.hasChild() && (node.getChildren().get(0) instanceof ProcedureColumnNode) ) queryString.append(toString() + ", "); - else if (node.hasChild() && (node.getChildren().get(0) instanceof ProcedureColumnNode)) + else if (node.hasChild() && !(node.getChildren().get(0) instanceof ProcedureColumnNode)) queryString.append(toString() + " "); for (ASTNode child : getChildren()) { diff --git a/sqlTranslate/src/main/java/Parser/AST/Procedure/ProcedurePLStatementNode.java b/sqlTranslate/src/main/java/Parser/AST/Procedure/ProcedurePLStatementNode.java index bee1052fb..66a89a582 100644 --- a/sqlTranslate/src/main/java/Parser/AST/Procedure/ProcedurePLStatementNode.java +++ b/sqlTranslate/src/main/java/Parser/AST/Procedure/ProcedurePLStatementNode.java @@ -9,6 +9,7 @@ import java.util.List; public class ProcedurePLStatementNode extends ASTNode { public ProcedurePLStatementNode() { super(); + setTokens(new ArrayList<>()); } public ProcedurePLStatementNode(ASTNode node) { super(node); diff --git a/sqlTranslate/src/main/java/Parser/AST/Procedure/ProcedureRetDefNode.java b/sqlTranslate/src/main/java/Parser/AST/Procedure/ProcedureRetDefNode.java index 402468059..3f675221f 100644 --- a/sqlTranslate/src/main/java/Parser/AST/Procedure/ProcedureRetDefNode.java +++ b/sqlTranslate/src/main/java/Parser/AST/Procedure/ProcedureRetDefNode.java @@ -9,6 +9,7 @@ import java.util.List; public class ProcedureRetDefNode extends ASTNode { public ProcedureRetDefNode() { super(); + setTokens(new ArrayList<>()); } public ProcedureRetDefNode(ASTNode node) { diff --git a/sqlTranslate/src/main/java/Parser/AST/Update/UpdateObjNode.java b/sqlTranslate/src/main/java/Parser/AST/Update/UpdateObjNode.java index d7492d223..f5fb6b84d 100644 --- a/sqlTranslate/src/main/java/Parser/AST/Update/UpdateObjNode.java +++ b/sqlTranslate/src/main/java/Parser/AST/Update/UpdateObjNode.java @@ -18,7 +18,7 @@ public class UpdateObjNode extends ASTNode { public void visit(ASTNode node, StringBuilder queryString) { queryString.append(toString() + " "); for (ASTNode child : getChildren()) { - child.visit(this, queryString); + child.visit(child, queryString); } } } diff --git a/sqlTranslate/src/main/java/Parser/AST/Update/UpdateSetNode.java b/sqlTranslate/src/main/java/Parser/AST/Update/UpdateSetNode.java index c9ad6cb18..a46c5f64e 100644 --- a/sqlTranslate/src/main/java/Parser/AST/Update/UpdateSetNode.java +++ b/sqlTranslate/src/main/java/Parser/AST/Update/UpdateSetNode.java @@ -18,7 +18,7 @@ public class UpdateSetNode extends ASTNode { public void visit(ASTNode node, StringBuilder queryString) { queryString.append(toString() + " "); for (ASTNode child : getChildren()) { - child.visit(this, queryString); + child.visit(child, queryString); } } } diff --git a/sqlTranslate/src/main/java/Parser/OracleParser.java b/sqlTranslate/src/main/java/Parser/OracleParser.java index f3a310f5b..4677e7980 100644 --- a/sqlTranslate/src/main/java/Parser/OracleParser.java +++ b/sqlTranslate/src/main/java/Parser/OracleParser.java @@ -26,6 +26,7 @@ import Parser.AST.Insert.InsertNode; import Parser.AST.Insert.InsertObjNode; import Parser.AST.Join.*; import Parser.AST.Loop.*; +import Parser.AST.Procedure.*; import Parser.AST.Select.*; import Parser.AST.Update.*; import Parser.AST.View.ViewCreateNode; @@ -73,6 +74,12 @@ public class OracleParser { || lexer.getTokens().get(0).getValue().equalsIgnoreCase("WHILE") ) { return parseLoop(lexer.getTokens()); + } else if ( + (lexer.getTokens().get(0).getValue().equalsIgnoreCase("CREATE") && lexer.getTokens().get(1).getValue().equalsIgnoreCase("PROCEDURE")) + || (lexer.getTokens().get(0).getValue().equalsIgnoreCase("CREATE") && lexer.getTokens().get(1).getValue().equalsIgnoreCase("OR") && lexer.getTokens().get(2).getValue().equalsIgnoreCase("REPLACE") && lexer.getTokens().get(3).getValue().equalsIgnoreCase("PROCEDURE")) + ) + { + return parseProcedure(lexer.getTokens()); } else { try { @@ -1642,4 +1649,252 @@ public class OracleParser { return root; } + + /** + * Create PROCEDURE + * Grammar: CREATE [OR REPLACE] PROCEDURE procedure_name ([parameter_list]) + * IS [LOCAL DECLARATIONS] + * BEGIN + * -- PL/SQL statements + * [EXCEPTION] + * -- Exception handling + * END [procedure_name]; + * Example: CREATE OR REPLACE PROCEDURE update_salary( + * employee_id IN NUMBER, + * new_salary IN OUT NUMBER + * ) IS + * BEGIN + * IF new_salary < 3000 THEN + * new_salary := new_salary * 1.1; + * ELSE + * new_salary := new_salary * 1.05; + * END IF; + * END update_salary; + * / + */ + private ASTNode parseProcedure(List parseTokens) { + ASTNode root = new ProcedureNode(new ArrayList<>()); + ASTNode currentNode = root; + for (int i = 0; i < parseTokens.size(); i++) { + // match CREATE PROCEDURE + if (root.getTokens().isEmpty()) { + for (int j = i; j < parseTokens.size(); j++) { + root.addToken(parseTokens.get(j)); + if (parseTokens.get(j).hasType(Token.TokenType.KEYWORD) && parseTokens.get(j).getValue().equalsIgnoreCase("PROCEDURE")) { + i = j; + break; + } + } + } + // match procedure name + else if (currentNode == root && parseTokens.get(i).hasType(Token.TokenType.IDENTIFIER)) { + ASTNode childNode = new ProcedureObjNode(); + childNode.addToken(parseTokens.get(i)); + currentNode.addChild(childNode); + currentNode = childNode; + } + // match parameter list + else if (currentNode instanceof ProcedureObjNode && parseTokens.get(i).hasType(Token.TokenType.SYMBOL) && parseTokens.get(i).getValue().equals("(")) { + i++; + for (int ii = i; ii < parseTokens.size(); ii++) { + if (parseTokens.get(ii).hasType(Token.TokenType.KEYWORD) && parseTokens.get(ii).getValue().equalsIgnoreCase("IS")) { + i = ii - 1; + break; + } + List tokens = new ArrayList<>(); + ProcedureColumnNode child = new ProcedureColumnNode(); + child.setName(parseTokens.get(ii)); + tokens.add(parseTokens.get(ii)); + List constraint = new ArrayList<>(); + boolean typeMatch = false; + int typeIdx = -1; + for (int j = ii + 1; j < parseTokens.size(); j++) { + if (!typeMatch && !(parseTokens.get(j).hasType(Token.TokenType.KEYWORD) && (parseTokens.get(j).getValue().equalsIgnoreCase("IN") || parseTokens.get(j).getValue().equalsIgnoreCase("OUT")))) + { + child.setType(parseTokens.get(j)); + typeMatch = true; + typeIdx = j; + } + if (parseTokens.get(j).hasType(Token.TokenType.KEYWORD) && (parseTokens.get(j).getValue().equalsIgnoreCase("IN") || parseTokens.get(j).getValue().equalsIgnoreCase("OUT"))) { + child.addInOut(parseTokens.get(j)); + } + // Check () or REFERENCES other_table(other_column) + if (parseTokens.get(j).hasType(Token.TokenType.KEYWORD) && + (parseTokens.get(j).getValue().equalsIgnoreCase("REFERENCES") + || parseTokens.get(j).getValue().equalsIgnoreCase("CHECK"))) { + tokens.add(parseTokens.get(j)); + if (!(parseTokens.get(j).hasType(Token.TokenType.KEYWORD) && (parseTokens.get(j).getValue().equalsIgnoreCase("IN") || parseTokens.get(j).getValue().equalsIgnoreCase("OUT")))) + constraint.add(parseTokens.get(j)); + Stack stack = new Stack<>(); + for (int k = j + 1; k < parseTokens.size(); k++) { + tokens.add(parseTokens.get(k)); + if (!(parseTokens.get(k).hasType(Token.TokenType.KEYWORD) && (parseTokens.get(k).getValue().equalsIgnoreCase("IN") || parseTokens.get(k).getValue().equalsIgnoreCase("OUT")))) + constraint.add(parseTokens.get(k)); + if (parseTokens.get(k).getValue().equals("(")) { + stack.push("("); + for (int t = k + 1; t < parseTokens.size(); t++) { + tokens.add(parseTokens.get(t)); + if (!(parseTokens.get(t).hasType(Token.TokenType.KEYWORD) && (parseTokens.get(t).getValue().equalsIgnoreCase("IN") || parseTokens.get(t).getValue().equalsIgnoreCase("OUT")))) + constraint.add(parseTokens.get(t)); + if (parseTokens.get(t).getValue().equals("(")) { + stack.push("("); + } else if (parseTokens.get(t).getValue().equals(")")) { + stack.pop(); + if (stack.empty()) { + ii = t; + break; + } + } + } + break; + } + } + break; + } + if ((parseTokens.get(j).hasType(Token.TokenType.SYMBOL) && parseTokens.get(j).getValue().equals(",")) || + (parseTokens.get(j).hasType(Token.TokenType.SYMBOL) && parseTokens.get(j).getValue().equals(")"))) { + ii = j; + break; + } + tokens.add(parseTokens.get(j)); + if (typeMatch && j != typeIdx) { + if (!(parseTokens.get(j).hasType(Token.TokenType.KEYWORD) && (parseTokens.get(j).getValue().equalsIgnoreCase("IN") || parseTokens.get(j).getValue().equalsIgnoreCase("OUT")))) + constraint.add(parseTokens.get(j)); + } + } + child.setTokens(tokens); + child.setConstraint(constraint); + currentNode.addChild(child); + currentNode = child; + } + } + // match procedure return definition + else if (parseTokens.get(i).hasType(Token.TokenType.KEYWORD) && parseTokens.get(i).getValue().equalsIgnoreCase("IS")) { + ASTNode childNode = new ProcedureRetDefNode(); + for (int j = i; j < parseTokens.size(); j++) { + if (parseTokens.get(j).hasType(Token.TokenType.KEYWORD) && parseTokens.get(j).getValue().equalsIgnoreCase("BEGIN")) { + i = j - 1; + break; + } + childNode.addToken(parseTokens.get(j)); + } + currentNode.addChild(childNode); + currentNode = childNode; + } + // match procedure begin + else if (parseTokens.get(i).hasType(Token.TokenType.KEYWORD) && parseTokens.get(i).getValue().equalsIgnoreCase("BEGIN")) { + ASTNode childNode = new ProcedureBeginNode(); + childNode.addToken(parseTokens.get(i)); + currentNode.addChild(childNode); + currentNode = childNode; + } + // match procedure body + else if (currentNode instanceof ProcedureBeginNode) { + for (int ii = i; ii < parseTokens.size(); ii++) { + if (parseTokens.get(ii).hasType(Token.TokenType.KEYWORD) && parseTokens.get(ii).getValue().equalsIgnoreCase("END")) { + i = ii - 1; + break; + } + else if (parseTokens.get(ii).hasType(Token.TokenType.KEYWORD) && parseTokens.get(ii).getValue().equalsIgnoreCase("EXCEPTION")) { + i = ii - 1; + break; + } + else if (parseTokens.get(ii).hasType(Token.TokenType.KEYWORD) && parseTokens.get(ii).getValue().equalsIgnoreCase("IF")) { + int index = -1; + for (int j = ii; j < parseTokens.size(); j++) { + if (parseTokens.get(j).hasType(Token.TokenType.KEYWORD) && parseTokens.get(j).getValue().equalsIgnoreCase("END") + && j + 1 < parseTokens.size() && parseTokens.get(j + 1).hasType(Token.TokenType.KEYWORD) && parseTokens.get(j + 1).getValue().equalsIgnoreCase("IF") + && j + 2 < parseTokens.size() && parseTokens.get(j + 2).hasType(Token.TokenType.SYMBOL) && parseTokens.get(j + 2).getValue().equals(";") ) { + index = j + 2; + break; + } + } + if (index == -1) { + try { + throw new ParseFailedException("There exists syntax error and no END if block is found!"); + } catch (ParseFailedException e) { + e.printStackTrace(); + } + } + else { + ASTNode childNode = parseIFELSE(parseTokens.subList(ii, index + 1)); + currentNode.addChild(childNode); + currentNode = childNode.getDeepestChild(); + ii = index; + } + } + else { + ASTNode childNode = new ProcedurePLStatementNode(); + for (int j = ii; j < parseTokens.size(); j++) { + childNode.addToken(parseTokens.get(j)); + if (parseTokens.get(j).hasType(Token.TokenType.SYMBOL) && parseTokens.get(j).getValue().equals(";")) { + ii = j; + break; + } + } + currentNode.addChild(childNode); + currentNode = childNode; + } + } + } + // match exception + else if (parseTokens.get(i).hasType(Token.TokenType.KEYWORD) && parseTokens.get(i).getValue().equalsIgnoreCase("EXCEPTION")) { + int index = -1; + for (int j = i; j < parseTokens.size(); j++) { + if (parseTokens.get(j).hasType(Token.TokenType.KEYWORD) && parseTokens.get(j).getValue().equalsIgnoreCase("END")) { + index = j; + break; + } + } + if (index == -1) { + try { + throw new ParseFailedException("There exists syntax error and no END block is found!"); + } catch (ParseFailedException e) { + e.printStackTrace(); + } + } + ASTNode childNode = parseException(parseTokens.subList(i, index + 1)); + currentNode.addChild(childNode); + currentNode = childNode.getDeepestChild(); + childNode = new ProcedureEndNode(); + for (int j = index; j < parseTokens.size(); j++) { + childNode.addToken(parseTokens.get(j)); + if (parseTokens.get(j).hasType(Token.TokenType.EOF)) { + break; + } + } + currentNode.addChild(childNode); + currentNode = childNode; + break; + } + // match procedure end + else if (parseTokens.get(i).hasType(Token.TokenType.KEYWORD) && parseTokens.get(i).getValue().equalsIgnoreCase("END")) { + ASTNode childNode = new ProcedureEndNode(); + for (int j = i; j < parseTokens.size(); j++) { + childNode.addToken(parseTokens.get(j)); + if (parseTokens.get(j).hasType(Token.TokenType.EOF)) { + break; + } + } + currentNode.addChild(childNode); + currentNode = childNode; + break; + } + else if (parseTokens.get(i).hasType(Token.TokenType.EOF)) { + break; + } + else { + try { + throw new ParseFailedException("Failed to parse:" + parseTokens.get(i)); + } + catch (ParseFailedException e) { + e.printStackTrace(); + } + } + + } + + return root; + } + }