cpyte 2.0.0__tar.gz → 2.0.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {cpyte-2.0.0 → cpyte-2.0.2}/PKG-INFO +42 -1
- {cpyte-2.0.0 → cpyte-2.0.2}/pyproject.toml +1 -1
- {cpyte-2.0.0 → cpyte-2.0.2}/readme.md +41 -0
- {cpyte-2.0.0 → cpyte-2.0.2}/source/cpyte/astparse.py +292 -49
- {cpyte-2.0.0 → cpyte-2.0.2}/source/cpyte/bytecoding.py +264 -24
- {cpyte-2.0.0 → cpyte-2.0.2}/source/cpyte/compiling.py +98 -82
- cpyte-2.0.2/source/cpyte/extension_hooks.py +460 -0
- {cpyte-2.0.0 → cpyte-2.0.2}/source/cpyte/lexar.py +52 -3
- {cpyte-2.0.0 → cpyte-2.0.2}/source/cpyte/mainpie.py +108 -22
- cpyte-2.0.2/source/cpyte/package_manifest.py +410 -0
- {cpyte-2.0.0 → cpyte-2.0.2}/source/cpyte/semantic_analasis.py +284 -28
- {cpyte-2.0.0 → cpyte-2.0.2}/source/cpyte.egg-info/PKG-INFO +42 -1
- {cpyte-2.0.0 → cpyte-2.0.2}/source/cpyte.egg-info/SOURCES.txt +2 -1
- cpyte-2.0.0/source/cpyte/cpy_language_documentation.md +0 -1522
- {cpyte-2.0.0 → cpyte-2.0.2}/setup.cfg +0 -0
- {cpyte-2.0.0 → cpyte-2.0.2}/source/cpyte/__init__.py +0 -0
- {cpyte-2.0.0 → cpyte-2.0.2}/source/cpyte/__main__.py +0 -0
- {cpyte-2.0.0 → cpyte-2.0.2}/source/cpyte/_bignum_bc.py +0 -0
- {cpyte-2.0.0 → cpyte-2.0.2}/source/cpyte/_runtime_bc.py +0 -0
- {cpyte-2.0.0 → cpyte-2.0.2}/source/cpyte/clib.py +0 -0
- {cpyte-2.0.0 → cpyte-2.0.2}/source/cpyte/generate_bc.py +0 -0
- {cpyte-2.0.0 → cpyte-2.0.2}/source/cpyte/linker.py +0 -0
- {cpyte-2.0.0 → cpyte-2.0.2}/source/cpyte/lsp_server.py +0 -0
- {cpyte-2.0.0 → cpyte-2.0.2}/source/cpyte.egg-info/dependency_links.txt +0 -0
- {cpyte-2.0.0 → cpyte-2.0.2}/source/cpyte.egg-info/entry_points.txt +0 -0
- {cpyte-2.0.0 → cpyte-2.0.2}/source/cpyte.egg-info/requires.txt +0 -0
- {cpyte-2.0.0 → cpyte-2.0.2}/source/cpyte.egg-info/top_level.txt +0 -0
- {cpyte-2.0.0 → cpyte-2.0.2}/test/test_bignum_jit.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cpyte
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.2
|
|
4
4
|
Summary: The Cpyte programming language compiler
|
|
5
5
|
Author: Hoang Duy Tung
|
|
6
6
|
License: MIT
|
|
@@ -13,6 +13,46 @@ Check out the official documentation [here](https://gitea.5gnew.io.vn/duytung/Cp
|
|
|
13
13
|
|
|
14
14
|
**Note:** Version 1.6.1 had a critical division by zero error that wasn't caught.
|
|
15
15
|
|
|
16
|
+
## Package Extensions
|
|
17
|
+
|
|
18
|
+
Cpyte supports a package extension system that allows packages to extend the compiler with custom keywords, operators, and compiler hooks. Packages can provide:
|
|
19
|
+
|
|
20
|
+
- **Custom Keywords**: Add new language keywords via `package.json`
|
|
21
|
+
- **Custom Operators**: Define new operators for syntax extensions
|
|
22
|
+
- **Compiler Hooks**: Extend lexing, parsing, semantic analysis, and code generation
|
|
23
|
+
- **Runtime Extensions**: Add runtime code and libraries
|
|
24
|
+
|
|
25
|
+
### Package Structure
|
|
26
|
+
|
|
27
|
+
Packages are stored in `.cpm/modules/package_name/version/` and can include:
|
|
28
|
+
|
|
29
|
+
- `package.json` - Extension manifest declaring capabilities
|
|
30
|
+
- `parser_hooks.py` - Custom syntax extensions
|
|
31
|
+
- `semantic_hooks.py` - Custom type checking and analysis rules
|
|
32
|
+
- `codegen_hooks.py` - Custom LLVM IR generation
|
|
33
|
+
- `runtime_hooks.py` - Runtime code and libraries
|
|
34
|
+
- `*.cpy` - Main package entry point
|
|
35
|
+
- `*.ll` - Prebuilt LLVM IR
|
|
36
|
+
|
|
37
|
+
### Example Package
|
|
38
|
+
|
|
39
|
+
See `examples/example_package/` for a complete example package with:
|
|
40
|
+
- Custom keywords: `async`, `await`, `defer`
|
|
41
|
+
- Custom operator: `~~`
|
|
42
|
+
- Parser, semantic, codegen, and runtime hooks
|
|
43
|
+
|
|
44
|
+
### Using Extension Packages
|
|
45
|
+
|
|
46
|
+
```cpy
|
|
47
|
+
import @package_name
|
|
48
|
+
|
|
49
|
+
# Use custom keywords and syntax from the package
|
|
50
|
+
async def my_function() -> Promise:
|
|
51
|
+
# Custom syntax
|
|
52
|
+
defer cleanup()
|
|
53
|
+
return result
|
|
54
|
+
```
|
|
55
|
+
|
|
16
56
|
## Examples
|
|
17
57
|
|
|
18
58
|
You'll find comprehensive examples in the `examples/` directory that cover C imports, header imports, 64-bit support, and standard library usage:
|
|
@@ -28,6 +68,7 @@ You'll find comprehensive examples in the `examples/` directory that cover C imp
|
|
|
28
68
|
| `examples/mixed_features.cpy` | Combines C imports, H imports, 64-bit, structs, pointers, linked lists |
|
|
29
69
|
| `examples/mixed_helpers.c` / `mixed_helpers.h` | Supporting C/header files for the mixed features example |
|
|
30
70
|
| `examples/test.cpy` | macOS event tap example that intercepts keyboard events using ApplicationServices framework |
|
|
71
|
+
| `examples/example_package/` | Example package demonstrating extension capabilities with custom keywords, operators, and compiler hooks |
|
|
31
72
|
|
|
32
73
|
You can also import `.cpy` files — the examples show how to use `import "other.cpy"` to bring in public functions and structs from other Cpy files.
|
|
33
74
|
|
|
@@ -2,6 +2,46 @@ Check out the official documentation [here](https://gitea.5gnew.io.vn/duytung/Cp
|
|
|
2
2
|
|
|
3
3
|
**Note:** Version 1.6.1 had a critical division by zero error that wasn't caught.
|
|
4
4
|
|
|
5
|
+
## Package Extensions
|
|
6
|
+
|
|
7
|
+
Cpyte supports a package extension system that allows packages to extend the compiler with custom keywords, operators, and compiler hooks. Packages can provide:
|
|
8
|
+
|
|
9
|
+
- **Custom Keywords**: Add new language keywords via `package.json`
|
|
10
|
+
- **Custom Operators**: Define new operators for syntax extensions
|
|
11
|
+
- **Compiler Hooks**: Extend lexing, parsing, semantic analysis, and code generation
|
|
12
|
+
- **Runtime Extensions**: Add runtime code and libraries
|
|
13
|
+
|
|
14
|
+
### Package Structure
|
|
15
|
+
|
|
16
|
+
Packages are stored in `.cpm/modules/package_name/version/` and can include:
|
|
17
|
+
|
|
18
|
+
- `package.json` - Extension manifest declaring capabilities
|
|
19
|
+
- `parser_hooks.py` - Custom syntax extensions
|
|
20
|
+
- `semantic_hooks.py` - Custom type checking and analysis rules
|
|
21
|
+
- `codegen_hooks.py` - Custom LLVM IR generation
|
|
22
|
+
- `runtime_hooks.py` - Runtime code and libraries
|
|
23
|
+
- `*.cpy` - Main package entry point
|
|
24
|
+
- `*.ll` - Prebuilt LLVM IR
|
|
25
|
+
|
|
26
|
+
### Example Package
|
|
27
|
+
|
|
28
|
+
See `examples/example_package/` for a complete example package with:
|
|
29
|
+
- Custom keywords: `async`, `await`, `defer`
|
|
30
|
+
- Custom operator: `~~`
|
|
31
|
+
- Parser, semantic, codegen, and runtime hooks
|
|
32
|
+
|
|
33
|
+
### Using Extension Packages
|
|
34
|
+
|
|
35
|
+
```cpy
|
|
36
|
+
import @package_name
|
|
37
|
+
|
|
38
|
+
# Use custom keywords and syntax from the package
|
|
39
|
+
async def my_function() -> Promise:
|
|
40
|
+
# Custom syntax
|
|
41
|
+
defer cleanup()
|
|
42
|
+
return result
|
|
43
|
+
```
|
|
44
|
+
|
|
5
45
|
## Examples
|
|
6
46
|
|
|
7
47
|
You'll find comprehensive examples in the `examples/` directory that cover C imports, header imports, 64-bit support, and standard library usage:
|
|
@@ -17,6 +57,7 @@ You'll find comprehensive examples in the `examples/` directory that cover C imp
|
|
|
17
57
|
| `examples/mixed_features.cpy` | Combines C imports, H imports, 64-bit, structs, pointers, linked lists |
|
|
18
58
|
| `examples/mixed_helpers.c` / `mixed_helpers.h` | Supporting C/header files for the mixed features example |
|
|
19
59
|
| `examples/test.cpy` | macOS event tap example that intercepts keyboard events using ApplicationServices framework |
|
|
60
|
+
| `examples/example_package/` | Example package demonstrating extension capabilities with custom keywords, operators, and compiler hooks |
|
|
20
61
|
|
|
21
62
|
You can also import `.cpy` files — the examples show how to use `import "other.cpy"` to bring in public functions and structs from other Cpy files.
|
|
22
63
|
|
|
@@ -1,4 +1,40 @@
|
|
|
1
1
|
from .lexar import Token, TokenType
|
|
2
|
+
from typing import Optional, Dict, Any, List
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
_parser_hooks: List[Any] = []
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def register_parser_hook(hook: Any) -> None:
|
|
9
|
+
_parser_hooks.append(hook)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def unregister_parser_hook(hook: Any) -> None:
|
|
13
|
+
if hook in _parser_hooks:
|
|
14
|
+
_parser_hooks.remove(hook)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def get_parser_hooks() -> List[Any]:
|
|
18
|
+
return _parser_hooks.copy()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def clear_parser_hooks() -> None:
|
|
22
|
+
_parser_hooks.clear()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _get_all_parser_hooks(enable_extensions: bool) -> List[Any]:
|
|
26
|
+
if not enable_extensions:
|
|
27
|
+
return []
|
|
28
|
+
hooks = list(_parser_hooks)
|
|
29
|
+
try:
|
|
30
|
+
from .extension_hooks import get_global_hook_registry
|
|
31
|
+
registry = get_global_hook_registry()
|
|
32
|
+
for hook in registry.get_parser_hooks():
|
|
33
|
+
if hook not in hooks:
|
|
34
|
+
hooks.append(hook)
|
|
35
|
+
except ImportError:
|
|
36
|
+
pass
|
|
37
|
+
return hooks
|
|
2
38
|
|
|
3
39
|
|
|
4
40
|
def parse_f(tree: list[Token]):
|
|
@@ -45,7 +81,7 @@ class Number:
|
|
|
45
81
|
return f'Number({self.value})'
|
|
46
82
|
|
|
47
83
|
class String:
|
|
48
|
-
__slots__ = ('value', '_token')
|
|
84
|
+
__slots__ = ('value', '_token', 'inferred_type')
|
|
49
85
|
def __init__(self, value: str, token=None):
|
|
50
86
|
self.value = value
|
|
51
87
|
self._token = token
|
|
@@ -53,7 +89,7 @@ class String:
|
|
|
53
89
|
return f'String({self.value})'
|
|
54
90
|
|
|
55
91
|
class Variable:
|
|
56
|
-
__slots__ = ('name', '_token', 'const_value')
|
|
92
|
+
__slots__ = ('name', '_token', 'const_value', 'inferred_type')
|
|
57
93
|
def __init__(self, name: str, token=None):
|
|
58
94
|
self.name = name
|
|
59
95
|
self._token = token
|
|
@@ -263,6 +299,9 @@ def _parse_atom(tokens: list[Token], pos: int):
|
|
|
263
299
|
pos += 1
|
|
264
300
|
return NewExpr(type_str, size, token=tok), pos
|
|
265
301
|
|
|
302
|
+
if tok.type == TokenType.KEYWORD and tok.value == 'asm':
|
|
303
|
+
return parse_inline_asm(tokens, pos)
|
|
304
|
+
|
|
266
305
|
if tok.type == TokenType.KEYWORD and tok.value == 'sizeof':
|
|
267
306
|
pos += 1
|
|
268
307
|
if pos >= len(tokens) or tokens[pos].type != TokenType.LPAREN:
|
|
@@ -321,7 +360,7 @@ def _parse_postfix(tokens: list[Token], pos: int, node):
|
|
|
321
360
|
return node, pos
|
|
322
361
|
|
|
323
362
|
|
|
324
|
-
def parse_file(tokens: list[Token], pos: int = 0):
|
|
363
|
+
def parse_file(tokens: list[Token], pos: int = 0, enable_extensions: bool = True):
|
|
325
364
|
nodes = []
|
|
326
365
|
while pos < len(tokens) and tokens[pos].type not in (TokenType.EOF, TokenType.DEDENT):
|
|
327
366
|
while pos < len(tokens) and tokens[pos].type == TokenType.NEWLINE:
|
|
@@ -331,55 +370,79 @@ def parse_file(tokens: list[Token], pos: int = 0):
|
|
|
331
370
|
break
|
|
332
371
|
|
|
333
372
|
tok = tokens[pos]
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
node, pos = parse_class(tokens, pos)
|
|
340
|
-
elif tok.type == TokenType.KEYWORD and tok.value == 'struct':
|
|
341
|
-
node, pos = parse_struct_def(tokens, pos)
|
|
342
|
-
elif tok.type == TokenType.KEYWORD and tok.value == 'if':
|
|
343
|
-
node, pos = parse_if(tokens, pos)
|
|
344
|
-
elif tok.type == TokenType.KEYWORD and tok.value == 'return':
|
|
345
|
-
node, pos = parse_return(tokens, pos)
|
|
346
|
-
elif tok.type == TokenType.KEYWORD and tok.value == 'while':
|
|
347
|
-
node, pos = parse_while(tokens, pos)
|
|
348
|
-
elif tok.type == TokenType.KEYWORD and tok.value == 'for':
|
|
349
|
-
node, pos = parse_for(tokens, pos)
|
|
350
|
-
elif tok.type == TokenType.KEYWORD and tok.value == 'import':
|
|
351
|
-
node, pos = parse_import(tokens, pos)
|
|
352
|
-
elif tok.type == TokenType.KEYWORD and tok.value == 'print':
|
|
353
|
-
node, pos = parse_print(tokens, pos)
|
|
354
|
-
elif tok.type == TokenType.KEYWORD and tok.value == 'break':
|
|
355
|
-
node, pos = parse_break(tokens, pos)
|
|
356
|
-
elif tok.type == TokenType.KEYWORD and tok.value == 'continue':
|
|
357
|
-
node, pos = parse_continue(tokens, pos)
|
|
358
|
-
elif tok.type == TokenType.KEYWORD and tok.value == 'switch':
|
|
359
|
-
node, pos = parse_switch(tokens, pos)
|
|
360
|
-
elif tok.type == TokenType.IDENTIFIER and pos + 1 < len(tokens):
|
|
361
|
-
if tok.value in _TYPE_NAMES or _looks_like_type(tokens, pos):
|
|
373
|
+
|
|
374
|
+
all_hooks = _get_all_parser_hooks(enable_extensions)
|
|
375
|
+
if all_hooks:
|
|
376
|
+
handled = False
|
|
377
|
+
for hook in all_hooks:
|
|
362
378
|
try:
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
if node is not None:
|
|
379
|
+
if hasattr(hook, 'should_handle_statement') and hook.should_handle_statement(tokens, pos):
|
|
380
|
+
node, pos = hook.parse_statement(tokens, pos, {'tokens': tokens, 'pos': pos})
|
|
366
381
|
nodes.append(node)
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
382
|
+
handled = True
|
|
383
|
+
break
|
|
384
|
+
except Exception:
|
|
385
|
+
pass
|
|
386
|
+
if not handled:
|
|
387
|
+
node, pos = _parse_standard_statement(tokens, pos)
|
|
388
|
+
nodes.append(node)
|
|
373
389
|
else:
|
|
374
|
-
node, pos =
|
|
375
|
-
|
|
376
|
-
if node is not None:
|
|
390
|
+
node, pos = _parse_standard_statement(tokens, pos)
|
|
377
391
|
nodes.append(node)
|
|
392
|
+
|
|
393
|
+
return nodes, pos
|
|
378
394
|
|
|
379
|
-
while pos < len(tokens) and tokens[pos].type == TokenType.NEWLINE:
|
|
380
|
-
pos += 1
|
|
381
395
|
|
|
382
|
-
|
|
396
|
+
def _parse_standard_statement(tokens: list[Token], pos: int):
|
|
397
|
+
"""Parse a statement using standard grammar (non-hooked)."""
|
|
398
|
+
tok = tokens[pos]
|
|
399
|
+
if tok.type == TokenType.KEYWORD and tok.value == 'def':
|
|
400
|
+
node, pos = parse_def(tokens, pos)
|
|
401
|
+
elif tok.type == TokenType.KEYWORD and tok.value in ('public', 'private', 'static', 'virtual', 'override'):
|
|
402
|
+
node, pos = parse_decorated_def(tokens, pos)
|
|
403
|
+
elif tok.type == TokenType.KEYWORD and tok.value == 'class':
|
|
404
|
+
node, pos = parse_class(tokens, pos)
|
|
405
|
+
elif tok.type == TokenType.KEYWORD and tok.value == 'struct':
|
|
406
|
+
node, pos = parse_struct_def(tokens, pos)
|
|
407
|
+
elif tok.type == TokenType.KEYWORD and tok.value == 'if':
|
|
408
|
+
node, pos = parse_if(tokens, pos)
|
|
409
|
+
elif tok.type == TokenType.KEYWORD and tok.value == 'return':
|
|
410
|
+
node, pos = parse_return(tokens, pos)
|
|
411
|
+
elif tok.type == TokenType.KEYWORD and tok.value == 'while':
|
|
412
|
+
node, pos = parse_while(tokens, pos)
|
|
413
|
+
elif tok.type == TokenType.KEYWORD and tok.value == 'for':
|
|
414
|
+
node, pos = parse_for(tokens, pos)
|
|
415
|
+
elif tok.type == TokenType.KEYWORD and tok.value == 'import':
|
|
416
|
+
node, pos = parse_import(tokens, pos)
|
|
417
|
+
elif tok.type == TokenType.KEYWORD and tok.value == 'print':
|
|
418
|
+
node, pos = parse_print(tokens, pos)
|
|
419
|
+
elif tok.type == TokenType.KEYWORD and tok.value == 'break':
|
|
420
|
+
node, pos = parse_break(tokens, pos)
|
|
421
|
+
elif tok.type == TokenType.KEYWORD and tok.value == 'continue':
|
|
422
|
+
node, pos = parse_continue(tokens, pos)
|
|
423
|
+
elif tok.type == TokenType.KEYWORD and tok.value == 'switch':
|
|
424
|
+
node, pos = parse_switch(tokens, pos)
|
|
425
|
+
elif tok.type == TokenType.KEYWORD and tok.value == 'try':
|
|
426
|
+
node, pos = parse_try(tokens, pos)
|
|
427
|
+
elif tok.type == TokenType.KEYWORD and tok.value == 'raise':
|
|
428
|
+
node, pos = parse_raise(tokens, pos)
|
|
429
|
+
elif tok.type == TokenType.IDENTIFIER and pos + 1 < len(tokens):
|
|
430
|
+
if tok.value in _TYPE_NAMES or _looks_like_type(tokens, pos):
|
|
431
|
+
try:
|
|
432
|
+
save = pos
|
|
433
|
+
node, pos = parse_var_decl(tokens, pos)
|
|
434
|
+
if node is None:
|
|
435
|
+
pos = save
|
|
436
|
+
node, pos = parse_expr_stmt(tokens, pos)
|
|
437
|
+
except ParseError:
|
|
438
|
+
pos = save
|
|
439
|
+
node, pos = parse_expr_stmt(tokens, pos)
|
|
440
|
+
else:
|
|
441
|
+
node, pos = parse_expr_stmt(tokens, pos)
|
|
442
|
+
else:
|
|
443
|
+
node, pos = parse_expr_stmt(tokens, pos)
|
|
444
|
+
|
|
445
|
+
return node, pos
|
|
383
446
|
|
|
384
447
|
|
|
385
448
|
def _parse_func_with_visibility(tokens: list[Token], pos: int, visibility: str | None, tok: Token):
|
|
@@ -451,8 +514,62 @@ def parse_class(tokens: list[Token], pos: int):
|
|
|
451
514
|
raise ParseError('Expected class name', tokens[pos] if pos < len(tokens) else None)
|
|
452
515
|
name = tokens[pos].value
|
|
453
516
|
pos += 1
|
|
517
|
+
base = None
|
|
518
|
+
if pos < len(tokens) and tokens[pos].type == TokenType.LPAREN:
|
|
519
|
+
pos += 1
|
|
520
|
+
if pos >= len(tokens) or tokens[pos].type != TokenType.IDENTIFIER:
|
|
521
|
+
raise ParseError('Expected base class name', tokens[pos] if pos < len(tokens) else None)
|
|
522
|
+
base = tokens[pos].value
|
|
523
|
+
pos += 1
|
|
524
|
+
if pos >= len(tokens) or tokens[pos].type != TokenType.RPAREN:
|
|
525
|
+
raise ParseError('Expected ")" after base class name', tokens[pos] if pos < len(tokens) else None)
|
|
526
|
+
pos += 1
|
|
454
527
|
body, pos = parse_suite(tokens, pos)
|
|
455
|
-
return {'type': 'class', 'name': name, 'body': body, '_token': tok}, pos
|
|
528
|
+
return {'type': 'class', 'name': name, 'base': base, 'body': body, '_token': tok}, pos
|
|
529
|
+
|
|
530
|
+
|
|
531
|
+
def parse_try(tokens: list[Token], pos: int):
|
|
532
|
+
tok = tokens[pos]
|
|
533
|
+
pos += 1
|
|
534
|
+
body, pos = parse_suite(tokens, pos)
|
|
535
|
+
handlers = []
|
|
536
|
+
while pos < len(tokens):
|
|
537
|
+
while pos < len(tokens) and tokens[pos].type == TokenType.NEWLINE:
|
|
538
|
+
pos += 1
|
|
539
|
+
if pos < len(tokens) and tokens[pos].type == TokenType.KEYWORD and tokens[pos].value == 'except':
|
|
540
|
+
etok = tokens[pos]
|
|
541
|
+
pos += 1
|
|
542
|
+
type_name = None
|
|
543
|
+
if pos < len(tokens) and tokens[pos].type == TokenType.IDENTIFIER:
|
|
544
|
+
type_name = tokens[pos].value
|
|
545
|
+
pos += 1
|
|
546
|
+
if pos >= len(tokens) or tokens[pos].type != TokenType.COLON:
|
|
547
|
+
raise ParseError('Expected ":" after except', tokens[pos] if pos < len(tokens) else None)
|
|
548
|
+
ebody, pos = parse_suite(tokens, pos)
|
|
549
|
+
handlers.append(ExceptHandler(type_name, ebody, token=etok))
|
|
550
|
+
else:
|
|
551
|
+
break
|
|
552
|
+
if not handlers:
|
|
553
|
+
raise ParseError('Expected at least one except clause', tok)
|
|
554
|
+
return Try(body, handlers, token=tok), pos
|
|
555
|
+
|
|
556
|
+
|
|
557
|
+
def parse_raise(tokens: list[Token], pos: int):
|
|
558
|
+
tok = tokens[pos]
|
|
559
|
+
pos += 1
|
|
560
|
+
if pos >= len(tokens) or tokens[pos].type != TokenType.IDENTIFIER:
|
|
561
|
+
raise ParseError('Expected exception class name after raise', tokens[pos] if pos < len(tokens) else None)
|
|
562
|
+
exc_type = tokens[pos].value
|
|
563
|
+
pos += 1
|
|
564
|
+
if pos >= len(tokens) or tokens[pos].type != TokenType.LPAREN:
|
|
565
|
+
raise ParseError('Expected "(" after exception class name', tokens[pos] if pos < len(tokens) else None)
|
|
566
|
+
pos += 1
|
|
567
|
+
message, pos = parse_expression(tokens, pos)
|
|
568
|
+
if pos >= len(tokens) or tokens[pos].type != TokenType.RPAREN:
|
|
569
|
+
raise ParseError('Expected ")" after message', tokens[pos] if pos < len(tokens) else None)
|
|
570
|
+
pos += 1
|
|
571
|
+
_expect_newline(tokens, pos, tok)
|
|
572
|
+
return Raise(exc_type, message, token=tok), pos
|
|
456
573
|
|
|
457
574
|
|
|
458
575
|
def parse_struct_def(tokens: list[Token], pos: int):
|
|
@@ -550,7 +667,6 @@ def parse_import(tokens: list[Token], pos: int):
|
|
|
550
667
|
pos += 1
|
|
551
668
|
module = '@' + '/'.join(parts)
|
|
552
669
|
node = Import(module, token=tok)
|
|
553
|
-
node.is_package = True
|
|
554
670
|
return node, pos
|
|
555
671
|
elif t.type == TokenType.IDENTIFIER:
|
|
556
672
|
module = t.value
|
|
@@ -687,7 +803,7 @@ class VarDecl:
|
|
|
687
803
|
return f'VarDecl({self.name}: {self.var_type} = {self.init})'
|
|
688
804
|
|
|
689
805
|
class Import:
|
|
690
|
-
__slots__ = ('module', 'symbols', 'src_file', '_token', 'sub_ast', 'frameworks', 'constants', 'sdk_path', 'var_names', 'is_package')
|
|
806
|
+
__slots__ = ('module', 'symbols', 'src_file', '_token', 'sub_ast', 'frameworks', 'constants', 'sdk_path', 'var_names', 'is_package', 'prebuilt_ll_files')
|
|
691
807
|
def __init__(self, module: str, symbols=None, token=None):
|
|
692
808
|
self.module = module
|
|
693
809
|
self.symbols = symbols or []
|
|
@@ -699,10 +815,41 @@ class Import:
|
|
|
699
815
|
self.sdk_path = None
|
|
700
816
|
self.var_names = set()
|
|
701
817
|
self.is_package = False
|
|
818
|
+
self.prebuilt_ll_files = None
|
|
819
|
+
|
|
702
820
|
def __repr__(self):
|
|
703
821
|
return f'Import({self.module})'
|
|
704
822
|
|
|
705
823
|
|
|
824
|
+
class ExceptHandler:
|
|
825
|
+
__slots__ = ('type_name', 'body', '_token')
|
|
826
|
+
def __init__(self, type_name: str | None, body: list, token=None):
|
|
827
|
+
self.type_name = type_name
|
|
828
|
+
self.body = body
|
|
829
|
+
self._token = token
|
|
830
|
+
def __repr__(self):
|
|
831
|
+
return f'ExceptHandler({self.type_name})'
|
|
832
|
+
|
|
833
|
+
|
|
834
|
+
class Try:
|
|
835
|
+
__slots__ = ('body', 'handlers', '_token')
|
|
836
|
+
def __init__(self, body: list, handlers: list[ExceptHandler], token=None):
|
|
837
|
+
self.body = body
|
|
838
|
+
self.handlers = handlers
|
|
839
|
+
self._token = token
|
|
840
|
+
def __repr__(self):
|
|
841
|
+
return f'Try({self.body}, handlers={self.handlers})'
|
|
842
|
+
|
|
843
|
+
|
|
844
|
+
class Raise:
|
|
845
|
+
__slots__ = ('exc_type', 'message', '_token')
|
|
846
|
+
def __init__(self, exc_type: str, message, token=None):
|
|
847
|
+
self.exc_type = exc_type
|
|
848
|
+
self.message = message
|
|
849
|
+
self._token = token
|
|
850
|
+
def __repr__(self):
|
|
851
|
+
return f'Raise({self.exc_type}, {self.message})'
|
|
852
|
+
|
|
706
853
|
class NewExpr:
|
|
707
854
|
__slots__ = ('type_expr', 'size', '_token')
|
|
708
855
|
def __init__(self, type_expr, size=None, token=None):
|
|
@@ -761,6 +908,93 @@ class Field:
|
|
|
761
908
|
return f'Field({self.name}: {self.type_expr})'
|
|
762
909
|
|
|
763
910
|
|
|
911
|
+
class InlineAsm:
|
|
912
|
+
__slots__ = ('template', 'outputs', 'inputs', 'clobbers', 'volatile', '_token')
|
|
913
|
+
def __init__(self, template: str, outputs=None, inputs=None, clobbers=None, volatile=False, token=None):
|
|
914
|
+
self.template = template
|
|
915
|
+
self.outputs = outputs or []
|
|
916
|
+
self.inputs = inputs or []
|
|
917
|
+
self.clobbers = clobbers or []
|
|
918
|
+
self.volatile = volatile
|
|
919
|
+
self._token = token
|
|
920
|
+
def __repr__(self):
|
|
921
|
+
return f'InlineAsm({self.template}, volatile={self.volatile})'
|
|
922
|
+
|
|
923
|
+
|
|
924
|
+
def parse_inline_asm(tokens: list[Token], pos: int):
|
|
925
|
+
tok = tokens[pos]
|
|
926
|
+
pos += 1
|
|
927
|
+
volatile = False
|
|
928
|
+
if pos < len(tokens) and tokens[pos].type == TokenType.IDENTIFIER and tokens[pos].value == 'volatile':
|
|
929
|
+
volatile = True
|
|
930
|
+
pos += 1
|
|
931
|
+
if pos >= len(tokens) or tokens[pos].type != TokenType.LPAREN:
|
|
932
|
+
raise ParseError('Expected "(" after asm', tokens[pos] if pos < len(tokens) else tok)
|
|
933
|
+
pos += 1
|
|
934
|
+
if pos >= len(tokens) or tokens[pos].type != TokenType.STRING:
|
|
935
|
+
raise ParseError('Expected asm template string', tokens[pos] if pos < len(tokens) else tok)
|
|
936
|
+
template = tokens[pos].value
|
|
937
|
+
pos += 1
|
|
938
|
+
outputs = []
|
|
939
|
+
inputs = []
|
|
940
|
+
clobbers = []
|
|
941
|
+
if pos < len(tokens) and tokens[pos].type == TokenType.COLON:
|
|
942
|
+
pos += 1
|
|
943
|
+
if pos < len(tokens) and tokens[pos].type not in (TokenType.COLON, TokenType.RPAREN):
|
|
944
|
+
while True:
|
|
945
|
+
if pos >= len(tokens) or tokens[pos].type != TokenType.STRING:
|
|
946
|
+
raise ParseError('Expected constraint string', tokens[pos] if pos < len(tokens) else tok)
|
|
947
|
+
constraint = tokens[pos].value
|
|
948
|
+
pos += 1
|
|
949
|
+
if pos >= len(tokens) or tokens[pos].type != TokenType.LPAREN:
|
|
950
|
+
raise ParseError('Expected "(" after constraint', tokens[pos] if pos < len(tokens) else tok)
|
|
951
|
+
pos += 1
|
|
952
|
+
var_expr, pos = parse_expression(tokens, pos)
|
|
953
|
+
if pos >= len(tokens) or tokens[pos].type != TokenType.RPAREN:
|
|
954
|
+
raise ParseError('Expected ")"', tokens[pos] if pos < len(tokens) else tok)
|
|
955
|
+
pos += 1
|
|
956
|
+
outputs.append((constraint, var_expr))
|
|
957
|
+
if pos < len(tokens) and tokens[pos].type == TokenType.COMMA:
|
|
958
|
+
pos += 1
|
|
959
|
+
else:
|
|
960
|
+
break
|
|
961
|
+
if pos < len(tokens) and tokens[pos].type == TokenType.COLON:
|
|
962
|
+
pos += 1
|
|
963
|
+
if pos < len(tokens) and tokens[pos].type not in (TokenType.COLON, TokenType.RPAREN):
|
|
964
|
+
while True:
|
|
965
|
+
if pos >= len(tokens) or tokens[pos].type != TokenType.STRING:
|
|
966
|
+
raise ParseError('Expected constraint string', tokens[pos] if pos < len(tokens) else tok)
|
|
967
|
+
constraint = tokens[pos].value
|
|
968
|
+
pos += 1
|
|
969
|
+
if pos >= len(tokens) or tokens[pos].type != TokenType.LPAREN:
|
|
970
|
+
raise ParseError('Expected "(" after constraint', tokens[pos] if pos < len(tokens) else tok)
|
|
971
|
+
pos += 1
|
|
972
|
+
arg_expr, pos = parse_expression(tokens, pos)
|
|
973
|
+
if pos >= len(tokens) or tokens[pos].type != TokenType.RPAREN:
|
|
974
|
+
raise ParseError('Expected ")"', tokens[pos] if pos < len(tokens) else tok)
|
|
975
|
+
pos += 1
|
|
976
|
+
inputs.append((constraint, arg_expr))
|
|
977
|
+
if pos < len(tokens) and tokens[pos].type == TokenType.COMMA:
|
|
978
|
+
pos += 1
|
|
979
|
+
else:
|
|
980
|
+
break
|
|
981
|
+
if pos < len(tokens) and tokens[pos].type == TokenType.COLON:
|
|
982
|
+
pos += 1
|
|
983
|
+
while True:
|
|
984
|
+
if pos >= len(tokens) or tokens[pos].type != TokenType.STRING:
|
|
985
|
+
raise ParseError('Expected clobber string', tokens[pos] if pos < len(tokens) else tok)
|
|
986
|
+
clobbers.append(tokens[pos].value)
|
|
987
|
+
pos += 1
|
|
988
|
+
if pos < len(tokens) and tokens[pos].type == TokenType.COMMA:
|
|
989
|
+
pos += 1
|
|
990
|
+
else:
|
|
991
|
+
break
|
|
992
|
+
if pos >= len(tokens) or tokens[pos].type != TokenType.RPAREN:
|
|
993
|
+
raise ParseError('Expected ")" after asm', tokens[pos] if pos < len(tokens) else tok)
|
|
994
|
+
pos += 1
|
|
995
|
+
return InlineAsm(template, outputs, inputs, clobbers, volatile=volatile, token=tok), pos
|
|
996
|
+
|
|
997
|
+
|
|
764
998
|
def parse_block(tokens: list[Token], pos: int = 0):
|
|
765
999
|
stmts = []
|
|
766
1000
|
while pos < len(tokens) and tokens[pos].type not in (TokenType.DEDENT, TokenType.EOF):
|
|
@@ -928,6 +1162,15 @@ def parse_statement(tokens: list[Token], pos: int):
|
|
|
928
1162
|
handler = {'while': parse_while, 'for': parse_for, 'class': parse_class, 'struct': parse_struct_def, 'import': parse_import}[tok.value]
|
|
929
1163
|
return handler(tokens, pos)
|
|
930
1164
|
|
|
1165
|
+
if tok.type == TokenType.KEYWORD and tok.value == 'try':
|
|
1166
|
+
return parse_try(tokens, pos)
|
|
1167
|
+
|
|
1168
|
+
if tok.type == TokenType.KEYWORD and tok.value == 'raise':
|
|
1169
|
+
return parse_raise(tokens, pos)
|
|
1170
|
+
|
|
1171
|
+
if tok.type == TokenType.KEYWORD and tok.value == 'asm':
|
|
1172
|
+
return parse_inline_asm(tokens, pos)
|
|
1173
|
+
|
|
931
1174
|
if tok.type == TokenType.IDENTIFIER:
|
|
932
1175
|
if tok.value in _TYPE_NAMES or _looks_like_type(tokens, pos):
|
|
933
1176
|
try:
|