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.
Files changed (28) hide show
  1. {cpyte-2.0.0 → cpyte-2.0.2}/PKG-INFO +42 -1
  2. {cpyte-2.0.0 → cpyte-2.0.2}/pyproject.toml +1 -1
  3. {cpyte-2.0.0 → cpyte-2.0.2}/readme.md +41 -0
  4. {cpyte-2.0.0 → cpyte-2.0.2}/source/cpyte/astparse.py +292 -49
  5. {cpyte-2.0.0 → cpyte-2.0.2}/source/cpyte/bytecoding.py +264 -24
  6. {cpyte-2.0.0 → cpyte-2.0.2}/source/cpyte/compiling.py +98 -82
  7. cpyte-2.0.2/source/cpyte/extension_hooks.py +460 -0
  8. {cpyte-2.0.0 → cpyte-2.0.2}/source/cpyte/lexar.py +52 -3
  9. {cpyte-2.0.0 → cpyte-2.0.2}/source/cpyte/mainpie.py +108 -22
  10. cpyte-2.0.2/source/cpyte/package_manifest.py +410 -0
  11. {cpyte-2.0.0 → cpyte-2.0.2}/source/cpyte/semantic_analasis.py +284 -28
  12. {cpyte-2.0.0 → cpyte-2.0.2}/source/cpyte.egg-info/PKG-INFO +42 -1
  13. {cpyte-2.0.0 → cpyte-2.0.2}/source/cpyte.egg-info/SOURCES.txt +2 -1
  14. cpyte-2.0.0/source/cpyte/cpy_language_documentation.md +0 -1522
  15. {cpyte-2.0.0 → cpyte-2.0.2}/setup.cfg +0 -0
  16. {cpyte-2.0.0 → cpyte-2.0.2}/source/cpyte/__init__.py +0 -0
  17. {cpyte-2.0.0 → cpyte-2.0.2}/source/cpyte/__main__.py +0 -0
  18. {cpyte-2.0.0 → cpyte-2.0.2}/source/cpyte/_bignum_bc.py +0 -0
  19. {cpyte-2.0.0 → cpyte-2.0.2}/source/cpyte/_runtime_bc.py +0 -0
  20. {cpyte-2.0.0 → cpyte-2.0.2}/source/cpyte/clib.py +0 -0
  21. {cpyte-2.0.0 → cpyte-2.0.2}/source/cpyte/generate_bc.py +0 -0
  22. {cpyte-2.0.0 → cpyte-2.0.2}/source/cpyte/linker.py +0 -0
  23. {cpyte-2.0.0 → cpyte-2.0.2}/source/cpyte/lsp_server.py +0 -0
  24. {cpyte-2.0.0 → cpyte-2.0.2}/source/cpyte.egg-info/dependency_links.txt +0 -0
  25. {cpyte-2.0.0 → cpyte-2.0.2}/source/cpyte.egg-info/entry_points.txt +0 -0
  26. {cpyte-2.0.0 → cpyte-2.0.2}/source/cpyte.egg-info/requires.txt +0 -0
  27. {cpyte-2.0.0 → cpyte-2.0.2}/source/cpyte.egg-info/top_level.txt +0 -0
  28. {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.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
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "cpyte"
7
- version = "2.0.0"
7
+ version = "2.0.2"
8
8
  description = "The Cpyte programming language compiler"
9
9
  readme = "readme.md"
10
10
  requires-python = ">=3.11"
@@ -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
- if tok.type == TokenType.KEYWORD and tok.value == 'def':
335
- node, pos = parse_def(tokens, pos)
336
- elif tok.type == TokenType.KEYWORD and tok.value in ('public', 'private', 'static', 'virtual', 'override'):
337
- node, pos = parse_decorated_def(tokens, pos)
338
- elif tok.type == TokenType.KEYWORD and tok.value == 'class':
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
- save = pos
364
- node, pos = parse_var_decl(tokens, pos)
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
- while pos < len(tokens) and tokens[pos].type == TokenType.NEWLINE:
368
- pos += 1
369
- continue
370
- except ParseError:
371
- pos = save
372
- node, pos = parse_expr_stmt(tokens, pos)
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 = parse_expr_stmt(tokens, 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
- return nodes, pos
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: