Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
T
typon-compiler
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
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
typon
typon-compiler
Commits
4b427fb6
Commit
4b427fb6
authored
Feb 13, 2024
by
Tom Niget
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Start work on exprs
parent
2f8bdf7b
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
461 additions
and
157 deletions
+461
-157
typon/trans/tests/user_generics.py
typon/trans/tests/user_generics.py
+1
-1
typon/trans/transpiler/phases/emit_cpp/expr.py
typon/trans/transpiler/phases/emit_cpp/expr.py
+296
-0
typon/trans/transpiler/phases/emit_cpp/function.py
typon/trans/transpiler/phases/emit_cpp/function.py
+155
-156
typon/trans/transpiler/phases/emit_cpp/visitors.py
typon/trans/transpiler/phases/emit_cpp/visitors.py
+9
-0
No files found.
typon/trans/tests/user_generics.py
View file @
4b427fb6
...
...
@@ -23,4 +23,4 @@
if
__name__
==
"__main__"
:
print
(
5
)
\ No newline at end of file
print
(
"abc"
)
\ No newline at end of file
typon/trans/transpiler/phases/emit_cpp/expr.py
0 → 100644
View file @
4b427fb6
# coding: utf-8
import
ast
from
dataclasses
import
dataclass
,
field
from
typing
import
Iterable
from
transpiler.phases.emit_cpp.visitors
import
NodeVisitor
,
CoroutineMode
,
join
from
transpiler.phases.typing.scope
import
Scope
# noinspection PyPep8Naming
@
dataclass
class
ExpressionVisitor
(
NodeVisitor
):
scope
:
Scope
generator
:
CoroutineMode
def
visit
(
self
,
node
):
if
False
and
type
(
node
)
in
SYMBOLS
:
yield
SYMBOLS
[
type
(
node
)]
else
:
yield
from
NodeVisitor
.
visit
(
self
,
node
)
def
visit_Tuple
(
self
,
node
:
ast
.
Tuple
)
->
Iterable
[
str
]:
yield
"std::make_tuple("
yield
from
join
(
", "
,
map
(
self
.
visit
,
node
.
elts
))
yield
")"
def
visit_Constant
(
self
,
node
:
ast
.
Constant
)
->
Iterable
[
str
]:
if
isinstance
(
node
.
value
,
str
):
# TODO: escape sequences
yield
f"
\
"
{
repr
(
node
.
value
)[
1
:
-
1
]
}\
"
_ps"
elif
isinstance
(
node
.
value
,
bool
):
yield
str
(
node
.
value
).
lower
()
elif
isinstance
(
node
.
value
,
int
):
# TODO: bigints
yield
str
(
node
.
value
)
elif
isinstance
(
node
.
value
,
float
):
yield
repr
(
node
.
value
)
elif
isinstance
(
node
.
value
,
complex
):
yield
f"PyComplex(
{
node
.
value
.
real
}
,
{
node
.
value
.
imag
}
)"
elif
node
.
value
is
None
:
yield
"PyNone"
else
:
raise
NotImplementedError
(
node
,
type
(
node
))
def
visit_Slice
(
self
,
node
:
ast
.
Slice
)
->
Iterable
[
str
]:
yield
"PySlice("
yield
from
join
(
", "
,
(
self
.
visit
(
x
or
ast
.
Constant
(
value
=
None
))
for
x
in
(
node
.
lower
,
node
.
upper
,
node
.
step
)))
yield
")"
def
visit_Name
(
self
,
node
:
ast
.
Name
)
->
Iterable
[
str
]:
res
=
self
.
fix_name
(
node
.
id
)
if
self
.
scope
.
function
and
(
decl
:
=
self
.
scope
.
get
(
res
))
and
decl
.
type
is
self
.
scope
.
function
.
obj_type
:
if
not
self
.
scope
.
function
.
parent
.
function
:
res
=
"(*this)"
#if decl.kind == VarKind.SELF:
# res = "(*this)"
#elif decl.future and CoroutineMode.ASYNC in self.generator:
# res = f"{res}.get()"
# if decl.future == "future":
# res = "co_await " + res
yield
res
# def visit_Compare(self, node: ast.Compare) -> Iterable[str]:
# def make_lnd(op1, op2):
# return {
# "lineno": op1.lineno,
# "col_offset": op1.col_offset,
# "end_lineno": op2.end_lineno,
# "end_col_offset": op2.end_col_offset
# }
#
# operands = [node.left, *node.comparators]
# with self.prec_ctx("&&"):
# yield from self.visit_binary_operation(node.ops[0], operands[0], operands[1], make_lnd(operands[0], operands[1]))
# for (left, right), op in zip(zip(operands[1:], operands[2:]), node.ops[1:]):
# # TODO: cleaner code
# yield " && "
# yield from self.visit_binary_operation(op, left, right, make_lnd(left, right))
def
visit_BoolOp
(
self
,
node
:
ast
.
BoolOp
)
->
Iterable
[
str
]:
if
len
(
node
.
values
)
==
1
:
yield
from
self
.
visit
(
node
.
values
[
0
])
return
cpp_op
=
{
ast
.
And
:
"&&"
,
ast
.
Or
:
"||"
}[
type
(
node
.
op
)]
with
self
.
prec_ctx
(
cpp_op
):
yield
from
self
.
visit_binary_operation
(
cpp_op
,
node
.
values
[
0
],
node
.
values
[
1
],
make_lnd
(
node
.
values
[
0
],
node
.
values
[
1
]))
for
left
,
right
in
zip
(
node
.
values
[
1
:],
node
.
values
[
2
:]):
yield
f"
{
cpp_op
}
"
yield
from
self
.
visit_binary_operation
(
cpp_op
,
left
,
right
,
make_lnd
(
left
,
right
))
def
visit_Call
(
self
,
node
:
ast
.
Call
)
->
Iterable
[
str
]:
# TODO
# if getattr(node, "keywords", None):
# raise NotImplementedError(node, "keywords")
if
getattr
(
node
,
"starargs"
,
None
):
raise
NotImplementedError
(
node
,
"varargs"
)
if
getattr
(
node
,
"kwargs"
,
None
):
raise
NotImplementedError
(
node
,
"kwargs"
)
func
=
node
.
func
if
isinstance
(
func
,
ast
.
Attribute
):
if
sym
:
=
DUNDER_SYMBOLS
.
get
(
func
.
attr
,
None
):
if
len
(
node
.
args
)
==
1
:
yield
from
self
.
visit_binary_operation
(
sym
,
func
.
value
,
node
.
args
[
0
],
linenodata
(
node
))
else
:
yield
from
self
.
visit_unary_operation
(
sym
,
func
.
value
)
return
for
name
in
(
"fork"
,
"future"
):
if
compare_ast
(
func
,
ast
.
parse
(
name
,
mode
=
"eval"
).
body
):
assert
len
(
node
.
args
)
==
1
arg
=
node
.
args
[
0
]
assert
isinstance
(
arg
,
ast
.
Lambda
)
node
.
is_future
=
name
vis
=
self
.
reset
()
vis
.
generator
=
CoroutineMode
.
SYNC
# todo: bad code
if
CoroutineMode
.
ASYNC
in
self
.
generator
:
yield
f"co_await typon::
{
name
}
("
yield
from
vis
.
visit
(
arg
.
body
)
yield
")"
return
elif
CoroutineMode
.
FAKE
in
self
.
generator
:
yield
from
self
.
visit
(
arg
.
body
)
return
if
compare_ast
(
func
,
ast
.
parse
(
'sync'
,
mode
=
"eval"
).
body
):
if
CoroutineMode
.
ASYNC
in
self
.
generator
:
yield
"co_await typon::Sync()"
elif
CoroutineMode
.
FAKE
in
self
.
generator
:
yield
from
()
return
# TODO: precedence needed?
if
CoroutineMode
.
ASYNC
in
self
.
generator
and
node
.
is_await
:
yield
"("
# TODO: temporary
yield
"co_await "
node
.
in_await
=
True
elif
CoroutineMode
.
FAKE
in
self
.
generator
:
func
=
ast
.
Attribute
(
value
=
func
,
attr
=
"sync"
,
ctx
=
ast
.
Load
())
yield
from
self
.
prec
(
"()"
).
visit
(
func
)
yield
"("
yield
from
join
(
", "
,
map
(
self
.
reset
().
visit
,
node
.
args
))
yield
")"
if
CoroutineMode
.
ASYNC
in
self
.
generator
and
node
.
is_await
:
yield
")"
def
visit_Lambda
(
self
,
node
:
ast
.
Lambda
)
->
Iterable
[
str
]:
yield
"[]"
templ
,
args
,
_
=
self
.
process_args
(
node
.
args
)
yield
templ
yield
args
yield
"{"
yield
"return"
yield
from
self
.
reset
().
visit
(
node
.
body
)
yield
";"
yield
"}"
def
visit_BinOp
(
self
,
node
:
ast
.
BinOp
)
->
Iterable
[
str
]:
yield
from
self
.
visit_binary_operation
(
node
.
op
,
node
.
left
,
node
.
right
,
linenodata
(
node
))
def
visit_Compare
(
self
,
node
:
ast
.
Compare
)
->
Iterable
[
str
]:
yield
from
self
.
visit_binary_operation
(
node
.
ops
[
0
],
node
.
left
,
node
.
comparators
[
0
],
linenodata
(
node
))
def
visit_binary_operation
(
self
,
op
,
left
:
ast
.
AST
,
right
:
ast
.
AST
,
lnd
:
dict
)
->
Iterable
[
str
]:
# if type(op) == ast.In:
# call = ast.Call(ast.Attribute(right, "__contains__", **lnd), [left], [], **lnd)
# call.is_await = False
# yield from self.visit_Call(call)
# print(call.func.type)
# return
if
type
(
op
)
!=
str
:
op
=
SYMBOLS
[
type
(
op
)]
# TODO: handle precedence locally since only binops really need it
# we could just store the history of traversed nodes and check if the last one was a binop
prio
=
self
.
precedence
and
PRECEDENCE_LEVELS
[
self
.
precedence
[
-
1
]]
<
PRECEDENCE_LEVELS
[
op
]
if
prio
:
yield
"("
with
self
.
prec_ctx
(
op
):
yield
from
self
.
visit
(
left
)
yield
op
yield
from
self
.
visit
(
right
)
if
prio
:
yield
")"
def
visit_Attribute
(
self
,
node
:
ast
.
Attribute
)
->
Iterable
[
str
]:
yield
"dot"
yield
"(("
yield
from
self
.
visit
(
node
.
value
)
yield
"), "
yield
self
.
fix_name
(
node
.
attr
)
yield
")"
def
visit_List
(
self
,
node
:
ast
.
List
)
->
Iterable
[
str
]:
if
node
.
elts
:
yield
"typon::PyList{"
yield
from
join
(
", "
,
map
(
self
.
reset
().
visit
,
node
.
elts
))
yield
"}"
else
:
yield
from
self
.
visit
(
node
.
type
)
yield
"{}"
def
visit_Set
(
self
,
node
:
ast
.
Set
)
->
Iterable
[
str
]:
if
node
.
elts
:
yield
"typon::PySet{"
yield
from
join
(
", "
,
map
(
self
.
reset
().
visit
,
node
.
elts
))
yield
"}"
else
:
yield
from
self
.
visit
(
node
.
type
)
yield
"{}"
def
visit_Dict
(
self
,
node
:
ast
.
Dict
)
->
Iterable
[
str
]:
def
visit_item
(
key
,
value
):
yield
"std::pair {"
yield
from
self
.
reset
().
visit
(
key
)
yield
", "
yield
from
self
.
reset
().
visit
(
value
)
yield
"}"
if
node
.
keys
:
yield
from
self
.
visit
(
node
.
type
)
yield
"{"
yield
from
join
(
", "
,
map
(
visit_item
,
node
.
keys
,
node
.
values
))
yield
"}"
else
:
yield
from
self
.
visit
(
node
.
type
)
yield
"{}"
def
visit_Subscript
(
self
,
node
:
ast
.
Subscript
)
->
Iterable
[
str
]:
if
isinstance
(
node
.
type
,
TypeType
)
and
isinstance
(
node
.
type
.
type_object
,
MonomorphizedUserType
):
yield
node
.
type
.
type_object
.
name
return
yield
from
self
.
prec
(
"[]"
).
visit
(
node
.
value
)
yield
"["
yield
from
self
.
reset
().
visit
(
node
.
slice
)
yield
"]"
def
visit_UnaryOp
(
self
,
node
:
ast
.
UnaryOp
)
->
Iterable
[
str
]:
yield
from
self
.
visit_unary_operation
(
node
.
op
,
node
.
operand
)
def
visit_unary_operation
(
self
,
op
,
operand
)
->
Iterable
[
str
]:
if
type
(
op
)
!=
str
:
op
=
SYMBOLS
[
type
(
op
)]
yield
op
yield
from
self
.
prec
(
"unary"
).
visit
(
operand
)
def
visit_IfExp
(
self
,
node
:
ast
.
IfExp
)
->
Iterable
[
str
]:
with
self
.
prec_ctx
(
"?:"
):
yield
from
self
.
visit
(
node
.
test
)
yield
" ? "
yield
from
self
.
visit
(
node
.
body
)
yield
" : "
yield
from
self
.
visit
(
node
.
orelse
)
def
visit_Yield
(
self
,
node
:
ast
.
Yield
)
->
Iterable
[
str
]:
#if CoroutineMode.GENERATOR in self.generator:
# yield "co_yield"
# yield from self.prec("co_yield").visit(node.value)
#elif CoroutineMode.FAKE in self.generator:
# yield "return"
# yield from self.visit(node.value)
#else:
# raise NotImplementedError(node)
yield
"co_yield"
yield
from
self
.
prec
(
"co_yield"
).
visit
(
node
.
value
)
def
visit_ListComp
(
self
,
node
:
ast
.
ListComp
)
->
Iterable
[
str
]:
if
len
(
node
.
generators
)
!=
1
:
raise
NotImplementedError
(
"Multiple generators not handled yet"
)
gen
:
ast
.
comprehension
=
node
.
generators
[
0
]
yield
"mapFilter([]("
yield
from
self
.
visit
(
node
.
input_item_type
)
yield
from
self
.
visit
(
gen
.
target
)
yield
") { return "
yield
from
self
.
visit
(
node
.
elt
)
yield
"; }, "
yield
from
self
.
visit
(
gen
.
iter
)
if
gen
.
ifs
:
yield
", "
yield
"[]("
yield
from
self
.
visit
(
node
.
input_item_type
)
yield
from
self
.
visit
(
gen
.
target
)
yield
") { return "
yield
from
self
.
visit
(
gen
.
ifs_node
)
yield
"; }"
yield
")"
# iter_type = get_iter(self.visit(gen.iter))
# next_type = get_next(iter_type)
# virt_scope = self.scope.child(ScopeKind.FUNCTION_INNER)
# from transpiler import ScoperBlockVisitor
# visitor = ScoperBlockVisitor(virt_scope)
# visitor.visit_assign_target(gen.target, next_type)
# res_item_type = visitor.expr().visit(node.elt)
# for if_ in gen.ifs:
# visitor.expr().visit(if_)
# return PyList(res_item_type)
\ No newline at end of file
typon/trans/transpiler/phases/emit_cpp/function.py
View file @
4b427fb6
import
ast
from
dataclasses
import
dataclass
from
dataclasses
import
dataclass
,
field
from
typing
import
Iterable
,
Optional
from
transpiler.phases.emit_cpp.expr
import
ExpressionVisitor
from
transpiler.phases.typing.scope
import
Scope
from
transpiler.phases.emit_cpp.visitors
import
NodeVisitor
,
flatmap
from
transpiler.phases.emit_cpp.visitors
import
NodeVisitor
,
flatmap
,
CoroutineMode
from
transpiler.phases.typing.types
import
CallableInstanceType
,
BaseType
...
...
@@ -23,11 +23,10 @@ def emit_function(name: str, func: CallableInstanceType) -> Iterable[str]:
if
False
:
@
dataclass
class
BlockVisitor
(
NodeVisitor
):
@
dataclass
class
BlockVisitor
(
NodeVisitor
):
scope
:
Scope
#
generator: CoroutineMode = field(default=CoroutineMode.SYNC, kw_only=True)
generator
:
CoroutineMode
=
field
(
default
=
CoroutineMode
.
SYNC
,
kw_only
=
True
)
def
expr
(
self
)
->
ExpressionVisitor
:
return
ExpressionVisitor
(
self
.
scope
,
self
.
generator
)
...
...
@@ -38,34 +37,34 @@ if False:
# def visit_FunctionDef(self, node: ast.FunctionDef) -> Iterable[str]:
# yield from self.visit_free_func(node)
def
visit_free_func
(
self
,
node
:
ast
.
FunctionDef
,
emission
:
FunctionEmissionKind
)
->
Iterable
[
str
]:
if
getattr
(
node
,
"is_main"
,
False
):
if
emission
==
FunctionEmissionKind
.
DECLARATION
:
return
# 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
"typon::Root root() const"
def
block
():
yield
from
node
.
body
yield
ast
.
Return
()
from
transpiler.phases.emit_cpp.function
import
FunctionVisitor
yield
"{"
yield
from
self
.
visit_func_decls
(
block
(),
node
.
scope
,
CoroutineMode
.
TASK
)
yield
"}"
return
if
emission
==
FunctionEmissionKind
.
DECLARATION
:
yield
f"struct
{
node
.
name
}
_inner {{"
yield
from
self
.
visit_func_new
(
node
,
emission
)
if
emission
==
FunctionEmissionKind
.
DECLARATION
:
yield
f"}}
{
node
.
name
}
;"
#
def visit_free_func(self, node: ast.FunctionDef, emission: FunctionEmissionKind) -> Iterable[str]:
#
if getattr(node, "is_main", False):
#
if emission == FunctionEmissionKind.DECLARATION:
#
return
#
# 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 "typon::Root root() const"
#
#
def block():
#
yield from node.body
#
yield ast.Return()
#
#
from transpiler.phases.emit_cpp.function import FunctionVisitor
#
yield "{"
#
yield from self.visit_func_decls(block(), node.scope, CoroutineMode.TASK)
#
yield "}"
#
return
#
#
if emission == FunctionEmissionKind.DECLARATION:
#
yield f"struct {node.name}_inner {{"
#
yield from self.visit_func_new(node, emission)
#
if emission == FunctionEmissionKind.DECLARATION:
#
yield f"}} {node.name};"
def
visit_func_decls
(
self
,
body
:
list
[
ast
.
stmt
],
inner_scope
:
Scope
,
mode
=
CoroutineMode
.
ASYNC
)
->
Iterable
[
str
]:
for
child
in
body
:
...
...
typon/trans/transpiler/phases/emit_cpp/visitors.py
View file @
4b427fb6
import
ast
from
enum
import
Flag
from
itertools
import
chain
from
typing
import
Iterable
...
...
@@ -89,3 +90,11 @@ def join(sep: str, items: Iterable[Iterable[str]]) -> Iterable[str]:
def
flatmap
(
f
,
items
):
return
chain
.
from_iterable
(
map
(
f
,
items
))
class
CoroutineMode
(
Flag
):
SYNC
=
1
FAKE
=
2
|
SYNC
ASYNC
=
4
GENERATOR
=
8
|
ASYNC
TASK
=
16
|
ASYNC
JOIN
=
32
|
ASYNC
\ No newline at end of file
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