Move examples

This commit is contained in:
John Doty 2024-11-10 07:15:14 -08:00
parent 030406b2fb
commit 182c29c2a7
3 changed files with 1 additions and 0 deletions

1
examples/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
tree-sitter-fine/

537
examples/grammar.py Normal file
View file

@ -0,0 +1,537 @@
# This is an example grammar.
import parser
from parser import (
Assoc,
Grammar,
Re,
Rule,
Terminal,
TriviaMode,
alt,
br,
group,
highlight,
indent,
mark,
nl,
opt,
rule,
seq,
sp,
)
@rule("File")
def file() -> Rule:
return _file_statement_list
@rule
def _file_statement_list() -> Rule:
return alt(
_file_statement,
_file_statement_list + nl + _file_statement,
)
@rule
def _file_statement() -> Rule:
return (
import_statement | class_declaration | export_statement | _statement
)
@rule
def import_statement() -> Rule:
return group(
IMPORT, sp, STRING, sp, AS, sp, IDENTIFIER, sp, SEMICOLON
)
@rule("ClassDeclaration")
def class_declaration() -> Rule:
return seq(
group(
CLASS,
sp,
mark(IDENTIFIER, field="name", highlight=highlight.entity.name.type),
sp,
LCURLY,
),
indent(nl, mark(opt(class_body), field="body")),
nl,
RCURLY,
nl, # Extra newline at the end of the class
)
@rule("ClassBody")
def class_body() -> Rule:
return _class_members
@rule
def _class_members() -> Rule:
return _class_member | seq(_class_members, nl, _class_member)
@rule
def _class_member() -> Rule:
return field_declaration | function_declaration
@rule("FieldDecl")
def field_declaration() -> Rule:
return group(IDENTIFIER, COLON, sp, type_expression, SEMICOLON)
# Types
@rule("TypeExpression")
def type_expression() -> Rule:
return alternate_type | type_identifier
@rule("AlternateType")
def alternate_type() -> Rule:
return group(type_expression, sp, OR, sp, type_identifier)
@rule("TypeIdentifier")
def type_identifier() -> Rule:
return mark(IDENTIFIER, field="id", highlight=highlight.entity.name.type)
@rule
def export_statement() -> Rule:
return alt(
group(EXPORT, sp, class_declaration),
group(EXPORT, sp, function_declaration),
group(EXPORT, sp, let_statement),
group(EXPORT, sp, export_list, SEMICOLON),
)
@rule
def export_list() -> Rule:
return IDENTIFIER | seq(IDENTIFIER, COMMA, sp, export_list)
# Functions
@rule("FunctionDecl")
def function_declaration() -> Rule:
return seq(
group(
group(
group(
FUN,
sp,
mark(
IDENTIFIER,
field="name",
highlight=highlight.entity.name.function,
),
),
nl,
mark(function_parameters, field="parameters"),
),
mark(
opt(indent(sp, group(ARROW, sp, type_expression))),
field="return_type",
),
),
sp,
mark(block, field="body"),
nl,
)
@rule("ParamList")
def function_parameters() -> Rule:
return group(
LPAREN,
indent(
nl,
opt(
_first_parameter
| seq(_first_parameter, COMMA)
| group(_first_parameter, COMMA, sp, _parameter_list)
),
),
nl,
RPAREN,
)
@rule
def _first_parameter() -> Rule:
return SELF | parameter
@rule
def _parameter_list() -> Rule:
return parameter | seq(parameter, COMMA, sp, _parameter_list)
@rule("Parameter")
def parameter() -> Rule:
return group(IDENTIFIER, COLON, sp, type_expression)
# Block
@rule("Block")
def block() -> Rule:
return alt(
group(LCURLY, nl, RCURLY),
group(LCURLY, indent(br, block_body), sp, RCURLY),
)
@rule("BlockBody")
def block_body() -> Rule:
return alt(
expression,
_statement_list,
seq(_statement_list, br, expression),
)
@rule
def _statement_list() -> Rule:
return _statement | seq(_statement_list, br, _statement)
@rule
def _statement() -> Rule:
return (
function_declaration
| let_statement
| return_statement
| for_statement
| if_statement
| while_statement
| expression_statement
)
@rule("LetStatement")
def let_statement() -> Rule:
return group(
group(
LET,
sp,
IDENTIFIER,
sp,
EQUAL,
),
indent(sp, expression, SEMICOLON),
)
@rule("ReturnStatement")
def return_statement() -> Rule:
return alt(
group(RETURN, indent(sp, group(expression, SEMICOLON))),
group(RETURN, SEMICOLON),
)
@rule("ForStatement")
def for_statement() -> Rule:
return group(
group(FOR, sp, iterator_variable, sp, IN, sp, group(expression)),
block,
)
@rule("IteratorVariable")
def iterator_variable() -> Rule:
return IDENTIFIER
@rule("IfStatement")
def if_statement() -> Rule:
return conditional_expression
@rule
def while_statement() -> Rule:
return group(group(WHILE, sp, expression), sp, block)
@rule
def expression_statement() -> Rule:
return seq(expression, SEMICOLON)
# Expressions
@rule(transparent=True)
def expression() -> Rule:
return binary_expression | is_expression | primary_expression
@rule("BinaryExpression")
def binary_expression() -> Rule:
return alt(
# Assignment gets special indentation.
group(group(expression, sp, EQUAL), indent(sp, expression)),
# Other ones do not.
group(group(expression, sp, OR), sp, expression),
group(group(expression, sp, AND), sp, expression),
group(group(expression, sp, EQUALEQUAL), sp, expression),
group(group(expression, sp, BANGEQUAL), sp, expression),
group(group(expression, sp, LESS), sp, expression),
group(group(expression, sp, LESSEQUAL), sp, expression),
group(group(expression, sp, GREATER), sp, expression),
group(group(expression, sp, GREATEREQUAL), sp, expression),
group(group(expression, sp, PLUS), sp, expression),
group(group(expression, sp, MINUS), sp, expression),
group(group(expression, sp, STAR), sp, expression),
group(group(expression, sp, SLASH), sp, expression),
)
@rule("IsExpression")
def is_expression() -> Rule:
return group(expression, sp, IS, indent(sp, pattern))
@rule
def primary_expression() -> Rule:
return (
identifier_expression
| literal_expression
| SELF
| seq(BANG, primary_expression)
| seq(MINUS, primary_expression)
| block
| conditional_expression
| list_constructor_expression
| object_constructor_expression
| match_expression
| seq(primary_expression, LPAREN, RPAREN)
| group(
primary_expression,
LPAREN,
indent(nl, _expression_list),
nl,
RPAREN,
)
| group(primary_expression, indent(nl, DOT, IDENTIFIER))
| group(LPAREN, indent(nl, expression), nl, RPAREN)
)
@rule("IdentifierExpression")
def identifier_expression():
return IDENTIFIER
@rule("Literal")
def literal_expression():
return NUMBER | STRING | TRUE | FALSE
@rule("ConditionalExpression")
def conditional_expression() -> Rule:
return (
seq(group(IF, sp, expression), sp, block)
| seq(
group(IF, sp, expression),
sp,
block,
sp,
ELSE,
sp,
conditional_expression,
)
| seq(
group(IF, sp, expression), sp, block, sp, ELSE, sp, block
)
)
@rule
def list_constructor_expression() -> Rule:
return alt(
group(LSQUARE, nl, RSQUARE),
group(LSQUARE, indent(nl, _expression_list), nl, RSQUARE),
)
@rule
def _expression_list() -> Rule:
return (
expression
| seq(expression, COMMA)
| seq(expression, COMMA, sp, _expression_list)
)
@rule
def match_expression() -> Rule:
return group(
group(MATCH, sp, expression, sp, LCURLY),
indent(sp, match_arms),
sp,
RCURLY,
)
@rule("MatchArms")
def match_arms() -> Rule:
return _match_arms
@rule
def _match_arms() -> Rule:
return (
match_arm
| seq(match_arm, COMMA)
| seq(match_arm, COMMA, br, _match_arms)
)
@rule("MatchArm")
def match_arm() -> Rule:
return group(pattern, sp, ARROW, sp, expression)
@rule("Pattern")
def pattern() -> Rule:
return (
group(variable_binding, _pattern_core, sp, AND, sp, expression)
| group(variable_binding, _pattern_core)
| _pattern_core
)
@rule
def _pattern_core() -> Rule:
return type_expression | wildcard_pattern
@rule("WildcardPattern")
def wildcard_pattern() -> Rule:
return UNDERSCORE
@rule("VariableBinding")
def variable_binding() -> Rule:
return seq(IDENTIFIER, COLON)
@rule
def object_constructor_expression() -> Rule:
return group(NEW, sp, type_identifier, sp, field_list)
@rule
def field_list() -> Rule:
return alt(
seq(LCURLY, RCURLY),
group(LCURLY, indent(nl, field_values), nl, RCURLY),
)
@rule
def field_values() -> Rule:
return (
field_value
| seq(field_value, COMMA)
| seq(field_value, COMMA, sp, field_values)
)
@rule
def field_value() -> Rule:
return IDENTIFIER | group(IDENTIFIER, COLON, indent(sp, expression))
BLANKS = Terminal("BLANKS", Re.set(" ", "\t").plus())
LINE_BREAK = Terminal("LINE_BREAK", Re.set("\r", "\n"), trivia_mode=TriviaMode.NewLine)
COMMENT = Terminal(
"COMMENT",
Re.seq(Re.literal("//"), Re.set("\n").invert().star()),
highlight=highlight.comment.line,
trivia_mode=TriviaMode.LineComment,
)
ARROW = Terminal("ARROW", "->", highlight=highlight.keyword.operator)
AS = Terminal("AS", "as", highlight=highlight.keyword.operator.expression)
BAR = Terminal("BAR", "|", highlight=highlight.keyword.operator.expression)
CLASS = Terminal("CLASS", "class", highlight=highlight.storage.type.klass)
COLON = Terminal("COLON", ":", highlight=highlight.punctuation.separator)
ELSE = Terminal("ELSE", "else", highlight=highlight.keyword.control.conditional)
FOR = Terminal("FOR", "for", highlight=highlight.keyword.control)
FUN = Terminal("FUN", "fun", highlight=highlight.storage.type.function)
IDENTIFIER = Terminal(
"IDENTIFIER",
Re.seq(
Re.set(("a", "z"), ("A", "Z"), "_"),
Re.set(("a", "z"), ("A", "Z"), ("0", "9"), "_").star(),
),
)
IF = Terminal("IF", "if", highlight=highlight.keyword.control.conditional)
IMPORT = Terminal("IMPORT", "import", highlight=highlight.keyword.other)
IN = Terminal("IN", "in", highlight=highlight.keyword.operator)
LCURLY = Terminal("LCURLY", "{", highlight=highlight.punctuation.curly_brace.open)
RCURLY = Terminal("RCURLY", "}", highlight=highlight.punctuation.curly_brace.close)
LET = Terminal("LET", "let", highlight=highlight.keyword.other)
RETURN = Terminal("RETURN", "return", highlight=highlight.keyword.control)
SEMICOLON = Terminal("SEMICOLON", ";", highlight=highlight.punctuation.separator)
STRING = Terminal(
"STRING",
# Double-quoted string.
Re.seq(
Re.literal('"'),
(~Re.set('"', "\\") | (Re.set("\\") + Re.any())).star(),
Re.literal('"'),
)
# Single-quoted string.
| Re.seq(
Re.literal("'"),
(~Re.set("'", "\\") | (Re.set("\\") + Re.any())).star(),
Re.literal("'"),
),
highlight=highlight.string.quoted,
)
WHILE = Terminal("WHILE", "while", highlight=highlight.keyword.control)
EQUAL = Terminal("EQUAL", "=", highlight=highlight.keyword.operator.expression)
LPAREN = Terminal("LPAREN", "(", highlight=highlight.punctuation.parenthesis.open)
RPAREN = Terminal("RPAREN", ")", highlight=highlight.punctuation.parenthesis.close)
COMMA = Terminal("COMMA", ",", highlight=highlight.punctuation.separator)
SELF = Terminal("SELFF", "self", highlight=highlight.variable.language)
OR = Terminal("OR", "or", highlight=highlight.keyword.operator.expression)
IS = Terminal("IS", "is", highlight=highlight.keyword.operator.expression)
AND = Terminal("AND", "and", highlight=highlight.keyword.operator.expression)
EQUALEQUAL = Terminal("EQUALEQUAL", "==", highlight=highlight.keyword.operator.expression)
BANGEQUAL = Terminal("BANGEQUAL", "!=", highlight=highlight.keyword.operator.expression)
LESS = Terminal("LESS", "<", highlight=highlight.keyword.operator.expression)
GREATER = Terminal("GREATER", ">", highlight=highlight.keyword.operator.expression)
LESSEQUAL = Terminal("LESSEQUAL", "<=", highlight=highlight.keyword.operator.expression)
GREATEREQUAL = Terminal("GREATEREQUAL", ">=", highlight=highlight.keyword.operator.expression)
PLUS = Terminal("PLUS", "+", highlight=highlight.keyword.operator.expression)
MINUS = Terminal("MINUS", "-", highlight=highlight.keyword.operator.expression)
STAR = Terminal("STAR", "*", highlight=highlight.keyword.operator.expression)
SLASH = Terminal("SLASH", "/", highlight=highlight.keyword.operator.expression)
NUMBER = Terminal(
"NUMBER",
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,
)
TRUE = Terminal("TRUE", "true", highlight=highlight.constant.language)
FALSE = Terminal("FALSE", "false", highlight=highlight.constant.language)
BANG = Terminal("BANG", "!", highlight=highlight.keyword.operator.expression)
DOT = Terminal("DOT", ".", highlight=highlight.punctuation.separator)
MATCH = Terminal("MATCH", "match", highlight=highlight.keyword.other)
EXPORT = Terminal("EXPORT", "export", highlight=highlight.keyword.other)
UNDERSCORE = Terminal("UNDERSCORE", "_", highlight=highlight.variable.language)
NEW = Terminal("NEW", "new", highlight=highlight.keyword.operator)
LSQUARE = Terminal("LSQUARE", "[", highlight=highlight.punctuation.square_bracket.open)
RSQUARE = Terminal("RSQUARE", "]", highlight=highlight.punctuation.square_bracket.close)
FineGrammar=Grammar(
start=file,
trivia=[BLANKS, LINE_BREAK, COMMENT],
pretty_indent=" ",
precedence=[
(Assoc.RIGHT, [EQUAL]),
(Assoc.LEFT, [OR]),
(Assoc.LEFT, [IS]),
(Assoc.LEFT, [AND]),
(Assoc.LEFT, [EQUALEQUAL, BANGEQUAL]),
(Assoc.LEFT, [LESS, GREATER, GREATEREQUAL, LESSEQUAL]),
(Assoc.LEFT, [PLUS, MINUS]),
(Assoc.LEFT, [STAR, SLASH]),
(Assoc.LEFT, [primary_expression]),
(Assoc.LEFT, [LPAREN]),
(Assoc.LEFT, [DOT]),
#
# If there's a confusion about whether to make an IF
# statement or an expression, prefer the statement.
#
(Assoc.NONE, [if_statement]),
],
)
if __name__ == "__main__":
from pathlib import Path
from parser.parser import dump_lexer_table
from parser.emacs import emit_emacs_major_mode
from parser.tree_sitter import emit_tree_sitter_grammar, emit_tree_sitter_queries
# TODO: Actually generate a lexer/parser for some runtime.
grammar = FineGrammar
table = grammar.build_table()
# print(table.format())
lexer = grammar.compile_lexer()
dump_lexer_table(lexer)
# Generate tree-sitter parser and emacs mode.
ts_path = Path(__file__).parent / "tree-sitter-fine"
emit_tree_sitter_grammar(grammar, ts_path)
emit_tree_sitter_queries(grammar, ts_path)
emit_emacs_major_mode(grammar, ts_path / "fine.el")
# TODO: Generate pretty-printer code.

761
examples/sql.py Normal file
View file

@ -0,0 +1,761 @@
from parser import *
NAME = Terminal(
"NAME",
Re.seq(
Re.set(("a", "z"), ("A", "Z"), "_"),
Re.set(("a", "z"), ("A", "Z"), ("0", "9"), "_").star(),
),
)
STRING = Terminal(
"STRING",
Re.seq(
Re.literal("'"),
(~Re.set("'", "\\") | (Re.set("\\") + Re.any())).star(),
Re.literal("'"),
),
highlight=highlight.string.quoted,
)
NUMBER = Terminal(
"NUMBER",
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", "or")
AND = Terminal("AND", "and")
NOT = Terminal("NOT", "not")
COMPARISON = Terminal(
"COMPARISON",
Re.literal("=")
| Re.literal("<>")
| Re.literal("<")
| Re.literal(">")
| Re.literal("<=")
| Re.literal(">="),
)
PLUS = Terminal("PLUS", "+")
MINUS = Terminal("MINUS", "-")
STAR = Terminal("STAR", "*")
SLASH = Terminal("SLASH", "/")
ALL = Terminal("ALL", "all")
AMMSC = Terminal("AMMSC", "ammsc")
ANY = Terminal("ANY", "any")
AS = Terminal("AS", "as")
ASC = Terminal("ASC", "asc")
AUTHORIZATION = Terminal("AUTHORIZATION", "authorization")
BETWEEN = Terminal("BETWEEN", "between")
BY = Terminal("BY", "by")
CHARACTER = Terminal("CHARACTER", "character")
CHECK = Terminal("CHECK", "check")
CLOSE = Terminal("CLOSE", "close")
COMMIT = Terminal("COMMIT", "commit")
CONTINUE = Terminal("CONTINUE", "continue")
CREATE = Terminal("CREATE", "create")
CURRENT = Terminal("CURRENT", "current")
CURSOR = Terminal("CURSOR", "cursor")
DECIMAL = Terminal("DECIMAL", "decimal")
DECLARE = Terminal("DECLARE", "declare")
DEFAULT = Terminal("DEFAULT", "default")
DELETE = Terminal("DELETE", "delete")
DESC = Terminal("DESC", "desc")
DISTINCT = Terminal("DISTINCT", "distinct")
DOUBLE = Terminal("DOUBLE", "double")
ESCAPE = Terminal("ESCAPE", "escape")
EXISTS = Terminal("EXISTS", "exists")
FETCH = Terminal("FETCH", "fetch")
FLOAT = Terminal("FLOAT", "float")
FOR = Terminal("FOR", "for")
FOREIGN = Terminal("FOREIGN", "foreign")
FOUND = Terminal("FOUND", "found")
FROM = Terminal("FROM", "from")
GOTO = Terminal("GOTO", "goto")
GRANT = Terminal("GRANT", "grant")
GROUP = Terminal("GROUP", "group")
HAVING = Terminal("HAVING", "having")
IN = Terminal("IN", "in")
INDICATOR = Terminal("INDICATOR", "indicator")
INSERT = Terminal("INSERT", "insert")
INTEGER = Terminal("INTEGER", "integer")
INTO = Terminal("INTO", "into")
IS = Terminal("IS", "is")
KEY = Terminal("KEY", "key")
LANGUAGE = Terminal("LANGUAGE", "language")
LIKE = Terminal("LIKE", "like")
NULL = Terminal("NULL", "null")
NUMERIC = Terminal("NUMERIC", "numeric")
OF = Terminal("OF", "of")
ON = Terminal("ON", "on")
OPEN = Terminal("OPEN", "open")
OPTION = Terminal("OPTION", "option")
ORDER = Terminal("ORDER", "order")
PARAMETER = Terminal("PARAMETER", "parameter")
PRECISION = Terminal("PRECISION", "precision")
PRIMARY = Terminal("PRIMARY", "primary")
PRIVILEGES = Terminal("PRIVILEGES", "privileges")
PROCEDURE = Terminal("PROCEDURE", "procedure")
PUBLIC = Terminal("PUBLIC", "public")
REAL = Terminal("REAL", "real")
REFERENCES = Terminal("REFERENCES", "references")
ROLLBACK = Terminal("ROLLBACK", "rollback")
SCHEMA = Terminal("SCHEMA", "schema")
SELECT = Terminal("SELECT", "select")
SET = Terminal("SET", "set")
SMALLINT = Terminal("SMALLINT", "smallint")
SOME = Terminal("SOME", "some")
SQLCODE = Terminal("SQLCODE", "sqlcode")
SQLERROR = Terminal("SQLERROR", "sqlerror")
TABLE = Terminal("TABLE", "table")
TO = Terminal("TO", "to")
UNION = Terminal("UNION", "union")
UNIQUE = Terminal("UNIQUE", "unique")
UPDATE = Terminal("UPDATE", "update")
USER = Terminal("USER", "user")
VALUES = Terminal("VALUES", "values")
VIEW = Terminal("VIEW", "view")
WHENEVER = Terminal("WHENEVER", "whenever")
WHERE = Terminal("WHERE", "where")
WITH = Terminal("WITH", "with")
WORK = Terminal("WORK", "work")
SEMICOLON = Terminal("SEMICOLON", ";")
LPAREN = Terminal("LPAREN", "(")
RPAREN = Terminal("RPAREN", ")")
COMMA = Terminal("COMMA", ",")
EQUAL = Terminal("EQUAL", "=")
DOT = Terminal("DOT", ".")
BLANKS = Terminal("BLANKS", Re.set(" ", "\t").plus())
LINE_BREAK = Terminal("LINE_BREAK", Re.set("\r", "\n"), trivia_mode=TriviaMode.NewLine)
COMMENT = Terminal(
"COMMENT",
Re.seq(Re.literal("--"), Re.set("\n").invert().star()),
highlight=highlight.comment.line,
trivia_mode=TriviaMode.LineComment,
)
@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
SQL = Grammar(
start=sql_list,
precedence=[
(Assoc.LEFT, [OR]),
(Assoc.LEFT, [AND]),
(Assoc.LEFT, [NOT]),
(Assoc.LEFT, [COMPARISON]),
(Assoc.LEFT, [PLUS, MINUS]),
(Assoc.LEFT, [STAR, SLASH]),
# TODO: Unary minus
],
trivia=[BLANKS, COMMENT, LINE_BREAK],
name="SQL",
)