Messing with a SQL grammar in an alternative style
Why do we have classes again, hmm?
This commit is contained in:
parent
ed4d299b22
commit
d6f1e7aba1
1 changed files with 742 additions and 0 deletions
742
sql.py
Normal file
742
sql.py
Normal file
|
|
@ -0,0 +1,742 @@
|
|||
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
|
||||
Loading…
Add table
Add a link
Reference in a new issue