from abc import (
ABC,
abstractmethod,
)
from dataclasses import dataclass
from .value import Value
from .domain import Domain
from .predicate import Comp
__all__ = (
'Ast',
'AstTable',
'AstAttr',
'AstCond',
'AstAssign',
'AstDefine',
'AstSelect',
'AstInsert',
'AstDelete',
'AstUpdate',
'AstCreateTable',
'AstDropTable',
'AstCreateIndex',
'AstDropIndex',
'AstShowTables',
'AstDescribeTable',
'AstDumpTable',
'AstExit',
)
[docs]
@dataclass(frozen=True, slots=True)
class Ast(ABC):
"""
An abstract base class for ASTs.
.. rubric:: Common Grammar
::
table: WORD | WORD "AS" WORD
tables: table | tables COMMA table
attr: WORD | WORD DOT WORD
attrs: attr | attrs COMMA attr
value: FLOAT | INT | BOOL | STRING
values: value | values COMMA value
hand: attr | value
cond: attr COMP hand
conds: cond | conds "AND" cond
assign: attr COMP hand
assigns: assign | assigns COMMA assign
domain: BOOL | INT | FLOAT | STRING LP INT RP
define: attr domain
defines: define | defines COMMA define
where_clause: | "WHERE" conds
.. rubric:: Dispatch Grammar
::
create_statement
: "CREATE" "TABLE" create_table_body SC
| "CREATE" "INDEX" create_index_body SC
drop_statement
: "DROP" "TABLE" drop_table_body SC
| "DROP" "INDEX" drop_index_body SC
.. rubric:: Partial Grammar
::
create_table_body: table LP defines RP
create_index_body: attr "OF" table
drop_table_body: table
drop_index_body: attr "OF" table
.. rubric:: Statement Grammar
::
select_statement: "SELECT" attrs "FROM" tables where_clause SC
insert_statement: "INSERT" "INTO" table "VALUES" LP values RP SC
delete_statement: "DELETE" "FROM" table where_clause SC
update_statement: "UPDATE" table "SET" assigns where_clause SC
show_tables_statement: "SHOW" "TABLES" SC
describe_table_statement: "DESCRIBE" "TABLE" table SC
dump_table_statement: "DUMP" "TABLE" table SC
exit_statement: "EXIT" SC
"""
@abstractmethod
def verbose(self) -> str: # pragma: no cover
pass
[docs]
@dataclass(frozen=True, slots=True)
class AstTable(Ast):
"""
An AST representing a ``table``.
Attributes:
name: the name of the ``table``
alias: the alias of the ``table`` if ``"AS"`` is specified
"""
name: str
alias: str | None
def verbose(self) -> str: # pragma: no cover
return self.name + (f" AS {self.alias}" if self.alias else "")
[docs]
@dataclass(frozen=True, slots=True)
class AstAttr(Ast):
"""
An AST representing a ``attr``.
Attributes:
ref: the reference of the ``attr`` if ``DOT`` is present
name: the name of the ``attr``
"""
ref: str | None
name: str
def verbose(self) -> str: # pragma: no cover
return ("" if self.ref is None else f"{self.ref}.") + self.name
[docs]
@dataclass(frozen=True, slots=True)
class AstCond(Ast):
"""
An AST representing a ``cond``.
Attributes:
left_attr: the left ``attr`` of the ``cond``
comp: the ``COMP`` of the ``cond``
right_hand: the right ``hand`` of the ``cond``
"""
left_attr: AstAttr
comp: Comp
right_hand: AstAttr | Value
def verbose(self) -> str: # pragma: no cover
tokens = [
self.left_attr.verbose(),
self.comp.value,
]
if isinstance(self.right_hand, AstAttr):
tokens.append(self.right_hand.verbose())
else:
tokens.append(str(self.right_hand))
return " ".join(tokens)
[docs]
@dataclass(frozen=True, slots=True)
class AstAssign(Ast):
"""
An AST representing a ``assign``.
Attributes:
left_attr: the left ``attr`` of the ``assign``
right_hand: the right ``hand`` of the ``assign``
"""
left_attr: AstAttr
right_hand: AstAttr | Value
def verbose(self) -> str: # pragma: no cover
tokens = [
self.left_attr.verbose(),
"=",
]
if isinstance(self.right_hand, AstAttr):
tokens.append(self.right_hand.verbose())
else:
tokens.append(str(self.right_hand))
return " ".join(tokens)
[docs]
@dataclass(frozen=True, slots=True)
class AstDefine(Ast):
"""
An AST representing a ``define``.
Attributes:
attr: the ``attr`` of the ``define``
domain: the ``domain`` of the ``define``
"""
attr: AstAttr
domain: Domain
def verbose(self) -> str: # pragma: no cover
return " ".join([
self.attr.verbose(),
self.domain.verbose(),
])
[docs]
@dataclass(frozen=True, slots=True)
class AstSelect(Ast):
"""
An AST representing a ``select_statement``.
Attributes:
attrs: the ``attrs`` of the ``select_statement``
tables: the ``tables`` of the ``select_statement``
conds: the ``conds`` of the ``select_statement``
"""
attrs: list[AstAttr]
tables: list[AstTable]
conds: list[AstCond]
def verbose(self) -> str: # pragma: no cover
tokens = [
"SELECT",
", ".join(attr.verbose() for attr in self.attrs),
"FROM",
", ".join(table.verbose() for table in self.tables),
]
if self.conds:
tokens.append("WHERE")
tokens.append(", ".join(cond.verbose() for cond in self.conds))
return " ".join(tokens)
[docs]
@dataclass(frozen=True, slots=True)
class AstInsert(Ast):
"""
An AST representing a ``insert_statement``.
Attributes:
table: the ``table`` of the ``insert_statement``
values: the ``values`` of the ``insert_statement``
"""
table: AstTable
values: tuple[Value, ...]
def verbose(self) -> str: # pragma: no cover
return " ".join([
"INSERT",
"INTO",
self.table.verbose(),
"VALUES",
f"({', '.join(str(value) for value in self.values)})",
])
[docs]
@dataclass(frozen=True, slots=True)
class AstDelete(Ast):
"""
An AST representing a ``delete_statement``.
Attributes:
table: the ``table`` of the ``delete_statement``
conds: the ``conds`` of the ``delete_statement``
"""
table: AstTable
conds: list[AstCond]
def verbose(self) -> str: # pragma: no cover
tokens = [
"DELETE",
"FROM",
self.table.verbose(),
]
if self.conds:
tokens.append("WHERE")
tokens.append(", ".join(cond.verbose() for cond in self.conds))
return " ".join(tokens)
[docs]
@dataclass(frozen=True, slots=True)
class AstUpdate(Ast):
"""
An AST representing a ``update_statement``.
Attributes:
table: the ``table`` of the ``update_statement``
assigns: the ``assigns`` of the ``update_statement``
conds: the ``conds`` of the ``update_statement``
"""
table: AstTable
assigns: list[AstAssign]
conds: list[AstCond]
def verbose(self) -> str: # pragma: no cover
tokens = [
"UPDATE",
self.table.verbose(),
"SET",
", ".join(assign.verbose() for assign in self.assigns),
]
if self.conds:
tokens.append("WHERE")
tokens.append(", ".join(cond.verbose() for cond in self.conds))
return " ".join(tokens)
[docs]
@dataclass(frozen=True, slots=True)
class AstCreateTable(Ast):
"""
An AST representing a ``create_table_body``.
Attributes:
table: the ``table`` of the ``create_table_body``
defines: the ``defines`` of the ``create_table_body``
"""
table: AstTable
defines: list[AstDefine]
def verbose(self) -> str: # pragma: no cover
return " ".join([
"CREATE",
"TABLE",
self.table.verbose(),
f"({', '.join(define.verbose() for define in self.defines)})",
])
[docs]
@dataclass(frozen=True, slots=True)
class AstCreateIndex(Ast):
"""
An AST representing a ``create_index_body``.
Attributes:
table: the ``table`` of the ``create_index_body``
attr: the ``attr`` of the ``create_index_body``
"""
table: AstTable
attr: AstAttr
def verbose(self) -> str: # pragma: no cover
return " ".join([
"CREATE",
"INDEX",
self.attr.verbose(),
"FROM",
self.table.verbose(),
])
[docs]
@dataclass(frozen=True, slots=True)
class AstDropTable(Ast):
"""
An AST representing a ``drop_table_body``.
Attributes:
table: the ``table`` of the ``drop_table_body``
"""
table: AstTable
def verbose(self) -> str: # pragma: no cover
return f"DROP TABLE {self.table.verbose()}"
[docs]
@dataclass(frozen=True, slots=True)
class AstDropIndex(Ast):
"""
An AST representing a ``drop_index_body``.
Attributes:
table: the ``table`` of the ``drop_index_body``
attr: the ``attr`` of the ``drop_index_body``
"""
table: AstTable
attr: AstAttr
def verbose(self) -> str: # pragma: no cover
return " ".join([
"DROP",
"INDEX",
self.attr.verbose(),
"FROM",
self.table.verbose(),
])
[docs]
@dataclass(frozen=True, slots=True)
class AstShowTables(Ast):
"""
An AST representing a ``show_tables_statement``.
"""
def verbose(self) -> str: # pragma: no cover
return "SHOW TABLES"
[docs]
@dataclass(frozen=True, slots=True)
class AstDescribeTable(Ast):
"""
An AST representing a ``describe_table_statement``.
Attributes:
table: the ``table`` of the ``describe_table_statement``
"""
table: AstTable
def verbose(self) -> str: # pragma: no cover
return f"DESCRIBE TABLE {self.table.verbose()}"
[docs]
@dataclass(frozen=True, slots=True)
class AstDumpTable(Ast):
"""
An AST representing a ``dump_table_statement``.
Attributes:
table: the ``table`` of the ``dump_table_statement``
"""
table: AstTable
def verbose(self) -> str: # pragma: no cover
return f"DUMP TABLE {self.table.verbose()}"
[docs]
@dataclass(frozen=True, slots=True)
class AstExit(Ast):
"""
An AST representing a ``exit_statement``.
"""
def verbose(self) -> str: # pragma: no cover
return "EXIT"