├── .gitignore
├── .idea
├── concise.iml
├── dictionaries
│ └── twshe.xml
├── markdown-navigator.xml
├── markdown-navigator
│ └── profiles_settings.xml
├── misc.xml
├── modules.xml
├── vcs.xml
└── workspace.xml
├── .travis.yml
├── LICENSE
├── README.md
├── haskell_test
├── comment.hs
├── hello-world.hs
├── import_hs.hs
├── operator.hs
├── sum_n.hs
└── test_prelude.hs
├── next_version
├── reley
├── __init__.py
├── __release_info__.py
├── impl
│ ├── __init__.py
│ ├── cli.py
│ ├── compiler.py
│ ├── expr_based_ast.py
│ ├── grammar.py
│ ├── grammar.rbnf
│ ├── helper.py
│ ├── precedence.py
│ ├── pycompat.py
│ └── reley_import.py
├── prelude.hs
└── unification
│ └── __init__.py
├── sample_test.py
└── setup.py
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | *.egg-info/
24 | .installed.cfg
25 | *.egg
26 | MANIFEST
27 |
28 | # PyInstaller
29 | # Usually these files are written by a python script from a template
30 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
31 | *.manifest
32 | *.spec
33 |
34 | # Installer logs
35 | pip-log.txt
36 | pip-delete-this-directory.txt
37 |
38 | # Unit test / coverage reports
39 | htmlcov/
40 | .tox/
41 | .coverage
42 | .coverage.*
43 | .cache
44 | nosetests.xml
45 | coverage.xml
46 | *.cover
47 | .hypothesis/
48 | .pytest_cache/
49 |
50 | # Translations
51 | *.mo
52 | *.pot
53 |
54 | # Django stuff:
55 | *.log
56 | local_settings.py
57 | db.sqlite3
58 |
59 | # Flask stuff:
60 | instance/
61 | .webassets-cache
62 |
63 | # Scrapy stuff:
64 | .scrapy
65 |
66 | # Sphinx documentation
67 | docs/_build/
68 |
69 | # PyBuilder
70 | target/
71 |
72 | # Jupyter Notebook
73 | .ipynb_checkpoints
74 |
75 | # pyenv
76 | .python-version
77 |
78 | # celery beat schedule file
79 | celerybeat-schedule
80 |
81 | # SageMath parsed files
82 | *.sage.py
83 |
84 | # Environments
85 | .env
86 | .venv
87 | env/
88 | venv/
89 | ENV/
90 | env.bak/
91 | venv.bak/
92 |
93 | # Spyder project settings
94 | .spyderproject
95 | .spyproject
96 |
97 | # Rope project settings
98 | .ropeproject
99 |
100 | # mkdocs documentation
101 | /site
102 |
103 | # mypy
104 | .mypy_cache/
105 |
--------------------------------------------------------------------------------
/.idea/concise.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/dictionaries/twshe.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | coroutines
5 | reley
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/markdown-navigator.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/.idea/markdown-navigator/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/workspace.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
120 |
121 |
122 |
123 | Lam
124 | Defun
125 | async_app
126 | Modu
127 | async_
128 | infix
129 | yield
130 | Defin
131 | Where
132 | lineno
133 | lineno.([^)]+)
134 | (lineno=)([^)]+)
135 | dum
136 | DefFu
137 | DefFun
138 | print
139 | test
140 | Modile
141 | Symbol
142 | Sy
143 | Mo
144 | dump
145 | dump_
146 | dump_bytecode
147 | ..
148 | \.\.
149 | import
150 | IMPORT_S
151 | Import
152 | Module
153 |
154 |
155 | $1
156 | $1 + 1
157 | $0 + 1
158 | lineno=$2 + 1
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 | true
217 | DEFINITION_ORDER
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 | 1536765968651
344 |
345 |
346 | 1536765968651
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 |
416 |
417 |
418 | file://$PROJECT_DIR$/reley/impl/expr_based_ast.py
419 |
420 |
421 |
422 |
423 |
424 |
425 |
426 |
427 |
428 |
429 |
430 |
431 |
432 |
433 |
434 |
435 |
436 |
437 |
438 |
439 |
440 |
441 |
442 |
443 |
444 |
445 |
446 |
447 |
448 |
449 |
450 |
451 |
452 |
453 |
454 |
455 |
456 |
457 |
458 |
459 |
460 |
461 |
462 |
463 |
464 |
465 |
466 |
467 |
468 |
469 |
470 |
471 |
472 |
473 |
474 |
475 |
476 |
477 |
478 |
479 |
480 |
481 |
482 |
483 |
484 |
485 |
486 |
487 |
488 |
489 |
490 |
491 |
492 |
493 |
494 |
495 |
496 |
497 |
498 |
499 |
500 |
501 |
502 |
503 |
504 |
505 |
506 |
507 |
508 |
509 |
510 |
511 |
512 |
513 |
514 |
515 |
516 |
517 |
518 |
519 |
520 |
521 |
522 |
523 |
524 |
525 |
526 |
527 |
528 |
529 |
530 |
531 |
532 |
533 |
534 |
535 |
536 |
537 |
538 |
539 |
540 |
541 |
542 |
543 |
544 |
545 |
546 |
547 |
548 |
549 |
550 |
551 |
552 |
553 |
554 |
555 |
556 |
557 |
558 |
559 |
560 |
561 |
562 |
563 |
564 |
565 |
566 |
567 |
568 |
569 |
570 |
571 |
572 |
573 |
574 |
575 |
576 |
577 |
578 |
579 |
580 |
581 |
582 |
583 |
584 |
585 |
586 |
587 |
588 |
589 |
590 |
591 |
592 |
593 |
594 |
595 |
596 |
597 |
598 |
599 |
600 |
601 |
602 |
603 |
604 |
605 |
606 |
607 |
608 |
609 |
610 |
611 |
612 |
613 |
614 |
615 |
616 |
617 |
618 |
619 |
620 |
621 |
622 |
623 |
624 |
625 |
626 |
627 |
628 |
629 |
630 |
631 |
632 |
633 |
634 |
635 |
636 |
637 |
638 |
639 |
640 |
641 |
642 |
643 |
644 |
645 |
646 |
647 |
648 |
649 |
650 |
651 |
652 |
653 |
654 |
655 |
656 |
657 |
658 |
659 |
660 |
661 |
662 |
663 |
664 |
665 |
666 |
667 |
668 |
669 |
670 |
671 |
672 |
673 |
674 |
675 |
676 |
677 |
678 |
679 |
680 |
681 |
682 |
683 |
684 |
685 |
686 |
687 |
688 |
689 |
690 |
691 |
692 |
693 |
694 |
695 |
696 |
697 |
698 |
699 |
700 |
701 |
702 |
703 |
704 |
705 |
706 |
707 |
708 |
709 |
710 |
711 |
712 |
713 |
714 |
715 |
716 |
717 |
718 |
719 |
720 |
721 |
722 |
723 |
724 |
725 |
726 |
727 |
728 |
729 |
730 |
731 |
732 |
733 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: python
2 | python:
3 | - "3.6"
4 | - "3.6-dev"
5 | - "3.7-dev"
6 | install:
7 | - pip install codecov coverage pytest==3.3.2 python_coveralls
8 | - pip install -U rbnf wisepy
9 | - python setup.py install
10 |
11 | script:
12 | - coverage run sample_test.py
13 | # exclude bootstrap for ast transformations cannot be detected by coverage, causing incorrect report.
14 | - coverage report
15 |
16 | after_success:
17 | - codecov
18 | - coveralls
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://travis-ci.org/thautwarm/reley)
2 |
3 | See examples at `haskell_test/*.hs`.
4 | Currently you can install reley compiler with `python setup.py install`.
5 |
6 | Usage
7 | ============
8 |
9 | - Compile:
10 |
11 | ```
12 | > reley cc .hs -o .pyc
13 | > python .pyc
14 | ```
15 |
16 | - Run Reley
17 |
18 | ```
19 | > reley run .hs
20 | ```
21 |
22 | - Import reley programs in Python
23 |
24 | If you have a reley source file `haskell_test/sum_n.hs`:
25 |
26 | ```haskell
27 | module
28 | m_sum, (==) -- export `m_sum` and `(==)`
29 | where
30 |
31 | import operator (add, eq)
32 | import functools (reduce)
33 | import toolz (curry)
34 | import reley.prelude ((+))
35 |
36 | infix 5 (==)
37 | infix 0 ($)
38 | (==) = curry eq
39 | ($) a b = a b
40 | (+) = curry add
41 |
42 |
43 | m_sum lst = if lst == [] then 0
44 | else destruct lst
45 | where
46 | destruct (a, b) = a + m_sum(b)
47 |
48 | main () =
49 | print $ m_sum [1, 2, 3, 4, 5, 6]
50 |
51 | ```
52 |
53 | Then you can import it in Python
54 |
55 | ```python
56 | import reley.impl.pycompat
57 |
58 | from haskell_test.sum_n import m_sum
59 | lst = (5, (2, (1, ())))
60 | print(m_sum(lst))
61 |
62 | ```
63 |
64 | About Reley
65 | ====================
66 | It's in an early stage with many shortages.
67 | Most of the crucial Haskell features are missing.
68 |
--------------------------------------------------------------------------------
/haskell_test/comment.hs:
--------------------------------------------------------------------------------
1 | module
2 | test1
3 | where
4 |
5 | import unittest (TestCase)
6 | import toolz (curry)
7 | import reley.prelude (*)
8 |
9 | infix 10 (.)
10 | (.) = curry getattr
11 |
12 | |||
13 | test1 () = ()
14 |
15 | main() =
16 | (curry (test . "assertEqual")) (test1. "__doc__") ""
17 | help test1
18 | print $ (\a b c -> a) 1 2 3
19 | where
20 | test = TestCase()
21 |
22 |
--------------------------------------------------------------------------------
/haskell_test/hello-world.hs:
--------------------------------------------------------------------------------
1 | main() =
2 | print "hello world!"
3 |
4 |
--------------------------------------------------------------------------------
/haskell_test/import_hs.hs:
--------------------------------------------------------------------------------
1 | import haskell_test.comment (test1)
2 |
3 | main() =
4 | help test1
--------------------------------------------------------------------------------
/haskell_test/operator.hs:
--------------------------------------------------------------------------------
1 | import operator (add, sub)
2 | import toolz (curry)
3 |
4 | infix 0 ($)
5 | infix 5 (+)
6 | infix 5 (-)
7 |
8 | ($) f a = f a
9 | (+) = curry add
10 | (-) = curry sub
11 |
12 | main () =
13 | print $ 1 + 2 - 3
14 |
15 |
--------------------------------------------------------------------------------
/haskell_test/sum_n.hs:
--------------------------------------------------------------------------------
1 | module
2 | m_sum, (==) -- export `m_sum` and `(==)`
3 | where
4 |
5 | import operator (add, eq)
6 | import functools (reduce)
7 | import toolz (curry)
8 | import reley.prelude ((+))
9 |
10 | infix 5 (==)
11 | infix 0 ($)
12 | (==) = curry eq
13 | ($) a b = a b
14 | (+) = curry add
15 |
16 |
17 | m_sum lst = if lst == [] then 0
18 | else destruct lst
19 | where
20 | destruct (a, b) = a + m_sum(b)
21 |
22 | main () =
23 | print $ m_sum [1, 2, 3, 4, 5, 6]
24 |
25 |
--------------------------------------------------------------------------------
/haskell_test/test_prelude.hs:
--------------------------------------------------------------------------------
1 | import reley.prelude (*)
2 | import reley.prelude as pre
3 |
4 | main() =
5 | print $ dir pre
--------------------------------------------------------------------------------
/next_version:
--------------------------------------------------------------------------------
1 | 0.1.19
--------------------------------------------------------------------------------
/reley/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thautwarm/reley/17e5730c1afbefaeb22103719c85f08333c65937/reley/__init__.py
--------------------------------------------------------------------------------
/reley/__release_info__.py:
--------------------------------------------------------------------------------
1 | __VERSION__ = '0.1.18'
2 | __AUTHOR__ = "thautwarm"
--------------------------------------------------------------------------------
/reley/impl/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thautwarm/reley/17e5730c1afbefaeb22103719c85f08333c65937/reley/impl/__init__.py
--------------------------------------------------------------------------------
/reley/impl/cli.py:
--------------------------------------------------------------------------------
1 | from .pycompat import sys
2 | from .reley_import import get_reley_module_spec_from_path, get_context_from_spec
3 | from wisepy.talking import Talking
4 | from Redy.Tools.PathLib import Path
5 | from importlib._bootstrap_external import MAGIC_NUMBER
6 | import marshal
7 | import struct, time
8 |
9 | talking = Talking()
10 |
11 |
12 | @talking
13 | def cc(f: 'input filename', o: 'output filename'):
14 | """
15 | compile reley source code into pyc files
16 | """
17 | spec = get_reley_module_spec_from_path('main', f)
18 | code = get_context_from_spec(spec).bc.to_code()
19 | timestamp = struct.pack('i', int(time.time()))
20 | marshalled_code_object = marshal.dumps(code)
21 | with Path(o).open('wb') as f:
22 | f.write(MAGIC_NUMBER)
23 | f.write(timestamp)
24 | f.write(b'A\x00\x00\x00')
25 | f.write(marshalled_code_object)
26 |
27 |
28 | @talking
29 | def run(f: 'input filename'):
30 | """
31 | compile reley source code into pyc files
32 | """
33 | spec = get_reley_module_spec_from_path('main', f)
34 | code = get_context_from_spec(spec).bc.to_code()
35 | exec(code)
36 |
37 |
38 | def main():
39 | talking.on()
40 |
--------------------------------------------------------------------------------
/reley/impl/compiler.py:
--------------------------------------------------------------------------------
1 | from Redy.Magic.Pattern import Pattern
2 | from typing import Dict
3 | from bytecode import Instr, Bytecode, FreeVar, CellVar, Label
4 | from toolz import curry
5 | from functools import reduce
6 | from .precedence import bin_reduce
7 | from .expr_based_ast import *
8 |
9 | map = curry(map)
10 | reduce = curry(reduce)
11 | getattr = curry(getattr)
12 |
13 | # code flags
14 | OPTIMIZED = 1
15 | NEWLOCALS = 2
16 | NESTED = 16
17 | NOFREE = 64
18 | GENERATOR = 32
19 | COROUTINE = 128
20 | ITERABLE_COROUTINE = 256
21 |
22 | # compiling future
23 | DECLARED = 0
24 | EVALUATED = -1
25 | RESOLVED = -2
26 | END = -3
27 |
28 |
29 | @curry
30 | def flip(f, a, b):
31 | return f(b, a)
32 |
33 |
34 | def fst(it):
35 | return it[0]
36 |
37 |
38 | class Val:
39 | pass
40 |
41 |
42 | def lens(**kwargs):
43 | def apply(it):
44 | return {**it, **kwargs}
45 |
46 | return apply
47 |
48 |
49 | def identity(it):
50 | return it
51 |
52 |
53 | def fix_bytecode(bc: Bytecode):
54 | for each in bc:
55 | if not isinstance(each, Instr):
56 | continue
57 | arg = each.arg
58 | if not isinstance(arg, FreeVar):
59 | continue
60 | name = arg.name
61 | if name not in bc.freevars:
62 | bc.freevars.append(name)
63 |
64 |
65 | class Ctx:
66 | # precedences: dict
67 | symtb: Dict[str, Val]
68 | local: Dict[str, Val]
69 | bc: Bytecode
70 | precedences: Dict[str, int]
71 | is_nested: bool
72 | exported: list
73 |
74 | def __init__(self, symtb, local, bc, precedences, is_nested):
75 | self.symtb = symtb
76 | self.local = local
77 | self.bc = bc
78 | self.precedences = precedences
79 | self.is_nested = is_nested
80 | self.exported = []
81 |
82 | def new(self) -> 'Ctx':
83 | return Ctx(self.symtb, {}, Bytecode(), self.precedences, True)
84 |
85 | def add_infix(self, name, priority):
86 | self.precedences = {**self.precedences, **{name: priority}}
87 |
88 | def add_non_local(self, name, val):
89 | if name not in self.symtb:
90 | self.symtb = symtb = {**self.symtb}
91 | symtb[name] = val
92 |
93 | def add_local(self, name, val):
94 | if name not in self.local:
95 | self.local[name] = val
96 | symtb = self.symtb = {**self.symtb}
97 | symtb[name] = val
98 | self.bc.flags |= NEWLOCALS
99 |
100 | def add_locals(self, name_vals):
101 | if any(name_vals):
102 | self.bc.flags |= NEWLOCALS
103 | for name, val in name_vals:
104 | if name not in self.local:
105 | self.local[name] = val
106 | symtb = self.symtb = {**self.symtb}
107 | symtb[name] = val
108 |
109 | def load_name(self, tast):
110 | name = tast.name
111 |
112 | if name in self.local:
113 | if name not in self.bc.cellvars:
114 | self.bc.append(
115 | Instr('LOAD_FAST', name, lineno=tast.lineno + 1))
116 | else:
117 | self.bc.append(
118 | Instr('LOAD_DEREF', CellVar(name), lineno=tast.lineno + 1))
119 | return
120 |
121 | if name in self.symtb:
122 | self.bc.append(
123 | Instr('LOAD_DEREF', FreeVar(name), lineno=tast.lineno + 1))
124 | else:
125 | self.bc.append(Instr('LOAD_GLOBAL', name, lineno=tast.lineno + 1))
126 |
127 | def visit(self, tast):
128 | return visit(tast, self)
129 |
130 |
131 | @Pattern
132 | def visit(tast, _):
133 | return type(tast)
134 |
135 |
136 | @visit.case(DefVar)
137 | def _(tast: DefVar, ctx: Ctx):
138 | name = tast.name
139 |
140 | # declare
141 | ctx.add_local(name, Val())
142 | yield from wait(DECLARED)
143 | new_ctx = ctx.new()
144 | # evaluating internal area
145 | bc = new_ctx.bc
146 | bc.kwonlyargcount = 0
147 | bc.filename = tast.loc.filename
148 | bc.argcount = 0
149 | new_ctx.visit(tast.value)
150 | bc.name = name or ''
151 | bc.append(Instr("RETURN_VALUE"))
152 | fix_bytecode(new_ctx.bc)
153 | yield from wait(EVALUATED)
154 | # resolve
155 | delay = []
156 | delay_append = delay.append
157 | if any(bc.freevars):
158 | for each in bc.freevars:
159 | if each not in ctx.local:
160 | if each not in ctx.bc.freevars:
161 | ctx.bc.freevars.append(each)
162 | delay_append(
163 | Instr(
164 | 'LOAD_CLOSURE', FreeVar(each), lineno=tast.lineno + 1))
165 | else:
166 | if each not in ctx.bc.cellvars:
167 | ctx.bc.cellvars.append(each)
168 | delay_append(
169 | Instr(
170 | 'LOAD_CLOSURE', CellVar(each), lineno=tast.lineno + 1))
171 | delay_append(
172 | Instr("BUILD_TUPLE", arg=len(bc.freevars), lineno=tast.lineno + 1))
173 |
174 | if ctx.is_nested:
175 | bc.flags |= NESTED
176 | bc.flags |= OPTIMIZED
177 | yield from wait(RESOLVED)
178 |
179 | code = bc.to_code()
180 |
181 | # dis.show_code(code)
182 | ctx.bc.extend(delay)
183 | ctx.bc.append(Instr('LOAD_CONST', arg=code, lineno=tast.lineno + 1))
184 | ctx.bc.append(Instr('LOAD_CONST', arg=bc.name, lineno=tast.lineno + 1))
185 | ctx.bc.append(
186 | Instr(
187 | 'MAKE_FUNCTION',
188 | arg=8 if any(bc.freevars) else 0,
189 | lineno=tast.lineno + 1))
190 |
191 | ctx.bc.append(Instr("CALL_FUNCTION", arg=0, lineno=tast.lineno + 1))
192 | if name in ctx.bc.cellvars:
193 | ctx.bc.append(Instr("STORE_DEREF", arg=CellVar(name)))
194 | else:
195 | ctx.bc.append(Instr('STORE_FAST', arg=tast.name))
196 |
197 |
198 | @visit.case(Lam)
199 | def _(lam: DefFun, ctx: Ctx):
200 | cos = (visit_def_fun(lam, ctx), )
201 | start(cos)
202 | reach(END, cos)
203 |
204 |
205 | @visit.case(DefFun)
206 | def visit_def_fun(def_fun: DefFun, ctx: Ctx):
207 | name = def_fun.name
208 | if name:
209 | ctx.add_local(name, Val())
210 | yield from wait(DECLARED)
211 | args = [arg.name for arg in def_fun.args]
212 | new_ctx = ctx.new()
213 | for each in def_fun.args:
214 | new_ctx.visit(each)
215 |
216 | bc = new_ctx.bc
217 | if isinstance(def_fun, DefFun) and def_fun.doc:
218 | bc.docstring = def_fun.doc.text
219 | bc.kwonlyargcount = 0
220 | bc.filename = def_fun.loc.filename
221 |
222 | if any(args):
223 | bc.argcount = 1
224 | if len(args) is 1:
225 | bc.argnames = args
226 | else:
227 | arg_name = '.'.join(args)
228 | bc.argnames = [arg_name]
229 | bc.append(
230 | Instr(
231 | 'LOAD_FAST', arg_name, lineno=def_fun.args[0].lineno + 1))
232 | bc.append(Instr('UNPACK_SEQUENCE', arg=len(args)))
233 | for arg in args:
234 | bc.append(Instr('STORE_FAST', arg=arg))
235 | else:
236 | bc.argcount = 0
237 |
238 | new_ctx.visit(def_fun.body)
239 | bc.name = name or ''
240 | bc.append(Instr("RETURN_VALUE"))
241 | fix_bytecode(bc)
242 | # for each in (bc):
243 | # print(each)
244 | # print('++++++++++')
245 | yield from wait(EVALUATED)
246 | delay = []
247 | delay_append = delay.append
248 | if any(bc.freevars):
249 | for each in bc.freevars:
250 | if each not in ctx.local:
251 | if each not in ctx.bc.freevars:
252 | ctx.bc.freevars.append(each)
253 | delay_append(
254 | Instr(
255 | 'LOAD_CLOSURE',
256 | FreeVar(each),
257 | lineno=def_fun.lineno + 1))
258 | else:
259 | if each not in ctx.bc.cellvars:
260 | ctx.bc.cellvars.append(each)
261 | delay_append(
262 | Instr(
263 | 'LOAD_CLOSURE',
264 | CellVar(each),
265 | lineno=def_fun.lineno + 1))
266 | delay_append(
267 | Instr(
268 | "BUILD_TUPLE", arg=len(bc.freevars),
269 | lineno=def_fun.lineno + 1))
270 |
271 | if ctx.is_nested:
272 | bc.flags |= NESTED
273 | bc.flags |= OPTIMIZED
274 | # dump_bytecode(bc)
275 | # print(name, ctx.local.keys(), ctx.bc.freevars, ctx.bc.cellvars)
276 | yield from wait(RESOLVED)
277 | code = bc.to_code()
278 | ctx.bc.extend(delay)
279 | # dis.show_code(code)
280 | ctx.bc.append(Instr('LOAD_CONST', arg=code, lineno=def_fun.lineno + 1))
281 | ctx.bc.append(Instr('LOAD_CONST', arg=bc.name, lineno=def_fun.lineno + 1))
282 | ctx.bc.append(
283 | Instr(
284 | 'MAKE_FUNCTION',
285 | arg=8 if any(bc.freevars) else 0,
286 | lineno=def_fun.lineno + 1))
287 |
288 | if name:
289 | if name in ctx.bc.cellvars:
290 | ctx.bc.append(
291 | Instr("STORE_DEREF", CellVar(name), lineno=def_fun.lineno + 1))
292 | else:
293 | ctx.bc.append(Instr('STORE_FAST', name, lineno=def_fun.lineno + 1))
294 |
295 |
296 | @visit.case(Number)
297 | def _(tast: Number, ctx: Ctx):
298 | ctx.bc.append(Instr("LOAD_CONST", tast.value, lineno=tast.lineno + 1))
299 |
300 |
301 | @visit.case(Str)
302 | def _(tast: Str, ctx: Ctx):
303 | ctx.bc.append(Instr("LOAD_CONST", tast.value, lineno=tast.lineno + 1))
304 |
305 |
306 | @visit.case(Symbol)
307 | def _(tast: Symbol, ctx: Ctx):
308 | ctx.load_name(tast)
309 |
310 |
311 | @visit.case(Call)
312 | def visit_call(ast: Call, ctx: Ctx):
313 | ctx.visit(ast.callee)
314 |
315 | if isinstance(ast.arg, Void):
316 | ctx.bc.append(Instr("CALL_FUNCTION", arg=0, lineno=ast.lineno + 1))
317 | else:
318 | ctx.visit(ast.arg)
319 | ctx.bc.append(Instr("CALL_FUNCTION", arg=1, lineno=ast.lineno + 1))
320 |
321 |
322 | @visit.case(If)
323 | def _(ast: If, ctx: Ctx):
324 | label1 = Label()
325 | label2 = Label()
326 | ctx.visit(ast.cond)
327 | ctx.bc.append(
328 | Instr("POP_JUMP_IF_FALSE", arg=label1, lineno=ast.cond.lineno + 1))
329 | ctx.visit(ast.iftrue)
330 | ctx.bc.append(
331 | Instr('JUMP_FORWARD', arg=label2, lineno=ast.iftrue.lineno + 1))
332 | ctx.bc.append(label1)
333 | ctx.visit(ast.iffalse)
334 | ctx.bc.append(label2)
335 |
336 |
337 | @visit.case(Arg)
338 | def _(ast: Arg, ctx: Ctx):
339 | ctx.bc.argnames.append(ast.name)
340 | # TODO:
341 | ctx.add_local(ast.name, ast.ty)
342 |
343 |
344 | @visit.case(Suite)
345 | def _(ast: Suite, ctx: Ctx):
346 | if ast.statements:
347 |
348 | def pop(lineno):
349 | return lambda: ctx.bc.append(Instr('POP_TOP', lineno=lineno + 1))
350 |
351 | def now():
352 | return ()
353 |
354 | for each in ast.statements:
355 | now()
356 | pop(each.lineno)
357 | ctx.visit(each)
358 | now = pop(each.lineno)
359 | else:
360 | ctx.bc.append(Instr('LOAD_CONST', None))
361 |
362 |
363 | @visit.case(Definition)
364 | def _(ast: Suite, ctx: Ctx):
365 | if ast.statements:
366 | for each in ast.statements:
367 | yield ctx.visit(each)
368 |
369 |
370 | @visit.case(Module)
371 | def _(ast: Module, ctx: Ctx):
372 | cos = list(ctx.visit(ast.stmts))
373 | start(cos)
374 | reach(DECLARED, cos)
375 | reach(EVALUATED, cos)
376 | reach(RESOLVED, cos)
377 | reach(END, cos)
378 |
379 | exports = ast.exports
380 |
381 | if exports:
382 | for each in exports:
383 | ctx.load_name(each)
384 | ctx.bc.append(
385 | Instr("STORE_GLOBAL", arg=each.name, lineno=each.lineno + 1))
386 | ctx.exported.append(each.name)
387 |
388 | if ctx.local.get('main'):
389 | ctx.bc.append(Instr('LOAD_FAST', arg='main'))
390 | ctx.bc.append(Instr('CALL_FUNCTION', arg=0))
391 | ctx.bc.append(Instr("RETURN_VALUE"))
392 | else:
393 | ctx.bc.append(Instr("LOAD_CONST", 0))
394 | ctx.bc.append(Instr("RETURN_VALUE"))
395 | if ast.doc:
396 | ctx.bc.docstring = ast.doc.text
397 |
398 |
399 | @visit.case(BinSeq)
400 | def _(ast: BinSeq, ctx: Ctx):
401 | reduce = bin_reduce(ctx.precedences)
402 | ctx.visit(reduce(ast.seq))
403 |
404 |
405 | @visit.case(Infix)
406 | def _(ast: Infix, ctx: Ctx):
407 | ctx.add_infix(ast.op, ast.precedence)
408 | yield
409 |
410 |
411 | @visit.case(Void)
412 | def _(_, ctx: Ctx):
413 | ctx.bc.append(Instr("LOAD_CONST", None))
414 |
415 |
416 | @visit.case(Tuple)
417 | def _(tp: Tuple, ctx: Ctx):
418 | for each in tp.seq:
419 | ctx.visit(each)
420 | ctx.bc.append(Instr("BUILD_TUPLE", arg=len(tp.seq), lineno=tp.lineno + 1))
421 |
422 |
423 | @visit.case(Return)
424 | def _(ret: Return, ctx: Ctx):
425 | ctx.visit(ret.expr)
426 | ctx.bc.append(Instr('RETURN_VALUE', lineno=ret.lineno + 1))
427 |
428 |
429 | @visit.case(Yield)
430 | def _(yd: Yield, ctx: Ctx):
431 | ctx.visit(yd.expr)
432 | ctx.bc.append(Instr("YIELD_VALUE", lineno=yd.lineno + 1))
433 |
434 |
435 | @visit.case(HList)
436 | def _(lst: HList, ctx: Ctx):
437 |
438 | seq = lst.seq
439 | for each in seq:
440 | ctx.visit(each)
441 | ctx.bc.append(Instr("LOAD_CONST", arg=(), lineno=lst.lineno))
442 | for _ in range(len(seq)):
443 | ctx.bc.append(Instr("BUILD_TUPLE", arg=2, lineno=lst.lineno))
444 |
445 |
446 | @visit.case(Where)
447 | def _(where: Where, ctx: Ctx):
448 | bc = ctx.bc
449 | names = new_entered_names(where.pre_def)
450 | count = len(bc)
451 | targets = {origin: f'{origin}.{count}' for origin in names}
452 |
453 | def substitute(it: TAST):
454 | if hasattr(it, 'name') and it.name:
455 | value = targets.get(it.name)
456 | if value:
457 | return type(it)(**dict(it.iter_fields, name=value))
458 | return it
459 | elif hasattr(it, 'names') and it.names:
460 | return type(it)(**dict(
461 | it.iter_fields,
462 | names=tuple(targets.get(each) or each for each in it.names)))
463 | return it
464 |
465 | pre_def = transform(substitute)(where.pre_def)
466 | out = transform(substitute)(where.out)
467 |
468 | cos = list(ctx.visit(pre_def))
469 | start(cos)
470 | reach(DECLARED, cos)
471 | n1 = len(bc)
472 | ctx.visit(out)
473 | n2 = len(bc)
474 | reach(EVALUATED, cos)
475 | reach(RESOLVED, cos)
476 | reach(END, cos)
477 | n3 = len(bc)
478 | bc[n1:n3] = bc[n2:n3] + bc[n1:n2]
479 |
480 |
481 | def new_entered_names(it: Definition):
482 | for each in it.statements:
483 | if isinstance(each, (DefFun, DefTy, DefVar)):
484 | yield each.name
485 |
486 |
487 | def wait(signal):
488 | while (yield signal) == signal:
489 | pass
490 |
491 |
492 | def start(coroutines):
493 | for each in coroutines:
494 | each.send(None)
495 |
496 |
497 | def reach(signal, coroutines):
498 | for each in coroutines:
499 | try:
500 | while each.send(signal) != signal:
501 | pass
502 | except StopIteration:
503 | pass
504 |
--------------------------------------------------------------------------------
/reley/impl/expr_based_ast.py:
--------------------------------------------------------------------------------
1 | from typing import NamedTuple, List, Tuple
2 | from Redy.Magic.Classic import record
3 | from numpy import number
4 | from rbnf.easy import Tokenizer
5 | globals()['NamedTuple'] = object
6 |
7 |
8 | class Loc:
9 | __slots__ = ['lineno', 'colno', 'filename']
10 |
11 | lineno: int
12 | colno: int
13 | filename: str
14 |
15 | def __init__(self, lineno, colno, filename):
16 | self.lineno = lineno
17 | self.colno = colno
18 | self.filename = filename
19 |
20 | def __matmul__(self, other):
21 | if isinstance(other, Tokenizer):
22 | return Loc(other.lineno, other.colno, getattr(other, 'filename') or '')
23 | return Loc(*other.loc)
24 |
25 | def __iter__(self):
26 | yield self.lineno
27 | yield self.colno
28 | yield self.filename
29 |
30 | def __repr__(self):
31 | return str(self)
32 |
33 | def __str__(self):
34 | return 'Loc(lineno={!r}, colno={!r}, filename={!r})'.format(
35 | self.lineno, self.colno, self.filename)
36 |
37 | def update(self, lineno=None, colno=None, filename=None):
38 | if lineno:
39 | self.lineno = lineno
40 | if colno:
41 | self.colno = colno
42 | if filename:
43 | self.filename = filename
44 |
45 |
46 | class TAST:
47 | loc: Loc
48 |
49 | @property
50 | def iter_fields(self):
51 | for it in self.__annotations__:
52 | if not it.startswith('_') and it not in ('iter_fields', 'lineno'):
53 | yield it, getattr(self, it)
54 |
55 | @property
56 | def lineno(self):
57 | return self.loc.lineno
58 |
59 |
60 | loc = Loc(1, 1, "")
61 |
62 |
63 | @record
64 | class DefTy(TAST, NamedTuple):
65 | loc: Loc
66 | name: str
67 | structure: TAST
68 |
69 |
70 | @record
71 | class DefFun(TAST, NamedTuple):
72 | loc: Loc
73 | name: str
74 | args: 'List[Arg]'
75 | body: TAST
76 | doc: 'Doc'
77 |
78 |
79 | @record
80 | class Lam(TAST, NamedTuple):
81 | loc: Loc
82 | name: str
83 | args: 'List[Arg]'
84 | body: TAST
85 |
86 |
87 | @record
88 | class Arg(TAST, NamedTuple):
89 | loc: Loc
90 | name: str
91 | ty: TAST
92 |
93 |
94 | @record
95 | class Suite(TAST, NamedTuple):
96 | loc: Loc
97 | statements: List[TAST]
98 |
99 |
100 | @record
101 | class Definition(TAST, NamedTuple):
102 | loc: Loc
103 | statements: List[TAST]
104 |
105 |
106 | @record
107 | class Where(TAST, NamedTuple):
108 | loc: Loc
109 | out: Suite
110 | pre_def: Definition
111 |
112 |
113 | @record
114 | class DefVar(TAST, NamedTuple):
115 | loc: Loc
116 | name: str
117 | value: TAST
118 |
119 |
120 | @record
121 | class If(TAST, NamedTuple):
122 | loc: Loc
123 | cond: TAST
124 | iftrue: TAST
125 | iffalse: TAST
126 |
127 |
128 | @record
129 | class Call(TAST, NamedTuple):
130 | loc: Loc
131 | callee: TAST
132 | arg: TAST
133 |
134 |
135 | @record
136 | class Symbol(TAST, NamedTuple):
137 | loc: Loc
138 | name: str
139 |
140 |
141 | @record
142 | class Number(TAST, NamedTuple):
143 | loc: Loc
144 | value: number
145 |
146 |
147 | @record
148 | class Str(TAST, NamedTuple):
149 | loc: Loc
150 | value: str
151 |
152 |
153 | @record
154 | class HList(TAST, NamedTuple):
155 | loc: Loc
156 | seq: List[TAST]
157 |
158 |
159 | @record
160 | class HDict(TAST, NamedTuple):
161 | loc: Loc
162 | seq: List[Tuple[TAST, TAST]]
163 |
164 |
165 | def make_set(seq: List[TAST]):
166 | return tuple((each, Void(each.loc)) for each in seq)
167 |
168 |
169 | @record
170 | class Tuple(TAST, NamedTuple):
171 | loc: Loc
172 | seq: Tuple[TAST, ...]
173 |
174 |
175 | @record
176 | class Return(TAST, NamedTuple):
177 | loc: Loc
178 | expr: TAST
179 |
180 |
181 | @record
182 | class Yield(TAST, NamedTuple):
183 | loc: Loc
184 | expr: TAST
185 |
186 |
187 | @record
188 | class BinSeq(TAST, NamedTuple):
189 | loc: Loc
190 | seq: List[TAST]
191 |
192 |
193 | @record
194 | class Infix(TAST, NamedTuple):
195 | loc: Loc
196 | precedence: int
197 | op: str
198 |
199 |
200 | @record
201 | class Operator(TAST, NamedTuple):
202 | loc: Loc
203 | name: str
204 |
205 |
206 | @record
207 | class Void(TAST, NamedTuple):
208 | loc: Loc
209 |
210 |
211 | @record
212 | class Alias(TAST, NamedTuple):
213 | loc: Loc
214 | imp_name: str
215 | name: str
216 |
217 |
218 | @record
219 | class Doc(TAST, NamedTuple):
220 | loc: Loc
221 | text: str
222 |
223 |
224 | @record
225 | class Import(TAST, NamedTuple):
226 | loc: Loc
227 | imp_name: str
228 | name: str
229 | stuffs: List[Alias]
230 |
231 |
232 | @record
233 | class Module(NamedTuple):
234 | stmts: Definition
235 | doc: Doc
236 | exports: List[Operator]
237 |
238 |
239 | def transform(f):
240 | def ff(it):
241 | return generic_visit(f(it))
242 |
243 | def generic_visit(ast: TAST):
244 | def stream():
245 | for key, value in ast.iter_fields:
246 | if type(value) is tuple or isinstance(value, list):
247 | yield key, list(ff(e) for e in value)
248 | else:
249 | yield key, ff(value)
250 |
251 | if hasattr(ast, 'iter_fields'):
252 |
253 | return type(ast)(**dict(stream()))
254 |
255 | return ast
256 |
257 | return ff
258 |
--------------------------------------------------------------------------------
/reley/impl/grammar.py:
--------------------------------------------------------------------------------
1 | RBNF=\
2 | r"""
3 | import std.common.[Space Name DoubleQuotedStr]
4 | ignore [Space comment]
5 | [python] import reley.impl.expr_based_ast.[*]
6 | [python] import reley.impl.helper.[*]
7 | [python] import functools.[*]
8 |
9 |
10 | comment := R'(--.*)|(((\{\-\|)+?[\w\W]+?(\-\})+))'
11 | docstr := R'(\|\|\|.*)'
12 | num := R'0[Xx][\da-fA-F]+|\d+(?:\.\d+|)(?:E\-{0,1}\d+|)'
13 | lam := R'\\'
14 |
15 | to_const cast := 'type' 'fn' 'def' '->' 'import' 'module'
16 | '[|' '|]'
17 | '{|' '|}'
18 | 'return' 'if' 'else' 'let' 'or' 'and' 'not' 'xor' 'where' 'as'
19 | '::' 'in' '<-' 'then' 'infix' '=='
20 |
21 | doc ::= items=docstr+ -> Doc(loc@items[0], '\n'.join(each.value[3:] for each in items))
22 | identifier ::= mark=Name | '(' as mark ID=binOp ')' -> Operator(loc @ mark, ID.name if ID else mark.value)
23 |
24 | def_lambda ::= mark=lam fn=lambda_trailer -> Lam(loc @ mark, None, fn.args, fn.body)
25 |
26 | lambda_trailer ::= (mark='(' [args=arguments] ')' | args=(mark=arg){1 1}) ('->' body=expr_stmts | body=lambda_trailer)
27 | -> Lam(loc @ mark, None, args or (), body)
28 |
29 | define ::= [doc=doc] id=identifier fn=fun_trailer
30 | ->
31 | if isinstance(fn, Lam):
32 | return DefFun(loc @ id, id.name, fn.args, fn.body, doc)
33 | else:
34 | return DefVar(loc @ id, id.name, fn)
35 |
36 | fun_trailer ::= '=' body=expr_stmts | ((mark='(' [args=arguments] ')' | args=(mark=arg){1 1}) fun=fun_trailer)
37 | -> Lam(loc @ mark, None, args or (), fun) if fun else body
38 |
39 | defty ::= mark='type' id=identifier '=' ty=ty -> DefTy(loc @ mark, id.name, ty)
40 |
41 | arguments ::= head=arg tail=(',' arg)* -> [head, *tail[1::2]]
42 |
43 | arg ::= name=Name [':' ty=ty] -> Arg(loc @ name, name.value, ty)
44 |
45 | ty ::= it=expr -> it
46 |
47 |
48 | alias_pair ::= imp_name=identifier ['as' name=identifier] -> Alias(loc @ imp_name, imp_name.name, name.name if name else imp_name.name)
49 |
50 | dot_lst ::= head=alias_pair tail=(',' alias_pair)* -> (head, *tail[1::2])
51 |
52 |
53 | wildcard ::= '*' -> 0
54 | import_ ::= mark='import' names=(Name ('.' Name)*) ['as' name=Name] ['(' (lst=dot_lst | star=wildcard)')']
55 | -> imp_name = ''.join(map(lambda _: _.value, names))
56 | name = name.value if name else None
57 | if not lst and not name: name = imp_name
58 | Import(loc @ mark, imp_name, name, lst or star)
59 |
60 | infix ::= mark='infix' precedence=num op=identifier -> Infix(loc @ mark, eval(precedence.value), op.name)
61 |
62 | expr ::=| BIN=expr (binOp expr)+ as tail
63 | | JUST=factor
64 | -> if BIN : return BinSeq(loc @ BIN, [BIN, *tail]) if tail else BIN
65 | return JUST
66 |
67 | binOp ::=
68 | | ('or' | '-' | 'and') as basic
69 | | '`' names=(~'`')+ '`'
70 | | ('*' | '^' | '%' | '&' | '@' | '$' | '+' | '/' | '|' | '>' | '<' | '==' | '~' | '.' | '?' | '!' | '::')+ as seq
71 | -> bin_op_rewrite(state.ctx.get)
72 |
73 | factor ::= [neg='-'] call=call -> Call(loc @ neg, Symbol(loc@neg, "neg"), call) if neg else call
74 |
75 | call ::= leader=atom tail=(atom{is_indented})*
76 | -> reduce(lambda a, b: Call(loc @ a, a, b), tail, leader)
77 |
78 | atom ::= | SYMBOL=identifier
79 | | IF='if' cond=expr 'then' iftrue=expr_stmts 'else' iffalse=expr_stmts
80 | | LET='let' stmts=stmts 'in' out=expr_stmts
81 | | RET='return' [expr=expr_stmts]
82 | | YIELD='yield' [expr=expr_stmts]
83 | | STRS=DoubleQuotedStr+
84 | | NUM=num
85 | | LITERAL=literal
86 | -> atom_rewrite(state.ctx.get)
87 |
88 | tuple ::= '(' as mark [head=expr tail=(',' expr)*] ')'
89 | -> if tail: return Tuple(loc @ mark, (head, *tail[1::2]))
90 | if head:
91 | head.loc.update(*(loc @ mark))
92 | return head
93 | Void(loc@mark)
94 | list ::= '[' as mark [head=expr tail=(',' expr)*] ']' -> HList(loc @ mark, (head, *tail[1::2]) if head else ())
95 |
96 | set ::= '{' as mark head=expr tail=(',' expr)* '}' -> HDict(loc @ mark, make_set(head, *tail[1::2]))
97 |
98 | dict ::= '{' as mark head=pair tail=(',' pair)* '}' -> HDict(loc @ mark, (head, *tail[1::2]))
99 |
100 | pair ::= key=expr ':' value=expr -> (key, value)
101 |
102 | literal ::= it=(tuple | list | set | dict | def_lambda) -> it
103 |
104 | suites ::= '{|' as mark expr=expr_stmts '|}'
105 | -> expr.loc.update(*(loc @ mark))
106 | expr
107 |
108 | expr_stmt ::= expr=expr [';'] -> expr
109 |
110 | expr_stmts ::= (leader=expr_stmt [(expr_stmt{is_aligned})+] as tail | '{|' expr_stmt+ as no_indented '|}') ['where' stmts=stmts]
111 | -> suite = Suite(loc @ leader, no_indented or [leader, *tail])
112 | if stmts:
113 | return Where(loc @ leader, suite, stmts)
114 | suite
115 | stmt ::=
116 | | it=import_[';']
117 | | it=define [';']
118 | | it=defty [';']
119 | | it=infix [';']
120 | -> it
121 |
122 | stmts ::= leader=stmt [(stmt{is_aligned})+] as tail | '{|' stmt+ as no_indented '|}'
123 | -> Definition(loc @ leader, no_indented or [leader, *tail])
124 |
125 | module ::= [[doc=doc] 'module' [ exports=(identifier (',' identifier)*)] 'where'] it=stmts -> Module(it, doc, exports[::2] if exports else None)
126 | """
127 |
--------------------------------------------------------------------------------
/reley/impl/grammar.rbnf:
--------------------------------------------------------------------------------
1 | import std.common.[Space Name DoubleQuotedStr]
2 | ignore [Space comment]
3 | [python] import reley.impl.expr_based_ast.[*]
4 | [python] import reley.impl.helper.[*]
5 | [python] import functools.[*]
6 |
7 |
8 | comment := R'(--.*)|(((\{\-\|)+?[\w\W]+?(\-\})+))'
9 | docstr := R'(\|\|\|.*)'
10 | num := R'0[Xx][\da-fA-F]+|\d+(?:\.\d+|)(?:E\-{0,1}\d+|)'
11 | lam := R'\\'
12 |
13 | to_const cast := 'type' 'fn' 'def' '->' 'import' 'module'
14 | '[|' '|]'
15 | '{|' '|}'
16 | 'return' 'if' 'else' 'let' 'or' 'and' 'not' 'xor' 'where' 'as'
17 | '::' 'in' '<-' 'then' 'infix' '=='
18 |
19 | doc ::= items=docstr+ -> Doc(loc@items[0], '\n'.join(each.value[3:] for each in items))
20 | identifier ::= mark=Name | '(' as mark ID=binOp ')' -> Operator(loc @ mark, ID.name if ID else mark.value)
21 |
22 | def_lambda ::= mark=lam fn=lambda_trailer -> Lam(loc @ mark, None, fn.args, fn.body)
23 |
24 | lambda_trailer ::= (mark='(' [args=arguments] ')' | args=(mark=arg){1 1}) ('->' body=expr_stmts | body=lambda_trailer)
25 | -> Lam(loc @ mark, None, args or (), body)
26 |
27 | define ::= [doc=doc] id=identifier fn=fun_trailer
28 | ->
29 | if isinstance(fn, Lam):
30 | return DefFun(loc @ id, id.name, fn.args, fn.body, doc)
31 | else:
32 | return DefVar(loc @ id, id.name, fn)
33 |
34 | fun_trailer ::= '=' body=expr_stmts | ((mark='(' [args=arguments] ')' | args=(mark=arg){1 1}) fun=fun_trailer)
35 | -> Lam(loc @ mark, None, args or (), fun) if fun else body
36 |
37 | defty ::= mark='type' id=identifier '=' ty=ty -> DefTy(loc @ mark, id.name, ty)
38 |
39 | arguments ::= head=arg tail=(',' arg)* -> [head, *tail[1::2]]
40 |
41 | arg ::= name=Name [':' ty=ty] -> Arg(loc @ name, name.value, ty)
42 |
43 | ty ::= it=expr -> it
44 |
45 |
46 | alias_pair ::= imp_name=identifier ['as' name=identifier] -> Alias(loc @ imp_name, imp_name.name, name.name if name else imp_name.name)
47 |
48 | dot_lst ::= head=alias_pair tail=(',' alias_pair)* -> (head, *tail[1::2])
49 |
50 |
51 | wildcard ::= '*' -> 0
52 | import_ ::= mark='import' names=(Name ('.' Name)*) ['as' name=Name] ['(' (lst=dot_lst | star=wildcard)')']
53 | -> imp_name = ''.join(map(lambda _: _.value, names))
54 | name = name.value if name else None
55 | if not lst and not name: name = imp_name
56 | Import(loc @ mark, imp_name, name, lst or star)
57 |
58 | infix ::= mark='infix' precedence=num op=identifier -> Infix(loc @ mark, eval(precedence.value), op.name)
59 |
60 | expr ::=| BIN=expr (binOp expr)+ as tail
61 | | JUST=factor
62 | -> if BIN : return BinSeq(loc @ BIN, [BIN, *tail]) if tail else BIN
63 | return JUST
64 |
65 | binOp ::=
66 | | ('or' | '-' | 'and') as basic
67 | | '`' names=(~'`')+ '`'
68 | | ('*' | '^' | '%' | '&' | '@' | '$' | '+' | '/' | '|' | '>' | '<' | '==' | '~' | '.' | '?' | '!' | '::')+ as seq
69 | -> bin_op_rewrite(state.ctx.get)
70 |
71 | factor ::= [neg='-'] call=call -> Call(loc @ neg, Symbol(loc@neg, "neg"), call) if neg else call
72 |
73 | call ::= leader=atom tail=(atom{is_indented})*
74 | -> reduce(lambda a, b: Call(loc @ a, a, b), tail, leader)
75 |
76 | atom ::= | SYMBOL=identifier
77 | | IF='if' cond=expr 'then' iftrue=expr_stmts 'else' iffalse=expr_stmts
78 | | LET='let' stmts=stmts 'in' out=expr_stmts
79 | | RET='return' [expr=expr_stmts]
80 | | YIELD='yield' [expr=expr_stmts]
81 | | STRS=DoubleQuotedStr+
82 | | NUM=num
83 | | LITERAL=literal
84 | -> atom_rewrite(state.ctx.get)
85 |
86 | tuple ::= '(' as mark [head=expr tail=(',' expr)*] ')'
87 | -> if tail: return Tuple(loc @ mark, (head, *tail[1::2]))
88 | if head:
89 | head.loc.update(*(loc @ mark))
90 | return head
91 | Void(loc@mark)
92 | list ::= '[' as mark [head=expr tail=(',' expr)*] ']' -> HList(loc @ mark, (head, *tail[1::2]) if head else ())
93 |
94 | set ::= '{' as mark head=expr tail=(',' expr)* '}' -> HDict(loc @ mark, make_set(head, *tail[1::2]))
95 |
96 | dict ::= '{' as mark head=pair tail=(',' pair)* '}' -> HDict(loc @ mark, (head, *tail[1::2]))
97 |
98 | pair ::= key=expr ':' value=expr -> (key, value)
99 |
100 | literal ::= it=(tuple | list | set | dict | def_lambda) -> it
101 |
102 | suites ::= '{|' as mark expr=expr_stmts '|}'
103 | -> expr.loc.update(*(loc @ mark))
104 | expr
105 |
106 | expr_stmt ::= expr=expr [';'] -> expr
107 |
108 | expr_stmts ::= (leader=expr_stmt [(expr_stmt{is_aligned})+] as tail | '{|' expr_stmt+ as no_indented '|}') ['where' stmts=stmts]
109 | -> suite = Suite(loc @ leader, no_indented or [leader, *tail])
110 | if stmts:
111 | return Where(loc @ leader, suite, stmts)
112 | suite
113 | stmt ::=
114 | | it=import_[';']
115 | | it=define [';']
116 | | it=defty [';']
117 | | it=infix [';']
118 | -> it
119 |
120 | stmts ::= leader=stmt [(stmt{is_aligned})+] as tail | '{|' stmt+ as no_indented '|}'
121 | -> Definition(loc @ leader, no_indented or [leader, *tail])
122 |
123 | module ::= [[doc=doc] 'module' [ exports=(identifier (',' identifier)*)] 'where'] it=stmts -> Module(it, doc, exports[::2] if exports else None)
--------------------------------------------------------------------------------
/reley/impl/helper.py:
--------------------------------------------------------------------------------
1 | from rbnf.std.common import recover_codes
2 | from .expr_based_ast import *
3 |
4 |
5 | def is_indented(trailer, state):
6 | leader = state.ctx['leader']
7 |
8 | return leader.loc.colno < trailer.loc.colno
9 |
10 |
11 | is_indented.name = 'is_indented'
12 |
13 |
14 | def is_aligned(trailer, state):
15 | leader = state.ctx['leader']
16 | colno1 = leader.loc.colno
17 | colno2 = trailer.loc.colno
18 | # THIS is a BUG in RBNF! colno could be less than 1 when at the very first of text!
19 | return (colno1 is 0 and colno2 is 1) or colno1 == colno2
20 |
21 |
22 | is_aligned.name = 'is_aligned'
23 |
24 |
25 | def atom_rewrite(get):
26 | suite_, num_, strs_, symbol_, if_, let_, ret_, yield_, lit_ = map(
27 | get, ('SUITE', 'NUM', 'STRS', 'SYMBOL', 'IF', 'LET', 'RET', 'YIELD',
28 | 'LITERAL'))
29 | if suite_:
30 | mark = get('mark')
31 | suite_.loc.update(loc @ mark)
32 | return suite_
33 | if num_: return Number(loc @ num_, eval(num_.value))
34 | if strs_: return Str(loc @ strs_[0], ''.join(eval(_.value) for _ in strs_))
35 | if symbol_: return Symbol(loc @ symbol_, symbol_.name)
36 | if if_: return If(loc @ if_, get('cond'), get('iftrue'), get('iffalse'))
37 | if let_: return Where(loc @ let_, get('out'), get('stmts'))
38 | if ret_: return Return(loc @ ret_, get('expr'))
39 | if yield_: return Yield(loc @ yield_, get('expr'))
40 | if lit_: return lit_
41 | raise TypeError
42 |
43 |
44 | def bin_op_rewrite(get):
45 |
46 | basic, names, seq = map(get, ('basic', 'names', 'seq'))
47 | if basic:
48 | return Operator(loc @ basic, basic.value)
49 | if names:
50 | return Operator(loc @ names[0], recover_codes(names))
51 | if seq:
52 | return Operator(loc @ seq[0], ''.join(map(lambda _: _.value, seq)))
53 | raise TypeError
54 |
--------------------------------------------------------------------------------
/reley/impl/precedence.py:
--------------------------------------------------------------------------------
1 | from typing import Generic, Iterable, TypeVar, Optional, Iterator
2 | from functools import reduce
3 | from .expr_based_ast import Operator, Call, Symbol
4 |
5 | T = TypeVar('T')
6 |
7 |
8 | class TwoSideLink(Iterable, Generic[T]):
9 | def __init__(self,
10 | content: T,
11 | prev: 'Optional[TwoSideLink[T]]' = None,
12 | next: 'Optional[TwoSideLink]' = None):
13 | self.content: T = content
14 | self.next = next
15 | self.prev = prev
16 |
17 | def __iter__(self) -> 'Iterator[TwoSideLink[T]]':
18 | yield self
19 | if self.next:
20 | yield from self.next
21 |
22 | def __str__(self):
23 | return 'L<{}>'.format(self.content)
24 |
25 | __repr__ = __str__
26 |
27 | @classmethod
28 | def from_iter(cls, iterable: 'Iterable') -> 'Optional[TwoSideLink]':
29 | if not iterable:
30 | return None
31 | s_iterable = iter(iterable)
32 | try:
33 | fst = cls(next(s_iterable))
34 | except StopIteration:
35 | return None
36 |
37 | reduce(lambda a, b: setattr(a, "next", cls(b)) or setattr(a.next, "prev", a) or a.next, s_iterable, fst)
38 | return fst
39 |
40 |
41 | def bin_reduce(op_priorities):
42 | def bin_reduce(seq: Iterable):
43 | seq = TwoSideLink.from_iter(seq)
44 |
45 | def sort_by_func(e: 'TwoSideLink'):
46 | return op_priorities[e.content.name]
47 |
48 | op_nodes = (each for each in seq if isinstance(each.content, Operator))
49 | op_nodes = sorted(op_nodes, key=sort_by_func, reverse=True)
50 |
51 | bin_expr = None
52 |
53 | for each in op_nodes:
54 | sym = Symbol(loc=each.content.loc, name=each.content.name)
55 | bin_expr = Call(sym.loc, Call(sym.loc, sym, each.prev.content),
56 | each.next.content)
57 | each.content = bin_expr
58 | try:
59 | each.prev.prev.next = each
60 | each.prev = each.prev.prev
61 | except AttributeError:
62 | pass
63 |
64 | try:
65 | each.next.next.prev = each
66 | each.next = each.next.next
67 | except AttributeError:
68 | pass
69 |
70 | return bin_expr
71 |
72 | return bin_reduce
73 |
--------------------------------------------------------------------------------
/reley/impl/pycompat.py:
--------------------------------------------------------------------------------
1 | import sys
2 | from .reley_import import find_reley_module_spec
3 |
4 |
5 | class ReleyFinder:
6 | @classmethod
7 | def find_spec(cls, fullname: str, paths, target=None):
8 |
9 | return find_reley_module_spec(fullname)
10 |
11 |
12 | sys.meta_path.append(ReleyFinder)
13 |
--------------------------------------------------------------------------------
/reley/impl/reley_import.py:
--------------------------------------------------------------------------------
1 | import os
2 | from importlib.machinery import ModuleSpec
3 | from rbnf.easy import *
4 | from rbnf.edsl.rbnf_analyze import check_parsing_complete
5 | from Redy.Tools.PathLib import Path
6 | from .grammar import RBNF
7 | from .compiler import *
8 | import sys, types
9 |
10 | parse_fn = None
11 | reley_module_specs = {}
12 |
13 |
14 | @visit.case(Import)
15 | def _(imp: Import, ctx: Ctx):
16 | reley_mod_ctx = None
17 | spec = find_reley_module_spec(imp.imp_name)
18 | if spec:
19 | reley_mod_ctx: Ctx = get_context_from_spec(spec)
20 |
21 | if imp.name:
22 | ctx.add_local(imp.name, Val())
23 |
24 | stuffs = imp.stuffs or (imp.stuffs is 0 and tuple(
25 | Alias(imp.loc, each, each) for each in reley_mod_ctx.exported))
26 |
27 | if stuffs:
28 | for each in stuffs:
29 | if reley_mod_ctx:
30 | if each.imp_name in reley_mod_ctx.precedences:
31 | ctx.precedences[each.name] = reley_mod_ctx.precedences[
32 | each.imp_name]
33 |
34 | ctx.add_local(each.name, reley_mod_ctx.symtb[each.imp_name])
35 | else:
36 | ctx.add_local(each.name, Val())
37 |
38 | yield from wait(DECLARED)
39 | yield from wait(EVALUATED)
40 | yield from wait(RESOLVED)
41 | ctx.bc.append(Instr("LOAD_CONST", arg=0, lineno=imp.lineno + 1))
42 | ctx.bc.append(
43 | Instr(
44 | "LOAD_CONST",
45 | tuple(each.name for each in imp.stuffs) if imp.stuffs else
46 | ('*', ) if imp.stuffs is 0 else 0,
47 | lineno=imp.lineno + 1))
48 | ctx.bc.append(
49 | Instr("IMPORT_NAME", arg=imp.imp_name, lineno=imp.lineno + 1))
50 | if stuffs:
51 | for each in stuffs:
52 | ctx.bc.append(
53 | Instr(
54 | "IMPORT_FROM", arg=each.imp_name, lineno=each.lineno + 1))
55 | name = each.name
56 | if name in ctx.bc.cellvars:
57 | ctx.bc.append(
58 | Instr(
59 | "STORE_DEREF", CellVar(name), lineno=each.lineno + 1))
60 | else:
61 | ctx.bc.append(
62 | Instr('STORE_FAST', name, lineno=each.lineno + 1))
63 |
64 | elif imp.stuffs is 0:
65 | ctx.bc.append(Instr("IMPORT_STAR", lineno=imp.lineno + 1))
66 |
67 | if stuffs:
68 |
69 | if not imp.name:
70 | ctx.bc.append(Instr("POP_TOP", lineno=imp.lineno + 1))
71 | return
72 | if imp.name and imp.name in ctx.bc.cellvars:
73 | ctx.bc.append(
74 | Instr("STORE_DEREF", CellVar(imp.name), lineno=imp.lineno + 1))
75 | else:
76 | ctx.bc.append(Instr('STORE_FAST', imp.name, lineno=imp.lineno + 1))
77 |
78 |
79 | def get_parse_fn():
80 | global parse_fn
81 |
82 | if not parse_fn:
83 | language = Language('reley')
84 | file_path = Path(__file__).parent().into('grammar.rbnf')
85 | build_language(RBNF, language, str(file_path))
86 | lexer, impl, namespace = language.lexer, language.implementation, language.namespace
87 | top_parser = language.named_parsers['module']
88 |
89 | def match(text, filename) -> ze.ResultDescription:
90 | state = State(impl, filename=filename)
91 | tokens = tuple(
92 | setattr(each, 'filename', filename) or each
93 | for each in lexer(text))
94 | result: Result = top_parser.match(tokens, state)
95 |
96 | return ze.ResultDescription(state, result.value, tokens)
97 |
98 | parse_fn = match
99 |
100 | return parse_fn
101 |
102 |
103 | def print_ret(f):
104 | def call(*args, **kwargs):
105 | ret = f(*args, **kwargs)
106 | print(ret)
107 | return ret
108 |
109 | return call
110 |
111 |
112 | def get_reley_module_spec_from_path(names, module_path):
113 | with Path(module_path).open('r') as fr:
114 | spec = ModuleSpec(names, ReleyLoader(names, module_path))
115 | reley_module_specs[names] = spec
116 | code = fr.read()
117 | parse = get_parse_fn()
118 |
119 | result = parse(code, module_path)
120 | check_parsing_complete(code, result.tokens, result.state)
121 | ast = result.result
122 | ctx = Ctx({}, {}, Bytecode(), {}, False)
123 | ctx.visit(ast)
124 | ctx.bc.filename = module_path
125 | spec.source_code = code
126 | spec.context = ctx
127 | return spec
128 |
129 |
130 | def find_reley_module_spec(names, reload=False):
131 |
132 | reley_paths = sys.path
133 |
134 | for reley_path in reley_paths:
135 |
136 | if not reload:
137 | spec = reley_module_specs.get(names)
138 | if spec:
139 | return spec
140 |
141 | path_secs = (reley_path, *names.split('.'))
142 | *init, end = path_secs
143 | directory = Path(*init)
144 | if not directory.is_dir():
145 | continue
146 | end = end + '.hs'
147 | for each in os.listdir(str(directory)):
148 | if each.lower() == end:
149 | module_path = directory.into(each)
150 | return get_reley_module_spec_from_path(names, str(module_path))
151 |
152 |
153 | class ReleyLoader:
154 | def __init__(self, mod_name, mod_path):
155 | self.mod_name = mod_name
156 | self.mod_path = mod_path
157 |
158 | def create_module(self, spec):
159 | doc = spec.context.bc.docstring
160 | mod = types.ModuleType(self.mod_name, doc)
161 | return mod
162 |
163 | def exec_module(self, module):
164 | f = self.mod_path
165 | setattr(module, '__path__', f)
166 | setattr(module, '__package__', self.mod_name)
167 | setattr(module, '__loader__', self)
168 | code: Bytecode = module.__spec__.context.bc
169 | code.filename = str(self.mod_path)
170 |
171 | exec(code.to_code(), module.__dict__)
172 |
173 |
174 | def get_context_from_spec(spec: ModuleSpec) -> Ctx:
175 | return getattr(spec, 'context')
176 |
--------------------------------------------------------------------------------
/reley/prelude.hs:
--------------------------------------------------------------------------------
1 | module ($), (+), (-), (*), (/), (//), (`in`), attr, contains where
2 | import operator (add, sub, eq, mul, truediv, floordiv, contains, mod)
3 | import toolz (curry)
4 |
5 | infix 0 ($)
6 | infix 10 (+)
7 | infix 10 (-)
8 | infix 20 (*)
9 | infix 20 (/)
10 | infix 20 (//)
11 | infix 40 (`contains`)
12 | infix 40 (`in`)
13 | infix 50 (`attr`)
14 |
15 | ||| use `f $ arg` to avoid nested parentheses.
16 | ($) a b = a b
17 | (+) = curry add
18 | (-) = curry sub
19 | (*) = curry mul
20 | (/) = curry truediv
21 | (%) = curry mod
22 | (//) = curry floordiv
23 |
24 | flip f a b = f b a
25 |
26 | attr = curry getattr
27 | contains = curry contains
28 | (`in`) = flip contains
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/reley/unification/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thautwarm/reley/17e5730c1afbefaeb22103719c85f08333c65937/reley/unification/__init__.py
--------------------------------------------------------------------------------
/sample_test.py:
--------------------------------------------------------------------------------
1 | from reley.impl.pycompat import *
2 | import os
3 | import haskell_test.comment
4 | import haskell_test.import_hs
5 | import haskell_test.operator
6 | import haskell_test.sum_n
7 | import haskell_test.test_prelude
8 |
9 | from haskell_test.sum_n import m_sum
10 | lst = (5, (2, (1, ())))
11 | print(m_sum(lst))
12 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup
2 | from Redy.Tools.Version import Version
3 | from Redy.Tools.PathLib import Path
4 |
5 | with open('./README.md', encoding='utf-8') as f:
6 | readme = f.read()
7 |
8 | version_filename = 'next_version'
9 | with open(version_filename) as f:
10 | version = Version(f.read().strip())
11 |
12 | with Path("./reley/__release_info__.py").open('w') as f:
13 | f.write('__VERSION__ = {}\n__AUTHOR__ = "thautwarm"'.format(
14 | repr(str(version))))
15 |
16 | setup(
17 | name='reley',
18 | version=str(version),
19 | keywords='haskell, language, static typed, compiled',
20 | description=
21 | 'A static compiled language on Python with type safety, efficiency and syntax sugars.',
22 | long_description=readme,
23 | long_description_content_type='text/markdown',
24 | license='MIT',
25 | python_requires='>=3.6.0',
26 | url='https://github.com/thautwarm/reley',
27 | author='thautwarm',
28 | author_email='twshere@outlook.com',
29 | packages=['reley', 'reley.impl'],
30 | entry_points={'console_scripts': ['reley=reley.impl.cli:main']},
31 | package_data={'reley': ['impl/grammar.rbnf', '*.hs']},
32 | install_requires=['Redy', 'rbnf>=0.3.21', 'wisepy', 'bytecode==0.7.0', 'toolz'],
33 | platforms='any',
34 | classifiers=[
35 | 'Programming Language :: Python :: 3.6',
36 | 'Programming Language :: Python :: 3.7',
37 | 'Programming Language :: Python :: Implementation :: CPython'
38 | ],
39 | zip_safe=False)
40 |
41 | version.increment(version_number_idx=2, increment=1)
42 | if version[2] is 42:
43 | version.increment(version_number_idx=1, increment=1)
44 | if version[1] is 42:
45 | version.increment(version_number_idx=0, increment=1)
46 |
47 | with open(version_filename, 'w') as f:
48 | f.write(str(version))
49 |
--------------------------------------------------------------------------------