├── .gitignore ├── LICENSE ├── MANIFEST.in ├── ReadMe.md ├── conf ├── conf.py ├── config.py ├── flowpython ├── ReadMe.rst ├── __init__.py ├── __main__.py ├── composing.py ├── flowpy_switcher │ ├── __init__.py │ ├── setupfile.py │ └── utils.py ├── fp.py ├── origin_py │ └── ReadMe.md ├── py35 │ ├── .gitignore │ ├── Grammar │ │ └── Grammar │ ├── Parser │ │ └── Python.asdl │ ├── Python │ │ ├── ast.c │ │ ├── compile.c │ │ └── symtable.c │ └── ReadMe.md └── py36 │ ├── .gitignore │ ├── Grammar │ └── Grammar │ ├── Parser │ ├── Python.asdl │ └── acceler.c │ ├── Python │ ├── ast.c │ ├── compile.c │ └── symtable.c │ └── ReadMe.md ├── setup.py ├── temp_version └── py36 │ └── py365 │ ├── Grammar │ └── Grammar │ ├── Parser │ └── Python.asdl │ └── Python │ ├── ast.c │ ├── compile.c │ └── symtable.c ├── test ├── f_test.py ├── forfun.py ├── some.py ├── test_arrow.py ├── test_branch.py ├── test_patm.py └── test_where.py └── utils.py /.gitignore: -------------------------------------------------------------------------------- 1 | PythonDist/ 2 | .vscode 3 | __pycache__/ 4 | _build/ 5 | release/ 6 | .git/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The code for changing Python parser generator and bytecode compiler is derived from CPython, 2 | and is licensed under the terms of the PSF License Agreement. 3 | 4 | 5 | 1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and 6 | the Individual or Organization ("Licensee") accessing and otherwise using Python 7 | 3.5.x/3.6.x software in source or binary form and its associated documentation. 8 | 9 | 2. Subject to the terms and conditions of this License Agreement, PSF hereby 10 | grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, 11 | analyze, test, perform and/or display publicly, prepare derivative works, 12 | distribute, and otherwise use Python 3.5.x/3.6.x alone or in any derivative 13 | version, provided, however, that PSF's License Agreement and PSF's notice of 14 | copyright, i.e., "Copyright © 2001-2018 Python Software Foundation; All Rights 15 | Reserved" are retained in Python 3.5.x/3.6.x alone or in any derivative version 16 | prepared by Licensee. 17 | 18 | 3. In the event Licensee prepares a derivative work that is based on or 19 | incorporates Python 3.5.x/3.6.xor any part thereof, and wants to make the 20 | derivative work available to others as provided herein, then Licensee hereby 21 | agrees to include in any such work a brief summary of the changes made to Python 22 | 3.5.x/3.6.x. 23 | 24 | 4. PSF is making Python 3.5.x/3.6.x available to Licensee on an "AS IS" basis. 25 | PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF 26 | EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR 27 | WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE 28 | USE OF PYTHON 3.5.x/3.6.x WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. 29 | 30 | 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON 3.5.x/3.6.x 31 | FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF 32 | MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 3.5.x/3.6.x, OR ANY DERIVATIVE 33 | THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. 34 | 35 | 6. This License Agreement will automatically terminate upon a material breach of 36 | its terms and conditions. 37 | 38 | 7. Nothing in this License Agreement shall be deemed to create any relationship 39 | of agency, partnership, or joint venture between PSF and Licensee. This License 40 | Agreement does not grant permission to use PSF trademarks or trade name in a 41 | trademark sense to endorse or promote products or services of Licensee, or any 42 | third party. 43 | 44 | 8. By copying, installing or otherwise using Python 3.5.x/3.6.x, Licensee agrees 45 | to be bound by the terms and conditions of this License Agreement. 46 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include ReadMe.md 2 | recursive-include flowpython *.dll *.exe *.py *.pyc *.rst *.md python 3 | include origin_py 4 | -------------------------------------------------------------------------------- /ReadMe.md: -------------------------------------------------------------------------------- 1 | # License 2 | 3 | This project is totally based on the entire [CPython](https://github.com/python/cpython), 4 | so it is licensed under the terms of the PSF License Agreement. 5 | 6 | See [LICENSE](https://github.com/thautwarm/flowpython/blob/master/LICENSE) for the details. 7 | 8 | # Install & Uninstall 9 | 10 | - Support Windows 32bit/64bit and Linux 64bit for CPython 3.6.x/3.5.x. 11 | - Will support CPython 3.7 sooner. 12 | - Will never support CPython 2.x :) 13 | 14 | You now have to go to release page and download the binaries directly... 15 | 16 | After downloading, just replace the original files with flowpython items. 17 | 18 | 19 | 20 | # **Flowpython Project** 21 | 22 | - Version : 0.2.3 23 | 24 | - See [Flowpython project](/flowpython/ReadMe.rst) here. 25 | 26 | # **SourceForCompiling** 27 | 28 | - [BasedOnCPython3.5](https://github.com/thautwarm/cpython/tree/3.5) 29 | 30 | - [BasedOnCPython3.6](https://github.com/thautwarm/cpython/tree/3.6) 31 | 32 | Clone them and 33 | ```shell 34 | ./configure CC=clang 35 | make 36 | ... 37 | 38 | python 39 | Python 3.5.4+ (heads/3.5-dirty:0a8ff1b, Oct 8 2017, 13:56:29) 40 | [GCC 4.2.1 Compatible Clang 3.8.0 (tags/RELEASE_380/final)] on linux 41 | Type "help", "copyright", "credits" or "license" for more information. 42 | >>> .x -> x+1 43 | at 0x7f159379aae8> 44 | ``` 45 | 46 | 47 | 48 | 49 | # **History** 50 | 51 | 52 | ## Feature - List 53 | - [Old-Works](#old-works) 54 | - [Add-where-syntax](#add-where-syntax) 55 | - [Fix-lambda-closure](#fix-where-syntax-in-lambda-closure) 56 | - [Fix-keyword-conflictions](#fix-keyword-conflictions) 57 | - [Powerful-pattern-matching](#powerful-pattern-matching) 58 | - [Arrow-Transform](#arrow-transform) 59 | - [Matching-Filter](#matching-filter) 60 | - [Library fp.py](#fp-module) 61 | - [Branches](#branches) 62 | - [Pipeline/Monad](#pipeline) 63 | - [Logs](#logs) 64 | - [Auto Compose](#auto-compose) 65 | 66 | 67 | ---- 68 | 69 | ### Old Works 70 | 71 | (P.S This is not available for CPython 3.5.x 72 | 73 | fix `if-expr` and add some new ways to define `lambda`. 74 | 75 | - `if-expr` 76 | you can write 77 | ```python 78 | ret = e1 if j1 else 79 | e2 if j2 else 80 | e3 81 | ``` 82 | instead of 83 | 84 | ```python 85 | ret = e1 if j1 else \ 86 | e2 if j2 else \ 87 | e3 88 | ``` 89 | - `lambda` 90 | 91 | ```python 92 | .x -> x+1 93 | as-with x def x+1 94 | as-with x def as y def x+y 95 | ``` 96 | 97 | ### Add Where Syntax 98 | - principle: 99 | - **Parse**: 100 | - change Grammar/Grammar 101 | 1. Firstly, add a new grammar *where_stmt*. 102 |   ``where_stmt: 'where' ':' suite`` 103 | add this grammar to `compound_stmt` 104 | 2. Then change the end of *simple_stmt*,replace *NEWLINE* with 105 |   ``(NEWLINE | where_stmt)`` 106 | - change Parser/Python.asdl 107 | 1. Add a data structure *Where* as a kind of *expr* 108 | ``` 109 | Where(expr target, stmt* body) 110 | ``` 111 | 112 | - **AST**: 113 | - change Python/ast.c 114 | found the function `ast_for_stmt`, `ast_for_expr_stmt`,`ast_for_flow_stmt`,`ast_for_assert_stmt`, change it as what I did in **flowpython/Python/ast.c**. It tells the Python compiler how to get the data structure from the parsed codes. 115 | - **Compile&Interpret** 116 | - change Python/compile.c 117 | This part is kind of complicated to bring out, and I think that you'd better use *version controller* to detect out what's the differences between Flowpython and CPython 3.6.2. 118 | - change Python/symtable.c 119 | Quite similar to *compile.c*. 120 | 121 | - P.S for *Compile&Interpret* 122 | If your want to get a complete knowledge about how Python works, you should understand how the two C Module works firstly. 123 | 124 | 125 | ### Auto Compose 126 | 127 | ```python 128 | from flowpython.composing import auto_logger, auto_compose, flow_map, flow_filter 129 | auto_logger(__builtin__.__dict__) 130 | 131 | >> sum.filter(.x->x,[0,1,2,3,0]) 132 | >> 6 133 | ``` 134 | 135 | ### Fix Where Syntax in Lambda Closure 136 | - Particularly, fixed **where** syntax for *lambda*, to make the **scope** of statements in *where* syntax to be the **closure of the innermost lambda**. 137 | You can write following codes: 138 | ```python 139 | as-with x def as y def as z def ret_value where: 140 | ret_value = x + y +z 141 | ``` 142 | instead of: 143 | ```python 144 | as-with x def as y def as z def tmp(x+y+z) where: 145 | def tmp(x,y,z): 146 | return x +y +z 147 | ``` 148 | Does it seem to be currying? 149 | ``` 150 | .x->.y->.z-> ret where: 151 | ret = x +y +z 152 | ``` 153 | 154 | ### Fix Keyword Conflictions 155 | * **fix-keyword** 156 | * **switch-case-otherwise -> condef-case-otherwise** 157 | 158 | Some new keywords brought by Flowpython, such as **where, condef, case, otherwise**, used to conflict with **Standard Library** 159 | and some important Libaraies from **Third Party**. 160 | 161 | I fixed these conflictions with making the Parser module to ignore some grammar structures which would be checked in AST module. 162 | 163 | So you can write these codes now: 164 | 165 | ```python 166 | # no confliction 167 | 168 | where = 1 169 | where += where where: 170 | where += where 171 | 172 | case = 1 173 | otherwise = 2 174 | condef 1: 175 | case case => case 176 | otherwise => otherwise 177 | ``` 178 | 179 | Take care that each syntax in [ **where, case, otherwise** ] are not real keywords, and **condef** is. 180 | 181 | ```python 182 | condef = 1 183 | >>> SyntaxError: invalid syntax 184 | ``` 185 | 186 | ### Powerful Pattern Matching 187 | 188 | * **pattern-matching** 189 | 190 | There are four kinds of matching rules in Flowpython: 191 | 1. **comparing operator matching** 192 | ```C 193 | 194 | condef [ == ] expr: 195 | [>] 196 | case test1 => 197 | 198 | case test2 => 199 | 200 | otherwise => 201 | 202 | ``` 203 | which equals to 204 | ```python 205 | 206 | if (expr > test1 ) 207 | 208 | elif (expr == test2 ) 209 | 210 | else: 211 | 212 | ``` 213 | Each in `[], (), +(), +[], {} ` are called the **operator comparing mode**. 214 | **Giving a mode followed by *condef* keyword means giving a default mode.** 215 | The results are concluded [here](#conclusion-for-pattern-matching) 216 | 217 | for operator comparing mode "[]" 218 | can be 219 | == 220 | > 221 | < 222 | >= 223 | <= 224 | in 225 | not in 226 | is 227 | is not 228 | 229 | 2. **callable object matching** 230 | ```C 231 | condef (f) expr: 232 | case test1 => 233 | 234 | [!=] 235 | case test2 => 236 | 237 | ``` 238 | equals 239 | ```python 240 | if (f(expr) == test1): 241 | 242 | elif expr != test2: 243 | 244 | ``` 245 | 246 | 3. **dual callable comparing matching** 247 | 248 | ```C 249 | condef {f} expr: 250 | case test1 => 251 | 252 | ``` 253 | equals 254 | ```python 255 | if f(expr, test1): 256 | 257 | ``` 258 | 259 | 4. **Python Pattern Matching** 260 | - This one is the implementation for traditional pattern matching in CPython. 261 | ```C 262 | condef +[>] 1: 263 | case a:2 => 264 | 265 | +(type) 266 | case a:int => 267 | 268 | ``` 269 | The codes above can be explained as following process: 270 | 1. `if` we can do assignment `a = 1` and expression `a > 2` can be satisfied, then do ``. 271 | 2. `else if` we can do assignment `a = 1` and expression `type(a) == int` can be satisfied, then do ``. 272 | - There are much more ways to use **Pattern Matching**, take a look 273 | at [**test_patm.py**](https://github.com/thautwarm/flowpython/blob/master/test/test_patm.py) 274 | 3. **Take care** 275 | if you write the following codes without default mode, 276 | ```C 277 | condef [1,2,3]: 278 | ... 279 | condef {1,2,3}: 280 | ... 281 | condef (1,2,3): 282 | ... 283 | ``` 284 | it will lead to **syntax Error**. But you can use this instead: 285 | ```C 286 | condef() [1,2,3]: 287 | ... 288 | condef[] {1,2,3}: 289 | ... 290 | condef{} (1,2,3): 291 | ... 292 | ``` 293 | 294 | #### Conclusion for Pattern Matching 295 |
296 | 297 | | Matching Method | Identity | 298 | | ------------- |:-------------:| 299 | | comparing operator matching | [`operator`] | 300 | | callable object matching | (`callable`) | 301 | | dual callable comparing matching | {`callable`} | 302 | | python pattern matching(comparing) | +[`operator`] | 303 | | python pattern matching(callable) | +(`callable`) | 304 |
305 | 306 | ### Arrow Transform 307 | 308 | - **arrow transform expression** 309 | This one looks like **lambda**, and they have quite a lot of features in common. 310 | Look at this example: 311 | - `arrow transform` 312 | ```C 313 | >> 1 -> _+1 314 | >> 2 315 | >> x = [1,2,3] 316 | >> x -> map(.x->x+1, _) -> list(_) 317 | >> [2,3,4] 318 | ``` 319 | - `lambda` 320 | ```python 321 | >> .x -> x 322 | >> _(1) 323 | >> 1 324 | >> var = [1,2,3] 325 | >> .x -> map(.x->x+1, x) -> list(_) 326 | >> _(var) 327 | >> [2,3,4] 328 | ``` 329 | To conclude, `lambda` is the `lazy` form of `arrow transform`. 330 | The grammar identity `.` means **Take It As Lazy** 331 | 332 | ### Matching Filter 333 | 334 | ```C 335 | condef[] [1,2,3]: 336 | +(type) 337 | case (*a,b) -> a:list => 338 | print("just match 'a' with 'list' ") 339 | 340 | otherwise => 341 | print("emmmmmm,,") 342 | 343 | ``` 344 | 345 | ### FP Module 346 | 347 | - library: fp.py 348 | 349 | To support some basic operations in Functional Programming, here are methods implemented in `flowpython.fp`. 350 | 351 | ```C 352 | from flowpython.fp import compose, andThen, foldr, foldl, flat_map, flatten 353 | from flowpython.fp import strict, norecursion 354 | 355 | strict_flatten = strict.flatten 356 | strict_fastmap = strict.fastmap 357 | strict_flat_map= strict.flat_map 358 | norec_flatten = norecursion.lazy.flatten 359 | 360 | 361 | # fastmap( use generator instead of map in original Python ) 362 | fastmap(.x -> x+1, [1,2,3]) -> list(_) 363 | # -> [2,3,4] 364 | 365 | strict_flat_map(.x->x+1, [1,2,3]) # -> [2,3,4] 366 | 367 | # flatten 368 | flatten([1,2,[3,4],[[5],[6]]]) -> list(_) 369 | # -> [1,2,3,4,5,6] 370 | 371 | # compose : Callable->Callable->Any 372 | f1 -> compose(f2)(_) 373 | 374 | # andThen : Callable->Callable->Any 375 | f1 -> andThen(f2)(_) 376 | 377 | # foreach : Callable->Callable->Any 378 | range(20) -> foreach(print)(_) 379 | # -> 0 \n 1 \n 2 ... 380 | 381 | 382 | # fold : Dual Callable->(zero:Any)->Iterator->Any 383 | foldr # (not recommended) 384 | foldl # (not recommended) 385 | 386 | range(20) -> foldr(. x,y -> print(x) or x+y)(0)(_) 387 | range(20) -> foldr(. x,y -> print(y) or x+y)(0)(_) 388 | 389 | # flat_map : Iterator -> Callable -> Iterator 390 | # default lazy 391 | flat_map(.x->x+1)([[1,2,[3,4],[5,6]],[7,8]]) -> list(_) 392 | # -> [2,3,4,5,6,7,8,9] 393 | 394 | # object in norecursion class use no recursive methods. 395 | norec_flatten([[1,[2],[[3]],[[[4]]]]] -> list(_) 396 | 397 | 398 | 399 | ``` 400 | 401 | ### Branches 402 | 403 | An easy way to define `if-elif-else` statements: 404 | ( It's not `guard` in Haskell ! ) 405 | ```python 406 | 407 | otherwise = True 408 | 409 | | x == 1 => x += 1 410 | | type(x) is str => x = int(x) 411 | | otherwise => 412 | x = 2*x 413 | y = 1 414 | def defined(key): 415 | return key in globals() 416 | 417 | print(x) 418 | print(defined("y")) 419 | 420 | func = .x -> ret where: 421 | otherwise = True 422 | | x is 0 => ret = 0.0 423 | | type(x) in (str,int) => ret = float(x) 424 | | otherwise => ret = x 425 | 426 | ``` 427 | 428 | 429 | ## Pipeline 430 | 431 | Sorry for the shortage of documents for new grammar, and I'm busy with my new semester. 432 | It would be completed as sooner as possible. 433 | ```python 434 | 435 | >> 1 ->> .x -> x*10 => .x-> x+1 436 | >> 11 437 | 438 | ``` 439 | 440 | ```python 441 | 442 | >> range(100) ->> f1 \ 443 | => f2 \ 444 | => groupby(.x->x) \ 445 | => lambda Dict: map(.key->(key,len(Dict[key])), Dict) \ 446 | => dict \ 447 | => print where: 448 | from flowpython.fp import groupby 449 | f1 = . seq -> map(.x->x%2, seq) 450 | f2 = . seq -> filter(.x -> x, seq) 451 | >> {1:50} 452 | 453 | ``` 454 | 455 | 456 | 457 | ## Logs 458 | - date : before 2017-07-30 459 | - date : 2017-07-30 460 | - Add **where** syntax 461 | - date: 2017-08-06 462 | - Fix `closure` for `where` syntax in case of **Lambda Definition**. 463 | - date: 2017-08-07 464 | - Add `switch syntax`. 465 | - date: 2017-08-08 466 | - Fix the keyword conflicts against the standard libraries and the packages from Third Party. 467 | - Change the grammar of `switch` syntax. 468 | `switch-case-otherwise -> condef-case-otherwise` 469 | - date: 2017-08-10 470 | - Add `pattern matching` syntax. 471 | - Add arrow transform expression. 472 | - Remove `switch` syntax(which can be totally replaced by `pattern matching`). 473 | - date: 2017-08-10 474 | - Add matching filter syntax for `pattern matching` . 475 | - date: 2017-08-13 476 | - Add module `fp.py`. 477 | - date: 2017-08-15 478 | - Add `branches` grammar. 479 | - data: 2017-08-25 480 | - Add `pipeline` grammar. 481 | - Change keyword for pattern matching from `condic` to `condef`. 482 | -------------------------------------------------------------------------------- /conf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thautwarm/flowpython/450b857f7325519cb2ee7cadf2e95c05afc62ddd/conf -------------------------------------------------------------------------------- /conf.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | if __name__ == '__main__': 4 | os.system("python config.py {}".format(' '.join(sys.argv[1:]))) 5 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | """ 4 | author: thautwarm 5 | 6 | date: 25/07/17 7 | """ 8 | import time 9 | # ===================== 10 | # user configure files 11 | pythonDistPath = './PythonDist/{py_version}' 12 | flowpyDistPath = './flowpython/{py_version}' 13 | tempfilesPath = './temp_version/{py_version}/{commit_version}' 14 | # ====================== 15 | 16 | support_version = ['py36','py35'] 17 | # ===================== 18 | # version info 19 | __version__ = "0.2.2" 20 | modules={ 21 | 'ast':'Python/ast.c', 22 | 'symtable':'Python/symtable.c', 23 | 'parser':'Parser/Python.asdl', 24 | 'grammar':'Grammar/Grammar', 25 | 'compile':'Python/compile.c', 26 | # 'acceler':'Parser/acceler.c' 27 | } 28 | # ===================== 29 | 30 | make_args={ 31 | 'clean':'make clean', 32 | 'reconf': './configure CC=clang CXX=clang++ --without-gcc --with-pydebug', 33 | 'clear':'make distclean', 34 | 'grammar':'make regen-grammar', 35 | 'ast':'make regen-ast', 36 | 'all':['clear','reconf','grammar','ast'], 37 | 'rev':['grammar','ast'] 38 | } 39 | 40 | 41 | import sys 42 | import os 43 | from utils import createTemp, isFileExists , fload, fsave, tryAction, joinPath 44 | fdump = fsave 45 | 46 | 47 | def fileGen(module, *presentnames: ["pythonDistPath","flowpyDistPath","tempfilesPath"] ): 48 | if len(presentnames)!=3: return BaseException("Params InputNum Do Not Match 3.") 49 | 50 | _to, from_, temp = map(joinPath , presentnames ,(module, )*3 ) 51 | 52 | for _ in map(isFileExists, (_to, from_) ) : pass 53 | 54 | if temp: 55 | createTemp(temp) 56 | 57 | version_now = fload(from_) 58 | fdump(version_now, _to) 59 | 60 | if temp: 61 | fsave(version_now, temp) 62 | 63 | return "OK" 64 | 65 | 66 | if __name__ == '__main__': 67 | argv = sys.argv[1:] 68 | 69 | # this is a initial version of project-manager which means it's quite incomplete. 70 | 71 | main_arg = argv[0] 72 | dict_args=dict() 73 | read_in_arg_status = False 74 | 75 | for arg_i in argv: 76 | if arg_i.startswith('-'): 77 | key = arg_i[1:] 78 | read_in_arg_status = True 79 | elif read_in_arg_status: 80 | dict_args[key] = arg_i 81 | read_in_arg_status = False 82 | 83 | if 'pyv' not in dict_args: 84 | dict_args['pyv'] = "py35" 85 | else: 86 | if dict_args['pyv'] not in support_version: 87 | raise Exception(f"Do not support version {dict_args['pyv']} \n Supported versions are {support_version}") 88 | 89 | dict_args['py_version'] = dict_args['pyv'] 90 | action_version = dict_args['v'] if'v' in dict_args else time.time() 91 | tempfilesPath = tempfilesPath.format(commit_version = action_version, py_version = dict_args['pyv']) 92 | pythonDistPath = pythonDistPath.format(py_version = dict_args['pyv']) 93 | flowpyDistPath = flowpyDistPath.format(py_version = dict_args['pyv']) 94 | 95 | def version_control(id_str): 96 | _to, from_ ,temp = (pythonDistPath, flowpyDistPath, tempfilesPath) if id_str == 'commit' else\ 97 | (flowpyDistPath, pythonDistPath, tempfilesPath) if id_str == 'back' else\ 98 | (pythonDistPath, tempfilesPath, flowpyDistPath) 99 | for module in modules: 100 | fileGen(modules[module], _to, from_, temp ) 101 | 102 | if main_arg == 'commit': 103 | version_control('commit') 104 | elif main_arg == 'recover': 105 | version_control('recover') 106 | elif main_arg == 'back': 107 | version_control('back') 108 | elif main_arg == 'make': 109 | os.chdir(pythonDistPath) 110 | if 'm' not in dict_args: 111 | os.system("make") 112 | else: 113 | m = dict_args['m'] 114 | if m in ['all','rev']: 115 | args = make_args[m] 116 | for arg in args: 117 | os.system(make_args[arg]) 118 | os.system("make") 119 | elif m in make_args: 120 | os.system(make_args[m]) 121 | elif main_arg == 'test': 122 | 123 | os.chdir(pythonDistPath) 124 | testfilePath = '../../test' 125 | 126 | if 'f' in dict_args: 127 | files = filter(lambda x:x, dict_args['f'].split(" ")) 128 | else: 129 | files = os.listdir(testfilePath) 130 | files =map(lambda x: joinPath(testfilePath, x), files) 131 | for file in files: 132 | print('testing on {}'.format(file)) 133 | os.system("./python {}".format(file)) 134 | elif main_arg == 'run': 135 | os.system(f"{pythonDistPath}/python") 136 | else: 137 | print(BaseException("main argument cannot be identified.")) 138 | pass 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /flowpython/ReadMe.rst: -------------------------------------------------------------------------------- 1 | License 2 | ========================================= 3 | 4 | This porject is totally based on the entire ``_, 5 | so it is licensed under the terms of the PSF License Agreement. 6 | 7 | See `LICENSE (https://github.com/thautwarm/flowpython/blob/master/LICENSE`_ for the details. 8 | 9 | 10 | Additional Grammar Compatible to CPython 11 | 12 | ========================================== 13 | 14 | 15 | How to Get Flowpython 16 | ----------------------- 17 | 18 | Go to release page... 19 | 20 | VERSION 21 | ---------- 22 | flowpython == 0.2.3 23 | 24 | CPython == 3.6.x/3.5.x 25 | 26 | 27 | Requirement 28 | ------------ 29 | CPython == 3.6.x/3.5.x 30 | 31 | C/C++ Compiler 32 | 33 | - My version was built on clang-3.8.1/GCC-6.3.0 on linux-deepin 34 | 35 | 36 | Grammar 37 | ------------ 38 | 39 | **with quite few new additional keywords here** 40 | 41 | * **Where Syntax** 42 | 43 | .. code:: python 44 | 45 | (test) where: 46 | statements 47 | ... 48 | # "test" is the top one of expressions in Python Grammar. 49 | 50 | - Take a look at here: 51 | the **where** blocks would be **executed before the expression at the head**, 52 | but it should be **located at the end**. 53 | - Q :Why did I bring "where" syntax into Python? 54 | - A :For **combining the expressions and statements** in Python and enhanced the readability of procedure. 55 | 56 | - See the following codes: 57 | 58 | .. code:: python 59 | 60 | # 圆柱面积 / surface area of a cylinder 61 | from math import pi 62 | r = 1 # the radius 63 | h = 10 # the height 64 | 65 | S = (2*S_top + S_side) where: 66 | S_top = pi*r**2 67 | S_side = C * h where: 68 | C = 2*pi*r 69 | 70 | - And where syntax makes it possible for **Lambda** in Python to do everything that **def** can do. 71 | 72 | .. code:: python 73 | 74 | # multi-row lambda in python 75 | 76 | lambda x: someFunc(x) where: 77 | def someFunc(var): 78 | pass 79 | 80 | lambda x: ret where: 81 | def someFunc(var): 82 | ... 83 | ret = someFunc(x) 84 | 85 | However, in Flowpython, there are some other way more interesting supplied to define a "lambda" than "lambda x: f(x)" : 86 | 87 | * **Lambda Syntax** 88 | 89 | .. code:: python 90 | 91 | # Lambda 92 | 93 | lambda x,y,z : lambda a,b,c : x*a + y*b + z*c 94 | # which equals to 95 | .x,y,z -> .a,b,c -> x*a + y*b + z*c 96 | # which equals to 97 | as-with x,y,z def as a,b,c def x*a + y*b + z*c 98 | 99 | #look at this example: 100 | 101 | as-with x def as y def as z def x+y+z 102 | # which equals to 103 | as-with x def as-with y def as-with z def x+y+z 104 | 105 | And we know that there are some powerful tools in some FP languages, like 106 | 107 | * **Pattern matching** 108 | 109 | .. code:: python 110 | 111 | condef +[<] 1: 112 | case a:2 => 113 | assert a == 1 and a < 2 114 | +(.x->type(x)) 115 | case a:int => 116 | assert a == 1 and type(a) == int 117 | condef 1: 118 | +(.x->2*x) 119 | case a:3 => 120 | assert a == 1 and a*2 == 3 121 | +[is not] 122 | case a:2 => 123 | assert a == 1 and a is not 2 124 | otherwise => 125 | ... 126 | 127 | # new syntax: matching filter 128 | condef[] [1,2,3] : 129 | +(type) 130 | case (*a,b)->a:list => 131 | assert type(a) == list 132 | otherwise => 133 | assert False,"emmmm" 134 | 135 | 136 | There are more optional grammars, just see GitHub `Link `_. 137 | 138 | More about Pattern Matching to see `TestFile `_ 139 | And `Distribute History `_ 140 | 141 | Another useful tool in Flowpython is **Arrow Transform**, which enhances the readability greatly and makes it possible 142 | to **Avoid Prolix Parentheses**. 143 | 144 | * **Arrow Transform** 145 | 146 | .. code:: python 147 | 148 | range(100) -> reduce(.x,y->x+y, _) -> print(_) where: 149 | from functools import reduce 150 | 151 | [1,2,3] -> map(.x->x+1, _) -> list(_) -> print(_) 152 | 153 | Read `Arrow Transform `_ to get a konwledge of this grammar. 154 | 155 | 156 | 157 | Revision 158 | ------------ 159 | 160 | See `Distribution History `_ here. 161 | 162 | 163 | Compile FlowPython For Yourself 164 | -------------------------------------- 165 | 166 | **For Windows User** 167 | - You'd better use Visual Studio to make the Python project, and it must be the easiest thing in the world, I think. 168 | 169 | - I have tried with **Cygwin** but finally failed. 170 | 171 | **For Linux User** 172 | Firstly, you should have a C/C++ compiler like: 173 | 174 | - https://gcc.gnu.org/ 175 | 176 | - http://releases.llvm.org/ 177 | 178 | To give some advice, you can easily get C/C++ compiler with 179 | 180 | - **MinGW/Cygwin** on **windows** (Failed, please have a try with Visual Studio) 181 | 182 | - **sudo apt-get install gcc/clang** on **Ubuntu/Debian/Deepin** (maybe it also works for MacOS). 183 | 184 | Next, you can get the Flowpython sources which can be directly compiled in the same way as CPython, because Flowpython is truly an adjustment about CPython. 185 | 186 | The sources can be found at `ForCPython 3.5 `_ and `ForCPython 3.6 `_. 187 | Clone them and just type command `./configure CC=clang` and `make` is okay. 188 | 189 | .. code:: shell 190 | 191 | ./configure CC=clang 192 | make 193 | ... 194 | python 195 | Python 3.5.4+ (heads/3.5-dirty:0a8ff1b, Oct 8 2017, 13:56:29) 196 | [GCC 4.2.1 Compatible Clang 3.8.0 (tags/RELEASE_380/final)] on linux 197 | Type "help", "copyright", "credits" or "license" for more information. 198 | >>> .x -> x+1 199 | at 0x7f159379aae8> 200 | 201 | But for someone who want to get CPython source for yourself for some special reasons, 202 | there might be something useful in the following sections. 203 | 204 | For Developers 205 | --------------- 206 | 207 | You Could get a CPython distribution like Python-3.6.x and Python-3.5.x at 208 | 209 | - https://www.python.org/ 210 | 211 | And then you should **replace the files of the standard CPython distribution with those of Flowpython's** (They are at `flowpython/flowpython/$pythonVersion/`). 212 | 213 | If you change the variable of **pythonDistPath** in the file **config.py** with the path of your Python distribution, 214 | 215 | just run this command: 216 | 217 | .. code:: shell 218 | 219 | ./conf commit -v try_flowPython 220 | 221 | Everyting about Flowpython can be found in the directory "/flowpy". 222 | 223 | Finally open the CMD/Terminal at the root of CPython distribution, 224 | 225 | run the following commands: 226 | 227 | .. code:: shell 228 | 229 | ./configure CC= 230 | make regen-gramamr 231 | make regen-ast 232 | make 233 | ./python 234 | 235 | If you change the variable of **pythonDistPath** in the file **config.py** with the path of your Python distribution, 236 | 237 | just run this command: 238 | 239 | .. code:: shell 240 | 241 | ./conf make -m all -pyv [py36 | py35] 242 | 243 | And then you can enjoy Flowpython! 244 | 245 | 246 | I wrote config.py as the project-manage tool of Flowpython. 247 | 248 | It assembled the following modules: 249 | - make 250 | - customer version controler 251 | - debug&unittest 252 | 253 | It can be used like these way: 254 | 255 | .. code:: shell 256 | 257 | ./conf commit -v -pyv [py35 | py36] 258 | ./conf recover -pyv [py35 | py36] 259 | ./conf test -pyv [py35 | py36] 260 | ./conf make -m clean -pyv [py35 | py36] 261 | ./conf make -m ast -pyv [py35 | py36] 262 | ... 263 | 264 | It seems to be kind of complicated but it's quite easy to understand and operate in fact. 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | -------------------------------------------------------------------------------- /flowpython/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __version__ = VERSION = "0.3" 3 | __author__ = AUTHOR = "thautwarm" 4 | __license__ = LICENSE = "MIT" 5 | __doc__ = DOC = \ 6 | """ 7 | Additional 8 | 9 | See flowpython grammar in https://github.com/thautwarm/flowpython 10 | 11 | See https://github.com/thautwarm/flowpython/blob/master/ReadMe.md 12 | Wiki: https://github.com/thautwarm/flowpython/wiki 13 | 14 | lambdef : 15 | .x -> .y -> x+y 16 | as-with x def as y def x+y 17 | lamdba x: lambda y : x+y 18 | 19 | multi-row lambdef: 20 | .x -> .y -> ret where: 21 | import math 22 | ret1 = math.exp(x-y) 23 | 24 | where syntax: 25 | 26 | # surface area of a cylinder 27 | from math import pi 28 | 29 | r = 1 #radius 30 | h = 1 #height 31 | 32 | S = (2*S_top + S_side) where: 33 | S_top = pi*r**2 34 | S_side = C * h where: 35 | C = 2*pi*r 36 | 37 | Pattern Matching: 38 | 39 | condic 1: 40 | 41 | +(type) 42 | case a:int => a+1 43 | 44 | [is not] 45 | case 1 => assert 1 is not 1 46 | 47 | {.x,y->x+y == 5} 48 | case 4 => assert 1+4 == 5 49 | 50 | otherwise => print("otherwise") 51 | Plus: Matching Filter: 52 | condic[] [1,2,3]: # use a seq as test expression , you should write "condic[] seq", "condic{} seq", "condic() seq". 53 | +(type) 54 | case (*a,b) -> a : list => sum(a) 55 | 56 | 57 | Arrow Transform 58 | 59 | >> range(5) -> map(.x->x+1, _) -> list(_) 60 | >> [1,2,3,4,5] 61 | 62 | 63 | See the tutorials with more details and exmaples about Pattern Matching in https://github.com/thautwarm/flowpython/wiki. 64 | 65 | """ 66 | 67 | 68 | 69 | class helper: 70 | doc = __doc__ 71 | version = __version__ 72 | author = __author__ 73 | license = __license__ 74 | 75 | -------------------------------------------------------------------------------- /flowpython/__main__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import sys, os, platform, subprocess 4 | from .flowpy_switcher.setupfile import setup 5 | 6 | def getargs(argv): 7 | tuples = [] 8 | def gen(key): 9 | if key.startswith("-"): 10 | key = key[1:] 11 | if key == 'h': 12 | tuples.append((key,'True')) 13 | return gen 14 | def setarg(value): 15 | tuples.append( (key,value) ) 16 | return gen 17 | return setarg 18 | else: 19 | return gen 20 | op = gen 21 | for arg in argv: 22 | op = op(arg) 23 | return dict(tuples) 24 | 25 | if __name__ == '__main__': 26 | dict_args = getargs(sys.argv[1:]) 27 | Arch = platform.architecture()[0] 28 | __Platform__ = platform.platform().lower() 29 | 30 | if 'h' in dict_args: 31 | 32 | print("enable : python -m flowpy -m enable [-p PATH] \disable : python -m flowpy -m disable [-p PATH]") 33 | print("PATH : path of python intepreter.") 34 | 35 | else: 36 | 37 | assert 'm' in dict_args, "mode not selected: use command ' -m [enable|disable] ' " 38 | 39 | pattern_match_test = (dict_args["m"], Arch, __Platform__) 40 | 41 | try: 42 | (mode, arch, platf) = pattern_match_test 43 | jd = 'windows' in platf or 'linux' in platf 44 | if not jd: 45 | raise EnvironmentError("unsupported os") 46 | platf = 'windows' if 'windows' in platf else 'linux' 47 | def getpypath(): 48 | try: 49 | search_command = 'where' if platf == 'windows' else 'which' 50 | ret = subprocess.Popen([search_command, 'python'],stdout = subprocess.PIPE)\ 51 | .stdout\ 52 | .readlines()[0]\ 53 | .decode('utf8') 54 | except IndexError as e: 55 | raise BaseException('No python distribution found in PATH.') from e 56 | return ret 57 | pypath = dict_args['p'] if 'p' in dict_args else getpypath() 58 | setup(pypath, arch, platf)(mode) 59 | 60 | except Exception as e: 61 | 62 | raise BaseException('unknown platform...') from e 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /flowpython/composing.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | def auto_logger(closure): 3 | if not isinstance(closure, dict): 4 | raise Exception("You should input a dict!!") 5 | globals().update(closure) 6 | from collections import deque 7 | class auto_compose: 8 | __slots__ = "compfunc" 9 | def __init__(self, func): 10 | self.compfunc = func if isinstance(func, deque) else deque([func]) 11 | def __call__(self, *args, **kwargs): 12 | for func in self.compfunc: 13 | try: 14 | r = func(r) 15 | except: 16 | r = func(*args, **kwargs) 17 | return r 18 | def __getattribute__(self, name): 19 | if name.startswith("__") or name == 'compfunc': 20 | return super(auto_compose, self).__getattribute__(name) 21 | try: 22 | nextf = globals()[name] 23 | except KeyError: 24 | if hasattr(__builtin__, name): 25 | nextf = getattr(__builtin__, name) 26 | else: 27 | raise Exception("Cannot find object {}".format(name)) 28 | return auto_compose(deque([nextf])+self.compfunc) 29 | def __getitem__(self, v): 30 | maybe_curry = self(v) 31 | if callable(maybe_curry): 32 | return auto_compose(maybe_curry) 33 | return maybe_curry 34 | from .fp import flat_map, flow_map, flow_reduce, flow_filter, foreach, groupby, fastmap, flatten, foldr, foldl 35 | 36 | flat_map = auto_compose(flat_map) 37 | flow_map = auto_compose(flow_map) 38 | flow_filter = auto_compose(flow_filter) 39 | foreach = auto_compose(foreach) 40 | groupby = auto_compose(groupby) 41 | fastmap = auto_compose(fastmap) 42 | flatten = auto_compose(flatten) 43 | foldr = auto_compose(foldr) 44 | foldl = auto_compose(foldl) 45 | 46 | 47 | -------------------------------------------------------------------------------- /flowpython/flowpy_switcher/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thautwarm/flowpython/450b857f7325519cb2ee7cadf2e95c05afc62ddd/flowpython/flowpy_switcher/__init__.py -------------------------------------------------------------------------------- /flowpython/flowpy_switcher/setupfile.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import sys 3 | version = sys.version_info.minor 4 | class windows: 5 | reps = { 6 | 'python3.dll', 7 | 'python3{}.dll'.format(version), 8 | 'pythonw.exe', 9 | 'python.exe' 10 | } 11 | class linux: 12 | reps = { 13 | 'python' 14 | } 15 | 16 | import os,json 17 | from flowpython import __file__ as rootpath 18 | from .utils import makedir_from, moveto, bin_copyto 19 | try: 20 | user_path = os.environ["HOME"] 21 | except KeyError: 22 | user_path = os.environ["HOMEPATH"] 23 | 24 | cat = os.path.join 25 | def setup(path, arch, platform): 26 | flowpy_dir_path = cat(makedir_from(rootpath),"py3{version}/{platform}-{arch}".format(version=version, platform = platform, arch = arch)) 27 | save_dir_path = cat(makedir_from(rootpath),'origin_py') 28 | origin_dir_path = makedir_from(path) 29 | reps = eval("{platform}.reps".format(platform = platform)) 30 | manager_path = cat(user_path, '.flowpy') 31 | def enable(): 32 | if not os.path.exists(manager_path):pass 33 | else: 34 | with open(manager_path, 'r') as f: 35 | if json.load(f)['enabled'] == 'true': 36 | print("Flowpython has been enabled.") 37 | reps.clear() 38 | 39 | if reps: 40 | for rep in reps: 41 | # ================== ENABLE 42 | oripy_file = cat(origin_dir_path, rep) 43 | flowpy_file = cat(flowpy_dir_path, rep) 44 | save_file = cat(save_dir_path , rep) # save the original python intepreter in case of uninstalling. 45 | 46 | # oripy_file -> moveto(_, save_file) 47 | moveto(oripy_file, save_file) 48 | 49 | bin_copyto(flowpy_file, oripy_file) 50 | # flowpy_file -> bin_copyto(_, oripy_file) 51 | 52 | # ================== 53 | # f"enabled -- {rep}" -> print(_) 54 | print("enabled -- {rep}".format(rep = rep)) 55 | 56 | if platform == 'linux': 57 | os.system('chmod 777 {path}'.format(path = path)) 58 | with open(manager_path, 'w') as f: 59 | 60 | # {'enabled':'true'} -> json.dump(_, f) 61 | json.dump({'enabled': 'true'}, f) 62 | 63 | 64 | def disable(): 65 | if not os.path.exists(manager_path): 66 | print("Disable before enabled!!!") 67 | return 68 | with open(manager_path, 'r') as f: 69 | 70 | if json.load(f)['enabled'] == 'true': 71 | for rep in reps: 72 | # ================== DISABLE 73 | oripy_file = cat(origin_dir_path, rep) 74 | save_file = cat(save_dir_path , rep) 75 | moveto(save_file, oripy_file) 76 | print("disabled -- {rep}".format(rep = rep)) 77 | if platform == 'linux': 78 | os.system('chmod 777 {path}'.format(path = path)) 79 | with open(manager_path, 'w') as f: 80 | json.dump({'enabled':'false'}, f) 81 | else: 82 | print("Flowpython hasn't been enabled yet!!!") 83 | 84 | 85 | def _f_(option): 86 | ret = enable if option == 'enable' else \ 87 | disable if option == 'disable' else \ 88 | lambda : print('No option called {option} => do nothing.'.format(option = option)) 89 | return ret() 90 | return _f_ 91 | -------------------------------------------------------------------------------- /flowpython/flowpy_switcher/utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | def makedir_from(file): 3 | try: 4 | return os.path.split(file)[0] 5 | except: 6 | pass 7 | 8 | 9 | def bin_copyto(file_from, file_to): 10 | render_dict =dict(file_from = file_from, file_to=file_to) 11 | if not os.path.exists(file_from): 12 | raise BaseException("{file_from} not exists!".format(**render_dict)) 13 | print("writing {file_from} to {file_to}...".format(**render_dict)) 14 | with open(file_to, 'wb') as _to, open(file_from,'rb') as _from: 15 | _to.write(_from.read()) 16 | 17 | def moveto(file_from, file_to): 18 | render_dict =dict(file_from = file_from, file_to=file_to) 19 | if not os.path.exists(file_from): 20 | raise BaseException("{file_from} not exists!".format(**render_dict)) 21 | print("moving {file_from} to {file_to}...".format(**render_dict)) 22 | os.rename(file_from, file_to) 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /flowpython/fp.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Created on Sun Aug 13 12:48:26 2017 5 | 6 | @author: thautwarm 7 | """ 8 | from collections import defaultdict 9 | from functools import reduce 10 | import warnings 11 | # =============== 12 | # Basic 13 | 14 | compose = .f1 -> .f2 -> . *args, **kwargs -> f2(*args, **kwargs) -> f1(_) 15 | andThen = .f1 -> .f2 -> . *args, **kwargs -> f1(*args, **kwargs) -> f2(_) 16 | 17 | def AndThen(*func_stack): 18 | def _1(*args, **kwargs): 19 | for func in func_stack: 20 | try: 21 | mid = func(mid) 22 | except: 23 | mid = func(*args, **kwargs) 24 | return mid 25 | return _1 26 | 27 | def Compose(*func_stack): 28 | def _1(*args, **kwargs): 29 | for func in func_stack[::-1]: 30 | try: 31 | mid = func(mid) 32 | except: 33 | mid = func(*args, **kwargs) 34 | return mid 35 | return _1 36 | 37 | 38 | 39 | # =============== 40 | 41 | foreach = . f ->. self -> None where: 42 | for item in self: 43 | f(item) 44 | 45 | groupby = . function ->. self -> that where: 46 | that = defaultdict(list) 47 | self ->> foreach(do) where: 48 | def do(item): 49 | that[function(item)].append(item) 50 | 51 | 52 | # map and reduce 53 | # ==================== 54 | flow_map = as-with f def as *args def map(f, *args) 55 | flow_filter = as-with f def as *args def filter(f, *args) 56 | flow_reduce = as-with f def as *args def reduce(f, *args) 57 | ## =================== 58 | fastmap = as-with f def as *args def (f(*items) for items in zip(*args)) 59 | fastmap.__doc__ = """Generators might work faster! Howerver they cannot be deepcopied in Python and can be evaluated only once. """ 60 | # ==================== 61 | 62 | flatten = as-with seq:list def that where: 63 | def _f(seq:list): 64 | for item in seq: 65 | if not isinstance(item, list): 66 | yield item 67 | else: 68 | yield from _f(item) 69 | that = _f(seq) 70 | 71 | flat_map = lambda f: andThen(flatten)(flow_map(f)) 72 | 73 | 74 | # ========================== 75 | # fold 76 | foldr = . f -> . zero -> .seq -> ret where: 77 | warnings.warn("Not recommend to use 'fold' because there is no TCO in Python!") 78 | condef seq: 79 | case [] => ret = zero 80 | +[] 81 | case (a,*b) => ret = f(a, foldr(f)(zero)(b)) 82 | 83 | foldl = . f -> . zero -> .seq -> ret where: 84 | warnings.warn("Not recommend to use 'fold' because there is no TCO in Python!") 85 | condef seq: 86 | case [] => ret = zero 87 | +[] 88 | case (*a,b) => ret = f(foldl(f)(zero)(a), b) 89 | 90 | # =========================== 91 | 92 | class strict: 93 | """ 94 | Some method implemented with recursion. 95 | """ 96 | flatten = as-with seq:list def that where: 97 | that = [] 98 | def _f(seq:list): 99 | for item in seq: 100 | if not isinstance(item, list): 101 | that.append(item) 102 | else: 103 | _f(item) 104 | _f(seq) 105 | fastmap = as-with f def as *args def [f(*items) for items in zip(*args)] 106 | flat_map = lambda f: andThen(flatten)(flow_map(f)) 107 | 108 | 109 | class norecursion: 110 | """ 111 | Some method implemented without recursion. 112 | In most case, these methods are not recommended as a result of the very slow "while" in Python.abs 113 | However, if the algorithm bears the risk of StackoverflowError, these methods deserve to be tried. 114 | """ 115 | class lazy: 116 | @staticmethod 117 | def flatten(seq:list): 118 | """ 119 | this is the implementation of function flatten without recursion. 120 | """ 121 | head = [] 122 | tmp = seq 123 | idx = [0] 124 | while True: 125 | try: 126 | item = tmp[idx[-1]] 127 | except IndexError: 128 | try: 129 | tmp = head.pop() 130 | idx.pop() 131 | continue 132 | except IndexError: 133 | break 134 | idx[-1] += 1 135 | if not isinstance(item, list): 136 | yield item 137 | else: 138 | head.append(tmp) 139 | tmp = item 140 | idx.append(0) 141 | 142 | class strict: 143 | @staticmethod 144 | def flatten(seq:list): 145 | """ 146 | this is the implementation of function flatten without recursion. 147 | """ 148 | head = [] 149 | store = [] 150 | tmp = seq 151 | idx = [0] 152 | while True: 153 | try: 154 | item = tmp[idx[-1]] 155 | except IndexError: 156 | try: 157 | tmp = head.pop() 158 | idx.pop() 159 | continue 160 | except IndexError: 161 | break 162 | idx[-1] += 1 163 | if not isinstance(item, list): 164 | store.append(item) 165 | else: 166 | head.append(tmp) 167 | tmp = item 168 | idx.append(0) 169 | return store 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | -------------------------------------------------------------------------------- /flowpython/origin_py/ReadMe.md: -------------------------------------------------------------------------------- 1 | ## Save the original Python distribution. 2 | -------------------------------------------------------------------------------- /flowpython/py35/.gitignore: -------------------------------------------------------------------------------- 1 | windows-32bit/ 2 | windows-64bit/ 3 | linux-64bit/ 4 | -------------------------------------------------------------------------------- /flowpython/py35/Grammar/Grammar: -------------------------------------------------------------------------------- 1 | # Grammar for Python 2 | 3 | # Note: Changing the grammar specified in this file will most likely 4 | # require corresponding changes in the parser module 5 | # (../Modules/parsermodule.c). If you can't make the changes to 6 | # that module yourself, please co-ordinate the required changes 7 | # with someone who can; ask around on python-dev for help. Fred 8 | # Drake will probably be listening there. 9 | 10 | # NOTE WELL: You should also follow all the steps listed at 11 | # https://docs.python.org/devguide/grammar.html 12 | 13 | # Start symbols for the grammar: 14 | # single_input is a single interactive statement; 15 | # file_input is a module or sequence of commands read from an input file; 16 | # eval_input is the input for the eval() functions. 17 | # NB: compound_stmt in single_input is followed by extra NEWLINE! 18 | single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE 19 | file_input: (NEWLINE | stmt)* ENDMARKER 20 | eval_input: testlist NEWLINE* ENDMARKER 21 | 22 | decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE 23 | decorators: decorator+ 24 | decorated: decorators (classdef | funcdef | async_funcdef) 25 | 26 | async_funcdef: ASYNC funcdef 27 | funcdef: 'def' NAME parameters ['->' test] ':' suite 28 | 29 | parameters: '(' [typedargslist] ')' 30 | typedargslist: (tfpdef ['=' test] (',' tfpdef ['=' test])* [',' 31 | ['*' [tfpdef] (',' tfpdef ['=' test])* [',' '**' tfpdef] | '**' tfpdef]] 32 | | '*' [tfpdef] (',' tfpdef ['=' test])* [',' '**' tfpdef] | '**' tfpdef) 33 | tfpdef: NAME [':' test] 34 | varargslist: (vfpdef ['=' test] (',' vfpdef ['=' test])* [',' 35 | ['*' [vfpdef] (',' vfpdef ['=' test])* [',' '**' vfpdef] | '**' vfpdef]] 36 | | '*' [vfpdef] (',' vfpdef ['=' test])* [',' '**' vfpdef] | '**' vfpdef) 37 | vfpdef: NAME 38 | 39 | stmt: simple_stmt | compound_stmt 40 | simple_stmt: small_stmt ((';' small_stmt)* [';'] NEWLINE | where_handler_stmt) 41 | small_stmt: (expr_stmt | del_stmt | pass_stmt | flow_stmt | 42 | import_stmt | global_stmt | nonlocal_stmt | assert_stmt) 43 | expr_stmt: testlist_star_expr (augassign (yield_expr|testlist) | 44 | ('=' (yield_expr|testlist_star_expr))*) 45 | testlist_star_expr: (test|star_expr) (',' (test|star_expr))* [','] 46 | augassign: ('+=' | '-=' | '*=' | '@=' | '/=' | '%=' | '&=' | '|=' | '^=' | 47 | '<<=' | '>>=' | '**=' | '//=') 48 | # For normal assignments, additional restrictions enforced by the interpreter 49 | del_stmt: 'del' exprlist 50 | pass_stmt: 'pass' 51 | flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt | yield_stmt 52 | break_stmt: 'break' 53 | continue_stmt: 'continue' 54 | return_stmt: 'return' [testlist] 55 | yield_stmt: yield_expr 56 | raise_stmt: 'raise' [test ['from' test]] 57 | import_stmt: import_name | import_from 58 | import_name: 'import' dotted_as_names 59 | # note below: the ('.' | '...') is necessary because '...' is tokenized as ELLIPSIS 60 | import_from: ('from' (('.' | '...')* dotted_name | ('.' | '...')+) 61 | 'import' ('*' | '(' import_as_names ')' | import_as_names)) 62 | import_as_name: NAME ['as' NAME] 63 | dotted_as_name: dotted_name ['as' NAME] 64 | import_as_names: import_as_name (',' import_as_name)* [','] 65 | dotted_as_names: dotted_as_name (',' dotted_as_name)* 66 | dotted_name: NAME ('.' NAME)* 67 | global_stmt: 'global' NAME (',' NAME)* 68 | nonlocal_stmt: 'nonlocal' NAME (',' NAME)* 69 | assert_stmt: 'assert' test [',' test] 70 | 71 | compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated | async_stmt | switch_stmt | branch_stmt 72 | async_stmt: ASYNC (funcdef | with_stmt | for_stmt) 73 | if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite] 74 | switch_stmt: switch_head test ':' NEWLINE INDENT case_stmt+ DEDENT 75 | branch_stmt: ('|' test '=' '>' suite)+ 76 | while_stmt: 'while' test ':' suite ['else' ':' suite] 77 | for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite] 78 | try_stmt: ('try' ':' suite 79 | ((except_clause ':' suite)+ 80 | ['else' ':' suite] 81 | ['finally' ':' suite] | 82 | 'finally' ':' suite)) 83 | with_stmt: 'with' with_item (',' with_item)* ':' suite 84 | with_item: test ['as' expr] 85 | # NB compile.c makes sure that the default except clause is last 86 | except_clause: 'except' [test ['as' NAME]] 87 | suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT 88 | 89 | test: or_test ['if' or_test 'else' test | '->' ( '>' pipeline_expr | test) ] | lambdef 90 | test_nocond: or_test | lambdef_nocond 91 | lambdef: 'lambda' [varargslist] ':' test | lambdef_head [typedargslist] 'def' test | '.' [typedargslist] '->' test 92 | lambdef_nocond: 'lambda' [varargslist] ':' test_nocond | lambdef_head [typedargslist] 'def' test_nocond | '.' [typedargslist] '->' test_nocond 93 | or_test: and_test ('or' and_test)* 94 | and_test: not_test ('and' not_test)* 95 | not_test: 'not' not_test | comparison 96 | comparison: expr (comp_op expr)* 97 | # <> isn't actually a valid comparison operator in Python. It's here for the 98 | # sake of a __future__ import described in PEP 401 (which really works :-) 99 | comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not' 100 | star_expr: '*' expr 101 | expr: xor_expr ('|' xor_expr)* 102 | xor_expr: and_expr ('^' and_expr)* 103 | and_expr: shift_expr ('&' shift_expr)* 104 | shift_expr: arith_expr (('<<'|'>>') arith_expr)* 105 | arith_expr: term (('+'|'-') term)* 106 | term: factor (('*'|'@'|'/'|'%'|'//') factor)* 107 | factor: ('+'|'-'|'~') factor | power 108 | power: atom_expr ['**' factor] 109 | atom_expr: [AWAIT] atom trailer* 110 | atom: ('(' [yield_expr|testlist_comp] ')' | 111 | '[' [testlist_comp] ']' | 112 | '{' [dictorsetmaker] '}' | 113 | NAME | NUMBER | STRING+ | '...' | 'None' | 'True' | 'False') 114 | testlist_comp: (test|star_expr) ( comp_for | (',' (test|star_expr))* [','] ) 115 | trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME 116 | subscriptlist: subscript (',' subscript)* [','] 117 | subscript: test | [test] ':' [test] [sliceop] 118 | sliceop: ':' [test] 119 | exprlist: (expr|star_expr) (',' (expr|star_expr))* [','] 120 | testlist: test (',' test)* [','] 121 | dictorsetmaker: ( ((test ':' test | '**' expr) 122 | (comp_for | (',' (test ':' test | '**' expr))* [','])) | 123 | ((test | star_expr) 124 | (comp_for | (',' (test | star_expr))* [','])) ) 125 | 126 | classdef: 'class' NAME ['(' [arglist] ')'] ':' suite 127 | 128 | arglist: argument (',' argument)* [','] 129 | 130 | # The reason that keywords are test nodes instead of NAME is that using NAME 131 | # results in an ambiguity. ast.c makes sure it's a NAME. 132 | # "test '=' test" is really "keyword '=' test", but we have no such token. 133 | # These need to be in a single rule to avoid grammar that is ambiguous 134 | # to our LL(1) parser. Even though 'test' includes '*expr' in star_expr, 135 | # we explicitly match '*' here, too, to give it proper precedence. 136 | # Illegal combinations and orderings are blocked in ast.c: 137 | # multiple (test comp_for) arguments are blocked; keyword unpackings 138 | # that precede iterable unpackings are blocked; etc. 139 | argument: ( test [comp_for] | 140 | test '=' test | 141 | '**' test | 142 | '*' test ) 143 | 144 | comp_iter: comp_for | comp_if 145 | comp_for: 'for' exprlist 'in' or_test [comp_iter] 146 | comp_if: 'if' test_nocond [comp_iter] 147 | 148 | # not used in grammar, but may appear in "node" passed from Parser to Compiler 149 | encoding_decl: NAME 150 | 151 | yield_expr: 'yield' [yield_arg] 152 | yield_arg: 'from' test | testlist 153 | 154 | where_handler_stmt: NAME ':' suite 155 | lambdef_head: 'as' ['-' 'with'] 156 | 157 | case_stmt: case_head [test [':' test]] '=' '>' suite 158 | mode_stmt: '[' [comp_op] ']' | '(' [test] ')' | '{' [test] '}' | '+' ('[' [comp_op] ']' | '(' [test] ')') 159 | switch_head : ('condic' | 'condef') [mode_stmt] 160 | case_head : [mode_stmt [NEWLINE] ] NAME 161 | pipeline_expr : test ( '=' '>' test)* -------------------------------------------------------------------------------- /flowpython/py35/Parser/Python.asdl: -------------------------------------------------------------------------------- 1 | -- ASDL's six builtin types are identifier, int, string, bytes, object, singleton 2 | 3 | module Python 4 | { 5 | mod = Module(stmt* body) 6 | | Interactive(stmt* body) 7 | | Expression(expr body) 8 | 9 | -- not really an actual node but useful in Jython's typesystem. 10 | | Suite(stmt* body) 11 | 12 | stmt = FunctionDef(identifier name, arguments args, 13 | stmt* body, expr* decorator_list, expr? returns) 14 | | AsyncFunctionDef(identifier name, arguments args, 15 | stmt* body, expr* decorator_list, expr? returns) 16 | 17 | | ClassDef(identifier name, 18 | expr* bases, 19 | keyword* keywords, 20 | stmt* body, 21 | expr* decorator_list) 22 | | Return(expr? value) 23 | 24 | | Delete(expr* targets) 25 | | Assign(expr* targets, expr value) 26 | | AugAssign(expr target, operator op, expr value) 27 | 28 | -- use 'orelse' because else is a keyword in target languages 29 | | For(expr target, expr iter, stmt* body, stmt* orelse) 30 | | AsyncFor(expr target, expr iter, stmt* body, stmt* orelse) 31 | | While(expr test, stmt* body, stmt* orelse) 32 | | If(expr test, stmt* body, stmt* orelse) 33 | | With(withitem* items, stmt* body) 34 | | AsyncWith(withitem* items, stmt* body) 35 | 36 | | Raise(expr? exc, expr? cause) 37 | | Try(stmt* body, excepthandler* handlers, stmt* orelse, stmt* finalbody) 38 | | Assert(expr test, expr? msg) 39 | 40 | | Import(alias* names) 41 | | ImportFrom(identifier? module, alias* names, int? level) 42 | 43 | | Global(identifier* names) 44 | | Nonlocal(identifier* names) 45 | | Expr(expr value) 46 | | Pass | Break | Continue 47 | 48 | -- XXX Jython will be different 49 | -- col_offset is the byte offset in the utf8 string the parser uses 50 | attributes (int lineno, int col_offset) 51 | 52 | -- BoolOp() can use left & right? 53 | expr = BoolOp(boolop op, expr* values) 54 | | BinOp(expr left, operator op, expr right) 55 | | UnaryOp(unaryop op, expr operand) 56 | | Lambda(arguments args, expr body) 57 | | IfExp(expr test, expr body, expr orelse) 58 | | Dict(expr* keys, expr* values) 59 | | Set(expr* elts) 60 | | ListComp(expr elt, comprehension* generators) 61 | | SetComp(expr elt, comprehension* generators) 62 | | DictComp(expr key, expr value, comprehension* generators) 63 | | GeneratorExp(expr elt, comprehension* generators) 64 | -- the grammar constrains where yield expressions can occur 65 | | Await(expr value) 66 | | Yield(expr? value) 67 | | YieldFrom(expr value) 68 | -- need sequences for compare to distinguish between 69 | -- x < 4 < 3 and (x < 4) < 3 70 | | Compare(expr left, cmpop* ops, expr* comparators) 71 | | Call(expr func, expr* args, keyword* keywords) 72 | | Num(object n) -- a number as a PyObject. 73 | | Str(string s) -- need to specify raw, unicode, etc? 74 | | Bytes(bytes s) 75 | | NameConstant(singleton value) 76 | | Ellipsis 77 | 78 | -- the following expression can appear in assignment context 79 | | Attribute(expr value, identifier attr, expr_context ctx) 80 | | Subscript(expr value, slice slice, expr_context ctx) 81 | | Starred(expr value, expr_context ctx) 82 | | Name(identifier id, expr_context ctx) 83 | | List(expr* elts, expr_context ctx) 84 | | Tuple(expr* elts, expr_context ctx) 85 | | Where(expr target, stmt* body) 86 | 87 | -- col_offset is the byte offset in the utf8 string the parser uses 88 | attributes (int lineno, int col_offset) 89 | 90 | expr_context = Load | Store | Del | AugLoad | AugStore | Param 91 | 92 | slice = Slice(expr? lower, expr? upper, expr? step) 93 | | ExtSlice(slice* dims) 94 | | Index(expr value) 95 | 96 | boolop = And | Or 97 | 98 | operator = Add | Sub | Mult | MatMult | Div | Mod | Pow | LShift 99 | | RShift | BitOr | BitXor | BitAnd | FloorDiv 100 | 101 | unaryop = Invert | Not | UAdd | USub 102 | 103 | cmpop = Eq | NotEq | Lt | LtE | Gt | GtE | Is | IsNot | In | NotIn 104 | 105 | comprehension = (expr target, expr iter, expr* ifs) 106 | 107 | excepthandler = ExceptHandler(expr? type, identifier? name, stmt* body) 108 | attributes (int lineno, int col_offset) 109 | 110 | arguments = (arg* args, arg? vararg, arg* kwonlyargs, expr* kw_defaults, 111 | arg? kwarg, expr* defaults) 112 | 113 | arg = (identifier arg, expr? annotation) 114 | attributes (int lineno, int col_offset) 115 | 116 | -- keyword arguments supplied to call (NULL identifier for **kwargs) 117 | keyword = (identifier? arg, expr value) 118 | 119 | -- import name with optional 'as' alias. 120 | alias = (identifier name, identifier? asname) 121 | 122 | withitem = (expr context_expr, expr? optional_vars) 123 | } 124 | 125 | -------------------------------------------------------------------------------- /flowpython/py35/Python/symtable.c: -------------------------------------------------------------------------------- 1 | #include "Python.h" 2 | #include "Python-ast.h" 3 | #include "code.h" 4 | #include "symtable.h" 5 | #include "structmember.h" 6 | 7 | /* error strings used for warnings */ 8 | #define GLOBAL_AFTER_ASSIGN \ 9 | "name '%.400s' is assigned to before global declaration" 10 | 11 | #define NONLOCAL_AFTER_ASSIGN \ 12 | "name '%.400s' is assigned to before nonlocal declaration" 13 | 14 | #define GLOBAL_AFTER_USE \ 15 | "name '%.400s' is used prior to global declaration" 16 | 17 | #define NONLOCAL_AFTER_USE \ 18 | "name '%.400s' is used prior to nonlocal declaration" 19 | 20 | #define IMPORT_STAR_WARNING "import * only allowed at module level" 21 | 22 | static PySTEntryObject * 23 | ste_new(struct symtable *st, identifier name, _Py_block_ty block, 24 | void *key, int lineno, int col_offset) 25 | { 26 | PySTEntryObject *ste = NULL; 27 | PyObject *k = NULL; 28 | 29 | k = PyLong_FromVoidPtr(key); 30 | if (k == NULL) 31 | goto fail; 32 | ste = PyObject_New(PySTEntryObject, &PySTEntry_Type); 33 | if (ste == NULL) { 34 | Py_DECREF(k); 35 | goto fail; 36 | } 37 | ste->ste_table = st; 38 | ste->ste_id = k; /* ste owns reference to k */ 39 | 40 | Py_INCREF(name); 41 | ste->ste_name = name; 42 | 43 | ste->ste_symbols = NULL; 44 | ste->ste_varnames = NULL; 45 | ste->ste_children = NULL; 46 | 47 | ste->ste_directives = NULL; 48 | 49 | ste->ste_type = block; 50 | ste->ste_nested = 0; 51 | ste->ste_free = 0; 52 | ste->ste_varargs = 0; 53 | ste->ste_varkeywords = 0; 54 | ste->ste_opt_lineno = 0; 55 | ste->ste_opt_col_offset = 0; 56 | ste->ste_tmpname = 0; 57 | ste->ste_lineno = lineno; 58 | ste->ste_col_offset = col_offset; 59 | 60 | if (st->st_cur != NULL && 61 | (st->st_cur->ste_nested || 62 | st->st_cur->ste_type == FunctionBlock)) 63 | ste->ste_nested = 1; 64 | ste->ste_child_free = 0; 65 | ste->ste_generator = 0; 66 | ste->ste_returns_value = 0; 67 | ste->ste_needs_class_closure = 0; 68 | 69 | ste->ste_symbols = PyDict_New(); 70 | ste->ste_varnames = PyList_New(0); 71 | ste->ste_children = PyList_New(0); 72 | if (ste->ste_symbols == NULL 73 | || ste->ste_varnames == NULL 74 | || ste->ste_children == NULL) 75 | goto fail; 76 | 77 | if (PyDict_SetItem(st->st_blocks, ste->ste_id, (PyObject *)ste) < 0) 78 | goto fail; 79 | 80 | return ste; 81 | fail: 82 | Py_XDECREF(ste); 83 | return NULL; 84 | } 85 | 86 | static PyObject * 87 | ste_repr(PySTEntryObject *ste) 88 | { 89 | return PyUnicode_FromFormat("", 90 | ste->ste_name, 91 | PyLong_AS_LONG(ste->ste_id), ste->ste_lineno); 92 | } 93 | 94 | static void 95 | ste_dealloc(PySTEntryObject *ste) 96 | { 97 | ste->ste_table = NULL; 98 | Py_XDECREF(ste->ste_id); 99 | Py_XDECREF(ste->ste_name); 100 | Py_XDECREF(ste->ste_symbols); 101 | Py_XDECREF(ste->ste_varnames); 102 | Py_XDECREF(ste->ste_children); 103 | Py_XDECREF(ste->ste_directives); 104 | PyObject_Del(ste); 105 | } 106 | 107 | #define OFF(x) offsetof(PySTEntryObject, x) 108 | 109 | static PyMemberDef ste_memberlist[] = { 110 | {"id", T_OBJECT, OFF(ste_id), READONLY}, 111 | {"name", T_OBJECT, OFF(ste_name), READONLY}, 112 | {"symbols", T_OBJECT, OFF(ste_symbols), READONLY}, 113 | {"varnames", T_OBJECT, OFF(ste_varnames), READONLY}, 114 | {"children", T_OBJECT, OFF(ste_children), READONLY}, 115 | {"nested", T_INT, OFF(ste_nested), READONLY}, 116 | {"type", T_INT, OFF(ste_type), READONLY}, 117 | {"lineno", T_INT, OFF(ste_lineno), READONLY}, 118 | {NULL} 119 | }; 120 | 121 | PyTypeObject PySTEntry_Type = { 122 | PyVarObject_HEAD_INIT(&PyType_Type, 0) 123 | "symtable entry", 124 | sizeof(PySTEntryObject), 125 | 0, 126 | (destructor)ste_dealloc, /* tp_dealloc */ 127 | 0, /* tp_print */ 128 | 0, /* tp_getattr */ 129 | 0, /* tp_setattr */ 130 | 0, /* tp_reserved */ 131 | (reprfunc)ste_repr, /* tp_repr */ 132 | 0, /* tp_as_number */ 133 | 0, /* tp_as_sequence */ 134 | 0, /* tp_as_mapping */ 135 | 0, /* tp_hash */ 136 | 0, /* tp_call */ 137 | 0, /* tp_str */ 138 | PyObject_GenericGetAttr, /* tp_getattro */ 139 | 0, /* tp_setattro */ 140 | 0, /* tp_as_buffer */ 141 | Py_TPFLAGS_DEFAULT, /* tp_flags */ 142 | 0, /* tp_doc */ 143 | 0, /* tp_traverse */ 144 | 0, /* tp_clear */ 145 | 0, /* tp_richcompare */ 146 | 0, /* tp_weaklistoffset */ 147 | 0, /* tp_iter */ 148 | 0, /* tp_iternext */ 149 | 0, /* tp_methods */ 150 | ste_memberlist, /* tp_members */ 151 | 0, /* tp_getset */ 152 | 0, /* tp_base */ 153 | 0, /* tp_dict */ 154 | 0, /* tp_descr_get */ 155 | 0, /* tp_descr_set */ 156 | 0, /* tp_dictoffset */ 157 | 0, /* tp_init */ 158 | 0, /* tp_alloc */ 159 | 0, /* tp_new */ 160 | }; 161 | 162 | static int symtable_analyze(struct symtable *st); 163 | static int symtable_warn(struct symtable *st, char *msg, int lineno); 164 | static int symtable_enter_block(struct symtable *st, identifier name, 165 | _Py_block_ty block, void *ast, int lineno, 166 | int col_offset); 167 | static int symtable_exit_block(struct symtable *st, void *ast); 168 | static int symtable_visit_stmt(struct symtable *st, stmt_ty s); 169 | static int symtable_visit_expr(struct symtable *st, expr_ty s); 170 | static int symtable_visit_genexp(struct symtable *st, expr_ty s); 171 | static int symtable_visit_listcomp(struct symtable *st, expr_ty s); 172 | static int symtable_visit_setcomp(struct symtable *st, expr_ty s); 173 | static int symtable_visit_dictcomp(struct symtable *st, expr_ty s); 174 | static int symtable_visit_arguments(struct symtable *st, arguments_ty); 175 | static int symtable_visit_excepthandler(struct symtable *st, excepthandler_ty); 176 | static int symtable_visit_alias(struct symtable *st, alias_ty); 177 | static int symtable_visit_comprehension(struct symtable *st, comprehension_ty); 178 | static int symtable_visit_keyword(struct symtable *st, keyword_ty); 179 | static int symtable_visit_slice(struct symtable *st, slice_ty); 180 | static int symtable_visit_params(struct symtable *st, asdl_seq *args); 181 | static int symtable_visit_argannotations(struct symtable *st, asdl_seq *args); 182 | static int symtable_implicit_arg(struct symtable *st, int pos); 183 | static int symtable_visit_annotations(struct symtable *st, stmt_ty s, arguments_ty, expr_ty); 184 | static int symtable_visit_withitem(struct symtable *st, withitem_ty item); 185 | 186 | 187 | static identifier top = NULL, lambda = NULL, genexpr = NULL, 188 | listcomp = NULL, setcomp = NULL, dictcomp = NULL, 189 | __class__ = NULL; 190 | 191 | #define GET_IDENTIFIER(VAR) \ 192 | ((VAR) ? (VAR) : ((VAR) = PyUnicode_InternFromString(# VAR))) 193 | 194 | #define DUPLICATE_ARGUMENT \ 195 | "duplicate argument '%U' in function definition" 196 | 197 | static struct symtable * 198 | symtable_new(void) 199 | { 200 | struct symtable *st; 201 | 202 | st = (struct symtable *)PyMem_Malloc(sizeof(struct symtable)); 203 | if (st == NULL) 204 | return NULL; 205 | 206 | st->st_filename = NULL; 207 | st->st_blocks = NULL; 208 | 209 | if ((st->st_stack = PyList_New(0)) == NULL) 210 | goto fail; 211 | if ((st->st_blocks = PyDict_New()) == NULL) 212 | goto fail; 213 | st->st_cur = NULL; 214 | st->st_private = NULL; 215 | return st; 216 | fail: 217 | PySymtable_Free(st); 218 | return NULL; 219 | } 220 | 221 | /* When compiling the use of C stack is probably going to be a lot 222 | lighter than when executing Python code but still can overflow 223 | and causing a Python crash if not checked (e.g. eval("()"*300000)). 224 | Using the current recursion limit for the compiler seems too 225 | restrictive (it caused at least one test to fail) so a factor is 226 | used to allow deeper recursion when compiling an expression. 227 | 228 | Using a scaling factor means this should automatically adjust when 229 | the recursion limit is adjusted for small or large C stack allocations. 230 | */ 231 | #define COMPILER_STACK_FRAME_SCALE 3 232 | 233 | struct symtable * 234 | PySymtable_BuildObject(mod_ty mod, PyObject *filename, PyFutureFeatures *future) 235 | { 236 | struct symtable *st = symtable_new(); 237 | asdl_seq *seq; 238 | int i; 239 | PyThreadState *tstate; 240 | int recursion_limit = Py_GetRecursionLimit(); 241 | 242 | if (st == NULL) 243 | return NULL; 244 | if (filename == NULL) { 245 | PySymtable_Free(st); 246 | return NULL; 247 | } 248 | Py_INCREF(filename); 249 | st->st_filename = filename; 250 | st->st_future = future; 251 | 252 | /* Setup recursion depth check counters */ 253 | tstate = PyThreadState_GET(); 254 | if (!tstate) { 255 | PySymtable_Free(st); 256 | return NULL; 257 | } 258 | /* Be careful here to prevent overflow. */ 259 | st->recursion_depth = (tstate->recursion_depth < INT_MAX / COMPILER_STACK_FRAME_SCALE) ? 260 | tstate->recursion_depth * COMPILER_STACK_FRAME_SCALE : tstate->recursion_depth; 261 | st->recursion_limit = (recursion_limit < INT_MAX / COMPILER_STACK_FRAME_SCALE) ? 262 | recursion_limit * COMPILER_STACK_FRAME_SCALE : recursion_limit; 263 | 264 | /* Make the initial symbol information gathering pass */ 265 | if (!GET_IDENTIFIER(top) || 266 | !symtable_enter_block(st, top, ModuleBlock, (void *)mod, 0, 0)) { 267 | PySymtable_Free(st); 268 | return NULL; 269 | } 270 | 271 | st->st_top = st->st_cur; 272 | switch (mod->kind) { 273 | case Module_kind: 274 | seq = mod->v.Module.body; 275 | for (i = 0; i < asdl_seq_LEN(seq); i++) 276 | if (!symtable_visit_stmt(st, 277 | (stmt_ty)asdl_seq_GET(seq, i))) 278 | goto error; 279 | break; 280 | case Expression_kind: 281 | if (!symtable_visit_expr(st, mod->v.Expression.body)) 282 | goto error; 283 | break; 284 | case Interactive_kind: 285 | seq = mod->v.Interactive.body; 286 | for (i = 0; i < asdl_seq_LEN(seq); i++) 287 | if (!symtable_visit_stmt(st, 288 | (stmt_ty)asdl_seq_GET(seq, i))) 289 | goto error; 290 | break; 291 | case Suite_kind: 292 | PyErr_SetString(PyExc_RuntimeError, 293 | "this compiler does not handle Suites"); 294 | goto error; 295 | } 296 | if (!symtable_exit_block(st, (void *)mod)) { 297 | PySymtable_Free(st); 298 | return NULL; 299 | } 300 | /* Make the second symbol analysis pass */ 301 | if (symtable_analyze(st)) 302 | return st; 303 | PySymtable_Free(st); 304 | return NULL; 305 | error: 306 | (void) symtable_exit_block(st, (void *)mod); 307 | PySymtable_Free(st); 308 | return NULL; 309 | } 310 | 311 | struct symtable * 312 | PySymtable_Build(mod_ty mod, const char *filename_str, PyFutureFeatures *future) 313 | { 314 | PyObject *filename; 315 | struct symtable *st; 316 | filename = PyUnicode_DecodeFSDefault(filename_str); 317 | if (filename == NULL) 318 | return NULL; 319 | st = PySymtable_BuildObject(mod, filename, future); 320 | Py_DECREF(filename); 321 | return st; 322 | } 323 | 324 | void 325 | PySymtable_Free(struct symtable *st) 326 | { 327 | Py_XDECREF(st->st_filename); 328 | Py_XDECREF(st->st_blocks); 329 | Py_XDECREF(st->st_stack); 330 | PyMem_Free((void *)st); 331 | } 332 | 333 | PySTEntryObject * 334 | PySymtable_Lookup(struct symtable *st, void *key) 335 | { 336 | PyObject *k, *v; 337 | 338 | k = PyLong_FromVoidPtr(key); 339 | if (k == NULL) 340 | return NULL; 341 | v = PyDict_GetItem(st->st_blocks, k); 342 | if (v) { 343 | assert(PySTEntry_Check(v)); 344 | Py_INCREF(v); 345 | } 346 | else { 347 | PyErr_SetString(PyExc_KeyError, 348 | "unknown symbol table entry"); 349 | } 350 | 351 | Py_DECREF(k); 352 | return (PySTEntryObject *)v; 353 | } 354 | 355 | int 356 | PyST_GetScope(PySTEntryObject *ste, PyObject *name) 357 | { 358 | PyObject *v = PyDict_GetItem(ste->ste_symbols, name); 359 | if (!v) 360 | return 0; 361 | assert(PyLong_Check(v)); 362 | return (PyLong_AS_LONG(v) >> SCOPE_OFFSET) & SCOPE_MASK; 363 | } 364 | 365 | static int 366 | error_at_directive(PySTEntryObject *ste, PyObject *name) 367 | { 368 | Py_ssize_t i; 369 | PyObject *data; 370 | assert(ste->ste_directives); 371 | for (i = 0; i < PyList_GET_SIZE(ste->ste_directives); i++) { 372 | data = PyList_GET_ITEM(ste->ste_directives, i); 373 | assert(PyTuple_CheckExact(data)); 374 | assert(PyUnicode_CheckExact(PyTuple_GET_ITEM(data, 0))); 375 | if (PyUnicode_Compare(PyTuple_GET_ITEM(data, 0), name) == 0) { 376 | PyErr_SyntaxLocationObject(ste->ste_table->st_filename, 377 | PyLong_AsLong(PyTuple_GET_ITEM(data, 1)), 378 | PyLong_AsLong(PyTuple_GET_ITEM(data, 2))); 379 | 380 | return 0; 381 | } 382 | } 383 | PyErr_SetString(PyExc_RuntimeError, 384 | "BUG: internal directive bookkeeping broken"); 385 | return 0; 386 | } 387 | 388 | 389 | /* Analyze raw symbol information to determine scope of each name. 390 | 391 | The next several functions are helpers for symtable_analyze(), 392 | which determines whether a name is local, global, or free. In addition, 393 | it determines which local variables are cell variables; they provide 394 | bindings that are used for free variables in enclosed blocks. 395 | 396 | There are also two kinds of global variables, implicit and explicit. An 397 | explicit global is declared with the global statement. An implicit 398 | global is a free variable for which the compiler has found no binding 399 | in an enclosing function scope. The implicit global is either a global 400 | or a builtin. Python's module and class blocks use the xxx_NAME opcodes 401 | to handle these names to implement slightly odd semantics. In such a 402 | block, the name is treated as global until it is assigned to; then it 403 | is treated as a local. 404 | 405 | The symbol table requires two passes to determine the scope of each name. 406 | The first pass collects raw facts from the AST via the symtable_visit_* 407 | functions: the name is a parameter here, the name is used but not defined 408 | here, etc. The second pass analyzes these facts during a pass over the 409 | PySTEntryObjects created during pass 1. 410 | 411 | When a function is entered during the second pass, the parent passes 412 | the set of all name bindings visible to its children. These bindings 413 | are used to determine if non-local variables are free or implicit globals. 414 | Names which are explicitly declared nonlocal must exist in this set of 415 | visible names - if they do not, a syntax error is raised. After doing 416 | the local analysis, it analyzes each of its child blocks using an 417 | updated set of name bindings. 418 | 419 | The children update the free variable set. If a local variable is added to 420 | the free variable set by the child, the variable is marked as a cell. The 421 | function object being defined must provide runtime storage for the variable 422 | that may outlive the function's frame. Cell variables are removed from the 423 | free set before the analyze function returns to its parent. 424 | 425 | During analysis, the names are: 426 | symbols: dict mapping from symbol names to flag values (including offset scope values) 427 | scopes: dict mapping from symbol names to scope values (no offset) 428 | local: set of all symbol names local to the current scope 429 | bound: set of all symbol names local to a containing function scope 430 | free: set of all symbol names referenced but not bound in child scopes 431 | global: set of all symbol names explicitly declared as global 432 | */ 433 | 434 | #define SET_SCOPE(DICT, NAME, I) { \ 435 | PyObject *o = PyLong_FromLong(I); \ 436 | if (!o) \ 437 | return 0; \ 438 | if (PyDict_SetItem((DICT), (NAME), o) < 0) { \ 439 | Py_DECREF(o); \ 440 | return 0; \ 441 | } \ 442 | Py_DECREF(o); \ 443 | } 444 | 445 | /* Decide on scope of name, given flags. 446 | 447 | The namespace dictionaries may be modified to record information 448 | about the new name. For example, a new global will add an entry to 449 | global. A name that was global can be changed to local. 450 | */ 451 | 452 | static int 453 | analyze_name(PySTEntryObject *ste, PyObject *scopes, PyObject *name, long flags, 454 | PyObject *bound, PyObject *local, PyObject *free, 455 | PyObject *global) 456 | { 457 | if (flags & DEF_GLOBAL) { 458 | if (flags & DEF_PARAM) { 459 | PyErr_Format(PyExc_SyntaxError, 460 | "name '%U' is parameter and global", 461 | name); 462 | return error_at_directive(ste, name); 463 | } 464 | if (flags & DEF_NONLOCAL) { 465 | PyErr_Format(PyExc_SyntaxError, 466 | "name '%U' is nonlocal and global", 467 | name); 468 | return error_at_directive(ste, name); 469 | } 470 | SET_SCOPE(scopes, name, GLOBAL_EXPLICIT); 471 | if (PySet_Add(global, name) < 0) 472 | return 0; 473 | if (bound && (PySet_Discard(bound, name) < 0)) 474 | return 0; 475 | return 1; 476 | } 477 | if (flags & DEF_NONLOCAL) { 478 | if (flags & DEF_PARAM) { 479 | PyErr_Format(PyExc_SyntaxError, 480 | "name '%U' is parameter and nonlocal", 481 | name); 482 | return error_at_directive(ste, name); 483 | } 484 | if (!bound) { 485 | PyErr_Format(PyExc_SyntaxError, 486 | "nonlocal declaration not allowed at module level"); 487 | return error_at_directive(ste, name); 488 | } 489 | if (!PySet_Contains(bound, name)) { 490 | PyErr_Format(PyExc_SyntaxError, 491 | "no binding for nonlocal '%U' found", 492 | name); 493 | 494 | return error_at_directive(ste, name); 495 | } 496 | SET_SCOPE(scopes, name, FREE); 497 | ste->ste_free = 1; 498 | return PySet_Add(free, name) >= 0; 499 | } 500 | if (flags & DEF_BOUND) { 501 | SET_SCOPE(scopes, name, LOCAL); 502 | if (PySet_Add(local, name) < 0) 503 | return 0; 504 | if (PySet_Discard(global, name) < 0) 505 | return 0; 506 | return 1; 507 | } 508 | /* If an enclosing block has a binding for this name, it 509 | is a free variable rather than a global variable. 510 | Note that having a non-NULL bound implies that the block 511 | is nested. 512 | */ 513 | if (bound && PySet_Contains(bound, name)) { 514 | SET_SCOPE(scopes, name, FREE); 515 | ste->ste_free = 1; 516 | return PySet_Add(free, name) >= 0; 517 | } 518 | /* If a parent has a global statement, then call it global 519 | explicit? It could also be global implicit. 520 | */ 521 | if (global && PySet_Contains(global, name)) { 522 | SET_SCOPE(scopes, name, GLOBAL_IMPLICIT); 523 | return 1; 524 | } 525 | if (ste->ste_nested) 526 | ste->ste_free = 1; 527 | SET_SCOPE(scopes, name, GLOBAL_IMPLICIT); 528 | return 1; 529 | } 530 | 531 | #undef SET_SCOPE 532 | 533 | /* If a name is defined in free and also in locals, then this block 534 | provides the binding for the free variable. The name should be 535 | marked CELL in this block and removed from the free list. 536 | 537 | Note that the current block's free variables are included in free. 538 | That's safe because no name can be free and local in the same scope. 539 | */ 540 | 541 | static int 542 | analyze_cells(PyObject *scopes, PyObject *free) 543 | { 544 | PyObject *name, *v, *v_cell; 545 | int success = 0; 546 | Py_ssize_t pos = 0; 547 | 548 | v_cell = PyLong_FromLong(CELL); 549 | if (!v_cell) 550 | return 0; 551 | while (PyDict_Next(scopes, &pos, &name, &v)) { 552 | long scope; 553 | assert(PyLong_Check(v)); 554 | scope = PyLong_AS_LONG(v); 555 | if (scope != LOCAL) 556 | continue; 557 | if (!PySet_Contains(free, name)) 558 | continue; 559 | /* Replace LOCAL with CELL for this name, and remove 560 | from free. It is safe to replace the value of name 561 | in the dict, because it will not cause a resize. 562 | */ 563 | if (PyDict_SetItem(scopes, name, v_cell) < 0) 564 | goto error; 565 | if (PySet_Discard(free, name) < 0) 566 | goto error; 567 | } 568 | success = 1; 569 | error: 570 | Py_DECREF(v_cell); 571 | return success; 572 | } 573 | 574 | static int 575 | drop_class_free(PySTEntryObject *ste, PyObject *free) 576 | { 577 | int res; 578 | if (!GET_IDENTIFIER(__class__)) 579 | return 0; 580 | res = PySet_Discard(free, __class__); 581 | if (res < 0) 582 | return 0; 583 | if (res) 584 | ste->ste_needs_class_closure = 1; 585 | return 1; 586 | } 587 | 588 | /* Enter the final scope information into the ste_symbols dict. 589 | * 590 | * All arguments are dicts. Modifies symbols, others are read-only. 591 | */ 592 | static int 593 | update_symbols(PyObject *symbols, PyObject *scopes, 594 | PyObject *bound, PyObject *free, int classflag) 595 | { 596 | PyObject *name = NULL, *itr = NULL; 597 | PyObject *v = NULL, *v_scope = NULL, *v_new = NULL, *v_free = NULL; 598 | Py_ssize_t pos = 0; 599 | 600 | /* Update scope information for all symbols in this scope */ 601 | while (PyDict_Next(symbols, &pos, &name, &v)) { 602 | long scope, flags; 603 | assert(PyLong_Check(v)); 604 | flags = PyLong_AS_LONG(v); 605 | v_scope = PyDict_GetItem(scopes, name); 606 | assert(v_scope && PyLong_Check(v_scope)); 607 | scope = PyLong_AS_LONG(v_scope); 608 | flags |= (scope << SCOPE_OFFSET); 609 | v_new = PyLong_FromLong(flags); 610 | if (!v_new) 611 | return 0; 612 | if (PyDict_SetItem(symbols, name, v_new) < 0) { 613 | Py_DECREF(v_new); 614 | return 0; 615 | } 616 | Py_DECREF(v_new); 617 | } 618 | 619 | /* Record not yet resolved free variables from children (if any) */ 620 | v_free = PyLong_FromLong(FREE << SCOPE_OFFSET); 621 | if (!v_free) 622 | return 0; 623 | 624 | itr = PyObject_GetIter(free); 625 | if (!itr) 626 | goto error; 627 | 628 | while ((name = PyIter_Next(itr))) { 629 | v = PyDict_GetItem(symbols, name); 630 | 631 | /* Handle symbol that already exists in this scope */ 632 | if (v) { 633 | /* Handle a free variable in a method of 634 | the class that has the same name as a local 635 | or global in the class scope. 636 | */ 637 | if (classflag && 638 | PyLong_AS_LONG(v) & (DEF_BOUND | DEF_GLOBAL)) { 639 | long flags = PyLong_AS_LONG(v) | DEF_FREE_CLASS; 640 | v_new = PyLong_FromLong(flags); 641 | if (!v_new) { 642 | goto error; 643 | } 644 | if (PyDict_SetItem(symbols, name, v_new) < 0) { 645 | Py_DECREF(v_new); 646 | goto error; 647 | } 648 | Py_DECREF(v_new); 649 | } 650 | /* It's a cell, or already free in this scope */ 651 | Py_DECREF(name); 652 | continue; 653 | } 654 | /* Handle global symbol */ 655 | if (!PySet_Contains(bound, name)) { 656 | Py_DECREF(name); 657 | continue; /* it's a global */ 658 | } 659 | /* Propagate new free symbol up the lexical stack */ 660 | if (PyDict_SetItem(symbols, name, v_free) < 0) { 661 | goto error; 662 | } 663 | Py_DECREF(name); 664 | } 665 | Py_DECREF(itr); 666 | Py_DECREF(v_free); 667 | return 1; 668 | error: 669 | Py_XDECREF(v_free); 670 | Py_XDECREF(itr); 671 | Py_XDECREF(name); 672 | return 0; 673 | } 674 | 675 | /* Make final symbol table decisions for block of ste. 676 | 677 | Arguments: 678 | ste -- current symtable entry (input/output) 679 | bound -- set of variables bound in enclosing scopes (input). bound 680 | is NULL for module blocks. 681 | free -- set of free variables in enclosed scopes (output) 682 | globals -- set of declared global variables in enclosing scopes (input) 683 | 684 | The implementation uses two mutually recursive functions, 685 | analyze_block() and analyze_child_block(). analyze_block() is 686 | responsible for analyzing the individual names defined in a block. 687 | analyze_child_block() prepares temporary namespace dictionaries 688 | used to evaluated nested blocks. 689 | 690 | The two functions exist because a child block should see the name 691 | bindings of its enclosing blocks, but those bindings should not 692 | propagate back to a parent block. 693 | */ 694 | 695 | static int 696 | analyze_child_block(PySTEntryObject *entry, PyObject *bound, PyObject *free, 697 | PyObject *global, PyObject* child_free); 698 | 699 | static int 700 | analyze_block(PySTEntryObject *ste, PyObject *bound, PyObject *free, 701 | PyObject *global) 702 | { 703 | PyObject *name, *v, *local = NULL, *scopes = NULL, *newbound = NULL; 704 | PyObject *newglobal = NULL, *newfree = NULL, *allfree = NULL; 705 | PyObject *temp; 706 | int i, success = 0; 707 | Py_ssize_t pos = 0; 708 | 709 | local = PySet_New(NULL); /* collect new names bound in block */ 710 | if (!local) 711 | goto error; 712 | scopes = PyDict_New(); /* collect scopes defined for each name */ 713 | if (!scopes) 714 | goto error; 715 | 716 | /* Allocate new global and bound variable dictionaries. These 717 | dictionaries hold the names visible in nested blocks. For 718 | ClassBlocks, the bound and global names are initialized 719 | before analyzing names, because class bindings aren't 720 | visible in methods. For other blocks, they are initialized 721 | after names are analyzed. 722 | */ 723 | 724 | /* TODO(jhylton): Package these dicts in a struct so that we 725 | can write reasonable helper functions? 726 | */ 727 | newglobal = PySet_New(NULL); 728 | if (!newglobal) 729 | goto error; 730 | newfree = PySet_New(NULL); 731 | if (!newfree) 732 | goto error; 733 | newbound = PySet_New(NULL); 734 | if (!newbound) 735 | goto error; 736 | 737 | /* Class namespace has no effect on names visible in 738 | nested functions, so populate the global and bound 739 | sets to be passed to child blocks before analyzing 740 | this one. 741 | */ 742 | if (ste->ste_type == ClassBlock) { 743 | /* Pass down known globals */ 744 | temp = PyNumber_InPlaceOr(newglobal, global); 745 | if (!temp) 746 | goto error; 747 | Py_DECREF(temp); 748 | /* Pass down previously bound symbols */ 749 | if (bound) { 750 | temp = PyNumber_InPlaceOr(newbound, bound); 751 | if (!temp) 752 | goto error; 753 | Py_DECREF(temp); 754 | } 755 | } 756 | 757 | while (PyDict_Next(ste->ste_symbols, &pos, &name, &v)) { 758 | long flags = PyLong_AS_LONG(v); 759 | if (!analyze_name(ste, scopes, name, flags, 760 | bound, local, free, global)) 761 | goto error; 762 | } 763 | 764 | /* Populate global and bound sets to be passed to children. */ 765 | if (ste->ste_type != ClassBlock) { 766 | /* Add function locals to bound set */ 767 | if (ste->ste_type == FunctionBlock) { 768 | temp = PyNumber_InPlaceOr(newbound, local); 769 | if (!temp) 770 | goto error; 771 | Py_DECREF(temp); 772 | } 773 | /* Pass down previously bound symbols */ 774 | if (bound) { 775 | temp = PyNumber_InPlaceOr(newbound, bound); 776 | if (!temp) 777 | goto error; 778 | Py_DECREF(temp); 779 | } 780 | /* Pass down known globals */ 781 | temp = PyNumber_InPlaceOr(newglobal, global); 782 | if (!temp) 783 | goto error; 784 | Py_DECREF(temp); 785 | } 786 | else { 787 | /* Special-case __class__ */ 788 | if (!GET_IDENTIFIER(__class__)) 789 | goto error; 790 | if (PySet_Add(newbound, __class__) < 0) 791 | goto error; 792 | } 793 | 794 | /* Recursively call analyze_child_block() on each child block. 795 | 796 | newbound, newglobal now contain the names visible in 797 | nested blocks. The free variables in the children will 798 | be collected in allfree. 799 | */ 800 | allfree = PySet_New(NULL); 801 | if (!allfree) 802 | goto error; 803 | for (i = 0; i < PyList_GET_SIZE(ste->ste_children); ++i) { 804 | PyObject *c = PyList_GET_ITEM(ste->ste_children, i); 805 | PySTEntryObject* entry; 806 | assert(c && PySTEntry_Check(c)); 807 | entry = (PySTEntryObject*)c; 808 | if (!analyze_child_block(entry, newbound, newfree, newglobal, 809 | allfree)) 810 | goto error; 811 | /* Check if any children have free variables */ 812 | if (entry->ste_free || entry->ste_child_free) 813 | ste->ste_child_free = 1; 814 | } 815 | 816 | temp = PyNumber_InPlaceOr(newfree, allfree); 817 | if (!temp) 818 | goto error; 819 | Py_DECREF(temp); 820 | 821 | /* Check if any local variables must be converted to cell variables */ 822 | if (ste->ste_type == FunctionBlock && !analyze_cells(scopes, newfree)) 823 | goto error; 824 | else if (ste->ste_type == ClassBlock && !drop_class_free(ste, newfree)) 825 | goto error; 826 | /* Records the results of the analysis in the symbol table entry */ 827 | if (!update_symbols(ste->ste_symbols, scopes, bound, newfree, 828 | ste->ste_type == ClassBlock)) 829 | goto error; 830 | 831 | temp = PyNumber_InPlaceOr(free, newfree); 832 | if (!temp) 833 | goto error; 834 | Py_DECREF(temp); 835 | success = 1; 836 | error: 837 | Py_XDECREF(scopes); 838 | Py_XDECREF(local); 839 | Py_XDECREF(newbound); 840 | Py_XDECREF(newglobal); 841 | Py_XDECREF(newfree); 842 | Py_XDECREF(allfree); 843 | if (!success) 844 | assert(PyErr_Occurred()); 845 | return success; 846 | } 847 | 848 | static int 849 | analyze_child_block(PySTEntryObject *entry, PyObject *bound, PyObject *free, 850 | PyObject *global, PyObject* child_free) 851 | { 852 | PyObject *temp_bound = NULL, *temp_global = NULL, *temp_free = NULL; 853 | PyObject *temp; 854 | 855 | /* Copy the bound and global dictionaries. 856 | 857 | These dictionaries are used by all blocks enclosed by the 858 | current block. The analyze_block() call modifies these 859 | dictionaries. 860 | 861 | */ 862 | temp_bound = PySet_New(bound); 863 | if (!temp_bound) 864 | goto error; 865 | temp_free = PySet_New(free); 866 | if (!temp_free) 867 | goto error; 868 | temp_global = PySet_New(global); 869 | if (!temp_global) 870 | goto error; 871 | 872 | if (!analyze_block(entry, temp_bound, temp_free, temp_global)) 873 | goto error; 874 | temp = PyNumber_InPlaceOr(child_free, temp_free); 875 | if (!temp) 876 | goto error; 877 | Py_DECREF(temp); 878 | Py_DECREF(temp_bound); 879 | Py_DECREF(temp_free); 880 | Py_DECREF(temp_global); 881 | return 1; 882 | error: 883 | Py_XDECREF(temp_bound); 884 | Py_XDECREF(temp_free); 885 | Py_XDECREF(temp_global); 886 | return 0; 887 | } 888 | 889 | static int 890 | symtable_analyze(struct symtable *st) 891 | { 892 | PyObject *free, *global; 893 | int r; 894 | 895 | free = PySet_New(NULL); 896 | if (!free) 897 | return 0; 898 | global = PySet_New(NULL); 899 | if (!global) { 900 | Py_DECREF(free); 901 | return 0; 902 | } 903 | r = analyze_block(st->st_top, NULL, free, global); 904 | Py_DECREF(free); 905 | Py_DECREF(global); 906 | return r; 907 | } 908 | 909 | 910 | static int 911 | symtable_warn(struct symtable *st, char *msg, int lineno) 912 | { 913 | PyObject *message = PyUnicode_FromString(msg); 914 | if (message == NULL) 915 | return 0; 916 | if (PyErr_WarnExplicitObject(PyExc_SyntaxWarning, message, st->st_filename, 917 | lineno, NULL, NULL) < 0) { 918 | Py_DECREF(message); 919 | if (PyErr_ExceptionMatches(PyExc_SyntaxWarning)) { 920 | PyErr_SetString(PyExc_SyntaxError, msg); 921 | PyErr_SyntaxLocationObject(st->st_filename, st->st_cur->ste_lineno, 922 | st->st_cur->ste_col_offset); 923 | } 924 | return 0; 925 | } 926 | Py_DECREF(message); 927 | return 1; 928 | } 929 | 930 | /* symtable_enter_block() gets a reference via ste_new. 931 | This reference is released when the block is exited, via the DECREF 932 | in symtable_exit_block(). 933 | */ 934 | 935 | static int 936 | symtable_exit_block(struct symtable *st, void *ast) 937 | { 938 | Py_ssize_t size; 939 | 940 | st->st_cur = NULL; 941 | size = PyList_GET_SIZE(st->st_stack); 942 | if (size) { 943 | if (PyList_SetSlice(st->st_stack, size - 1, size, NULL) < 0) 944 | return 0; 945 | if (--size) 946 | st->st_cur = (PySTEntryObject *)PyList_GET_ITEM(st->st_stack, size - 1); 947 | } 948 | return 1; 949 | } 950 | 951 | static int 952 | symtable_enter_block(struct symtable *st, identifier name, _Py_block_ty block, 953 | void *ast, int lineno, int col_offset) 954 | { 955 | PySTEntryObject *prev = NULL, *ste; 956 | 957 | ste = ste_new(st, name, block, ast, lineno, col_offset); 958 | if (ste == NULL) 959 | return 0; 960 | if (PyList_Append(st->st_stack, (PyObject *)ste) < 0) { 961 | Py_DECREF(ste); 962 | return 0; 963 | } 964 | prev = st->st_cur; 965 | /* The entry is owned by the stack. Borrow it for st_cur. */ 966 | Py_DECREF(ste); 967 | st->st_cur = ste; 968 | if (block == ModuleBlock) 969 | st->st_global = st->st_cur->ste_symbols; 970 | if (prev) { 971 | if (PyList_Append(prev->ste_children, (PyObject *)ste) < 0) { 972 | return 0; 973 | } 974 | } 975 | return 1; 976 | } 977 | 978 | static long 979 | symtable_lookup(struct symtable *st, PyObject *name) 980 | { 981 | PyObject *o; 982 | PyObject *mangled = _Py_Mangle(st->st_private, name); 983 | if (!mangled) 984 | return 0; 985 | o = PyDict_GetItem(st->st_cur->ste_symbols, mangled); 986 | Py_DECREF(mangled); 987 | if (!o) 988 | return 0; 989 | return PyLong_AsLong(o); 990 | } 991 | 992 | static int 993 | symtable_add_def(struct symtable *st, PyObject *name, int flag) 994 | { 995 | PyObject *o; 996 | PyObject *dict; 997 | long val; 998 | PyObject *mangled = _Py_Mangle(st->st_private, name); 999 | 1000 | 1001 | if (!mangled) 1002 | return 0; 1003 | dict = st->st_cur->ste_symbols; 1004 | if ((o = PyDict_GetItem(dict, mangled))) { 1005 | val = PyLong_AS_LONG(o); 1006 | if ((flag & DEF_PARAM) && (val & DEF_PARAM)) { 1007 | /* Is it better to use 'mangled' or 'name' here? */ 1008 | PyErr_Format(PyExc_SyntaxError, DUPLICATE_ARGUMENT, name); 1009 | PyErr_SyntaxLocationObject(st->st_filename, 1010 | st->st_cur->ste_lineno, 1011 | st->st_cur->ste_col_offset); 1012 | goto error; 1013 | } 1014 | val |= flag; 1015 | } else 1016 | val = flag; 1017 | o = PyLong_FromLong(val); 1018 | if (o == NULL) 1019 | goto error; 1020 | if (PyDict_SetItem(dict, mangled, o) < 0) { 1021 | Py_DECREF(o); 1022 | goto error; 1023 | } 1024 | Py_DECREF(o); 1025 | 1026 | if (flag & DEF_PARAM) { 1027 | if (PyList_Append(st->st_cur->ste_varnames, mangled) < 0) 1028 | goto error; 1029 | } else if (flag & DEF_GLOBAL) { 1030 | /* XXX need to update DEF_GLOBAL for other flags too; 1031 | perhaps only DEF_FREE_GLOBAL */ 1032 | val = flag; 1033 | if ((o = PyDict_GetItem(st->st_global, mangled))) { 1034 | val |= PyLong_AS_LONG(o); 1035 | } 1036 | o = PyLong_FromLong(val); 1037 | if (o == NULL) 1038 | goto error; 1039 | if (PyDict_SetItem(st->st_global, mangled, o) < 0) { 1040 | Py_DECREF(o); 1041 | goto error; 1042 | } 1043 | Py_DECREF(o); 1044 | } 1045 | Py_DECREF(mangled); 1046 | return 1; 1047 | 1048 | error: 1049 | Py_DECREF(mangled); 1050 | return 0; 1051 | } 1052 | 1053 | /* VISIT, VISIT_SEQ and VIST_SEQ_TAIL take an ASDL type as their second argument. 1054 | They use the ASDL name to synthesize the name of the C type and the visit 1055 | function. 1056 | 1057 | VISIT_SEQ_TAIL permits the start of an ASDL sequence to be skipped, which is 1058 | useful if the first node in the sequence requires special treatment. 1059 | 1060 | VISIT_QUIT macro returns the specified value exiting from the function but 1061 | first adjusts current recursion counter depth. 1062 | */ 1063 | 1064 | #define VISIT_QUIT(ST, X) \ 1065 | return --(ST)->recursion_depth,(X) 1066 | 1067 | #define VISIT(ST, TYPE, V) \ 1068 | if (!symtable_visit_ ## TYPE((ST), (V))) \ 1069 | VISIT_QUIT((ST), 0); 1070 | 1071 | #define VISIT_SEQ(ST, TYPE, SEQ) { \ 1072 | int i; \ 1073 | asdl_seq *seq = (SEQ); /* avoid variable capture */ \ 1074 | for (i = 0; i < asdl_seq_LEN(seq); i++) { \ 1075 | TYPE ## _ty elt = (TYPE ## _ty)asdl_seq_GET(seq, i); \ 1076 | if (!symtable_visit_ ## TYPE((ST), elt)) \ 1077 | VISIT_QUIT((ST), 0); \ 1078 | } \ 1079 | } 1080 | 1081 | #define VISIT_SEQ_TAIL(ST, TYPE, SEQ, START) { \ 1082 | int i; \ 1083 | asdl_seq *seq = (SEQ); /* avoid variable capture */ \ 1084 | for (i = (START); i < asdl_seq_LEN(seq); i++) { \ 1085 | TYPE ## _ty elt = (TYPE ## _ty)asdl_seq_GET(seq, i); \ 1086 | if (!symtable_visit_ ## TYPE((ST), elt)) \ 1087 | VISIT_QUIT((ST), 0); \ 1088 | } \ 1089 | } 1090 | 1091 | #define VISIT_SEQ_WITH_NULL(ST, TYPE, SEQ) { \ 1092 | int i = 0; \ 1093 | asdl_seq *seq = (SEQ); /* avoid variable capture */ \ 1094 | for (i = 0; i < asdl_seq_LEN(seq); i++) { \ 1095 | TYPE ## _ty elt = (TYPE ## _ty)asdl_seq_GET(seq, i); \ 1096 | if (!elt) continue; /* can be NULL */ \ 1097 | if (!symtable_visit_ ## TYPE((ST), elt)) \ 1098 | VISIT_QUIT((ST), 0); \ 1099 | } \ 1100 | } 1101 | 1102 | static int 1103 | symtable_new_tmpname(struct symtable *st) 1104 | { 1105 | char tmpname[256]; 1106 | identifier tmp; 1107 | 1108 | PyOS_snprintf(tmpname, sizeof(tmpname), "_[%d]", 1109 | ++st->st_cur->ste_tmpname); 1110 | tmp = PyUnicode_InternFromString(tmpname); 1111 | if (!tmp) 1112 | return 0; 1113 | if (!symtable_add_def(st, tmp, DEF_LOCAL)) 1114 | return 0; 1115 | Py_DECREF(tmp); 1116 | return 1; 1117 | } 1118 | 1119 | 1120 | static int 1121 | symtable_record_directive(struct symtable *st, identifier name, stmt_ty s) 1122 | { 1123 | PyObject *data, *mangled; 1124 | int res; 1125 | if (!st->st_cur->ste_directives) { 1126 | st->st_cur->ste_directives = PyList_New(0); 1127 | if (!st->st_cur->ste_directives) 1128 | return 0; 1129 | } 1130 | mangled = _Py_Mangle(st->st_private, name); 1131 | if (!mangled) 1132 | return 0; 1133 | data = Py_BuildValue("(Nii)", mangled, s->lineno, s->col_offset); 1134 | if (!data) 1135 | return 0; 1136 | res = PyList_Append(st->st_cur->ste_directives, data); 1137 | Py_DECREF(data); 1138 | return res == 0; 1139 | } 1140 | 1141 | 1142 | static int 1143 | symtable_visit_stmt(struct symtable *st, stmt_ty s) 1144 | { 1145 | if (++st->recursion_depth > st->recursion_limit) { 1146 | PyErr_SetString(PyExc_RecursionError, 1147 | "maximum recursion depth exceeded during compilation"); 1148 | VISIT_QUIT(st, 0); 1149 | } 1150 | switch (s->kind) { 1151 | case FunctionDef_kind: 1152 | if (!symtable_add_def(st, s->v.FunctionDef.name, DEF_LOCAL)) 1153 | VISIT_QUIT(st, 0); 1154 | if (s->v.FunctionDef.args->defaults) 1155 | VISIT_SEQ(st, expr, s->v.FunctionDef.args->defaults); 1156 | if (s->v.FunctionDef.args->kw_defaults) 1157 | VISIT_SEQ_WITH_NULL(st, expr, s->v.FunctionDef.args->kw_defaults); 1158 | if (!symtable_visit_annotations(st, s, s->v.FunctionDef.args, 1159 | s->v.FunctionDef.returns)) 1160 | VISIT_QUIT(st, 0); 1161 | if (s->v.FunctionDef.decorator_list) 1162 | VISIT_SEQ(st, expr, s->v.FunctionDef.decorator_list); 1163 | if (!symtable_enter_block(st, s->v.FunctionDef.name, 1164 | FunctionBlock, (void *)s, s->lineno, 1165 | s->col_offset)) 1166 | VISIT_QUIT(st, 0); 1167 | VISIT(st, arguments, s->v.FunctionDef.args); 1168 | VISIT_SEQ(st, stmt, s->v.FunctionDef.body); 1169 | if (!symtable_exit_block(st, s)) 1170 | VISIT_QUIT(st, 0); 1171 | break; 1172 | case ClassDef_kind: { 1173 | PyObject *tmp; 1174 | if (!symtable_add_def(st, s->v.ClassDef.name, DEF_LOCAL)) 1175 | VISIT_QUIT(st, 0); 1176 | VISIT_SEQ(st, expr, s->v.ClassDef.bases); 1177 | VISIT_SEQ(st, keyword, s->v.ClassDef.keywords); 1178 | if (s->v.ClassDef.decorator_list) 1179 | VISIT_SEQ(st, expr, s->v.ClassDef.decorator_list); 1180 | if (!symtable_enter_block(st, s->v.ClassDef.name, ClassBlock, 1181 | (void *)s, s->lineno, s->col_offset)) 1182 | VISIT_QUIT(st, 0); 1183 | tmp = st->st_private; 1184 | st->st_private = s->v.ClassDef.name; 1185 | VISIT_SEQ(st, stmt, s->v.ClassDef.body); 1186 | st->st_private = tmp; 1187 | if (!symtable_exit_block(st, s)) 1188 | VISIT_QUIT(st, 0); 1189 | break; 1190 | } 1191 | case Return_kind: 1192 | if (s->v.Return.value) { 1193 | VISIT(st, expr, s->v.Return.value); 1194 | st->st_cur->ste_returns_value = 1; 1195 | } 1196 | break; 1197 | case Delete_kind: 1198 | VISIT_SEQ(st, expr, s->v.Delete.targets); 1199 | break; 1200 | case Assign_kind: 1201 | VISIT_SEQ(st, expr, s->v.Assign.targets); 1202 | VISIT(st, expr, s->v.Assign.value); 1203 | break; 1204 | case AugAssign_kind: 1205 | VISIT(st, expr, s->v.AugAssign.target); 1206 | VISIT(st, expr, s->v.AugAssign.value); 1207 | break; 1208 | case For_kind: 1209 | VISIT(st, expr, s->v.For.target); 1210 | VISIT(st, expr, s->v.For.iter); 1211 | VISIT_SEQ(st, stmt, s->v.For.body); 1212 | if (s->v.For.orelse) 1213 | VISIT_SEQ(st, stmt, s->v.For.orelse); 1214 | break; 1215 | case While_kind: 1216 | VISIT(st, expr, s->v.While.test); 1217 | VISIT_SEQ(st, stmt, s->v.While.body); 1218 | if (s->v.While.orelse) 1219 | VISIT_SEQ(st, stmt, s->v.While.orelse); 1220 | break; 1221 | case If_kind: 1222 | /* XXX if 0: and lookup_yield() hacks */ 1223 | VISIT(st, expr, s->v.If.test); 1224 | VISIT_SEQ(st, stmt, s->v.If.body); 1225 | if (s->v.If.orelse) 1226 | VISIT_SEQ(st, stmt, s->v.If.orelse); 1227 | break; 1228 | case Raise_kind: 1229 | if (s->v.Raise.exc) { 1230 | VISIT(st, expr, s->v.Raise.exc); 1231 | if (s->v.Raise.cause) { 1232 | VISIT(st, expr, s->v.Raise.cause); 1233 | } 1234 | } 1235 | break; 1236 | case Try_kind: 1237 | VISIT_SEQ(st, stmt, s->v.Try.body); 1238 | VISIT_SEQ(st, stmt, s->v.Try.orelse); 1239 | VISIT_SEQ(st, excepthandler, s->v.Try.handlers); 1240 | VISIT_SEQ(st, stmt, s->v.Try.finalbody); 1241 | break; 1242 | case Assert_kind: 1243 | VISIT(st, expr, s->v.Assert.test); 1244 | if (s->v.Assert.msg) 1245 | VISIT(st, expr, s->v.Assert.msg); 1246 | break; 1247 | case Import_kind: 1248 | VISIT_SEQ(st, alias, s->v.Import.names); 1249 | break; 1250 | case ImportFrom_kind: 1251 | VISIT_SEQ(st, alias, s->v.ImportFrom.names); 1252 | break; 1253 | case Global_kind: { 1254 | int i; 1255 | asdl_seq *seq = s->v.Global.names; 1256 | for (i = 0; i < asdl_seq_LEN(seq); i++) { 1257 | identifier name = (identifier)asdl_seq_GET(seq, i); 1258 | long cur = symtable_lookup(st, name); 1259 | if (cur < 0) 1260 | VISIT_QUIT(st, 0); 1261 | if (cur & (DEF_LOCAL | USE)) { 1262 | char buf[256]; 1263 | char *c_name = _PyUnicode_AsString(name); 1264 | if (!c_name) 1265 | return 0; 1266 | if (cur & DEF_LOCAL) 1267 | PyOS_snprintf(buf, sizeof(buf), 1268 | GLOBAL_AFTER_ASSIGN, 1269 | c_name); 1270 | else 1271 | PyOS_snprintf(buf, sizeof(buf), 1272 | GLOBAL_AFTER_USE, 1273 | c_name); 1274 | if (!symtable_warn(st, buf, s->lineno)) 1275 | VISIT_QUIT(st, 0); 1276 | } 1277 | if (!symtable_add_def(st, name, DEF_GLOBAL)) 1278 | VISIT_QUIT(st, 0); 1279 | if (!symtable_record_directive(st, name, s)) 1280 | VISIT_QUIT(st, 0); 1281 | } 1282 | break; 1283 | } 1284 | case Nonlocal_kind: { 1285 | int i; 1286 | asdl_seq *seq = s->v.Nonlocal.names; 1287 | for (i = 0; i < asdl_seq_LEN(seq); i++) { 1288 | identifier name = (identifier)asdl_seq_GET(seq, i); 1289 | long cur = symtable_lookup(st, name); 1290 | if (cur < 0) 1291 | VISIT_QUIT(st, 0); 1292 | if (cur & (DEF_LOCAL | USE)) { 1293 | char buf[256]; 1294 | char *c_name = _PyUnicode_AsString(name); 1295 | if (!c_name) 1296 | return 0; 1297 | if (cur & DEF_LOCAL) 1298 | PyOS_snprintf(buf, sizeof(buf), 1299 | NONLOCAL_AFTER_ASSIGN, 1300 | c_name); 1301 | else 1302 | PyOS_snprintf(buf, sizeof(buf), 1303 | NONLOCAL_AFTER_USE, 1304 | c_name); 1305 | if (!symtable_warn(st, buf, s->lineno)) 1306 | VISIT_QUIT(st, 0); 1307 | } 1308 | if (!symtable_add_def(st, name, DEF_NONLOCAL)) 1309 | VISIT_QUIT(st, 0); 1310 | if (!symtable_record_directive(st, name, s)) 1311 | VISIT_QUIT(st, 0); 1312 | } 1313 | break; 1314 | } 1315 | case Expr_kind: 1316 | VISIT(st, expr, s->v.Expr.value); 1317 | break; 1318 | case Pass_kind: 1319 | case Break_kind: 1320 | case Continue_kind: 1321 | /* nothing to do here */ 1322 | break; 1323 | case With_kind: 1324 | VISIT_SEQ(st, withitem, s->v.With.items); 1325 | VISIT_SEQ(st, stmt, s->v.With.body); 1326 | break; 1327 | case AsyncFunctionDef_kind: 1328 | if (!symtable_add_def(st, s->v.AsyncFunctionDef.name, DEF_LOCAL)) 1329 | VISIT_QUIT(st, 0); 1330 | if (s->v.AsyncFunctionDef.args->defaults) 1331 | VISIT_SEQ(st, expr, s->v.AsyncFunctionDef.args->defaults); 1332 | if (s->v.AsyncFunctionDef.args->kw_defaults) 1333 | VISIT_SEQ_WITH_NULL(st, expr, 1334 | s->v.AsyncFunctionDef.args->kw_defaults); 1335 | if (!symtable_visit_annotations(st, s, s->v.AsyncFunctionDef.args, 1336 | s->v.AsyncFunctionDef.returns)) 1337 | VISIT_QUIT(st, 0); 1338 | if (s->v.AsyncFunctionDef.decorator_list) 1339 | VISIT_SEQ(st, expr, s->v.AsyncFunctionDef.decorator_list); 1340 | if (!symtable_enter_block(st, s->v.AsyncFunctionDef.name, 1341 | FunctionBlock, (void *)s, s->lineno, 1342 | s->col_offset)) 1343 | VISIT_QUIT(st, 0); 1344 | VISIT(st, arguments, s->v.AsyncFunctionDef.args); 1345 | VISIT_SEQ(st, stmt, s->v.AsyncFunctionDef.body); 1346 | if (!symtable_exit_block(st, s)) 1347 | VISIT_QUIT(st, 0); 1348 | break; 1349 | case AsyncWith_kind: 1350 | VISIT_SEQ(st, withitem, s->v.AsyncWith.items); 1351 | VISIT_SEQ(st, stmt, s->v.AsyncWith.body); 1352 | break; 1353 | case AsyncFor_kind: 1354 | VISIT(st, expr, s->v.AsyncFor.target); 1355 | VISIT(st, expr, s->v.AsyncFor.iter); 1356 | VISIT_SEQ(st, stmt, s->v.AsyncFor.body); 1357 | if (s->v.AsyncFor.orelse) 1358 | VISIT_SEQ(st, stmt, s->v.AsyncFor.orelse); 1359 | break; 1360 | } 1361 | VISIT_QUIT(st, 1); 1362 | } 1363 | 1364 | static int 1365 | symtable_curry(struct symtable *st, expr_ty e, asdl_seq* multistatements){ 1366 | 1367 | if (!GET_IDENTIFIER(lambda)) 1368 | VISIT_QUIT(st, 0); 1369 | 1370 | if (e->v.Lambda.args->defaults) 1371 | VISIT_SEQ(st, expr, e->v.Lambda.args->defaults); 1372 | 1373 | if (e->v.Lambda.args->kw_defaults) 1374 | VISIT_SEQ_WITH_NULL(st, expr, e->v.Lambda.args->kw_defaults); 1375 | 1376 | if (!symtable_enter_block(st, lambda, 1377 | FunctionBlock, (void *)e, e->lineno, 1378 | e->col_offset)) 1379 | VISIT_QUIT(st, 0); 1380 | 1381 | VISIT(st, arguments, e->v.Lambda.args); 1382 | if (e->v.Lambda.body->kind == Lambda_kind){ 1383 | if (!symtable_curry(st, e->v.Lambda.body, multistatements)) 1384 | VISIT_QUIT(st, 0); 1385 | } 1386 | else{ 1387 | VISIT_SEQ(st, stmt, multistatements); 1388 | VISIT(st, expr, e->v.Lambda.body); 1389 | } 1390 | return symtable_exit_block(st, (void *)e); 1391 | } 1392 | 1393 | static int 1394 | symtable_visit_expr(struct symtable *st, expr_ty e) 1395 | { 1396 | if (++st->recursion_depth > st->recursion_limit) { 1397 | PyErr_SetString(PyExc_RecursionError, 1398 | "maximum recursion depth exceeded during compilation"); 1399 | VISIT_QUIT(st, 0); 1400 | } 1401 | switch (e->kind) { 1402 | case BoolOp_kind: 1403 | VISIT_SEQ(st, expr, e->v.BoolOp.values); 1404 | break; 1405 | case BinOp_kind: 1406 | VISIT(st, expr, e->v.BinOp.left); 1407 | VISIT(st, expr, e->v.BinOp.right); 1408 | break; 1409 | case UnaryOp_kind: 1410 | VISIT(st, expr, e->v.UnaryOp.operand); 1411 | break; 1412 | case Lambda_kind: { 1413 | if (!GET_IDENTIFIER(lambda)) 1414 | VISIT_QUIT(st, 0); 1415 | if (e->v.Lambda.args->defaults) 1416 | VISIT_SEQ(st, expr, e->v.Lambda.args->defaults); 1417 | if (e->v.Lambda.args->kw_defaults) 1418 | VISIT_SEQ_WITH_NULL(st, expr, e->v.Lambda.args->kw_defaults); 1419 | if (!symtable_enter_block(st, lambda, 1420 | FunctionBlock, (void *)e, e->lineno, 1421 | e->col_offset)) 1422 | VISIT_QUIT(st, 0); 1423 | VISIT(st, arguments, e->v.Lambda.args); 1424 | VISIT(st, expr, e->v.Lambda.body); 1425 | if (!symtable_exit_block(st, (void *)e)) 1426 | VISIT_QUIT(st, 0); 1427 | break; 1428 | } 1429 | case IfExp_kind: 1430 | VISIT(st, expr, e->v.IfExp.test); 1431 | VISIT(st, expr, e->v.IfExp.body); 1432 | VISIT(st, expr, e->v.IfExp.orelse); 1433 | break; 1434 | case Dict_kind: 1435 | VISIT_SEQ_WITH_NULL(st, expr, e->v.Dict.keys); 1436 | VISIT_SEQ(st, expr, e->v.Dict.values); 1437 | break; 1438 | case Set_kind: 1439 | VISIT_SEQ(st, expr, e->v.Set.elts); 1440 | break; 1441 | case GeneratorExp_kind: 1442 | if (!symtable_visit_genexp(st, e)) 1443 | VISIT_QUIT(st, 0); 1444 | break; 1445 | case ListComp_kind: 1446 | if (!symtable_visit_listcomp(st, e)) 1447 | VISIT_QUIT(st, 0); 1448 | break; 1449 | case SetComp_kind: 1450 | if (!symtable_visit_setcomp(st, e)) 1451 | VISIT_QUIT(st, 0); 1452 | break; 1453 | case DictComp_kind: 1454 | if (!symtable_visit_dictcomp(st, e)) 1455 | VISIT_QUIT(st, 0); 1456 | break; 1457 | case Yield_kind: 1458 | if (e->v.Yield.value) 1459 | VISIT(st, expr, e->v.Yield.value); 1460 | st->st_cur->ste_generator = 1; 1461 | break; 1462 | case YieldFrom_kind: 1463 | VISIT(st, expr, e->v.YieldFrom.value); 1464 | st->st_cur->ste_generator = 1; 1465 | break; 1466 | case Await_kind: 1467 | VISIT(st, expr, e->v.Await.value); 1468 | st->st_cur->ste_generator = 1; 1469 | break; 1470 | case Compare_kind: 1471 | VISIT(st, expr, e->v.Compare.left); 1472 | VISIT_SEQ(st, expr, e->v.Compare.comparators); 1473 | break; 1474 | case Call_kind: 1475 | VISIT(st, expr, e->v.Call.func); 1476 | VISIT_SEQ(st, expr, e->v.Call.args); 1477 | VISIT_SEQ_WITH_NULL(st, keyword, e->v.Call.keywords); 1478 | break; 1479 | case Num_kind: 1480 | case Str_kind: 1481 | case Bytes_kind: 1482 | case Ellipsis_kind: 1483 | case NameConstant_kind: 1484 | /* Nothing to do here. */ 1485 | break; 1486 | /* The following exprs can be assignment targets. */ 1487 | case Attribute_kind: 1488 | VISIT(st, expr, e->v.Attribute.value); 1489 | break; 1490 | case Subscript_kind: 1491 | VISIT(st, expr, e->v.Subscript.value); 1492 | VISIT(st, slice, e->v.Subscript.slice); 1493 | break; 1494 | case Starred_kind: 1495 | VISIT(st, expr, e->v.Starred.value); 1496 | break; 1497 | case Name_kind: 1498 | if (!symtable_add_def(st, e->v.Name.id, 1499 | e->v.Name.ctx == Load ? USE : DEF_LOCAL)) 1500 | VISIT_QUIT(st, 0); 1501 | /* Special-case super: it counts as a use of __class__ */ 1502 | if (e->v.Name.ctx == Load && 1503 | st->st_cur->ste_type == FunctionBlock && 1504 | _PyUnicode_EqualToASCIIString(e->v.Name.id, "super")) { 1505 | if (!GET_IDENTIFIER(__class__) || 1506 | !symtable_add_def(st, __class__, USE)) 1507 | VISIT_QUIT(st, 0); 1508 | } 1509 | break; 1510 | /* child nodes of List and Tuple will have expr_context set */ 1511 | case List_kind: 1512 | VISIT_SEQ(st, expr, e->v.List.elts); 1513 | break; 1514 | case Tuple_kind: 1515 | VISIT_SEQ(st, expr, e->v.Tuple.elts); 1516 | break; 1517 | case Where_kind: 1518 | if (e->v.Where.target->kind == Lambda_kind) 1519 | { 1520 | asdl_seq* multistate_ments; 1521 | multistate_ments = e->v.Where.body; 1522 | if(!symtable_curry(st, e->v.Where.target, multistate_ments)) 1523 | VISIT_QUIT(st, 0); 1524 | } 1525 | else{ 1526 | VISIT_SEQ(st, stmt, e->v.Where.body); 1527 | VISIT(st, expr, e->v.Where.target); 1528 | } 1529 | break; 1530 | } 1531 | VISIT_QUIT(st, 1); 1532 | } 1533 | 1534 | static int 1535 | symtable_implicit_arg(struct symtable *st, int pos) 1536 | { 1537 | PyObject *id = PyUnicode_FromFormat(".%d", pos); 1538 | if (id == NULL) 1539 | return 0; 1540 | if (!symtable_add_def(st, id, DEF_PARAM)) { 1541 | Py_DECREF(id); 1542 | return 0; 1543 | } 1544 | Py_DECREF(id); 1545 | return 1; 1546 | } 1547 | 1548 | static int 1549 | symtable_visit_params(struct symtable *st, asdl_seq *args) 1550 | { 1551 | int i; 1552 | 1553 | if (!args) 1554 | return -1; 1555 | 1556 | for (i = 0; i < asdl_seq_LEN(args); i++) { 1557 | arg_ty arg = (arg_ty)asdl_seq_GET(args, i); 1558 | if (!symtable_add_def(st, arg->arg, DEF_PARAM)) 1559 | return 0; 1560 | } 1561 | 1562 | return 1; 1563 | } 1564 | 1565 | static int 1566 | symtable_visit_argannotations(struct symtable *st, asdl_seq *args) 1567 | { 1568 | int i; 1569 | 1570 | if (!args) 1571 | return -1; 1572 | 1573 | for (i = 0; i < asdl_seq_LEN(args); i++) { 1574 | arg_ty arg = (arg_ty)asdl_seq_GET(args, i); 1575 | if (arg->annotation) 1576 | VISIT(st, expr, arg->annotation); 1577 | } 1578 | 1579 | return 1; 1580 | } 1581 | 1582 | static int 1583 | symtable_visit_annotations(struct symtable *st, stmt_ty s, 1584 | arguments_ty a, expr_ty returns) 1585 | { 1586 | if (a->args && !symtable_visit_argannotations(st, a->args)) 1587 | return 0; 1588 | if (a->vararg && a->vararg->annotation) 1589 | VISIT(st, expr, a->vararg->annotation); 1590 | if (a->kwarg && a->kwarg->annotation) 1591 | VISIT(st, expr, a->kwarg->annotation); 1592 | if (a->kwonlyargs && !symtable_visit_argannotations(st, a->kwonlyargs)) 1593 | return 0; 1594 | if (returns) 1595 | VISIT(st, expr, returns); 1596 | return 1; 1597 | } 1598 | 1599 | static int 1600 | symtable_visit_arguments(struct symtable *st, arguments_ty a) 1601 | { 1602 | /* skip default arguments inside function block 1603 | XXX should ast be different? 1604 | */ 1605 | if (a->args && !symtable_visit_params(st, a->args)) 1606 | return 0; 1607 | if (a->kwonlyargs && !symtable_visit_params(st, a->kwonlyargs)) 1608 | return 0; 1609 | if (a->vararg) { 1610 | if (!symtable_add_def(st, a->vararg->arg, DEF_PARAM)) 1611 | return 0; 1612 | st->st_cur->ste_varargs = 1; 1613 | } 1614 | if (a->kwarg) { 1615 | if (!symtable_add_def(st, a->kwarg->arg, DEF_PARAM)) 1616 | return 0; 1617 | st->st_cur->ste_varkeywords = 1; 1618 | } 1619 | return 1; 1620 | } 1621 | 1622 | 1623 | static int 1624 | symtable_visit_excepthandler(struct symtable *st, excepthandler_ty eh) 1625 | { 1626 | if (eh->v.ExceptHandler.type) 1627 | VISIT(st, expr, eh->v.ExceptHandler.type); 1628 | if (eh->v.ExceptHandler.name) 1629 | if (!symtable_add_def(st, eh->v.ExceptHandler.name, DEF_LOCAL)) 1630 | return 0; 1631 | VISIT_SEQ(st, stmt, eh->v.ExceptHandler.body); 1632 | return 1; 1633 | } 1634 | 1635 | static int 1636 | symtable_visit_withitem(struct symtable *st, withitem_ty item) 1637 | { 1638 | VISIT(st, expr, item->context_expr); 1639 | if (item->optional_vars) { 1640 | VISIT(st, expr, item->optional_vars); 1641 | } 1642 | return 1; 1643 | } 1644 | 1645 | 1646 | static int 1647 | symtable_visit_alias(struct symtable *st, alias_ty a) 1648 | { 1649 | /* Compute store_name, the name actually bound by the import 1650 | operation. It is different than a->name when a->name is a 1651 | dotted package name (e.g. spam.eggs) 1652 | */ 1653 | PyObject *store_name; 1654 | PyObject *name = (a->asname == NULL) ? a->name : a->asname; 1655 | Py_ssize_t dot = PyUnicode_FindChar(name, '.', 0, 1656 | PyUnicode_GET_LENGTH(name), 1); 1657 | if (dot != -1) { 1658 | store_name = PyUnicode_Substring(name, 0, dot); 1659 | if (!store_name) 1660 | return 0; 1661 | } 1662 | else { 1663 | store_name = name; 1664 | Py_INCREF(store_name); 1665 | } 1666 | if (!_PyUnicode_EqualToASCIIString(name, "*")) { 1667 | int r = symtable_add_def(st, store_name, DEF_IMPORT); 1668 | Py_DECREF(store_name); 1669 | return r; 1670 | } 1671 | else { 1672 | if (st->st_cur->ste_type != ModuleBlock) { 1673 | int lineno = st->st_cur->ste_lineno; 1674 | int col_offset = st->st_cur->ste_col_offset; 1675 | PyErr_SetString(PyExc_SyntaxError, IMPORT_STAR_WARNING); 1676 | PyErr_SyntaxLocationObject(st->st_filename, lineno, col_offset); 1677 | Py_DECREF(store_name); 1678 | return 0; 1679 | } 1680 | Py_DECREF(store_name); 1681 | return 1; 1682 | } 1683 | } 1684 | 1685 | 1686 | static int 1687 | symtable_visit_comprehension(struct symtable *st, comprehension_ty lc) 1688 | { 1689 | VISIT(st, expr, lc->target); 1690 | VISIT(st, expr, lc->iter); 1691 | VISIT_SEQ(st, expr, lc->ifs); 1692 | return 1; 1693 | } 1694 | 1695 | 1696 | static int 1697 | symtable_visit_keyword(struct symtable *st, keyword_ty k) 1698 | { 1699 | VISIT(st, expr, k->value); 1700 | return 1; 1701 | } 1702 | 1703 | 1704 | static int 1705 | symtable_visit_slice(struct symtable *st, slice_ty s) 1706 | { 1707 | switch (s->kind) { 1708 | case Slice_kind: 1709 | if (s->v.Slice.lower) 1710 | VISIT(st, expr, s->v.Slice.lower) 1711 | if (s->v.Slice.upper) 1712 | VISIT(st, expr, s->v.Slice.upper) 1713 | if (s->v.Slice.step) 1714 | VISIT(st, expr, s->v.Slice.step) 1715 | break; 1716 | case ExtSlice_kind: 1717 | VISIT_SEQ(st, slice, s->v.ExtSlice.dims) 1718 | break; 1719 | case Index_kind: 1720 | VISIT(st, expr, s->v.Index.value) 1721 | break; 1722 | } 1723 | return 1; 1724 | } 1725 | 1726 | static int 1727 | symtable_handle_comprehension(struct symtable *st, expr_ty e, 1728 | identifier scope_name, asdl_seq *generators, 1729 | expr_ty elt, expr_ty value) 1730 | { 1731 | int is_generator = (e->kind == GeneratorExp_kind); 1732 | int needs_tmp = !is_generator; 1733 | comprehension_ty outermost = ((comprehension_ty) 1734 | asdl_seq_GET(generators, 0)); 1735 | /* Outermost iterator is evaluated in current scope */ 1736 | VISIT(st, expr, outermost->iter); 1737 | /* Create comprehension scope for the rest */ 1738 | if (!scope_name || 1739 | !symtable_enter_block(st, scope_name, FunctionBlock, (void *)e, 1740 | e->lineno, e->col_offset)) { 1741 | return 0; 1742 | } 1743 | st->st_cur->ste_generator = is_generator; 1744 | /* Outermost iter is received as an argument */ 1745 | if (!symtable_implicit_arg(st, 0)) { 1746 | symtable_exit_block(st, (void *)e); 1747 | return 0; 1748 | } 1749 | /* Allocate temporary name if needed */ 1750 | if (needs_tmp && !symtable_new_tmpname(st)) { 1751 | symtable_exit_block(st, (void *)e); 1752 | return 0; 1753 | } 1754 | VISIT(st, expr, outermost->target); 1755 | VISIT_SEQ(st, expr, outermost->ifs); 1756 | VISIT_SEQ_TAIL(st, comprehension, generators, 1); 1757 | if (value) 1758 | VISIT(st, expr, value); 1759 | VISIT(st, expr, elt); 1760 | return symtable_exit_block(st, (void *)e); 1761 | } 1762 | 1763 | static int 1764 | symtable_visit_genexp(struct symtable *st, expr_ty e) 1765 | { 1766 | return symtable_handle_comprehension(st, e, GET_IDENTIFIER(genexpr), 1767 | e->v.GeneratorExp.generators, 1768 | e->v.GeneratorExp.elt, NULL); 1769 | } 1770 | 1771 | static int 1772 | symtable_visit_listcomp(struct symtable *st, expr_ty e) 1773 | { 1774 | return symtable_handle_comprehension(st, e, GET_IDENTIFIER(listcomp), 1775 | e->v.ListComp.generators, 1776 | e->v.ListComp.elt, NULL); 1777 | } 1778 | 1779 | static int 1780 | symtable_visit_setcomp(struct symtable *st, expr_ty e) 1781 | { 1782 | return symtable_handle_comprehension(st, e, GET_IDENTIFIER(setcomp), 1783 | e->v.SetComp.generators, 1784 | e->v.SetComp.elt, NULL); 1785 | } 1786 | 1787 | static int 1788 | symtable_visit_dictcomp(struct symtable *st, expr_ty e) 1789 | { 1790 | return symtable_handle_comprehension(st, e, GET_IDENTIFIER(dictcomp), 1791 | e->v.DictComp.generators, 1792 | e->v.DictComp.key, 1793 | e->v.DictComp.value); 1794 | } 1795 | -------------------------------------------------------------------------------- /flowpython/py35/ReadMe.md: -------------------------------------------------------------------------------- 1 | The precompiled binaries can be found at [Release Page](https://github.com/thautwarm/flowpython/releases). -------------------------------------------------------------------------------- /flowpython/py36/.gitignore: -------------------------------------------------------------------------------- 1 | windows-32bit/ 2 | windows-64bit/ 3 | linux-64bit/ 4 | -------------------------------------------------------------------------------- /flowpython/py36/Grammar/Grammar: -------------------------------------------------------------------------------- 1 | # Grammar for Python 2 | 3 | # NOTE WELL: You should also follow all the steps listed at 4 | # https://docs.python.org/devguide/grammar.html 5 | 6 | # Start symbols for the grammar: 7 | # single_input is a single interactive statement; 8 | # file_input is a module or sequence of commands read from an input file; 9 | # eval_input is the input for the eval() functions. 10 | # NB: compound_stmt in single_input is followed by extra NEWLINE! 11 | single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE 12 | file_input: (NEWLINE | stmt)* ENDMARKER 13 | eval_input: testlist NEWLINE* ENDMARKER 14 | 15 | decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE 16 | decorators: decorator+ 17 | decorated: decorators (classdef | funcdef | async_funcdef) 18 | 19 | async_funcdef: ASYNC funcdef 20 | funcdef: 'def' NAME parameters ['->' test] ':' suite 21 | 22 | parameters: '(' [typedargslist] ')' 23 | typedargslist: (tfpdef ['=' test] (',' tfpdef ['=' test])* [',' [ 24 | '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]] 25 | | '**' tfpdef [',']]] 26 | | '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]] 27 | | '**' tfpdef [',']) 28 | tfpdef: NAME [':' test] 29 | varargslist: (vfpdef ['=' test] (',' vfpdef ['=' test])* [',' [ 30 | '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]] 31 | | '**' vfpdef [',']]] 32 | | '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]] 33 | | '**' vfpdef [','] 34 | ) 35 | vfpdef: NAME 36 | 37 | 38 | stmt: simple_stmt | compound_stmt 39 | simple_stmt: small_stmt ((';' small_stmt)* [';'] NEWLINE | where_handler_stmt) 40 | small_stmt: (expr_stmt | del_stmt | pass_stmt | flow_stmt | 41 | import_stmt | global_stmt | nonlocal_stmt | assert_stmt) 42 | expr_stmt: testlist_star_expr (annassign | augassign (yield_expr|testlist) | 43 | ('=' (yield_expr|testlist_star_expr))*) 44 | annassign: ':' test ['=' test] 45 | testlist_star_expr: (test|star_expr) (',' (test|star_expr))* [','] 46 | augassign: ('+=' | '-=' | '*=' | '@=' | '/=' | '%=' | '&=' | '|=' | '^=' | 47 | '<<=' | '>>=' | '**=' | '//=') 48 | # For normal and annotated assignments, additional restrictions enforced by the interpreter 49 | del_stmt: 'del' exprlist 50 | pass_stmt: 'pass' 51 | flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt | yield_stmt 52 | break_stmt: 'break' 53 | continue_stmt: 'continue' 54 | return_stmt: 'return' [testlist] 55 | yield_stmt: yield_expr 56 | raise_stmt: 'raise' [test ['from' test]] 57 | import_stmt: import_name | import_from 58 | import_name: 'import' dotted_as_names 59 | # note below: the ('.' | '...') is necessary because '...' is tokenized as ELLIPSIS 60 | import_from: ('from' (('.' | '...')* dotted_name | ('.' | '...')+) 61 | 'import' ('*' | '(' import_as_names ')' | import_as_names)) 62 | import_as_name: NAME ['as' NAME] 63 | dotted_as_name: dotted_name ['as' NAME] 64 | import_as_names: import_as_name (',' import_as_name)* [','] 65 | dotted_as_names: dotted_as_name (',' dotted_as_name)* 66 | dotted_name: NAME ('.' NAME)* 67 | global_stmt: 'global' NAME (',' NAME)* 68 | nonlocal_stmt: 'nonlocal' NAME (',' NAME)* 69 | assert_stmt: 'assert' test [',' test] 70 | 71 | compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated | async_stmt | switch_stmt | branch_stmt 72 | async_stmt: ASYNC (funcdef | with_stmt | for_stmt) 73 | if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite] 74 | switch_stmt: switch_head test ':' NEWLINE INDENT case_stmt+ DEDENT 75 | branch_stmt: ('|' test '=' '>' suite)+ 76 | while_stmt: 'while' test ':' suite ['else' ':' suite] 77 | for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite] 78 | try_stmt: ('try' ':' suite 79 | ((except_clause ':' suite)+ 80 | ['else' ':' suite] 81 | ['finally' ':' suite] | 82 | 'finally' ':' suite)) 83 | with_stmt: 'with' with_item (',' with_item)* ':' suite 84 | with_item: test ['as' expr] 85 | # NB compile.c makes sure that the default except clause is last 86 | except_clause: 'except' [test ['as' NAME]] 87 | suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT 88 | 89 | test: or_test ['if' or_test 'else' [NEWLINE [INDENT]] test | '->' ( '>' pipeline_expr | test) ] | lambdef 90 | test_nocond: or_test | lambdef_nocond 91 | lambdef: 'lambda' [varargslist] ':' test | lambdef_head [typedargslist] 'def' test | '.' [typedargslist] '->' test 92 | lambdef_nocond: 'lambda' [varargslist] ':' test_nocond | lambdef_head [typedargslist] 'def' test_nocond | '.' [typedargslist] '->' test_nocond 93 | or_test: and_test ('or' and_test)* 94 | and_test: not_test ('and' not_test)* 95 | not_test: 'not' not_test | comparison 96 | comparison: expr (comp_op expr)* 97 | # <> isn't actually a valid comparison operator in Python. It's here for the 98 | # sake of a __future__ import described in PEP 401 (which really works :-) 99 | comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not' 100 | star_expr: '*' expr 101 | expr: xor_expr ('|' xor_expr)* 102 | xor_expr: and_expr ('^' and_expr)* 103 | and_expr: shift_expr ('&' shift_expr)* 104 | shift_expr: arith_expr (('<<'|'>>') arith_expr)* 105 | arith_expr: term (('+'|'-') term)* 106 | term: factor (('*'|'@'|'/'|'%'|'//') factor)* 107 | factor: ('+'|'-'|'~') factor | power 108 | power: atom_expr ['**' factor] 109 | atom_expr: [AWAIT] atom trailer* 110 | atom: ('(' [yield_expr|testlist_comp] ')' | 111 | '[' [testlist_comp] ']' | 112 | '{' [dictorsetmaker] '}' | 113 | NAME | NUMBER | STRING+ | '...' | 'None' | 'True' | 'False') 114 | testlist_comp: (test|star_expr) ( comp_for | (',' (test|star_expr))* [','] ) 115 | trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME 116 | subscriptlist: subscript (',' subscript)* [','] 117 | subscript: test | [test] ':' [test] [sliceop] 118 | sliceop: ':' [test] 119 | exprlist: (expr|star_expr) (',' (expr|star_expr))* [','] 120 | testlist: test (',' test)* [','] 121 | dictorsetmaker: ( ((test ':' test | '**' expr) 122 | (comp_for | (',' (test ':' test | '**' expr))* [','])) | 123 | ((test | star_expr) 124 | (comp_for | (',' (test | star_expr))* [','])) ) 125 | 126 | classdef: 'class' NAME ['(' [arglist] ')'] ':' suite 127 | 128 | arglist: argument (',' argument)* [','] 129 | 130 | # The reason that keywords are test nodes instead of NAME is that using NAME 131 | # results in an ambiguity. ast.c makes sure it's a NAME. 132 | # "test '=' test" is really "keyword '=' test", but we have no such token. 133 | # These need to be in a single rule to avoid grammar that is ambiguous 134 | # to our LL(1) parser. Even though 'test' includes '*expr' in star_expr, 135 | # we explicitly match '*' here, too, to give it proper precedence. 136 | # Illegal combinations and orderings are blocked in ast.c: 137 | # multiple (test comp_for) arguments are blocked; keyword unpackings 138 | # that precede iterable unpackings are blocked; etc. 139 | argument: ( test [comp_for] | 140 | test '=' test | 141 | '**' test | 142 | '*' test ) 143 | 144 | comp_iter: comp_for | comp_if 145 | comp_for: [ASYNC] 'for' exprlist 'in' or_test [comp_iter] 146 | comp_if: 'if' test_nocond [comp_iter] 147 | 148 | # not used in grammar, but may appear in "node" passed from Parser to Compiler 149 | encoding_decl: NAME 150 | 151 | yield_expr: 'yield' [yield_arg] 152 | yield_arg: 'from' test | testlist 153 | 154 | where_handler_stmt: NAME ':' suite 155 | lambdef_head: 'as' ['-' 'with'] 156 | 157 | case_stmt: case_head [test [':' test]] '=' '>' suite 158 | mode_stmt: '[' [comp_op] ']' | '(' [test] ')' | '{' [test] '}' | '+' ('[' [comp_op] ']' | '(' [test] ')') 159 | switch_head : ('condic' | 'condef') [mode_stmt] 160 | case_head : [mode_stmt [NEWLINE] ] NAME 161 | pipeline_expr : test ( '=' '>' test)* 162 | -------------------------------------------------------------------------------- /flowpython/py36/Parser/Python.asdl: -------------------------------------------------------------------------------- 1 | -- ASDL's 7 builtin types are: 2 | -- identifier, int, string, bytes, object, singleton, constant 3 | -- 4 | -- singleton: None, True or False 5 | -- constant can be None, whereas None means "no value" for object. 6 | 7 | module Python 8 | { 9 | mod = Module(stmt* body) 10 | | Interactive(stmt* body) 11 | | Expression(expr body) 12 | 13 | -- not really an actual node but useful in Jython's typesystem. 14 | | Suite(stmt* body) 15 | 16 | stmt = FunctionDef(identifier name, arguments args, 17 | stmt* body, expr* decorator_list, expr? returns) 18 | | AsyncFunctionDef(identifier name, arguments args, 19 | stmt* body, expr* decorator_list, expr? returns) 20 | 21 | | ClassDef(identifier name, 22 | expr* bases, 23 | keyword* keywords, 24 | stmt* body, 25 | expr* decorator_list) 26 | | Return(expr? value) 27 | 28 | | Delete(expr* targets) 29 | | Assign(expr* targets, expr value) 30 | | AugAssign(expr target, operator op, expr value) 31 | -- 'simple' indicates that we annotate simple name without parens 32 | | AnnAssign(expr target, expr annotation, expr? value, int simple) 33 | 34 | -- use 'orelse' because else is a keyword in target languages 35 | | For(expr target, expr iter, stmt* body, stmt* orelse) 36 | | AsyncFor(expr target, expr iter, stmt* body, stmt* orelse) 37 | | While(expr test, stmt* body, stmt* orelse) 38 | | If(expr test, stmt* body, stmt* orelse) 39 | | With(withitem* items, stmt* body) 40 | | AsyncWith(withitem* items, stmt* body) 41 | 42 | | Raise(expr? exc, expr? cause) 43 | | Try(stmt* body, excepthandler* handlers, stmt* orelse, stmt* finalbody) 44 | | Assert(expr test, expr? msg) 45 | 46 | | Import(alias* names) 47 | | ImportFrom(identifier? module, alias* names, int? level) 48 | 49 | | Global(identifier* names) 50 | | Nonlocal(identifier* names) 51 | | Expr(expr value) 52 | | Pass | Break | Continue 53 | 54 | -- XXX Jython will be different 55 | -- col_offset is the byte offset in the utf8 string the parser uses 56 | attributes (int lineno, int col_offset) 57 | 58 | -- BoolOp() can use left & right? 59 | expr = BoolOp(boolop op, expr* values) 60 | | BinOp(expr left, operator op, expr right) 61 | | UnaryOp(unaryop op, expr operand) 62 | | Lambda(arguments args, expr body) 63 | | IfExp(expr test, expr body, expr orelse) 64 | | Dict(expr* keys, expr* values) 65 | | Set(expr* elts) 66 | | ListComp(expr elt, comprehension* generators) 67 | | SetComp(expr elt, comprehension* generators) 68 | | DictComp(expr key, expr value, comprehension* generators) 69 | | GeneratorExp(expr elt, comprehension* generators) 70 | -- the grammar constrains where yield expressions can occur 71 | | Await(expr value) 72 | | Yield(expr? value) 73 | | YieldFrom(expr value) 74 | -- need sequences for compare to distinguish between 75 | -- x < 4 < 3 and (x < 4) < 3 76 | | Compare(expr left, cmpop* ops, expr* comparators) 77 | | Call(expr func, expr* args, keyword* keywords) 78 | | Num(object n) -- a number as a PyObject. 79 | | Str(string s) -- need to specify raw, unicode, etc? 80 | | FormattedValue(expr value, int? conversion, expr? format_spec) 81 | | JoinedStr(expr* values) 82 | | Bytes(bytes s) 83 | | NameConstant(singleton value) 84 | | Ellipsis 85 | | Constant(constant value) 86 | 87 | -- the following expression can appear in assignment context 88 | | Attribute(expr value, identifier attr, expr_context ctx) 89 | | Subscript(expr value, slice slice, expr_context ctx) 90 | | Starred(expr value, expr_context ctx) 91 | | Name(identifier id, expr_context ctx) 92 | | List(expr* elts, expr_context ctx) 93 | | Tuple(expr* elts, expr_context ctx) 94 | | Where(expr target, stmt* body) 95 | 96 | -- col_offset is the byte offset in the utf8 string the parser uses 97 | attributes (int lineno, int col_offset) 98 | 99 | expr_context = Load | Store | Del | AugLoad | AugStore | Param 100 | 101 | slice = Slice(expr? lower, expr? upper, expr? step) 102 | | ExtSlice(slice* dims) 103 | | Index(expr value) 104 | 105 | boolop = And | Or 106 | 107 | operator = Add | Sub | Mult | MatMult | Div | Mod | Pow | LShift 108 | | RShift | BitOr | BitXor | BitAnd | FloorDiv 109 | 110 | unaryop = Invert | Not | UAdd | USub 111 | 112 | cmpop = Eq | NotEq | Lt | LtE | Gt | GtE | Is | IsNot | In | NotIn 113 | 114 | comprehension = (expr target, expr iter, expr* ifs, int is_async) 115 | 116 | excepthandler = ExceptHandler(expr? type, identifier? name, stmt* body) 117 | attributes (int lineno, int col_offset) 118 | 119 | arguments = (arg* args, arg? vararg, arg* kwonlyargs, expr* kw_defaults, 120 | arg? kwarg, expr* defaults) 121 | 122 | arg = (identifier arg, expr? annotation) 123 | attributes (int lineno, int col_offset) 124 | 125 | -- keyword arguments supplied to call (NULL identifier for **kwargs) 126 | keyword = (identifier? arg, expr value) 127 | 128 | -- import name with optional 'as' alias. 129 | alias = (identifier name, identifier? asname) 130 | 131 | withitem = (expr context_expr, expr? optional_vars) 132 | } 133 | 134 | -------------------------------------------------------------------------------- /flowpython/py36/Parser/acceler.c: -------------------------------------------------------------------------------- 1 | /* Parser accelerator module */ 2 | 3 | /* The parser as originally conceived had disappointing performance. 4 | This module does some precomputation that speeds up the selection 5 | of a DFA based upon a token, turning a search through an array 6 | into a simple indexing operation. The parser now cannot work 7 | without the accelerators installed. Note that the accelerators 8 | are installed dynamically when the parser is initialized, they 9 | are not part of the static data structure written on graminit.[ch] 10 | by the parser generator. */ 11 | 12 | #include "pgenheaders.h" 13 | #include "grammar.h" 14 | #include "node.h" 15 | #include "token.h" 16 | #include "parser.h" 17 | 18 | /* Forward references */ 19 | static void fixdfa(grammar *, dfa *); 20 | static void fixstate(grammar *, state *); 21 | 22 | void 23 | PyGrammar_AddAccelerators(grammar *g) 24 | { 25 | dfa *d; 26 | int i; 27 | d = g->g_dfa; 28 | for (i = g->g_ndfas; --i >= 0; d++) 29 | fixdfa(g, d); 30 | g->g_accel = 1; 31 | } 32 | 33 | void 34 | PyGrammar_RemoveAccelerators(grammar *g) 35 | { 36 | dfa *d; 37 | int i; 38 | g->g_accel = 0; 39 | d = g->g_dfa; 40 | for (i = g->g_ndfas; --i >= 0; d++) { 41 | state *s; 42 | int j; 43 | s = d->d_state; 44 | for (j = 0; j < d->d_nstates; j++, s++) { 45 | if (s->s_accel) 46 | PyObject_FREE(s->s_accel); 47 | s->s_accel = NULL; 48 | } 49 | } 50 | } 51 | 52 | static void 53 | fixdfa(grammar *g, dfa *d) 54 | { 55 | state *s; 56 | int j; 57 | s = d->d_state; 58 | for (j = 0; j < d->d_nstates; j++, s++) 59 | fixstate(g, s); 60 | } 61 | 62 | static void 63 | fixstate(grammar *g, state *s) 64 | { 65 | arc *a; 66 | int k; 67 | int *accel; 68 | int nl = g->g_ll.ll_nlabels; 69 | s->s_accept = 0; 70 | accel = (int *) PyObject_MALLOC(nl * sizeof(int)); 71 | if (accel == NULL) { 72 | fprintf(stderr, "no mem to build parser accelerators\n"); 73 | exit(1); 74 | } 75 | for (k = 0; k < nl; k++) 76 | accel[k] = -1; 77 | a = s->s_arc; 78 | for (k = s->s_narcs; --k >= 0; a++) { 79 | int lbl = a->a_lbl; 80 | label *l = &g->g_ll.ll_label[lbl]; 81 | int type = l->lb_type; 82 | if (a->a_arrow >= (1 << 7)) { 83 | printf("XXX too many states!\n"); 84 | continue; 85 | } 86 | if (ISNONTERMINAL(type)) { 87 | dfa *d1 = PyGrammar_FindDFA(g, type); 88 | int ibit; 89 | if (type - NT_OFFSET >= (1 << 7)) { 90 | printf("XXX too high nonterminal number!\n"); 91 | continue; 92 | } 93 | for (ibit = 0; ibit < g->g_ll.ll_nlabels; ibit++) { 94 | if (testbit(d1->d_first, ibit)) { 95 | if (accel[ibit] != -1) 96 | printf("XXX ambiguity ?\n"); 97 | accel[ibit] = a->a_arrow | (1 << 7) | 98 | ((type - NT_OFFSET) << 8); 99 | } 100 | } 101 | } 102 | else if (lbl == EMPTY) 103 | s->s_accept = 1; 104 | else if (lbl >= 0 && lbl < nl) 105 | accel[lbl] = a->a_arrow; 106 | } 107 | while (nl > 0 && accel[nl-1] == -1) 108 | nl--; 109 | for (k = 0; k < nl && accel[k] == -1;) 110 | k++; 111 | if (k < nl) { 112 | int i; 113 | s->s_accel = (int *) PyObject_MALLOC((nl-k) * sizeof(int)); 114 | if (s->s_accel == NULL) { 115 | fprintf(stderr, "no mem to add parser accelerators\n"); 116 | exit(1); 117 | } 118 | s->s_lower = k; 119 | s->s_upper = nl; 120 | for (i = 0; k < nl; i++, k++) 121 | s->s_accel[i] = accel[k]; 122 | } 123 | PyObject_FREE(accel); 124 | } 125 | -------------------------------------------------------------------------------- /flowpython/py36/ReadMe.md: -------------------------------------------------------------------------------- 1 | The precompiled binaries can be found at [Release Page](https://github.com/thautwarm/flowpython/releases). -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # # -*- coding: utf-8 -*- 2 | # from setuptools import setup, find_packages 3 | # import io 4 | # import os 5 | # import sys 6 | # import platform 7 | # is_linux = any(platform.linux_distribution()) 8 | # HOME = os.environ['HOME' if is_linux else 'HOMEPATH'] 9 | 10 | # with io.open('./flowpython/ReadMe.rst', encoding='utf-8') as f: 11 | # readme = f.read() 12 | # if input("Are you using MacOS?[yes|no]") != 'yes': 13 | # print("I'm sorry to be so poor to get a MacOS machine, so no distribution for that os :(") 14 | # print("Or you can buy me one and I'll make the corresponding binaries for you :)") 15 | # sys.exit(0) 16 | # else: 17 | # cat = os.path.join 18 | # setup( 19 | # name = 'flowpython', 20 | # version = '0.3', 21 | # keywords='gramamr, ast, readability', 22 | # description = "Additional Grammar Compatible to CPython", 23 | # long_description=readme, 24 | # license = 'MIT License', 25 | # url = 'https://github.com/thautwarm/flowpython', 26 | # author = 'Thautwarm', 27 | # author_email = 'twshere@outlook.com', 28 | # packages = find_packages(), 29 | # include_package_data = True, 30 | # platforms = ['windows', 'linux'], 31 | # classifiers=['Programming Language :: Python :: 3.6','Programming Language :: Python :: Implementation :: CPython'] 32 | # ) 33 | # try: 34 | # os.remove(cat(HOME, '.flowpy')) 35 | # except FileNotFoundError: 36 | # pass 37 | 38 | 39 | -------------------------------------------------------------------------------- /temp_version/py36/py365/Grammar/Grammar: -------------------------------------------------------------------------------- 1 | # Grammar for Python 2 | 3 | # NOTE WELL: You should also follow all the steps listed at 4 | # https://docs.python.org/devguide/grammar.html 5 | 6 | # Start symbols for the grammar: 7 | # single_input is a single interactive statement; 8 | # file_input is a module or sequence of commands read from an input file; 9 | # eval_input is the input for the eval() functions. 10 | # NB: compound_stmt in single_input is followed by extra NEWLINE! 11 | single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE 12 | file_input: (NEWLINE | stmt)* ENDMARKER 13 | eval_input: testlist NEWLINE* ENDMARKER 14 | 15 | decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE 16 | decorators: decorator+ 17 | decorated: decorators (classdef | funcdef | async_funcdef) 18 | 19 | async_funcdef: ASYNC funcdef 20 | funcdef: 'def' NAME parameters ['->' test] ':' suite 21 | 22 | parameters: '(' [typedargslist] ')' 23 | typedargslist: (tfpdef ['=' test] (',' tfpdef ['=' test])* [',' [ 24 | '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]] 25 | | '**' tfpdef [',']]] 26 | | '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]] 27 | | '**' tfpdef [',']) 28 | tfpdef: NAME [':' test] 29 | varargslist: (vfpdef ['=' test] (',' vfpdef ['=' test])* [',' [ 30 | '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]] 31 | | '**' vfpdef [',']]] 32 | | '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]] 33 | | '**' vfpdef [','] 34 | ) 35 | vfpdef: NAME 36 | 37 | 38 | stmt: simple_stmt | compound_stmt 39 | simple_stmt: small_stmt ((';' small_stmt)* [';'] NEWLINE | where_handler_stmt) 40 | small_stmt: (expr_stmt | del_stmt | pass_stmt | flow_stmt | 41 | import_stmt | global_stmt | nonlocal_stmt | assert_stmt) 42 | expr_stmt: testlist_star_expr (annassign | augassign (yield_expr|testlist) | 43 | ('=' (yield_expr|testlist_star_expr))*) 44 | annassign: ':' test ['=' test] 45 | testlist_star_expr: (test|star_expr) (',' (test|star_expr))* [','] 46 | augassign: ('+=' | '-=' | '*=' | '@=' | '/=' | '%=' | '&=' | '|=' | '^=' | 47 | '<<=' | '>>=' | '**=' | '//=') 48 | # For normal and annotated assignments, additional restrictions enforced by the interpreter 49 | del_stmt: 'del' exprlist 50 | pass_stmt: 'pass' 51 | flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt | yield_stmt 52 | break_stmt: 'break' 53 | continue_stmt: 'continue' 54 | return_stmt: 'return' [testlist] 55 | yield_stmt: yield_expr 56 | raise_stmt: 'raise' [test ['from' test]] 57 | import_stmt: import_name | import_from 58 | import_name: 'import' dotted_as_names 59 | # note below: the ('.' | '...') is necessary because '...' is tokenized as ELLIPSIS 60 | import_from: ('from' (('.' | '...')* dotted_name | ('.' | '...')+) 61 | 'import' ('*' | '(' import_as_names ')' | import_as_names)) 62 | import_as_name: NAME ['as' NAME] 63 | dotted_as_name: dotted_name ['as' NAME] 64 | import_as_names: import_as_name (',' import_as_name)* [','] 65 | dotted_as_names: dotted_as_name (',' dotted_as_name)* 66 | dotted_name: NAME ('.' NAME)* 67 | global_stmt: 'global' NAME (',' NAME)* 68 | nonlocal_stmt: 'nonlocal' NAME (',' NAME)* 69 | assert_stmt: 'assert' test [',' test] 70 | 71 | compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated | async_stmt | switch_stmt | branch_stmt 72 | async_stmt: ASYNC (funcdef | with_stmt | for_stmt) 73 | if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite] 74 | switch_stmt: switch_head test ':' NEWLINE INDENT case_stmt+ DEDENT 75 | branch_stmt: ('|' test '=' '>' suite)+ 76 | while_stmt: 'while' test ':' suite ['else' ':' suite] 77 | for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite] 78 | try_stmt: ('try' ':' suite 79 | ((except_clause ':' suite)+ 80 | ['else' ':' suite] 81 | ['finally' ':' suite] | 82 | 'finally' ':' suite)) 83 | with_stmt: 'with' with_item (',' with_item)* ':' suite 84 | with_item: test ['as' expr] 85 | # NB compile.c makes sure that the default except clause is last 86 | except_clause: 'except' [test ['as' NAME]] 87 | suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT 88 | 89 | test: or_test ['if' or_test 'else' [NEWLINE [INDENT]] test | '->' ( '>' pipeline_expr | test) ] | lambdef 90 | test_nocond: or_test | lambdef_nocond 91 | lambdef: 'lambda' [varargslist] ':' test | lambdef_head [typedargslist] 'def' test | '.' [typedargslist] '->' test 92 | lambdef_nocond: 'lambda' [varargslist] ':' test_nocond | lambdef_head [typedargslist] 'def' test_nocond | '.' [typedargslist] '->' test_nocond 93 | or_test: and_test ('or' and_test)* 94 | and_test: not_test ('and' not_test)* 95 | not_test: 'not' not_test | comparison 96 | comparison: expr (comp_op expr)* 97 | # <> isn't actually a valid comparison operator in Python. It's here for the 98 | # sake of a __future__ import described in PEP 401 (which really works :-) 99 | comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not' 100 | star_expr: '*' expr 101 | expr: xor_expr ('|' xor_expr)* 102 | xor_expr: and_expr ('^' and_expr)* 103 | and_expr: shift_expr ('&' shift_expr)* 104 | shift_expr: arith_expr (('<<'|'>>') arith_expr)* 105 | arith_expr: term (('+'|'-') term)* 106 | term: factor (('*'|'@'|'/'|'%'|'//') factor)* 107 | factor: ('+'|'-'|'~') factor | power 108 | power: atom_expr ['**' factor] 109 | atom_expr: [AWAIT] atom trailer* 110 | atom: ('(' [yield_expr|testlist_comp] ')' | 111 | '[' [testlist_comp] ']' | 112 | '{' [dictorsetmaker] '}' | 113 | NAME | NUMBER | STRING+ | '...' | 'None' | 'True' | 'False') 114 | testlist_comp: (test|star_expr) ( comp_for | (',' (test|star_expr))* [','] ) 115 | trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME 116 | subscriptlist: subscript (',' subscript)* [','] 117 | subscript: test | [test] ':' [test] [sliceop] 118 | sliceop: ':' [test] 119 | exprlist: (expr|star_expr) (',' (expr|star_expr))* [','] 120 | testlist: test (',' test)* [','] 121 | dictorsetmaker: ( ((test ':' test | '**' expr) 122 | (comp_for | (',' (test ':' test | '**' expr))* [','])) | 123 | ((test | star_expr) 124 | (comp_for | (',' (test | star_expr))* [','])) ) 125 | 126 | classdef: 'class' NAME ['(' [arglist] ')'] ':' suite 127 | 128 | arglist: argument (',' argument)* [','] 129 | 130 | # The reason that keywords are test nodes instead of NAME is that using NAME 131 | # results in an ambiguity. ast.c makes sure it's a NAME. 132 | # "test '=' test" is really "keyword '=' test", but we have no such token. 133 | # These need to be in a single rule to avoid grammar that is ambiguous 134 | # to our LL(1) parser. Even though 'test' includes '*expr' in star_expr, 135 | # we explicitly match '*' here, too, to give it proper precedence. 136 | # Illegal combinations and orderings are blocked in ast.c: 137 | # multiple (test comp_for) arguments are blocked; keyword unpackings 138 | # that precede iterable unpackings are blocked; etc. 139 | argument: ( test [comp_for] | 140 | test '=' test | 141 | '**' test | 142 | '*' test ) 143 | 144 | comp_iter: comp_for | comp_if 145 | comp_for: [ASYNC] 'for' exprlist 'in' or_test [comp_iter] 146 | comp_if: 'if' test_nocond [comp_iter] 147 | 148 | # not used in grammar, but may appear in "node" passed from Parser to Compiler 149 | encoding_decl: NAME 150 | 151 | yield_expr: 'yield' [yield_arg] 152 | yield_arg: 'from' test | testlist 153 | 154 | where_handler_stmt: NAME ':' suite 155 | lambdef_head: 'as' ['-' 'with'] 156 | 157 | case_stmt: case_head [test [':' test]] '=' '>' suite 158 | mode_stmt: '[' [comp_op] ']' | '(' [test] ')' | '{' [test] '}' | '+' ('[' [comp_op] ']' | '(' [test] ')') 159 | switch_head : ('condic' | 'condef') [mode_stmt] 160 | case_head : [mode_stmt [NEWLINE] ] NAME 161 | pipeline_expr : test ( '=' '>' test)* 162 | -------------------------------------------------------------------------------- /temp_version/py36/py365/Parser/Python.asdl: -------------------------------------------------------------------------------- 1 | -- ASDL's 7 builtin types are: 2 | -- identifier, int, string, bytes, object, singleton, constant 3 | -- 4 | -- singleton: None, True or False 5 | -- constant can be None, whereas None means "no value" for object. 6 | 7 | module Python 8 | { 9 | mod = Module(stmt* body) 10 | | Interactive(stmt* body) 11 | | Expression(expr body) 12 | 13 | -- not really an actual node but useful in Jython's typesystem. 14 | | Suite(stmt* body) 15 | 16 | stmt = FunctionDef(identifier name, arguments args, 17 | stmt* body, expr* decorator_list, expr? returns) 18 | | AsyncFunctionDef(identifier name, arguments args, 19 | stmt* body, expr* decorator_list, expr? returns) 20 | 21 | | ClassDef(identifier name, 22 | expr* bases, 23 | keyword* keywords, 24 | stmt* body, 25 | expr* decorator_list) 26 | | Return(expr? value) 27 | 28 | | Delete(expr* targets) 29 | | Assign(expr* targets, expr value) 30 | | AugAssign(expr target, operator op, expr value) 31 | -- 'simple' indicates that we annotate simple name without parens 32 | | AnnAssign(expr target, expr annotation, expr? value, int simple) 33 | 34 | -- use 'orelse' because else is a keyword in target languages 35 | | For(expr target, expr iter, stmt* body, stmt* orelse) 36 | | AsyncFor(expr target, expr iter, stmt* body, stmt* orelse) 37 | | While(expr test, stmt* body, stmt* orelse) 38 | | If(expr test, stmt* body, stmt* orelse) 39 | | With(withitem* items, stmt* body) 40 | | AsyncWith(withitem* items, stmt* body) 41 | 42 | | Raise(expr? exc, expr? cause) 43 | | Try(stmt* body, excepthandler* handlers, stmt* orelse, stmt* finalbody) 44 | | Assert(expr test, expr? msg) 45 | 46 | | Import(alias* names) 47 | | ImportFrom(identifier? module, alias* names, int? level) 48 | 49 | | Global(identifier* names) 50 | | Nonlocal(identifier* names) 51 | | Expr(expr value) 52 | | Pass | Break | Continue 53 | 54 | -- XXX Jython will be different 55 | -- col_offset is the byte offset in the utf8 string the parser uses 56 | attributes (int lineno, int col_offset) 57 | 58 | -- BoolOp() can use left & right? 59 | expr = BoolOp(boolop op, expr* values) 60 | | BinOp(expr left, operator op, expr right) 61 | | UnaryOp(unaryop op, expr operand) 62 | | Lambda(arguments args, expr body) 63 | | IfExp(expr test, expr body, expr orelse) 64 | | Dict(expr* keys, expr* values) 65 | | Set(expr* elts) 66 | | ListComp(expr elt, comprehension* generators) 67 | | SetComp(expr elt, comprehension* generators) 68 | | DictComp(expr key, expr value, comprehension* generators) 69 | | GeneratorExp(expr elt, comprehension* generators) 70 | -- the grammar constrains where yield expressions can occur 71 | | Await(expr value) 72 | | Yield(expr? value) 73 | | YieldFrom(expr value) 74 | -- need sequences for compare to distinguish between 75 | -- x < 4 < 3 and (x < 4) < 3 76 | | Compare(expr left, cmpop* ops, expr* comparators) 77 | | Call(expr func, expr* args, keyword* keywords) 78 | | Num(object n) -- a number as a PyObject. 79 | | Str(string s) -- need to specify raw, unicode, etc? 80 | | FormattedValue(expr value, int? conversion, expr? format_spec) 81 | | JoinedStr(expr* values) 82 | | Bytes(bytes s) 83 | | NameConstant(singleton value) 84 | | Ellipsis 85 | | Constant(constant value) 86 | 87 | -- the following expression can appear in assignment context 88 | | Attribute(expr value, identifier attr, expr_context ctx) 89 | | Subscript(expr value, slice slice, expr_context ctx) 90 | | Starred(expr value, expr_context ctx) 91 | | Name(identifier id, expr_context ctx) 92 | | List(expr* elts, expr_context ctx) 93 | | Tuple(expr* elts, expr_context ctx) 94 | | Where(expr target, stmt* body) 95 | 96 | -- col_offset is the byte offset in the utf8 string the parser uses 97 | attributes (int lineno, int col_offset) 98 | 99 | expr_context = Load | Store | Del | AugLoad | AugStore | Param 100 | 101 | slice = Slice(expr? lower, expr? upper, expr? step) 102 | | ExtSlice(slice* dims) 103 | | Index(expr value) 104 | 105 | boolop = And | Or 106 | 107 | operator = Add | Sub | Mult | MatMult | Div | Mod | Pow | LShift 108 | | RShift | BitOr | BitXor | BitAnd | FloorDiv 109 | 110 | unaryop = Invert | Not | UAdd | USub 111 | 112 | cmpop = Eq | NotEq | Lt | LtE | Gt | GtE | Is | IsNot | In | NotIn 113 | 114 | comprehension = (expr target, expr iter, expr* ifs, int is_async) 115 | 116 | excepthandler = ExceptHandler(expr? type, identifier? name, stmt* body) 117 | attributes (int lineno, int col_offset) 118 | 119 | arguments = (arg* args, arg? vararg, arg* kwonlyargs, expr* kw_defaults, 120 | arg? kwarg, expr* defaults) 121 | 122 | arg = (identifier arg, expr? annotation) 123 | attributes (int lineno, int col_offset) 124 | 125 | -- keyword arguments supplied to call (NULL identifier for **kwargs) 126 | keyword = (identifier? arg, expr value) 127 | 128 | -- import name with optional 'as' alias. 129 | alias = (identifier name, identifier? asname) 130 | 131 | withitem = (expr context_expr, expr? optional_vars) 132 | } 133 | 134 | -------------------------------------------------------------------------------- /test/f_test.py: -------------------------------------------------------------------------------- 1 | 2 | f = .x -> r where: 3 | condic x: 4 | [==] 5 | case [] => r = 0 6 | 7 | +[==] 8 | case (a,*b) -> b:[] => r = a 9 | 10 | otherwise => r= a + f(b) 11 | 12 | 13 | 14 | from flowpython.fp import flow_map, flat_map,flatten, andThen, fastmap, flow_reduce 15 | a = [[1,2,3],[3,4,6], [2,3,4,5,6,7,8],[2,3,4,5,1,2,3]] 16 | fast_flat_map = lambda f: andThen(flatten)(fastmap(f)) 17 | def test1(): 18 | a ->> fast_flat_map(.x->x+1) => list 19 | def test2(): 20 | a ->> flat_map(.x->x+1) => list 21 | a ->> fast_flat_map(.x->x+1) => list => flow_reduce(.x,y->x-y) => print 22 | -------------------------------------------------------------------------------- /test/forfun.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Created on Sun Aug 13 15:36:58 2017 5 | 6 | @author: thautwarm 7 | """ 8 | 9 | """ 10 | this module is just for fun!!! 11 | """ 12 | from copy import deepcopy 13 | permutations = .seq -> seq_seq where: 14 | condic+[] seq: 15 | case (a, ) => seq_seq = [[a,]] 16 | case (a,*b) => 17 | seq_seq = permutations(b) -> map(.x -> insertAll(x, a), _) -> sum(_, []) where: 18 | insertAll = . x, a -> ret where: 19 | ret = [ deepcopy(x) -> _.insert(i, a) or _ for i in (len(x) -> range(_+1)) ] 20 | a = range(3) -> permutations(_) -------------------------------------------------------------------------------- /test/some.py: -------------------------------------------------------------------------------- 1 | print("FlowPython as-with-def Lambda") 2 | t=as-with a,b,c def as d,e,f def as g,h,i def ret_value where: 3 | middle_value1 = a*e*i + b*f*g + c*d*h 4 | middle_value2 = g*e*c + h*f*a + i*d*b 5 | ret_value = middle_value1 - middle_value2 6 | print(t (1,2,3)\ 7 | (0,2,1)\ 8 | (0,0,3) == 6) 9 | 10 | 11 | t=.a,b,c -> .d,e,f -> .g,h,i -> ret_value where: 12 | middle_value1 = a*e*i + b*f*g + c*d*h 13 | middle_value2 = g*e*c + h*f*a + i*d*b 14 | ret_value = middle_value1 - middle_value2 15 | print(t (1,2,3)\ 16 | (0,2,1)\ 17 | (0,0,3) == 6) 18 | 19 | print("Original CPython Lambda") 20 | t=lambda a,b,c : lambda d,e,f : lambda g,h,i : ret_value where: 21 | middle_value1 = a*e*i + b*f*g + c*d*h 22 | middle_value2 = g*e*c + h*f*a + i*d*b 23 | ret_value = middle_value1 - middle_value2 24 | print(t (1,2,3)\ 25 | (0,2,1)\ 26 | (0,0,3) == 6) 27 | 28 | 29 | -------------------------------------------------------------------------------- /test/test_arrow.py: -------------------------------------------------------------------------------- 1 | assert 1 -> 2 == 2 2 | print("pass: 1->2 == 2 ") 3 | assert 1 -> _ == 1 4 | print("pass: 1 -> _ == 1") 5 | assert [1,2,3] -> map(.x->x+1, _) -> list(_) -> print(_) or True 6 | print('pass: ...') 7 | assert range(100) -> reduce(.x,y->x+y, _) -> print(_) or True where: 8 | from functools import reduce 9 | print('pass: ...') 10 | 11 | 12 | foreach = .self, f -> None where: 13 | for item in self: 14 | f(item) 15 | 16 | groupby = .self, f -> ret where: 17 | ret = defaultdict(list) where: 18 | from collections import defaultdict 19 | self -> foreach(_, setv) where: 20 | setv = .item -> None where: 21 | ret[f(item)].append(item) 22 | 23 | range(20) -> groupby(_, .x->x%2) -> print(_) 24 | 25 | -------------------------------------------------------------------------------- /test/test_branch.py: -------------------------------------------------------------------------------- 1 | print("branches No.1:") 2 | | 1==2 => a = False 3 | | 2==3 => a = False 4 | | 3 => a = True 5 | print('branch res is {a}'.format(a=a)) 6 | 7 | b = ret where: 8 | | 1 == 2 => ret = 1 9 | | 2 == 2 => ret = 2 10 | assert b == 2 11 | 12 | otherwise = True 13 | x = 2 14 | 15 | | x == 1 => x += 1 16 | | type(x) is str => x = int(x) 17 | | otherwise => 18 | x = 2*x 19 | y = 1 20 | def defined(key): 21 | return key in globals() 22 | 23 | assert x == 4 24 | print('Is y defined? => ',defined("y")) -------------------------------------------------------------------------------- /test/test_patm.py: -------------------------------------------------------------------------------- 1 | condef (.x->2*x) 1: 2 | case 2 => 3 | print("(1*2) == 2") 4 | assert (1*2) == 2 5 | otherwise => 6 | assert False, "cannot match" 7 | 8 | condic[>] 1: 9 | case 0 => 10 | print('1 > 0') 11 | assert 1 > 0 12 | otherwise => 13 | assert False, "cannot match" 14 | 15 | condic[<] 10: 16 | case 5 => 17 | assert 10 < 5, "Error Match: 10 < 5" 18 | otherwise => 19 | print("10 > 5") 20 | assert 10 > 5 21 | 22 | 23 | condic +[==] 1: 24 | case a:1 => 25 | print(" 'a == 1' and can do assignment 'a = 1' ") 26 | assert a==1 27 | otherwise => 28 | assert False, "cannot match" 29 | 30 | condic +() [1,2,3]: 31 | case (a,b) => 32 | print("can do assignment (a,b) = [1,2,3]") 33 | assert [a,b] == [1,2,3] 34 | case (*a,b) => 35 | print("can do assignment '(*a,b) = [1,2,3]' ") 36 | assert [*a,b] == [1,2,3] 37 | otherwise => 38 | assert False, "cannot match" 39 | 40 | condic () [1,2,3]: 41 | +(type) 42 | case a:list => 43 | print("can do assignment 'a = [1,2,3]' and 'type(a) == list'") 44 | assert type(a) == list and a == [1,2,3] 45 | otherwise => 46 | assert False, "cannot match" 47 | 48 | # condic {.x,y->x+y == 5} 1: 49 | # case 3 => 50 | # assert 1+3 == 5, "1+3 == 5" 51 | # case 4 => 52 | # print("1+4 == 5") 53 | # assert 1+4 == 5 54 | 55 | # tomap = .f -> .var -> ret where: 56 | # ret = list(map(f, var)) 57 | 58 | 59 | # condic +(tomap(type)) [1,2,3]: 60 | # case (*a,b):[int]*3 => 61 | # print("can do assignment '(*a,b) = [1,2,3]' and tomap(type)((*a,b)) == [int]*3 ") 62 | # assert [*a,b] == [1,2,3] and tomap(type)((*a,b)) == [int]*3 63 | # otherwise => 64 | # assert False, "+(tomap(type)) case (*a,b):[int]*3 \n cannot match [1,2,3]!" 65 | 66 | # condic +(type) [1,2,3]: 67 | # case (*a, b)-> a: list => 68 | # print("niconico-ni!niconico-ni!niconico-ni!") 69 | # assert( a == [1,2] ) 70 | # otherwise => 71 | # assert type(a) is list 72 | 73 | # condic 1: 74 | # +(.x->2*x) 75 | # case a:3 => 76 | # assert a == 1 and a*2 == 3 77 | 78 | # +[is not] 79 | # case a:2 => 80 | # print("a == 1 and a is not 2") 81 | # assert a == 1 and a is not 2 82 | 83 | # otherwise => 84 | # ... 85 | -------------------------------------------------------------------------------- /test/test_where.py: -------------------------------------------------------------------------------- 1 | print('test_where') 2 | 1 where: 3 | a = 1 4 | print(a == 1) 5 | 6 | # ============================================ 7 | a = ret where: 8 | ret = 10 9 | print(a == 10) 10 | 11 | 12 | # ============================================ 13 | a = ret1*ret2 where: 14 | ret1 = ret2*r where: 15 | r = 10 16 | ret2 = 2 17 | print(a == 40) 18 | 19 | # ============================================ 20 | from math import pi 21 | r = 1 # the radius 22 | h = 10 # the height 23 | 24 | S = (2*S_top + S_side) where: 25 | S_top = pi*r**2 26 | S_side = C * h where: 27 | C = 2*pi*r 28 | 29 | 30 | -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | joinPath = os.path.join 4 | def makedir(file): 5 | try: 6 | os.makedirs(file[:file.rfind("/")+1]) 7 | except: 8 | pass 9 | def createTemp(file:"filename of temp file"): 10 | if not os.path.exists(file): 11 | makedir(file) 12 | def isFileExists(file:"filename")-> "# Judge if source file exists and deal with the errors.": 13 | return True if os.path.exists(file) else FileNotFoundError(file) 14 | 15 | def tryAction(string): 16 | try: 17 | return eval(string) 18 | except: 19 | return BaseException(string) 20 | 21 | def fload(file): 22 | if not os.path.exists(file): 23 | print(Warning("{} does not exists.".format(file))) 24 | return 25 | with open(file, encoding='gb18030') as f: 26 | s = f.read() 27 | return s 28 | 29 | def fsave(source, file): 30 | if not source: return 31 | if not os.path.exists(file): 32 | print(Warning("{} does not exists. create the file now...".format(file))) 33 | makedir(file) 34 | with open(file,'w', encoding='gb18030') as f: 35 | f.write(source) 36 | --------------------------------------------------------------------------------