742 lines
14 KiB
Python
742 lines
14 KiB
Python
from parser import *
|
|
|
|
|
|
NAME = Terminal(
|
|
Re.seq(
|
|
Re.set(("a", "z"), ("A", "Z"), "_"),
|
|
Re.set(("a", "z"), ("A", "Z"), ("0", "9"), "_").star(),
|
|
),
|
|
)
|
|
|
|
STRING = Terminal(
|
|
Re.seq(
|
|
Re.literal("'"),
|
|
(~Re.set("'", "\\") | (Re.set("\\") + Re.any())).star(),
|
|
Re.literal("'"),
|
|
),
|
|
highlight=highlight.string.quoted,
|
|
)
|
|
|
|
NUMBER = Terminal(
|
|
Re.seq(
|
|
Re.set(("0", "9")).plus(),
|
|
Re.seq(
|
|
Re.literal("."),
|
|
Re.set(("0", "9")).plus(),
|
|
).question(),
|
|
Re.seq(
|
|
Re.set("e", "E"),
|
|
Re.set("+", "-").question(),
|
|
Re.set(("0", "9")).plus(),
|
|
).question(),
|
|
),
|
|
highlight=highlight.constant.numeric,
|
|
)
|
|
|
|
OR = Terminal("or")
|
|
AND = Terminal("and")
|
|
NOT = Terminal("not")
|
|
COMPARISON = Terminal(
|
|
Re.literal("=")
|
|
| Re.literal("<>")
|
|
| Re.literal("<")
|
|
| Re.literal(">")
|
|
| Re.literal("<=")
|
|
| Re.literal(">=")
|
|
)
|
|
PLUS = Terminal("+")
|
|
MINUS = Terminal("-")
|
|
STAR = Terminal("*")
|
|
SLASH = Terminal("/")
|
|
|
|
precedence = [
|
|
(Assoc.LEFT, ["OR"]),
|
|
(Assoc.LEFT, ["AND"]),
|
|
(Assoc.LEFT, ["NOT"]),
|
|
(Assoc.LEFT, ["COMPARISON"]),
|
|
(Assoc.LEFT, ["PLUS", "MINUS"]),
|
|
(Assoc.LEFT, ["STAR", "SLASH"]),
|
|
# TODO: Unary minus
|
|
]
|
|
|
|
ALL = Terminal("all")
|
|
AMMSC = Terminal("ammsc")
|
|
ANY = Terminal("any")
|
|
ASC = Terminal("asc")
|
|
AUTHORIZATION = Terminal("authorization")
|
|
BETWEEN = Terminal("between")
|
|
BY = Terminal("by")
|
|
CHARACTER = Terminal("character")
|
|
CHECK = Terminal("check")
|
|
CLOSE = Terminal("close")
|
|
COMMIT = Terminal("commit")
|
|
CONTINUE = Terminal("continue")
|
|
CREATE = Terminal("create")
|
|
CURRENT = Terminal("current")
|
|
CURSOR = Terminal("cursor")
|
|
DECIMAL = Terminal("decimal")
|
|
DECLARE = Terminal("declare")
|
|
DEFAULT = Terminal("default")
|
|
DELETE = Terminal("delete")
|
|
DESC = Terminal("desc")
|
|
DISTINCT = Terminal("distinct")
|
|
DOUBLE = Terminal("double")
|
|
ESCAPE = Terminal("escape")
|
|
EXISTS = Terminal("exists")
|
|
FETCH = Terminal("fetch")
|
|
FLOAT = Terminal("float")
|
|
FOR = Terminal("for")
|
|
FOREIGN = Terminal("foreign")
|
|
FOUND = Terminal("found")
|
|
FROM = Terminal("from")
|
|
GOTO = Terminal("goto")
|
|
GRANT = Terminal("grant")
|
|
GROUP = Terminal("group")
|
|
HAVING = Terminal("having")
|
|
IN = Terminal("in")
|
|
INDICATOR = Terminal("indicator")
|
|
INSERT = Terminal("insert")
|
|
INTEGER = Terminal("integer")
|
|
INTO = Terminal("into")
|
|
IS = Terminal("is")
|
|
KEY = Terminal("key")
|
|
LANGUAGE = Terminal("language")
|
|
LIKE = Terminal("like")
|
|
NULL = Terminal("null")
|
|
NUMERIC = Terminal("numeric")
|
|
OF = Terminal("of")
|
|
ON = Terminal("on")
|
|
OPEN = Terminal("open")
|
|
OPTION = Terminal("option")
|
|
ORDER = Terminal("order")
|
|
PARAMETER = Terminal("parameter")
|
|
PRECISION = Terminal("precision")
|
|
PRIMARY = Terminal("primary")
|
|
PRIVILEGES = Terminal("privileges")
|
|
PROCEDURE = Terminal("procedure")
|
|
PUBLIC = Terminal("public")
|
|
REAL = Terminal("real")
|
|
REFERENCES = Terminal("references")
|
|
ROLLBACK = Terminal("rollback")
|
|
SCHEMA = Terminal("schema")
|
|
SELECT = Terminal("select")
|
|
SET = Terminal("set")
|
|
SMALLINT = Terminal("smallint")
|
|
SOME = Terminal("some")
|
|
SQLCODE = Terminal("sqlcode")
|
|
SQLERROR = Terminal("sqlerror")
|
|
TABLE = Terminal("table")
|
|
TO = Terminal("to")
|
|
UNION = Terminal("union")
|
|
UNIQUE = Terminal("unique")
|
|
UPDATE = Terminal("update")
|
|
USER = Terminal("user")
|
|
VALUES = Terminal("values")
|
|
VIEW = Terminal("view")
|
|
WHENEVER = Terminal("whenever")
|
|
WHERE = Terminal("where")
|
|
WITH = Terminal("with")
|
|
WORK = Terminal("work")
|
|
|
|
SEMICOLON = Terminal(";")
|
|
LPAREN = Terminal("(")
|
|
RPAREN = Terminal(")")
|
|
COMMA = Terminal(",")
|
|
EQUAL = Terminal("=")
|
|
DOT = Terminal(".")
|
|
AS = Terminal("as")
|
|
|
|
|
|
@rule
|
|
def sql_list():
|
|
return alt(
|
|
sql + SEMICOLON,
|
|
sql_list + sql + SEMICOLON,
|
|
)
|
|
|
|
|
|
@rule
|
|
def sql():
|
|
return alt(
|
|
schema,
|
|
cursor_def,
|
|
manipulative_statement,
|
|
WHENEVER + NOT + FOUND + when_action,
|
|
WHENEVER + SQLERROR + when_action,
|
|
)
|
|
|
|
|
|
@rule
|
|
def schema():
|
|
seq(
|
|
CREATE,
|
|
SCHEMA,
|
|
AUTHORIZATION,
|
|
user,
|
|
opt(schema_element_list),
|
|
)
|
|
|
|
|
|
@rule(transparent=True)
|
|
def schema_element_list() -> Rule:
|
|
return schema_element | (schema_element_list + schema_element)
|
|
|
|
|
|
@rule
|
|
def schema_element():
|
|
return base_table_def | view_def | privilege_def
|
|
|
|
|
|
@rule
|
|
def base_table_def():
|
|
return seq(
|
|
CREATE,
|
|
TABLE,
|
|
table,
|
|
LPAREN,
|
|
base_table_element_commalist,
|
|
RPAREN,
|
|
)
|
|
|
|
|
|
@rule(transparent=True)
|
|
def base_table_element_commalist() -> Rule:
|
|
return opt(base_table_element_commalist + COMMA) + base_table_element
|
|
|
|
|
|
@rule(transparent=True)
|
|
def base_table_element():
|
|
return column_def | table_constraint_def
|
|
|
|
|
|
@rule
|
|
def column_def():
|
|
return column + data_type + opt(column_def_list)
|
|
|
|
|
|
@rule(transparent=True)
|
|
def column_def_list() -> Rule:
|
|
return alt(
|
|
column_def_list + column_def_opt,
|
|
column_def_opt,
|
|
)
|
|
|
|
|
|
@rule
|
|
def column_def_opt():
|
|
return alt(
|
|
NOT + opt(NULL + opt(alt(UNIQUE, PRIMARY + KEY))),
|
|
DEFAULT + literal,
|
|
DEFAULT + NULL,
|
|
DEFAULT + USER,
|
|
CHECK + LPAREN + search_condition + RPAREN,
|
|
REFERENCES + table,
|
|
REFERENCES + table + LPAREN + column_commalist + RPAREN,
|
|
)
|
|
|
|
|
|
@rule
|
|
def table_constraint_def():
|
|
return alt(
|
|
UNIQUE + LPAREN + column_commalist + RPAREN,
|
|
PRIMARY + KEY + LPAREN + column_commalist + RPAREN,
|
|
seq(
|
|
FOREIGN,
|
|
KEY,
|
|
LPAREN,
|
|
column_commalist,
|
|
RPAREN,
|
|
REFERENCES,
|
|
table,
|
|
opt(LPAREN + column_commalist + RPAREN),
|
|
),
|
|
CHECK + LPAREN + search_condition + RPAREN,
|
|
)
|
|
|
|
|
|
@rule(transparent=True)
|
|
def column_commalist() -> Rule:
|
|
return opt(column_commalist + COMMA) + column
|
|
|
|
|
|
@rule
|
|
def view_def():
|
|
return seq(
|
|
CREATE,
|
|
VIEW,
|
|
table,
|
|
opt(LPAREN + column_commalist + RPAREN),
|
|
AS,
|
|
query_spec,
|
|
opt(with_check_option),
|
|
)
|
|
|
|
|
|
@rule
|
|
def with_check_option():
|
|
return WITH + CHECK + OPTION
|
|
|
|
|
|
@rule
|
|
def privilege_def():
|
|
return seq(
|
|
GRANT,
|
|
privileges,
|
|
ON,
|
|
table,
|
|
TO,
|
|
grantee_commalist,
|
|
opt(with_grant_option),
|
|
)
|
|
|
|
|
|
@rule
|
|
def with_grant_option():
|
|
return WITH + GRANT + OPTION
|
|
|
|
|
|
@rule
|
|
def privileges():
|
|
return (ALL + PRIVILEGES) | ALL | operation_commalist
|
|
|
|
|
|
@rule(transparent=True)
|
|
def operation_commalist() -> Rule:
|
|
return opt(operation_commalist + COMMA) + operation
|
|
|
|
|
|
@rule
|
|
def operation():
|
|
return alt(
|
|
SELECT,
|
|
INSERT,
|
|
DELETE,
|
|
UPDATE + opt(LPAREN + column_commalist + RPAREN),
|
|
REFERENCES + opt(LPAREN + column_commalist + RPAREN),
|
|
)
|
|
|
|
|
|
@rule(transparent=True)
|
|
def grantee_commalist() -> Rule:
|
|
return opt(grantee_commalist + COMMA) + grantee
|
|
|
|
|
|
@rule
|
|
def grantee():
|
|
return PUBLIC | user
|
|
|
|
|
|
@rule
|
|
def cursor_def():
|
|
return seq(
|
|
DECLARE,
|
|
cursor,
|
|
CURSOR,
|
|
FOR,
|
|
query_exp,
|
|
opt(order_by_clause),
|
|
)
|
|
|
|
|
|
@rule
|
|
def order_by_clause():
|
|
return ORDER + BY + ordering_spec_commalist
|
|
|
|
|
|
@rule
|
|
def ordering_spec_commalist() -> Rule:
|
|
return opt(ordering_spec_commalist + COMMA) + ordering_spec
|
|
|
|
|
|
@rule
|
|
def ordering_spec():
|
|
return (NUMBER + opt(asc_desc)) | (column_ref + opt(asc_desc))
|
|
|
|
|
|
@rule
|
|
def asc_desc():
|
|
return ASC | DESC
|
|
|
|
|
|
@rule
|
|
def manipulative_statement():
|
|
return alt(
|
|
close_statement,
|
|
commit_statement,
|
|
delete_statement_positioned,
|
|
delete_statement_searched,
|
|
fetch_statement,
|
|
insert_statement,
|
|
open_statement,
|
|
rollback_statement,
|
|
select_statement,
|
|
update_statement_positioned,
|
|
update_statement_searched,
|
|
)
|
|
|
|
|
|
@rule
|
|
def close_statement():
|
|
return CLOSE + cursor
|
|
|
|
|
|
@rule
|
|
def commit_statement():
|
|
return COMMIT + opt(WORK)
|
|
|
|
|
|
@rule
|
|
def delete_statement_positioned():
|
|
return DELETE + FROM + table + WHERE + CURRENT + OF + cursor
|
|
|
|
|
|
@rule
|
|
def delete_statement_searched():
|
|
return DELETE + FROM + table + opt(where_clause)
|
|
|
|
|
|
@rule
|
|
def fetch_statement():
|
|
return FETCH + cursor + INTO + target_commalist
|
|
|
|
|
|
@rule
|
|
def insert_statement():
|
|
return seq(
|
|
INSERT,
|
|
INTO,
|
|
table,
|
|
opt(LPAREN, column_commalist, RPAREN),
|
|
values_or_query_spec,
|
|
)
|
|
|
|
|
|
@rule
|
|
def values_or_query_spec():
|
|
return alt(
|
|
VALUES + LPAREN + insert_atom_commalist + RPAREN,
|
|
query_spec,
|
|
)
|
|
|
|
|
|
@rule(transparent=True)
|
|
def insert_atom_commalist() -> Rule:
|
|
return opt(insert_atom_commalist + COMMA) + insert_atom
|
|
|
|
|
|
@rule
|
|
def insert_atom():
|
|
return atom | NULL
|
|
|
|
|
|
@rule
|
|
def open_statement():
|
|
return OPEN + cursor
|
|
|
|
|
|
@rule
|
|
def rollback_statement():
|
|
return ROLLBACK + opt(WORK)
|
|
|
|
|
|
@rule
|
|
def select_statement():
|
|
return seq(
|
|
SELECT,
|
|
opt(all_distinct),
|
|
selection,
|
|
INTO,
|
|
target_commalist,
|
|
table_exp,
|
|
)
|
|
|
|
|
|
@rule(transparent=True)
|
|
def all_distinct():
|
|
return ALL | DISTINCT
|
|
|
|
|
|
@rule
|
|
def update_statement_positioned():
|
|
return UPDATE + table + SET + assignment_commalist + WHERE + CURRENT + OF + cursor
|
|
|
|
|
|
@rule(transparent=True)
|
|
def assignment_commalist() -> Rule:
|
|
return opt(assignment_commalist + COMMA) + assignment
|
|
|
|
|
|
@rule
|
|
def assignment():
|
|
return column + EQUAL + (scalar_exp | NULL)
|
|
|
|
|
|
@rule
|
|
def update_statement_searched():
|
|
return UPDATE + table + SET + assignment_commalist + opt(where_clause)
|
|
|
|
|
|
@rule(transparent=True)
|
|
def target_commalist() -> Rule:
|
|
# TODO: So many commalists, it would be great if we could make this a
|
|
# macro or something.
|
|
return opt(target_commalist + COMMA) + target
|
|
|
|
|
|
@rule
|
|
def target():
|
|
return parameter_ref
|
|
|
|
|
|
# /* query expressions */
|
|
|
|
|
|
@rule
|
|
def query_exp() -> Rule:
|
|
return query_term | (query_exp + UNION + opt(ALL) + query_term)
|
|
|
|
|
|
@rule
|
|
def query_term():
|
|
return query_spec | (LPAREN + query_exp + RPAREN)
|
|
|
|
|
|
@rule
|
|
def query_spec():
|
|
return SELECT + opt(all_distinct) + selection + table_exp
|
|
|
|
|
|
@rule
|
|
def selection():
|
|
return scalar_exp_commalist | STAR
|
|
|
|
|
|
@rule
|
|
def table_exp():
|
|
return from_clause + opt(where_clause) + opt(group_by_clause) + opt(having_clause)
|
|
|
|
|
|
@rule
|
|
def from_clause():
|
|
return FROM + table_ref_commalist
|
|
|
|
|
|
@rule(transparent=True)
|
|
def table_ref_commalist() -> Rule:
|
|
return opt(table_ref_commalist + COMMA) + table_ref
|
|
|
|
|
|
@rule
|
|
def table_ref():
|
|
return table + opt(range_variable)
|
|
|
|
|
|
@rule
|
|
def where_clause():
|
|
return WHERE + search_condition
|
|
|
|
|
|
@rule
|
|
def group_by_clause():
|
|
return GROUP + BY + column_ref_commalist
|
|
|
|
|
|
@rule(transparent=True)
|
|
def column_ref_commalist() -> Rule:
|
|
return opt(column_ref_commalist + COMMA) + column_ref
|
|
|
|
|
|
@rule
|
|
def having_clause():
|
|
return HAVING + search_condition
|
|
|
|
|
|
# /* search conditions */
|
|
|
|
|
|
@rule
|
|
def search_condition() -> Rule:
|
|
return alt(
|
|
search_condition + OR + search_condition,
|
|
search_condition + AND + search_condition,
|
|
NOT + search_condition,
|
|
LPAREN + search_condition + RPAREN,
|
|
predicate,
|
|
)
|
|
|
|
|
|
@rule
|
|
def predicate():
|
|
return alt(
|
|
comparison_predicate,
|
|
between_predicate,
|
|
like_predicate,
|
|
test_for_null,
|
|
in_predicate,
|
|
all_or_any_predicate,
|
|
existence_test,
|
|
)
|
|
|
|
|
|
@rule
|
|
def comparison_predicate():
|
|
return scalar_exp + COMPARISON + (scalar_exp | subquery)
|
|
|
|
|
|
@rule
|
|
def between_predicate():
|
|
return scalar_exp + opt(NOT) + BETWEEN + scalar_exp + AND + scalar_exp
|
|
|
|
|
|
@rule
|
|
def like_predicate():
|
|
return scalar_exp + opt(NOT) + LIKE + atom + opt(escape)
|
|
|
|
|
|
@rule
|
|
def escape():
|
|
return ESCAPE + atom
|
|
|
|
|
|
@rule
|
|
def test_for_null():
|
|
return column_ref + IS + opt(NOT) + NULL
|
|
|
|
|
|
@rule
|
|
def in_predicate():
|
|
return scalar_exp + opt(NOT) + IN + LPAREN + alt(subquery | atom_commalist) + RPAREN
|
|
|
|
|
|
@rule
|
|
def atom_commalist() -> Rule:
|
|
return opt(atom_commalist + COMMA) + atom
|
|
|
|
|
|
@rule
|
|
def all_or_any_predicate():
|
|
return scalar_exp + COMPARISON + any_all_some + subquery
|
|
|
|
|
|
@rule(transparent=True)
|
|
def any_all_some():
|
|
return ANY | ALL | SOME
|
|
|
|
|
|
@rule
|
|
def existence_test():
|
|
return EXISTS + subquery
|
|
|
|
|
|
@rule
|
|
def subquery():
|
|
return LPAREN + SELECT + opt(all_distinct) + selection + table_exp + RPAREN
|
|
|
|
|
|
# /* scalar expressions */
|
|
|
|
|
|
@rule
|
|
def scalar_exp():
|
|
return alt(
|
|
scalar_exp + (PLUS | MINUS | STAR | SLASH) + scalar_exp,
|
|
PLUS + scalar_exp,
|
|
MINUS + scalar_exp,
|
|
atom,
|
|
column_ref,
|
|
function_ref,
|
|
LPAREN + scalar_exp + RPAREN,
|
|
)
|
|
|
|
|
|
@rule
|
|
def scalar_exp_commalist() -> Rule:
|
|
return opt(scalar_exp_commalist + COMMA) + scalar_exp
|
|
|
|
|
|
@rule
|
|
def atom():
|
|
return parameter_ref | literal | USER
|
|
|
|
|
|
@rule
|
|
def parameter_ref():
|
|
return parameter | (parameter + parameter) | (parameter + INDICATOR + parameter)
|
|
|
|
|
|
@rule
|
|
def function_ref():
|
|
return alt(
|
|
AMMSC + LPAREN + STAR + RPAREN,
|
|
AMMSC + LPAREN + DISTINCT + column_ref + RPAREN,
|
|
AMMSC + LPAREN + ALL + scalar_exp + RPAREN,
|
|
AMMSC + LPAREN + scalar_exp + RPAREN,
|
|
)
|
|
|
|
|
|
@rule
|
|
def literal():
|
|
return STRING | NUMBER
|
|
|
|
|
|
# /* miscellaneous */
|
|
|
|
|
|
@rule
|
|
def table():
|
|
return opt(NAME + DOT) + NAME
|
|
|
|
|
|
@rule
|
|
def column_ref():
|
|
return opt(opt(NAME + DOT) + NAME + DOT) + NAME
|
|
|
|
|
|
# /* data types */
|
|
|
|
|
|
@rule
|
|
def data_type():
|
|
return alt(
|
|
CHARACTER + opt(LPAREN + NUMBER + RPAREN),
|
|
NUMERIC + opt(LPAREN + NUMBER + opt(COMMA + NUMBER) + RPAREN),
|
|
DECIMAL + opt(LPAREN + NUMBER + opt(COMMA + NUMBER) + RPAREN),
|
|
INTEGER,
|
|
SMALLINT,
|
|
FLOAT + opt(LPAREN + NUMBER + RPAREN),
|
|
REAL,
|
|
DOUBLE + PRECISION,
|
|
)
|
|
|
|
|
|
# /* the various things you can name */
|
|
|
|
|
|
@rule
|
|
def column():
|
|
return NAME
|
|
|
|
|
|
@rule
|
|
def cursor():
|
|
return NAME
|
|
|
|
|
|
@rule
|
|
def parameter():
|
|
return PARAMETER # :name handled in parser???
|
|
|
|
|
|
@rule
|
|
def range_variable():
|
|
return NAME
|
|
|
|
|
|
@rule
|
|
def user():
|
|
return NAME
|
|
|
|
|
|
@rule
|
|
def when_action():
|
|
return (GOTO + NAME) | CONTINUE
|