Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
T
typon
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Tom Niget
typon
Commits
47dae27f
Commit
47dae27f
authored
Mar 10, 2023
by
Tom Niget
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Separate visitors into File, Module and Function
parent
9eb76c50
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
100 additions
and
84 deletions
+100
-84
trans/transpiler/__init__.py
trans/transpiler/__init__.py
+100
-84
No files found.
trans/transpiler/__init__.py
View file @
47dae27f
...
@@ -42,7 +42,7 @@ def join(sep: str, items: Iterable[Iterable[str]]) -> Iterable[str]:
...
@@ -42,7 +42,7 @@ def join(sep: str, items: Iterable[Iterable[str]]) -> Iterable[str]:
def
transpile
(
source
):
def
transpile
(
source
):
tree
=
ast
.
parse
(
source
)
tree
=
ast
.
parse
(
source
)
# print(ast.unparse(tree))
# print(ast.unparse(tree))
return
"
\
n
"
.
join
(
filter
(
None
,
map
(
str
,
Block
Visitor
(
Scope
()).
visit
(
tree
))))
return
"
\
n
"
.
join
(
filter
(
None
,
map
(
str
,
File
Visitor
(
Scope
()).
visit
(
tree
))))
SYMBOLS
=
{
SYMBOLS
=
{
...
@@ -289,11 +289,13 @@ class ExpressionVisitor(NodeVisitor):
...
@@ -289,11 +289,13 @@ class ExpressionVisitor(NodeVisitor):
yield
" : "
yield
" : "
yield
from
self
.
visit
(
node
.
orelse
)
yield
from
self
.
visit
(
node
.
orelse
)
@
dataclass
@
dataclass
class
VarDecl
:
class
VarDecl
:
kind
:
VarKind
kind
:
VarKind
val
:
Optional
[
str
]
val
:
Optional
[
str
]
@
dataclass
@
dataclass
class
Scope
:
class
Scope
:
parent
:
Optional
[
"Scope"
]
=
None
parent
:
Optional
[
"Scope"
]
=
None
...
@@ -318,7 +320,8 @@ class Scope:
...
@@ -318,7 +320,8 @@ class Scope:
The check does not cross function boundaries; i.e. global variables are not taken into account from inside
The check does not cross function boundaries; i.e. global variables are not taken into account from inside
functions.
functions.
"""
"""
return
name
in
self
.
vars
or
(
not
self
.
is_function
and
self
.
parent
is
not
None
and
self
.
parent
.
exists_local
(
name
))
return
name
in
self
.
vars
or
(
not
self
.
is_function
and
self
.
parent
is
not
None
and
self
.
parent
.
exists_local
(
name
))
def
child
(
self
)
->
"Scope"
:
def
child
(
self
)
->
"Scope"
:
"""
"""
...
@@ -371,35 +374,6 @@ class BlockVisitor(NodeVisitor):
...
@@ -371,35 +374,6 @@ class BlockVisitor(NodeVisitor):
def
__init__
(
self
,
scope
:
Scope
):
def
__init__
(
self
,
scope
:
Scope
):
self
.
_scope
=
scope
self
.
_scope
=
scope
def
visit_Module
(
self
,
node
:
ast
.
Module
)
->
Iterable
[
str
]:
stmt
:
ast
.
AST
yield
"#include <python/builtins.hpp>"
for
stmt
in
node
.
body
:
yield
from
self
.
visit
(
stmt
)
def
visit_Expr
(
self
,
node
:
ast
.
Expr
)
->
Iterable
[
str
]:
yield
from
ExpressionVisitor
().
visit
(
node
.
value
)
yield
";"
def
visit_Import
(
self
,
node
:
ast
.
Import
)
->
Iterable
[
str
]:
for
alias
in
node
.
names
:
if
alias
.
name
==
"typon"
:
yield
""
else
:
yield
from
self
.
import_module
(
alias
.
name
)
yield
f'auto&
{
alias
.
asname
or
alias
.
name
}
= py_
{
alias
.
name
}
::all;'
def
import_module
(
self
,
name
:
str
)
->
Iterable
[
str
]:
yield
f'#include "python/
{
name
}
.hpp"'
def
visit_ImportFrom
(
self
,
node
:
ast
.
ImportFrom
)
->
Iterable
[
str
]:
if
node
.
module
==
"typon"
:
yield
""
else
:
yield
from
self
.
import_module
(
node
.
module
)
for
alias
in
node
.
names
:
yield
f"auto&
{
alias
.
asname
or
alias
.
name
}
= py_
{
node
.
module
}
::all.
{
alias
.
name
}
;"
def
visit_FunctionDef
(
self
,
node
:
ast
.
FunctionDef
)
->
Iterable
[
str
]:
def
visit_FunctionDef
(
self
,
node
:
ast
.
FunctionDef
)
->
Iterable
[
str
]:
templ
,
args
=
self
.
process_args
(
node
.
args
)
templ
,
args
=
self
.
process_args
(
node
.
args
)
if
templ
:
if
templ
:
...
@@ -408,7 +382,7 @@ class BlockVisitor(NodeVisitor):
...
@@ -408,7 +382,7 @@ class BlockVisitor(NodeVisitor):
yield
f"auto
{
node
.
name
}
"
yield
f"auto
{
node
.
name
}
"
yield
args
yield
args
yield
"{"
yield
"{"
inner
=
Block
Visitor
(
self
.
_scope
.
function
())
inner
=
Function
Visitor
(
self
.
_scope
.
function
())
for
child
in
node
.
body
:
for
child
in
node
.
body
:
# Python uses module- and function- level scoping. Blocks, like conditionals and loops, do not form scopes
# Python uses module- and function- level scoping. Blocks, like conditionals and loops, do not form scopes
# on their own. Variables are still accessible in the remainder of the parent function or in the global
# on their own. Variables are still accessible in the remainder of the parent function or in the global
...
@@ -448,7 +422,7 @@ class BlockVisitor(NodeVisitor):
...
@@ -448,7 +422,7 @@ class BlockVisitor(NodeVisitor):
# auto y = 2;
# auto y = 2;
# }
# }
# ```
# ```
child_visitor
=
Block
Visitor
(
inner
.
_scope
.
child
())
child_visitor
=
Function
Visitor
(
inner
.
_scope
.
child
())
# We need to do this in two-passes. This unfortunately breaks our nice generator state-machine architecture.
# We need to do this in two-passes. This unfortunately breaks our nice generator state-machine architecture.
# Fair enough.
# Fair enough.
...
@@ -463,60 +437,12 @@ class BlockVisitor(NodeVisitor):
...
@@ -463,60 +437,12 @@ class BlockVisitor(NodeVisitor):
yield
from
child_code
# Yeet back the child node code.
yield
from
child_code
# Yeet back the child node code.
yield
"}"
yield
"}"
def
visit_Global
(
self
,
node
:
ast
.
Global
)
->
Iterable
[
str
]:
for
name
in
map
(
self
.
fix_name
,
node
.
names
):
self
.
_scope
.
vars
[
name
]
=
VarDecl
(
VarKind
.
GLOBAL
,
None
)
yield
""
def
visit_Nonlocal
(
self
,
node
:
ast
.
Nonlocal
)
->
Iterable
[
str
]:
for
name
in
map
(
self
.
fix_name
,
node
.
names
):
self
.
_scope
.
vars
[
name
]
=
VarDecl
(
VarKind
.
NONLOCAL
,
None
)
yield
""
def
visit_If
(
self
,
node
:
ast
.
If
)
->
Iterable
[
str
]:
if
not
node
.
orelse
and
compare_ast
(
node
.
test
,
ast
.
parse
(
'__name__ == "__main__"'
,
mode
=
"eval"
).
body
):
# Special case handling for Python's interesting way of defining an entry point.
# I mean, it's not *that* bad, it's just an attempt at retrofitting an "entry point" logic in a scripting
# language that, by essence, uses "the start of the file" as the implicit entry point, since files are
# read and executed line-by-line, contrary to usual structured languages that mark a distinction between
# declarations (functions, classes, modules, ...) and code.
# Also, for nitpickers, the C++ standard explicitly allows for omitting a `return` statement in the `main`.
# 0 is returned by default.
yield
"int main()"
yield
from
self
.
emit_block
(
node
.
body
)
return
yield
"if ("
yield
from
ExpressionVisitor
().
visit
(
node
.
test
)
yield
")"
yield
from
self
.
emit_block
(
node
.
body
)
if
node
.
orelse
:
yield
"else "
if
isinstance
(
node
.
orelse
,
ast
.
If
):
yield
from
self
.
visit
(
node
.
orelse
)
else
:
yield
from
self
.
emit_block
(
node
.
orelse
)
def
visit_Return
(
self
,
node
:
ast
.
Return
)
->
Iterable
[
str
]:
yield
"return "
if
node
.
value
:
yield
from
ExpressionVisitor
().
visit
(
node
.
value
)
yield
";"
def
visit_While
(
self
,
node
:
ast
.
While
)
->
Iterable
[
str
]:
yield
"while ("
yield
from
ExpressionVisitor
().
visit
(
node
.
test
)
yield
")"
yield
from
self
.
emit_block
(
node
.
body
)
if
node
.
orelse
:
raise
NotImplementedError
(
node
,
"orelse"
)
def
visit_lvalue
(
self
,
lvalue
:
ast
.
expr
,
val
:
Optional
[
ast
.
AST
]
=
None
)
->
Iterable
[
str
]:
def
visit_lvalue
(
self
,
lvalue
:
ast
.
expr
,
val
:
Optional
[
ast
.
AST
]
=
None
)
->
Iterable
[
str
]:
if
isinstance
(
lvalue
,
ast
.
Tuple
):
if
isinstance
(
lvalue
,
ast
.
Tuple
):
yield
f"std::tie(
{
', '
.
join
(
flatmap
(
ExpressionVisitor
().
visit
,
lvalue
.
elts
))
}
)"
yield
f"std::tie(
{
', '
.
join
(
flatmap
(
ExpressionVisitor
().
visit
,
lvalue
.
elts
))
}
)"
elif
isinstance
(
lvalue
,
ast
.
Name
):
elif
isinstance
(
lvalue
,
ast
.
Name
):
name
=
self
.
fix_name
(
lvalue
.
id
)
name
=
self
.
fix_name
(
lvalue
.
id
)
#if name not in self._scope.vars:
#
if name not in self._scope.vars:
if
not
self
.
_scope
.
exists_local
(
name
):
if
not
self
.
_scope
.
exists_local
(
name
):
yield
self
.
_scope
.
declare
(
name
,
" "
.
join
(
ExpressionVisitor
().
visit
(
val
))
if
val
else
None
)
yield
self
.
_scope
.
declare
(
name
,
" "
.
join
(
ExpressionVisitor
().
visit
(
val
))
if
val
else
None
)
yield
name
yield
name
...
@@ -541,6 +467,60 @@ class BlockVisitor(NodeVisitor):
...
@@ -541,6 +467,60 @@ class BlockVisitor(NodeVisitor):
yield
from
ExpressionVisitor
().
visit
(
node
.
value
)
yield
from
ExpressionVisitor
().
visit
(
node
.
value
)
yield
";"
yield
";"
# noinspection PyPep8Naming
class
FileVisitor
(
BlockVisitor
):
def
visit_Module
(
self
,
node
:
ast
.
Module
)
->
Iterable
[
str
]:
stmt
:
ast
.
AST
yield
"#include <python/builtins.hpp>"
visitor
=
ModuleVisitor
(
self
.
_scope
)
for
stmt
in
node
.
body
:
yield
from
visitor
.
visit
(
stmt
)
# noinspection PyPep8Naming
class
ModuleVisitor
(
BlockVisitor
):
def
visit_Import
(
self
,
node
:
ast
.
Import
)
->
Iterable
[
str
]:
for
alias
in
node
.
names
:
if
alias
.
name
==
"typon"
:
yield
""
else
:
yield
from
self
.
import_module
(
alias
.
name
)
yield
f'auto&
{
alias
.
asname
or
alias
.
name
}
= py_
{
alias
.
name
}
::all;'
def
import_module
(
self
,
name
:
str
)
->
Iterable
[
str
]:
yield
f'#include "python/
{
name
}
.hpp"'
def
visit_ImportFrom
(
self
,
node
:
ast
.
ImportFrom
)
->
Iterable
[
str
]:
if
node
.
module
==
"typon"
:
yield
""
else
:
yield
from
self
.
import_module
(
node
.
module
)
for
alias
in
node
.
names
:
yield
f"auto&
{
alias
.
asname
or
alias
.
name
}
= py_
{
node
.
module
}
::all.
{
alias
.
name
}
;"
def
visit_If
(
self
,
node
:
ast
.
If
)
->
Iterable
[
str
]:
if
not
node
.
orelse
and
compare_ast
(
node
.
test
,
ast
.
parse
(
'__name__ == "__main__"'
,
mode
=
"eval"
).
body
):
# Special case handling for Python's interesting way of defining an entry point.
# I mean, it's not *that* bad, it's just an attempt at retrofitting an "entry point" logic in a scripting
# language that, by essence, uses "the start of the file" as the implicit entry point, since files are
# read and executed line-by-line, contrary to usual structured languages that mark a distinction between
# declarations (functions, classes, modules, ...) and code.
# Also, for nitpickers, the C++ standard explicitly allows for omitting a `return` statement in the `main`.
# 0 is returned by default.
yield
"int main()"
yield
from
FunctionVisitor
(
self
.
_scope
).
emit_block
(
node
.
body
)
return
raise
NotImplementedError
(
node
,
"global scope if"
)
# noinspection PyPep8Naming
class
FunctionVisitor
(
BlockVisitor
):
def
visit_Expr
(
self
,
node
:
ast
.
Expr
)
->
Iterable
[
str
]:
yield
from
ExpressionVisitor
().
visit
(
node
.
value
)
yield
";"
def
visit_AugAssign
(
self
,
node
:
ast
.
AugAssign
)
->
Iterable
[
str
]:
def
visit_AugAssign
(
self
,
node
:
ast
.
AugAssign
)
->
Iterable
[
str
]:
yield
from
self
.
visit_lvalue
(
node
.
target
)
yield
from
self
.
visit_lvalue
(
node
.
target
)
yield
SYMBOLS
[
type
(
node
.
op
)]
+
"="
yield
SYMBOLS
[
type
(
node
.
op
)]
+
"="
...
@@ -557,11 +537,47 @@ class BlockVisitor(NodeVisitor):
...
@@ -557,11 +537,47 @@ class BlockVisitor(NodeVisitor):
if
node
.
orelse
:
if
node
.
orelse
:
raise
NotImplementedError
(
node
,
"orelse"
)
raise
NotImplementedError
(
node
,
"orelse"
)
def
block
(
self
)
->
"BlockVisitor"
:
def
visit_If
(
self
,
node
:
ast
.
If
)
->
Iterable
[
str
]:
yield
"if ("
yield
from
ExpressionVisitor
().
visit
(
node
.
test
)
yield
")"
yield
from
self
.
emit_block
(
node
.
body
)
if
node
.
orelse
:
yield
"else "
if
isinstance
(
node
.
orelse
,
ast
.
If
):
yield
from
self
.
visit
(
node
.
orelse
)
else
:
yield
from
self
.
emit_block
(
node
.
orelse
)
def
visit_Return
(
self
,
node
:
ast
.
Return
)
->
Iterable
[
str
]:
yield
"return "
if
node
.
value
:
yield
from
ExpressionVisitor
().
visit
(
node
.
value
)
yield
";"
def
visit_While
(
self
,
node
:
ast
.
While
)
->
Iterable
[
str
]:
yield
"while ("
yield
from
ExpressionVisitor
().
visit
(
node
.
test
)
yield
")"
yield
from
self
.
emit_block
(
node
.
body
)
if
node
.
orelse
:
raise
NotImplementedError
(
node
,
"orelse"
)
def
visit_Global
(
self
,
node
:
ast
.
Global
)
->
Iterable
[
str
]:
for
name
in
map
(
self
.
fix_name
,
node
.
names
):
self
.
_scope
.
vars
[
name
]
=
VarDecl
(
VarKind
.
GLOBAL
,
None
)
yield
""
def
visit_Nonlocal
(
self
,
node
:
ast
.
Nonlocal
)
->
Iterable
[
str
]:
for
name
in
map
(
self
.
fix_name
,
node
.
names
):
self
.
_scope
.
vars
[
name
]
=
VarDecl
(
VarKind
.
NONLOCAL
,
None
)
yield
""
def
block
(
self
)
->
"FunctionVisitor"
:
# See the comments in visit_FunctionDef.
# See the comments in visit_FunctionDef.
# A Python code block does not introduce a new scope, so we create a new `Scope` object that shares the same
# A Python code block does not introduce a new scope, so we create a new `Scope` object that shares the same
# variables as the parent scope.
# variables as the parent scope.
return
Block
Visitor
(
self
.
_scope
.
child_share
())
return
Function
Visitor
(
self
.
_scope
.
child_share
())
def
emit_block
(
self
,
items
:
List
[
ast
.
stmt
])
->
Iterable
[
str
]:
def
emit_block
(
self
,
items
:
List
[
ast
.
stmt
])
->
Iterable
[
str
]:
yield
"{"
yield
"{"
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment