Move examples
This commit is contained in:
parent
030406b2fb
commit
182c29c2a7
3 changed files with 1 additions and 0 deletions
1
examples/.gitignore
vendored
Normal file
1
examples/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
tree-sitter-fine/
|
||||
537
examples/grammar.py
Normal file
537
examples/grammar.py
Normal 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
761
examples/sql.py
Normal 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",
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue