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
e3cc4548
Commit
e3cc4548
authored
Mar 19, 2023
by
Tom Niget
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add basic coroutine+await support
parent
45014685
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
155 additions
and
50 deletions
+155
-50
rt/include/python/builtins.hpp
rt/include/python/builtins.hpp
+3
-1
rt/include/python/builtins/print.hpp
rt/include/python/builtins/print.hpp
+22
-5
trans/tests/_gen_test.py
trans/tests/_gen_test.py
+0
-0
trans/tests/naive_fibonacci_fork.py
trans/tests/naive_fibonacci_fork.py
+8
-1
trans/transpiler/__init__.py
trans/transpiler/__init__.py
+122
-43
No files found.
rt/include/python/builtins.hpp
View file @
e3cc4548
...
...
@@ -10,6 +10,8 @@
#include <ostream>
#include <string>
#include <typon/typon.hpp>
#ifdef __cpp_lib_unreachable
#include <utility>
[[
noreturn
]]
inline
void
TYPON_UNREACHABLE
()
{
std
::
unreachable
();
}
...
...
@@ -58,7 +60,7 @@ std::ostream &operator<<(std::ostream &os, std::optional<T> const &opt) {
return
opt
?
os
<<
opt
.
value
()
:
os
<<
"None"
;
}
bool
is_cpp
()
{
return
true
;
}
typon
::
Task
<
bool
>
is_cpp
()
{
co_
return
true
;
}
class
NoneType
{
public:
...
...
rt/include/python/builtins/print.hpp
View file @
e3cc4548
...
...
@@ -8,6 +8,8 @@
#include <iostream>
#include <ostream>
#include <typon/typon.hpp>
template
<
typename
T
>
concept
Streamable
=
requires
(
const
T
&
x
,
std
::
ostream
&
s
)
{
{
s
<<
x
}
->
std
::
same_as
<
std
::
ostream
&>
;
...
...
@@ -39,13 +41,28 @@ template <typename T>
concept
Printable
=
requires
(
const
T
&
x
,
std
::
ostream
&
s
)
{
{
print_to
(
x
,
s
)
}
->
std
::
same_as
<
void
>
;
};
/*
template <Printable T, Printable... Args>
void
print
(
T
const
&
head
,
Args
const
&
...
args
)
{
typon::Task<void>
print(T const &head, Args const &...args) {
print_to(head, std::cout);
(((std::cout << ' '), print_to(args, std::cout)), ...);
std
::
cout
<<
'\n'
;
}
std::cout << '\n'; co_return;
}*/
struct
{
void
sync
()
{
std
::
cout
<<
'\n'
;
}
template
<
Printable
T
,
Printable
...
Args
>
void
sync
(
T
const
&
head
,
Args
const
&
...
args
)
{
print_to
(
head
,
std
::
cout
);
(((
std
::
cout
<<
' '
),
print_to
(
args
,
std
::
cout
)),
...);
std
::
cout
<<
'\n'
;
}
void
print
()
{
std
::
cout
<<
'\n'
;
}
template
<
Printable
...
Args
>
typon
::
Task
<
void
>
operator
()(
Args
const
&
...
args
)
{
co_return
sync
(
args
...);
}
}
print
;
//typon::Task<void> print() { std::cout << '\n'; co_return; }
#endif // TYPON_PRINT_HPP
trans/tests/gen_test.py
→
trans/tests/
_
gen_test.py
View file @
e3cc4548
File moved
trans/tests/naive_fibonacci_fork.py
View file @
e3cc4548
from
typon
import
fork
,
sync
def
fibo
(
n
):
if
n
<
2
:
return
n
a
=
fibo
(
n
-
1
)
b
=
fibo
(
n
-
2
)
return
a
+
b
#def fibo(n: int) -> int:
# if n < 2:
...
...
@@ -11,4 +18,4 @@ from typon import fork, sync
if
__name__
==
"__main__"
:
print
(
"res="
,
5
,
"."
)
\ No newline at end of file
print
(
fibo
(
30
))
# should display 832040
\ No newline at end of file
trans/transpiler/__init__.py
View file @
e3cc4548
...
...
@@ -41,7 +41,6 @@ def join(sep: str, items: Iterable[Iterable[str]]) -> Iterable[str]:
def
transpile
(
source
):
tree
=
ast
.
parse
(
source
)
# print(ast.unparse(tree))
return
"
\
n
"
.
join
(
filter
(
None
,
map
(
str
,
FileVisitor
(
Scope
()).
visit
(
tree
))))
...
...
@@ -74,7 +73,7 @@ SYMBOLS = {
PRECEDENCE
=
[
(
"()"
,
"[]"
,
"."
,),
(
"unary"
,),
(
"unary"
,
"co_await"
),
(
"*"
,
"/"
,
"%"
,),
(
"+"
,
"-"
),
(
"<<"
,
">>"
),
...
...
@@ -85,7 +84,7 @@ PRECEDENCE = [
(
"|"
,),
(
"&&"
,),
(
"||"
,),
(
"?:"
,),
(
"?:"
,
"co_yield"
),
(
","
,)
]
"""Precedence of C++ operators."""
...
...
@@ -106,6 +105,7 @@ class VarKind(Enum):
LOCAL
=
1
GLOBAL
=
2
NONLOCAL
=
3
SELF
=
4
@
dataclass
...
...
@@ -119,15 +119,19 @@ class UnsupportedNodeError(Exception):
class
NodeVisitor
:
def
visit
(
self
,
node
):
"""Visit a node."""
if
type
(
node
)
in
SYMBOLS
:
yield
SYMBOLS
[
type
(
node
)]
if
type
(
node
)
==
list
:
for
n
in
node
:
yield
from
self
.
visit
(
n
)
else
:
for
parent
in
node
.
__class__
.
__mro__
:
if
visitor
:
=
getattr
(
self
,
'visit_'
+
parent
.
__name__
,
None
):
yield
from
visitor
(
node
)
break
else
:
raise
UnsupportedNodeError
(
node
)
return
self
.
missing_impl
(
node
)
def
missing_impl
(
self
,
node
):
raise
UnsupportedNodeError
(
node
)
def
process_args
(
self
,
node
:
ast
.
arguments
)
->
(
str
,
str
,
str
):
for
field
in
(
"posonlyargs"
,
"vararg"
,
"kwonlyargs"
,
"kw_defaults"
,
"kwarg"
,
"defaults"
):
...
...
@@ -149,6 +153,21 @@ class NodeVisitor:
return
MAPPINGS
.
get
(
name
,
name
)
class
SearchVisitor
(
NodeVisitor
):
def
missing_impl
(
self
,
node
):
if
not
hasattr
(
node
,
"__dict__"
):
return
for
val
in
node
.
__dict__
.
values
():
if
isinstance
(
val
,
list
):
for
item
in
val
:
yield
from
self
.
visit
(
item
)
elif
isinstance
(
val
,
ast
.
AST
):
yield
from
self
.
visit
(
val
)
def
default
(
self
,
node
):
pass
class
PrecedenceContext
:
def
__init__
(
self
,
visitor
:
"ExpressionVisitor"
,
op
:
str
):
self
.
visitor
=
visitor
...
...
@@ -160,18 +179,27 @@ class PrecedenceContext:
def
__exit__
(
self
,
exc_type
,
exc_val
,
exc_tb
):
self
.
visitor
.
precedence
.
pop
()
class
CoroutineMode
(
Enum
):
NONE
=
0
GENERATOR
=
1
FAKE
=
2
TASK
=
3
# noinspection PyPep8Naming
@
dataclass
class
ExpressionVisitor
(
NodeVisitor
):
scope
:
"Scope"
precedence
:
List
=
field
(
default_factory
=
list
)
generator
:
CoroutineMode
=
CoroutineMode
.
NONE
def
visit
(
self
,
node
):
if
type
(
node
)
in
SYMBOLS
:
yield
SYMBOLS
[
type
(
node
)]
else
:
yield
from
NodeVisitor
.
visit
(
self
,
node
)
def
prec_ctx
(
self
,
op
:
str
)
->
PrecedenceContext
:
"""
Creates a context manager that sets the precedence of the next expression.
...
...
@@ -182,13 +210,13 @@ class ExpressionVisitor(NodeVisitor):
"""
Sets the precedence of the next expression.
"""
return
ExpressionVisitor
([
op
],
generator
=
self
.
generator
)
return
ExpressionVisitor
(
self
.
scope
,
[
op
],
generator
=
self
.
generator
)
def
reset
(
self
)
->
"ExpressionVisitor"
:
"""
Resets the precedence stack.
"""
return
ExpressionVisitor
(
generator
=
self
.
generator
)
return
ExpressionVisitor
(
self
.
scope
,
generator
=
self
.
generator
)
def
visit_Tuple
(
self
,
node
:
ast
.
Tuple
)
->
Iterable
[
str
]:
yield
"std::make_tuple("
...
...
@@ -212,7 +240,10 @@ class ExpressionVisitor(NodeVisitor):
raise
NotImplementedError
(
node
,
type
(
node
))
def
visit_Name
(
self
,
node
:
ast
.
Name
)
->
Iterable
[
str
]:
yield
self
.
fix_name
(
node
.
id
)
res
=
self
.
fix_name
(
node
.
id
)
if
(
decl
:
=
self
.
scope
.
get
(
res
))
and
decl
.
kind
==
VarKind
.
SELF
:
res
=
"(*this)"
yield
res
def
visit_Compare
(
self
,
node
:
ast
.
Compare
)
->
Iterable
[
str
]:
operands
=
[
node
.
left
,
*
node
.
comparators
]
...
...
@@ -230,10 +261,16 @@ class ExpressionVisitor(NodeVisitor):
raise
NotImplementedError
(
node
,
"varargs"
)
if
getattr
(
node
,
"kwargs"
,
None
):
raise
NotImplementedError
(
node
,
"kwargs"
)
yield
from
self
.
prec
(
"()"
).
visit
(
node
.
func
)
yield
"("
yield
from
join
(
", "
,
map
(
self
.
reset
().
visit
,
node
.
args
))
yield
")"
func
=
node
.
func
if
self
.
generator
==
CoroutineMode
.
TASK
:
yield
"co_await "
elif
self
.
generator
==
CoroutineMode
.
FAKE
:
func
=
ast
.
Attribute
(
value
=
func
,
attr
=
"sync"
,
ctx
=
ast
.
Load
())
with
self
.
prec_ctx
(
"co_await"
):
yield
from
self
.
prec
(
"()"
).
visit
(
func
)
yield
"("
yield
from
join
(
", "
,
map
(
self
.
reset
().
visit
,
node
.
args
))
yield
")"
def
visit_Lambda
(
self
,
node
:
ast
.
Lambda
)
->
Iterable
[
str
]:
yield
"[]"
...
...
@@ -313,9 +350,12 @@ class ExpressionVisitor(NodeVisitor):
raise
UnsupportedNodeError
(
node
)
elif
self
.
generator
==
CoroutineMode
.
GENERATOR
:
yield
"co_yield"
yield
from
self
.
prec
(
"co_yield"
).
visit
(
node
.
value
)
elif
self
.
generator
==
CoroutineMode
.
FAKE
:
yield
"return"
yield
from
self
.
visit
(
node
.
value
)
yield
from
self
.
visit
(
node
.
value
)
else
:
raise
NotImplementedError
(
node
)
@
dataclass
...
...
@@ -342,6 +382,16 @@ class Scope:
"""
return
name
in
self
.
vars
or
(
self
.
parent
is
not
None
and
self
.
parent
.
exists
(
name
))
def
get
(
self
,
name
:
str
)
->
Optional
[
VarDecl
]:
"""
Gets the variable declaration of a variable in the current scope or any parent scope.
"""
if
res
:
=
self
.
vars
.
get
(
name
):
return
res
if
self
.
parent
is
not
None
:
return
self
.
parent
.
get
(
name
)
return
None
def
exists_local
(
self
,
name
:
str
)
->
bool
:
"""
Determines whether a variable exists in the current function or global scope.
...
...
@@ -401,35 +451,58 @@ class Scope:
@
dataclass
class
BlockVisitor
(
NodeVisitor
):
scope
:
Scope
generator
:
CoroutineMode
=
CoroutineMode
.
NONE
def
expr
(
self
)
->
ExpressionVisitor
:
return
ExpressionVisitor
(
self
.
scope
,
generator
=
self
.
generator
)
def
visit_FunctionDef
(
self
,
node
:
ast
.
FunctionDef
)
->
Iterable
[
str
]:
try
:
[
*
result
]
=
self
.
visit_func
(
node
,
CoroutineMode
.
NONE
)
return
result
except
UnsupportedNodeError
as
e
:
if
isinstance
(
e
.
node
,
ast
.
Yield
):
return
self
.
visit_coroutine
(
node
)
raise
yield
from
self
.
visit_coroutine
(
node
)
# try:
# [*result] = self.visit_func(node, CoroutineMode.NONE)
# return result
# except UnsupportedNodeError as e:
# if isinstance(e.node, ast.Yield):
# return self.visit_coroutine(node)
# raise
def
visit_coroutine
(
self
,
node
:
ast
.
FunctionDef
)
->
Iterable
[
str
]:
yield
"struct {"
yield
from
self
.
visit_func
(
node
,
CoroutineMode
.
FAKE
)
yield
from
self
.
visit_func
(
node
,
CoroutineMode
.
GENERATOR
)
yield
from
self
.
visit_func
(
node
,
CoroutineMode
.
TASK
)
yield
f"}}
{
node
.
name
}
;"
def
visit_func
(
self
,
node
:
ast
.
FunctionDef
,
generator
:
CoroutineMode
)
->
Iterable
[
str
]:
templ
,
args
,
names
=
self
.
process_args
(
node
.
args
)
if
templ
:
yield
"template"
yield
templ
faked
=
f"FAKED_
{
node
.
name
}
"
class
ReturnVisitor
(
SearchVisitor
):
def
visit_Return
(
self
,
node
:
ast
.
Return
)
->
bool
:
yield
True
def
visit_FunctionDef
(
self
,
node
:
ast
.
FunctionDef
):
yield
from
()
def
visit_ClassDef
(
self
,
node
:
ast
.
ClassDef
):
yield
from
()
has_return
=
next
(
ReturnVisitor
().
visit
(
node
.
body
),
False
)
if
generator
==
CoroutineMode
.
FAKE
:
yield
f"static auto
{
faked
}
"
if
has_return
:
yield
"auto"
else
:
yield
"void"
yield
"sync"
else
:
yield
f"auto
{
node
.
name
}
"
yield
f"auto
operator()
"
yield
args
if
generator
==
CoroutineMode
.
GENERATOR
:
yield
f"-> typon::
Generator<decltype(
{
faked
}
(
{
', '
.
join
(
names
)
}
))>"
if
generator
==
CoroutineMode
.
TASK
:
yield
f"-> typon::
Task<decltype(sync
(
{
', '
.
join
(
names
)
}
))>"
yield
"{"
inner_scope
=
self
.
scope
.
function
()
inner_scope
=
self
.
scope
.
function
(
vars
=
{
node
.
name
:
VarDecl
(
VarKind
.
SELF
,
None
)}
)
for
child
in
node
.
body
:
# 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
...
...
@@ -484,19 +557,22 @@ class BlockVisitor(NodeVisitor):
yield
from
child_code
# Yeet back the child node code.
if
generator
==
CoroutineMode
.
FAKE
:
yield
"TYPON_UNREACHABLE();"
# So the compiler doesn't complain about missing return statements.
elif
generator
==
CoroutineMode
.
TASK
:
if
not
has_return
:
yield
"co_return;"
yield
"}"
def
visit_lvalue
(
self
,
lvalue
:
ast
.
expr
,
val
:
Optional
[
ast
.
AST
]
=
None
)
->
Iterable
[
str
]:
if
isinstance
(
lvalue
,
ast
.
Tuple
):
yield
f"std::tie(
{
', '
.
join
(
flatmap
(
ExpressionVisito
r
().
visit
,
lvalue
.
elts
))
}
)"
yield
f"std::tie(
{
', '
.
join
(
flatmap
(
self
.
exp
r
().
visit
,
lvalue
.
elts
))
}
)"
elif
isinstance
(
lvalue
,
ast
.
Name
):
name
=
self
.
fix_name
(
lvalue
.
id
)
# if name not in self._scope.vars:
if
not
self
.
scope
.
exists_local
(
name
):
yield
self
.
scope
.
declare
(
name
,
" "
.
join
(
ExpressionVisito
r
().
visit
(
val
))
if
val
else
None
)
yield
self
.
scope
.
declare
(
name
,
" "
.
join
(
self
.
exp
r
().
visit
(
val
))
if
val
else
None
)
yield
name
elif
isinstance
(
lvalue
,
ast
.
Subscript
):
yield
from
ExpressionVisito
r
().
visit
(
lvalue
)
yield
from
self
.
exp
r
().
visit
(
lvalue
)
else
:
raise
NotImplementedError
(
lvalue
)
...
...
@@ -505,7 +581,7 @@ class BlockVisitor(NodeVisitor):
raise
NotImplementedError
(
node
)
yield
from
self
.
visit_lvalue
(
node
.
targets
[
0
],
node
.
value
)
yield
" = "
yield
from
ExpressionVisito
r
().
visit
(
node
.
value
)
yield
from
self
.
exp
r
().
visit
(
node
.
value
)
yield
";"
def
visit_AnnAssign
(
self
,
node
:
ast
.
AnnAssign
)
->
Iterable
[
str
]:
...
...
@@ -513,7 +589,7 @@ class BlockVisitor(NodeVisitor):
raise
NotImplementedError
(
node
,
"empty value"
)
yield
from
self
.
visit_lvalue
(
node
.
target
,
node
.
value
)
yield
" = "
yield
from
ExpressionVisito
r
().
visit
(
node
.
value
)
yield
from
self
.
exp
r
().
visit
(
node
.
value
)
yield
";"
...
...
@@ -557,8 +633,14 @@ class ModuleVisitor(BlockVisitor):
# 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
.
function
()).
emit_block
(
node
.
body
)
yield
"typon::Root root()"
def
block
():
yield
from
node
.
body
yield
ast
.
Return
()
yield
from
FunctionVisitor
(
self
.
scope
.
function
(),
CoroutineMode
.
TASK
).
emit_block
(
block
())
yield
"int main() { root().call(); }"
return
raise
NotImplementedError
(
node
,
"global scope if"
)
...
...
@@ -567,10 +649,7 @@ class ModuleVisitor(BlockVisitor):
# noinspection PyPep8Naming
@
dataclass
class
FunctionVisitor
(
BlockVisitor
):
generator
:
CoroutineMode
=
CoroutineMode
.
NONE
def
visit_Expr
(
self
,
node
:
ast
.
Expr
)
->
Iterable
[
str
]:
print
(
ast
.
dump
(
node
))
yield
from
self
.
expr
().
visit
(
node
.
value
)
yield
";"
...
...
@@ -603,7 +682,10 @@ class FunctionVisitor(BlockVisitor):
yield
from
self
.
emit_block
(
node
.
orelse
)
def
visit_Return
(
self
,
node
:
ast
.
Return
)
->
Iterable
[
str
]:
yield
"return "
if
self
.
generator
==
CoroutineMode
.
TASK
:
yield
"co_return "
else
:
yield
"return "
if
node
.
value
:
yield
from
self
.
expr
().
visit
(
node
.
value
)
yield
";"
...
...
@@ -632,10 +714,7 @@ class FunctionVisitor(BlockVisitor):
# variables as the parent scope.
return
FunctionVisitor
(
self
.
scope
.
child_share
(),
self
.
generator
)
def
expr
(
self
)
->
ExpressionVisitor
:
return
ExpressionVisitor
(
generator
=
self
.
generator
)
def
emit_block
(
self
,
items
:
List
[
ast
.
stmt
])
->
Iterable
[
str
]:
def
emit_block
(
self
,
items
:
Iterable
[
ast
.
stmt
])
->
Iterable
[
str
]:
yield
"{"
block
=
self
.
block
()
for
child
in
items
:
...
...
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