diff --git a/sqlTranslate/.idea/workspace.xml b/sqlTranslate/.idea/workspace.xml index c8f8f17a4..3306e3462 100644 --- a/sqlTranslate/.idea/workspace.xml +++ b/sqlTranslate/.idea/workspace.xml @@ -4,18 +4,20 @@ - @@ -496,7 +497,8 @@ - diff --git a/sqlTranslate/src/main/java/Generator/OpenGaussGenerator.java b/sqlTranslate/src/main/java/Generator/OpenGaussGenerator.java index a00185ee6..764d557f1 100644 --- a/sqlTranslate/src/main/java/Generator/OpenGaussGenerator.java +++ b/sqlTranslate/src/main/java/Generator/OpenGaussGenerator.java @@ -1,6 +1,6 @@ package Generator; -import Interface.ColumnType; +import Interface.DataType; import Lexer.OracleLexer; import Lexer.Token; import Parser.AST.ASTNode; @@ -15,6 +15,10 @@ import Parser.AST.Drop.DropNode; import Parser.AST.Drop.DropOptionNode; import Parser.AST.Exception.ExceptionActionNode; import Parser.AST.Exception.ExceptionNode; +import Parser.AST.Function.FunctionColumnNode; +import Parser.AST.Function.FunctionEndNode; +import Parser.AST.Function.FunctionNode; +import Parser.AST.Function.FunctionRetDefNode; import Parser.AST.Insert.InsertNode; import Parser.AST.Join.JoinConditionNode; import Parser.AST.Join.JoinSourceTabNode; @@ -82,6 +86,9 @@ public class OpenGaussGenerator { else if (node instanceof ProcedureNode) { return GenProcedureSQL(node); } + else if (node instanceof FunctionNode) { + return GenFunctionSQL(node); + } else { try { throw new GenerateFailedException("Root node:" + node.getClass() + "(Unsupported node type!)"); @@ -154,7 +161,12 @@ public class OpenGaussGenerator { private String GenProcedureSQL(ASTNode node) { visitPL(node); - System.out.println(node.getASTString()); +// System.out.println(node.getASTString()); + return node.toQueryString(); + } + + private String GenFunctionSQL(ASTNode node) { + visitFunc(node); return node.toQueryString(); } @@ -271,6 +283,25 @@ public class OpenGaussGenerator { visitPL(child); } } + + private void visitFunc(ASTNode node) { + if (node instanceof FunctionRetDefNode) { + PLConvert(node); + } + else if (node instanceof ExceptionNode) { + visitException(node); + } + else if (node instanceof FunctionColumnNode) { + ColumnTypeConvert((FunctionColumnNode) node); + } + else if (node instanceof FunctionEndNode) { + PLConvert(node); + } + for (ASTNode child : node.getChildren()) { + visitFunc(child); + } + } + private void PLConvert(ASTNode node) { if (node.checkExistsByRegex("(?i)DBMS_OUTPUT.PUT_LINE\\(.*?\\)")) { String printObj = ""; @@ -359,10 +390,36 @@ public class OpenGaussGenerator { tokens.add(new Token(Token.TokenType.SYMBOL, ";")); node.setTokens(tokens); } + if (node instanceof FunctionEndNode) { + 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); + } + if (node instanceof FunctionRetDefNode) { + List tokens = new ArrayList<>(); + for (Token token: node.getTokens()) { + if (token.hasType(Token.TokenType.KEYWORD) && token.getValue().equalsIgnoreCase("RETURN")) { + tokens.add(new Token(Token.TokenType.KEYWORD, "RETURNS")); + } + else if (token.hasType(Token.TokenType.KEYWORD) && token.getValue().equalsIgnoreCase("IS")) { + tokens.add(new Token(Token.TokenType.KEYWORD, "AS")); + tokens.add(new Token(Token.TokenType.KEYWORD, "$$")); + } + else { + tokens.add(token); + } + } + node.setTokens(tokens); + } } - private void ColumnTypeConvert(ColumnType node) { + private void ColumnTypeConvert(DataType node) { // type convert if (node.getType().getValue().equalsIgnoreCase("NUMBER")) { node.setType(new Token(Token.TokenType.KEYWORD, "NUMERIC")); diff --git a/sqlTranslate/src/main/java/Interface/ColumnType.java b/sqlTranslate/src/main/java/Interface/DataType.java similarity index 82% rename from sqlTranslate/src/main/java/Interface/ColumnType.java rename to sqlTranslate/src/main/java/Interface/DataType.java index 1e82d1016..2174d4a8e 100644 --- a/sqlTranslate/src/main/java/Interface/ColumnType.java +++ b/sqlTranslate/src/main/java/Interface/DataType.java @@ -2,7 +2,7 @@ package Interface; import Lexer.Token; -public interface ColumnType { +public interface DataType { Token getType(); void setType(Token type); void ResetTokensbyNameTypeConstraint(); diff --git a/sqlTranslate/src/main/java/Lexer/OracleLexer.java b/sqlTranslate/src/main/java/Lexer/OracleLexer.java index 6f63c9604..5f0c77948 100644 --- a/sqlTranslate/src/main/java/Lexer/OracleLexer.java +++ b/sqlTranslate/src/main/java/Lexer/OracleLexer.java @@ -37,6 +37,8 @@ public class OracleLexer { , "EXCEPTION", "SQLERRM", "ZERO_DIVIDE", "INVALID_NUMBER", "OTHERS" // Procedure , "PROCEDURE", "IS", "IN", "OUT", "$$", "COMMIT", "ROLLBACK", "EXEC", "EXECUTE" + // function + , "FUNCTION", "RETURN" }; private static final Pattern TOKEN_PATTERN = Pattern.compile( "(NUMBER\\(.*?\\))|" + // NUMBER() function @@ -55,6 +57,7 @@ public class OracleLexer { "(MIN\\(.*?\\))|" + // MIN() function "(\\|\\|\\s*'.*?'\\s*\\|\\|)|" + // || '*' || "(DBMS_OUTPUT.PUT_LINE\\(.*?\\))|" + // DBMS_OUTPUT.PUT_LINE() function + "(LENGTH\\(.*?\\))|" + // LENGTH() function "(NOT NULL)|" + "(PRIMARY KEY)|" + @@ -177,6 +180,9 @@ public class OracleLexer { } else if (tokenValue.matches("(?i)DBMS_OUTPUT.PUT_LINE\\(.*?\\)")) { // RAW() function, CASE_INSENSITIVE return new Token(Token.TokenType.KEYWORD, tokenValue); + } else if (tokenValue.matches("(?i)LENGTH\\(.*?\\)")) { + // RAW() function, CASE_INSENSITIVE + return new Token(Token.TokenType.KEYWORD, tokenValue); } else if (isKeyword(tokenValue)) { diff --git a/sqlTranslate/src/main/java/Main.java b/sqlTranslate/src/main/java/Main.java index 1f62f6ff3..5952be8f8 100644 --- a/sqlTranslate/src/main/java/Main.java +++ b/sqlTranslate/src/main/java/Main.java @@ -60,33 +60,40 @@ public class Main { // " 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" + - " /"; +// 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" + +// " /"; + String sql = "CREATE OR REPLACE FUNCTION string_length (\n" + + " input_string VARCHAR2\n" + + ") RETURN NUMBER IS\n" + + "BEGIN\n" + + " RETURN LENGTH(input_string);\n" + + "END;\n" + + "/"; OracleLexer lexer = new OracleLexer(sql); lexer.printTokens(); OracleParser parser = new OracleParser(lexer); diff --git a/sqlTranslate/src/main/java/Parser/AST/AlterTable/AlterAddColumnNode.java b/sqlTranslate/src/main/java/Parser/AST/AlterTable/AlterAddColumnNode.java index 0eb23739e..ccd44a30b 100644 --- a/sqlTranslate/src/main/java/Parser/AST/AlterTable/AlterAddColumnNode.java +++ b/sqlTranslate/src/main/java/Parser/AST/AlterTable/AlterAddColumnNode.java @@ -1,13 +1,13 @@ package Parser.AST.AlterTable; -import Interface.ColumnType; +import Interface.DataType; import Lexer.Token; import Parser.AST.ASTNode; import java.util.ArrayList; import java.util.List; -public class AlterAddColumnNode extends ASTNode implements ColumnType { +public class AlterAddColumnNode extends ASTNode implements DataType { private Token name; private Token type; private List constraint; diff --git a/sqlTranslate/src/main/java/Parser/AST/AlterTable/AlterModifyColumnNode.java b/sqlTranslate/src/main/java/Parser/AST/AlterTable/AlterModifyColumnNode.java index 495b174d9..2ff319ba1 100644 --- a/sqlTranslate/src/main/java/Parser/AST/AlterTable/AlterModifyColumnNode.java +++ b/sqlTranslate/src/main/java/Parser/AST/AlterTable/AlterModifyColumnNode.java @@ -1,13 +1,13 @@ package Parser.AST.AlterTable; -import Interface.ColumnType; +import Interface.DataType; import Lexer.Token; import Parser.AST.ASTNode; import java.util.ArrayList; import java.util.List; -public class AlterModifyColumnNode extends ASTNode implements ColumnType { +public class AlterModifyColumnNode extends ASTNode implements DataType { private Token name; private Token type; diff --git a/sqlTranslate/src/main/java/Parser/AST/CreateTable/ColumnNode.java b/sqlTranslate/src/main/java/Parser/AST/CreateTable/ColumnNode.java index bd6a1e346..1bd8ad177 100644 --- a/sqlTranslate/src/main/java/Parser/AST/CreateTable/ColumnNode.java +++ b/sqlTranslate/src/main/java/Parser/AST/CreateTable/ColumnNode.java @@ -1,13 +1,13 @@ package Parser.AST.CreateTable; -import Interface.ColumnType; +import Interface.DataType; import Lexer.Token; import Parser.AST.ASTNode; import java.util.ArrayList; import java.util.List; -public class ColumnNode extends ASTNode implements ColumnType { +public class ColumnNode extends ASTNode implements DataType { private Token name; private Token type; private List constraint; diff --git a/sqlTranslate/src/main/java/Parser/AST/Function/FunctionColumnNode.java b/sqlTranslate/src/main/java/Parser/AST/Function/FunctionColumnNode.java index b00eaca4b..0cefba2bc 100644 --- a/sqlTranslate/src/main/java/Parser/AST/Function/FunctionColumnNode.java +++ b/sqlTranslate/src/main/java/Parser/AST/Function/FunctionColumnNode.java @@ -1,14 +1,13 @@ package Parser.AST.Function; -import Interface.ColumnType; +import Interface.DataType; import Lexer.Token; import Parser.AST.ASTNode; -import Parser.AST.Procedure.ProcedureColumnNode; import java.util.ArrayList; import java.util.List; -public class FunctionColumnNode extends ASTNode implements ColumnType { +public class FunctionColumnNode extends ASTNode implements DataType { private Token name; private Token type; private List constraint; @@ -16,6 +15,7 @@ public class FunctionColumnNode extends ASTNode implements ColumnType { public FunctionColumnNode() { super(); setTokens(new ArrayList<>()); + setInOut(new ArrayList<>()); } public FunctionColumnNode(ASTNode node) { diff --git a/sqlTranslate/src/main/java/Parser/AST/Function/FunctionNameNode.java b/sqlTranslate/src/main/java/Parser/AST/Function/FunctionNameNode.java index e7fd6e639..f5defc464 100644 --- a/sqlTranslate/src/main/java/Parser/AST/Function/FunctionNameNode.java +++ b/sqlTranslate/src/main/java/Parser/AST/Function/FunctionNameNode.java @@ -22,7 +22,7 @@ public class FunctionNameNode extends ASTNode { @Override public void visit(ASTNode node, StringBuilder queryString) { - queryString.append(toString() + " "); + queryString.append(toString() + " ( "); for (ASTNode child : node.getChildren()) { child.visit(child, queryString); } diff --git a/sqlTranslate/src/main/java/Parser/AST/Function/FunctionRetDefNode.java b/sqlTranslate/src/main/java/Parser/AST/Function/FunctionRetDefNode.java index 6d33b6a01..dd0b294a5 100644 --- a/sqlTranslate/src/main/java/Parser/AST/Function/FunctionRetDefNode.java +++ b/sqlTranslate/src/main/java/Parser/AST/Function/FunctionRetDefNode.java @@ -1,12 +1,14 @@ package Parser.AST.Function; +import Interface.DataType; import Lexer.Token; import Parser.AST.ASTNode; import java.util.ArrayList; import java.util.List; -public class FunctionRetDefNode extends ASTNode { +public class FunctionRetDefNode extends ASTNode implements DataType { + private Token type; public FunctionRetDefNode() { super(); setTokens(new ArrayList<>()); @@ -22,9 +24,28 @@ public class FunctionRetDefNode extends ASTNode { @Override public void visit(ASTNode node, StringBuilder queryString) { - queryString.append(toString() + " "); + queryString.append(") " + toString() + " "); for (ASTNode child : node.getChildren()) { child.visit(child, queryString); } } + + @Override + public Token getType() { + return type; + } + + @Override + public void setType(Token type) { + this.type = type; + } + + @Override + public void ResetTokensbyNameTypeConstraint() { + List tokens = new ArrayList<>(); + tokens.add(new Token(Token.TokenType.KEYWORD, "RETURN")); + tokens.add(type); + tokens.add(new Token(Token.TokenType.KEYWORD, "IS")); + setTokens(tokens); + } } diff --git a/sqlTranslate/src/main/java/Parser/AST/Procedure/ProcedureColumnNode.java b/sqlTranslate/src/main/java/Parser/AST/Procedure/ProcedureColumnNode.java index e158fe8a1..38a1b5aad 100644 --- a/sqlTranslate/src/main/java/Parser/AST/Procedure/ProcedureColumnNode.java +++ b/sqlTranslate/src/main/java/Parser/AST/Procedure/ProcedureColumnNode.java @@ -1,14 +1,13 @@ package Parser.AST.Procedure; -import Interface.ColumnType; +import Interface.DataType; import Lexer.Token; import Parser.AST.ASTNode; -import Parser.AST.CreateTable.CRTEndNode; import java.util.ArrayList; import java.util.List; -public class ProcedureColumnNode extends ASTNode implements ColumnType { +public class ProcedureColumnNode extends ASTNode implements DataType { private Token name; private Token type; private List constraint; diff --git a/sqlTranslate/src/main/java/Parser/OracleParser.java b/sqlTranslate/src/main/java/Parser/OracleParser.java index 4677e7980..c71d21eff 100644 --- a/sqlTranslate/src/main/java/Parser/OracleParser.java +++ b/sqlTranslate/src/main/java/Parser/OracleParser.java @@ -19,6 +19,7 @@ import Parser.AST.Drop.DropObjNameNode; import Parser.AST.Drop.DropNode; import Parser.AST.Drop.DropOptionNode; import Parser.AST.Exception.*; +import Parser.AST.Function.*; import Parser.AST.IFELSIF.*; import Parser.AST.Insert.InsertDataNode; import Parser.AST.Insert.InsertEndNode; @@ -80,6 +81,12 @@ public class OracleParser { ) { return parseProcedure(lexer.getTokens()); + } else if ( + (lexer.getTokens().get(0).getValue().equalsIgnoreCase("CREATE") && lexer.getTokens().get(1).getValue().equalsIgnoreCase("FUNCTION")) + || (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("FUNCTION")) + ) + { + return parseFunction(lexer.getTokens()); } else { try { @@ -1859,7 +1866,8 @@ public class OracleParser { childNode = new ProcedureEndNode(); for (int j = index; j < parseTokens.size(); j++) { childNode.addToken(parseTokens.get(j)); - if (parseTokens.get(j).hasType(Token.TokenType.EOF)) { + if (parseTokens.get(j).hasType(Token.TokenType.SYMBOL) && parseTokens.get(j).getValue().equals(";")) { + i = j; break; } } @@ -1872,7 +1880,8 @@ public class OracleParser { 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)) { + if (parseTokens.get(j).hasType(Token.TokenType.SYMBOL) && parseTokens.get(j).getValue().equals(";")) { + i = j; break; } } @@ -1897,4 +1906,240 @@ public class OracleParser { return root; } + /** + * Create FUNCTION + * Grammar: CREATE OR REPLACE FUNCTION function_name ( + * parameter1 [IN | OUT] datatype1 , + * parameter2 [IN | OUT] datatype2 , + * ... + * ) RETURN return_datatype IS + * BEGIN + * -- function body + * RETURN result; + * END; + * / + * Example: CREATE OR REPLACE FUNCTION string_length ( + * input_string VARCHAR2 + * ) RETURN NUMBER IS + * BEGIN + * RETURN LENGTH(input_string); + * END; + * / + */ + private ASTNode parseFunction(List parseTokens) { + ASTNode root = new FunctionNode(); + ASTNode currentNode = root; + for (int i = 0; i < parseTokens.size(); i++) { + // CREATE [OR REPLACE] FUNCTION + 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("FUNCTION")) { + i = j; + break; + } + } + } + // match function name + else if (currentNode == root && parseTokens.get(i).hasType(Token.TokenType.IDENTIFIER)) { + ASTNode childNode = new FunctionNameNode(); + childNode.addToken(parseTokens.get(i)); + currentNode.addChild(childNode); + currentNode = childNode; + i++; + } + // match parameters + else if (currentNode instanceof FunctionNameNode && parseTokens.get(i).hasType(Token.TokenType.IDENTIFIER)) { + for (int ii = i; ii < parseTokens.size(); ii++) { + if (parseTokens.get(ii).hasType(Token.TokenType.KEYWORD) && parseTokens.get(ii).getValue().equalsIgnoreCase("RETURN")) { + i = ii - 1; + break; + } + List tokens = new ArrayList<>(); + FunctionColumnNode child = new FunctionColumnNode(); + 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 return definition + else if (currentNode instanceof FunctionColumnNode && parseTokens.get(i).hasType(Token.TokenType.KEYWORD) && parseTokens.get(i).getValue().equalsIgnoreCase("RETURN")) { + FunctionRetDefNode childNode = new FunctionRetDefNode(); + for (int j = i; j < parseTokens.size(); j++) { + if (parseTokens.get(j).hasType(Token.TokenType.KEYWORD) && parseTokens.get(j).getValue().equalsIgnoreCase("RETURN")) { + childNode.addToken(parseTokens.get(j)); + } + else if (parseTokens.get(j).hasType(Token.TokenType.KEYWORD) && parseTokens.get(j).getValue().equals("IS")) { + childNode.addToken(parseTokens.get(j)); + i = j; + break; + } + else { + childNode.addToken(parseTokens.get(j)); + childNode.setType(parseTokens.get(j)); + } + } + currentNode.addChild(childNode); + currentNode = childNode; + } + // match function begin + else if (currentNode instanceof FunctionRetDefNode && parseTokens.get(i).hasType(Token.TokenType.KEYWORD) && parseTokens.get(i).getValue().equalsIgnoreCase("BEGIN")) { + ASTNode childNode = new FunctionBeginNode(); + childNode.addToken(parseTokens.get(i)); + currentNode.addChild(childNode); + currentNode = childNode; + } + // match function body + else if (currentNode instanceof FunctionBeginNode) { + 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 if (parseTokens.get(ii).hasType(Token.TokenType.KEYWORD) && parseTokens.get(ii).getValue().equalsIgnoreCase("RETURN")) { + ASTNode childNode = new FunctionReturnNode(); + 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; + } + else { + ASTNode childNode = new FunctionBodyNode(); + 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 function end + else if (parseTokens.get(i).hasType(Token.TokenType.KEYWORD) && parseTokens.get(i).getValue().equalsIgnoreCase("END")) { + ASTNode childNode = new FunctionEndNode(); + for (int j = i; j < parseTokens.size(); j++) { + childNode.addToken(parseTokens.get(j)); + if (parseTokens.get(j).hasType(Token.TokenType.SYMBOL) && parseTokens.get(j).getValue().equals(";")) { + i = j; + 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; + } + + }