feat(report): add python ast nodes
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
#import "@preview/acrostiche:0.7.0": acr
|
||||
#import "@local/codly:1.3.1": codly-disable, codly-enable
|
||||
#import "utils.typ": format-ast-nodes
|
||||
|
||||
#let token-types = (
|
||||
"Punctuation": (
|
||||
@@ -188,47 +188,14 @@
|
||||
```),
|
||||
)
|
||||
|
||||
#let format-ast-nodes(name, base-cls, code) = {
|
||||
let nodes = code.text.split(regex(`\n\nclass`.text)).map(n => if n.starts-with("class") {n} else {"class" + n})
|
||||
|
||||
let cells = nodes.map(n => raw(n, block: true, lang: "python"))
|
||||
|
||||
if calc.rem(cells.len(), 3) == 2 {
|
||||
let last-two = cells.slice(-2)
|
||||
cells = cells.slice(0, -2)
|
||||
let last = grid.cell(
|
||||
colspan: 3,
|
||||
stack(
|
||||
dir: ltr,
|
||||
spacing: 1fr,
|
||||
none,
|
||||
..last-two,
|
||||
none
|
||||
)
|
||||
)
|
||||
cells.push(last)
|
||||
}
|
||||
|
||||
align(center)[*#name*]
|
||||
if base-cls != none [Base class: #base-cls]
|
||||
|
||||
codly-disable()
|
||||
grid(
|
||||
columns: 3,
|
||||
column-gutter: 1fr,
|
||||
row-gutter: 2em,
|
||||
align: left + top,
|
||||
..cells
|
||||
)
|
||||
codly-enable()
|
||||
}
|
||||
|
||||
#let midas-ast-nodes = stack(
|
||||
dir: ttb,
|
||||
spacing: 2em,
|
||||
#let midas-ast-nodes = table(
|
||||
inset: .5em,
|
||||
row-gutter: 2em,
|
||||
stroke: gray,
|
||||
..ast-nodes.map(nodes => format-ast-nodes(..nodes))
|
||||
)
|
||||
|
||||
#show figure: set block(breakable: true)
|
||||
#figure(
|
||||
grid(
|
||||
columns: 4,
|
||||
|
||||
172
report/appendices/python_parsing.typ
Normal file
172
report/appendices/python_parsing.typ
Normal file
@@ -0,0 +1,172 @@
|
||||
#import "@preview/acrostiche:0.7.0": acr
|
||||
#import "utils.typ": format-ast-nodes
|
||||
|
||||
#let ast-nodes = (
|
||||
("Other classes", none, ```python
|
||||
class ParamSpec:
|
||||
pos: list[Function.Parameter]
|
||||
mixed: list[Function.Parameter]
|
||||
kw: list[Function.Parameter]
|
||||
@property
|
||||
def all(self) -> list[Function.Parameter]:
|
||||
return self.pos + self.mixed + self.kw
|
||||
|
||||
|
||||
class ImportAlias:
|
||||
location: Location
|
||||
name: str
|
||||
alias: Optional[str] = None
|
||||
@property
|
||||
def imported_name(self) -> str:
|
||||
return (
|
||||
self.alias
|
||||
if self.alias is not None else
|
||||
self.name
|
||||
)
|
||||
```),
|
||||
|
||||
("Type annotations", `MidasType`, ```python
|
||||
class BaseType:
|
||||
base: str
|
||||
args: tuple[MidasType,...]
|
||||
|
||||
class FrameColumn:
|
||||
name: Optional[str]
|
||||
type: Optional[MidasType]
|
||||
|
||||
class FrameType:
|
||||
columns: list[FrameColumn]
|
||||
```),
|
||||
|
||||
("Statements", `Stmt`, ```python
|
||||
class ExpressionStmt:
|
||||
expr: Expr
|
||||
|
||||
class Function:
|
||||
name: str
|
||||
params: ParamSpec
|
||||
returns: Optional[MidasType]
|
||||
body: list[Stmt]
|
||||
|
||||
class Parameter:
|
||||
location: Optional[Location] = None
|
||||
name: str
|
||||
type: Optional[MidasType]
|
||||
default: Optional[Expr]
|
||||
|
||||
class TypeAssign:
|
||||
name: str
|
||||
type: MidasType
|
||||
|
||||
class AssignStmt:
|
||||
targets: list[Expr]
|
||||
value: Expr
|
||||
|
||||
class ReturnStmt:
|
||||
value: Optional[Expr]
|
||||
|
||||
class IfStmt:
|
||||
test: Expr
|
||||
body: list[Stmt]
|
||||
orelse: list[Stmt]
|
||||
|
||||
class Pass: ...
|
||||
|
||||
class ForStmt:
|
||||
target: Expr
|
||||
iterator: Expr
|
||||
body: list[Stmt]
|
||||
|
||||
class ImportStmt:
|
||||
imports: list[ImportAlias]
|
||||
|
||||
class FromImportStmt:
|
||||
module: Optional[str]
|
||||
imports: list[ImportAlias]
|
||||
level: int
|
||||
|
||||
class RawStmt:
|
||||
stmt: ast.stmt
|
||||
```),
|
||||
|
||||
("Expressions", `Expr`, ```python
|
||||
class BinaryExpr:
|
||||
left: Expr
|
||||
operator: ast.operator
|
||||
right: Expr
|
||||
|
||||
class CompareExpr:
|
||||
left: Expr
|
||||
operator: ast.cmpop
|
||||
right: Expr
|
||||
|
||||
class UnaryExpr:
|
||||
operator: ast.unaryop
|
||||
right: Expr
|
||||
|
||||
class CallExpr:
|
||||
callee: Expr
|
||||
arguments: list[Expr]
|
||||
keywords: dict[str, Expr]
|
||||
|
||||
class GetExpr:
|
||||
object: Expr
|
||||
name: str
|
||||
|
||||
class LiteralExpr:
|
||||
value: Any
|
||||
|
||||
class VariableExpr:
|
||||
name: str
|
||||
|
||||
class LogicalExpr:
|
||||
left: Expr
|
||||
operator: ast.boolop
|
||||
right: Expr
|
||||
|
||||
class CastExpr:
|
||||
type: MidasType
|
||||
expr: Expr
|
||||
unsafe: bool
|
||||
|
||||
class TernaryExpr:
|
||||
test: Expr
|
||||
if_true: Expr
|
||||
if_false: Expr
|
||||
|
||||
class ListExpr:
|
||||
items: list[Expr]
|
||||
|
||||
class DictExpr:
|
||||
keys: list[Optional[Expr]]
|
||||
values: list[Expr]
|
||||
|
||||
class SubscriptExpr:
|
||||
object: Expr
|
||||
index: Expr
|
||||
|
||||
class SliceExpr:
|
||||
lower: Optional[Expr]
|
||||
upper: Optional[Expr]
|
||||
step: Optional[Expr]
|
||||
|
||||
class TupleExpr:
|
||||
items: tuple[Expr, ...]
|
||||
|
||||
class RawExpr:
|
||||
expr: ast.expr
|
||||
```),
|
||||
)
|
||||
|
||||
#let python-ast-nodes = table(
|
||||
inset: .5em,
|
||||
stroke: gray,
|
||||
row-gutter: 2em,
|
||||
..ast-nodes.map(nodes => format-ast-nodes(..nodes))
|
||||
)
|
||||
|
||||
#show figure: set block(breakable: true)
|
||||
#figure(
|
||||
python-ast-nodes,
|
||||
caption: [Python #acr("AST") Nodes],
|
||||
) <tab:python-ast-nodes>
|
||||
37
report/appendices/utils.typ
Normal file
37
report/appendices/utils.typ
Normal file
@@ -0,0 +1,37 @@
|
||||
#import "@preview/codly:1.3.0": codly-disable, codly-enable
|
||||
#import "../requirements.typ": isc-hei-bthesis
|
||||
|
||||
#let format-ast-nodes(name, base-cls, code) = {
|
||||
let nodes = code.text.split(regex(`\n\nclass`.text)).map(n => if n.starts-with("class") {n} else {"class" + n})
|
||||
|
||||
let cells = nodes.map(n => raw(n, block: true, lang: "python"))
|
||||
|
||||
if calc.rem(cells.len(), 3) == 2 {
|
||||
let last-two = cells.slice(-2)
|
||||
cells = cells.slice(0, -2)
|
||||
let last = grid.cell(
|
||||
colspan: 3,
|
||||
stack(
|
||||
dir: ltr,
|
||||
spacing: 1fr,
|
||||
none,
|
||||
..last-two,
|
||||
none
|
||||
)
|
||||
)
|
||||
cells.push(last)
|
||||
}
|
||||
|
||||
align(center)[*#name*]
|
||||
if base-cls != none [Base class: #base-cls]
|
||||
|
||||
codly-disable()
|
||||
grid(
|
||||
columns: 3,
|
||||
column-gutter: 1fr,
|
||||
row-gutter: 2em,
|
||||
align: left + top,
|
||||
..cells
|
||||
)
|
||||
codly-enable()
|
||||
}
|
||||
@@ -38,7 +38,7 @@ Concretely, the lexer scans the source code character by character and produces
|
||||
caption: [`Token` and `Position` classes]
|
||||
) <fig:token-class>
|
||||
|
||||
`MidasLexer` has a simple constructor, taking in a source code string and optional file path, and a concise `process` method returning a list of tokens, as shown in @fig:lexer-signatures.
|
||||
`MidasLexer` has a simple constructor taking in a source code string and optional file path, and a concise `process` method returning a list of tokens, as shown in @fig:lexer-signatures.
|
||||
|
||||
#figure(
|
||||
```python
|
||||
|
||||
@@ -1,12 +1,26 @@
|
||||
#import "../../requirements.typ": isc-hei-bthesis
|
||||
#import isc-hei-bthesis: todo
|
||||
#import "@preview/acrostiche:0.7.0": acr
|
||||
#import "../../utils.typ": code-ref
|
||||
#import "@preview/codly:1.3.0": codly
|
||||
|
||||
= Python Type Checking <sec:impl-python>
|
||||
|
||||
We have now built a type definition language and registry, but it is of no use if we cannot type-check our Python code. In a similar fashion to `MidasTyper`, we must now implement a `PythonTyper`. Its role is to take in some Python source code and the types registry, walk through each statement and expression, infer the type of each term and verify the program's soundness. We will need several tools to concretize this process, such as a resolver to keep track of which value each variable references, and an environment to store the inferred types of local variables.
|
||||
|
||||
== Parsing Python <sec:python-parsing>
|
||||
#todo[]
|
||||
|
||||
For consistency and resource sharing, we will mirror a number of structures and methods used for the Midas language. However, we do not need to implement a lexer and parser for Python as it already provides an `ast` module just for this purpose. Nonetheless, we will implement our own #acr("AST") nodes similar to those discussed in @sec:midas-lexing-parsing. This allows having the same structure as the Midas #acr("AST") and explicitly only implement supported Python constructs. Additionally, it will make some node namings and internal structures clearer.
|
||||
|
||||
The same generation script is used to create dataclasses for each node type (see @sec:midas-lexing-parsing).
|
||||
|
||||
For Python, we will also use three kinds of nodes:
|
||||
- Statements (`Stmt`): top-level constructs introduced which can have side-effects in the environment
|
||||
- Expressions (`Expr`): composable building blocks with an inferrable type
|
||||
- Types (`MidasType`): type expressions used in annotations
|
||||
|
||||
The full list of #acr("AST") nodes used for Python is available in @tab:python-ast-nodes and their concrete implementation in the repository in #code-ref(<python-ast>, "midas/ast/python.py").
|
||||
|
||||
|
||||
== Resolving References <sec:python-resolver>
|
||||
#todo[]
|
||||
|
||||
Reference in New Issue
Block a user