jaclang 0.0.6__py3-none-any.whl → 0.0.8__py3-none-any.whl

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.

Potentially problematic release.


This version of jaclang might be problematic. Click here for more details.

Files changed (82) hide show
  1. jaclang/__init__.py +2 -1
  2. jaclang/cli/__jac_gen__/__init__.py +0 -0
  3. jaclang/cli/__jac_gen__/cli.py +175 -0
  4. jaclang/cli/__jac_gen__/cmds.py +132 -0
  5. jaclang/cli/cmds.jac +3 -0
  6. jaclang/cli/impl/__jac_gen__/__init__.py +0 -0
  7. jaclang/cli/impl/__jac_gen__/cli_impl.py +16 -0
  8. jaclang/cli/impl/__jac_gen__/cmds_impl.py +26 -0
  9. jaclang/cli/impl/cmds_impl.jac +17 -3
  10. jaclang/core/__jac_gen__/__init__.py +0 -0
  11. jaclang/core/__jac_gen__/primitives.py +567 -0
  12. jaclang/core/impl/__jac_gen__/__init__.py +0 -0
  13. jaclang/core/impl/__jac_gen__/arch_impl.py +24 -0
  14. jaclang/core/impl/__jac_gen__/element_impl.py +26 -0
  15. jaclang/core/impl/__jac_gen__/exec_ctx_impl.py +12 -0
  16. jaclang/core/impl/__jac_gen__/memory_impl.py +14 -0
  17. jaclang/core/impl/element_impl.jac +2 -2
  18. jaclang/core/primitives.jac +1 -0
  19. jaclang/jac/absyntree.py +65 -42
  20. jaclang/jac/constant.py +4 -0
  21. jaclang/jac/importer.py +18 -60
  22. jaclang/jac/langserve.py +26 -0
  23. jaclang/jac/lexer.py +9 -1
  24. jaclang/jac/parser.py +135 -123
  25. jaclang/jac/passes/blue/ast_build_pass.py +410 -353
  26. jaclang/jac/passes/blue/blue_pygen_pass.py +15 -0
  27. jaclang/jac/passes/blue/decl_def_match_pass.py +33 -21
  28. jaclang/jac/passes/blue/import_pass.py +1 -1
  29. jaclang/jac/passes/blue/pyout_pass.py +47 -12
  30. jaclang/jac/passes/blue/sym_tab_build_pass.py +38 -127
  31. jaclang/jac/passes/blue/tests/test_ast_build_pass.py +2 -2
  32. jaclang/jac/passes/blue/tests/test_blue_pygen_pass.py +9 -30
  33. jaclang/jac/passes/blue/tests/test_decl_def_match_pass.py +13 -13
  34. jaclang/jac/passes/blue/tests/test_sym_tab_build_pass.py +6 -4
  35. jaclang/jac/passes/ir_pass.py +1 -1
  36. jaclang/jac/passes/purple/__jac_gen__/__init__.py +0 -0
  37. jaclang/jac/passes/purple/__jac_gen__/analyze_pass.py +37 -0
  38. jaclang/jac/passes/purple/__jac_gen__/purple_pygen_pass.py +305 -0
  39. jaclang/jac/passes/purple/impl/__jac_gen__/__init__.py +0 -0
  40. jaclang/jac/passes/purple/impl/__jac_gen__/purple_pygen_pass_impl.py +23 -0
  41. jaclang/jac/symtable.py +12 -4
  42. jaclang/jac/tests/fixtures/__jac_gen__/__init__.py +0 -0
  43. jaclang/jac/tests/fixtures/__jac_gen__/hello_world.py +16 -0
  44. jaclang/jac/tests/fixtures/fam.jac +7 -8
  45. jaclang/jac/transform.py +4 -3
  46. jaclang/jac/transpiler.py +13 -9
  47. jaclang/utils/fstring_parser.py +2 -2
  48. jaclang/utils/helpers.py +41 -0
  49. jaclang/utils/test.py +30 -0
  50. jaclang/vendor/__init__.py +1 -0
  51. jaclang/vendor/pygls/__init__.py +25 -0
  52. jaclang/vendor/pygls/capabilities.py +502 -0
  53. jaclang/vendor/pygls/client.py +176 -0
  54. jaclang/vendor/pygls/constants.py +26 -0
  55. jaclang/vendor/pygls/exceptions.py +220 -0
  56. jaclang/vendor/pygls/feature_manager.py +241 -0
  57. jaclang/vendor/pygls/lsp/__init__.py +139 -0
  58. jaclang/vendor/pygls/lsp/client.py +2224 -0
  59. jaclang/vendor/pygls/lsprotocol/__init__.py +2 -0
  60. jaclang/vendor/pygls/lsprotocol/_hooks.py +1233 -0
  61. jaclang/vendor/pygls/lsprotocol/converters.py +17 -0
  62. jaclang/vendor/pygls/lsprotocol/types.py +12820 -0
  63. jaclang/vendor/pygls/lsprotocol/validators.py +47 -0
  64. jaclang/vendor/pygls/progress.py +79 -0
  65. jaclang/vendor/pygls/protocol.py +1184 -0
  66. jaclang/vendor/pygls/server.py +620 -0
  67. jaclang/vendor/pygls/uris.py +184 -0
  68. jaclang/vendor/pygls/workspace/__init__.py +81 -0
  69. jaclang/vendor/pygls/workspace/position.py +204 -0
  70. jaclang/vendor/pygls/workspace/text_document.py +234 -0
  71. jaclang/vendor/pygls/workspace/workspace.py +311 -0
  72. {jaclang-0.0.6.dist-info → jaclang-0.0.8.dist-info}/METADATA +1 -1
  73. jaclang-0.0.8.dist-info/RECORD +118 -0
  74. jaclang/core/jaclang.jac +0 -62
  75. jaclang-0.0.6.dist-info/RECORD +0 -76
  76. /jaclang/{utils → vendor}/sly/__init__.py +0 -0
  77. /jaclang/{utils → vendor}/sly/docparse.py +0 -0
  78. /jaclang/{utils → vendor}/sly/lex.py +0 -0
  79. /jaclang/{utils → vendor}/sly/yacc.py +0 -0
  80. {jaclang-0.0.6.dist-info → jaclang-0.0.8.dist-info}/WHEEL +0 -0
  81. {jaclang-0.0.6.dist-info → jaclang-0.0.8.dist-info}/entry_points.txt +0 -0
  82. {jaclang-0.0.6.dist-info → jaclang-0.0.8.dist-info}/top_level.txt +0 -0
jaclang/jac/absyntree.py CHANGED
@@ -14,7 +14,7 @@ class AstNode:
14
14
 
15
15
  def __init__(
16
16
  self,
17
- parent: Optional["AstNode"],
17
+ parent: Optional[AstNode],
18
18
  mod_link: Optional[Module],
19
19
  kid: list,
20
20
  line: int,
@@ -180,7 +180,7 @@ class Test(AstNode):
180
180
  sym_tab=name.sym_tab,
181
181
  )
182
182
  )
183
- kid[1] = self.name
183
+ kid[0] = self.name # Index is 0 since Doc string is inserted after init
184
184
  self.body = body
185
185
  super().__init__(
186
186
  parent=parent, mod_link=mod_link, kid=kid, line=line, sym_tab=sym_tab
@@ -210,6 +210,22 @@ class ModuleCode(AstNode):
210
210
  )
211
211
 
212
212
 
213
+ class PyInlineCode(AstNode):
214
+ """Inline Python code node type for Jac Ast."""
215
+
216
+ def __init__(
217
+ self,
218
+ code: Token,
219
+ parent: Optional[AstNode],
220
+ mod_link: Optional[Module],
221
+ kid: list[AstNode],
222
+ line: int,
223
+ ) -> None:
224
+ """Initialize inline python code node."""
225
+ self.code = code
226
+ super().__init__(parent=parent, mod_link=mod_link, kid=kid, line=line)
227
+
228
+
213
229
  class Import(AstNode):
214
230
  """Import node type for Jac Ast."""
215
231
 
@@ -343,8 +359,7 @@ class ArchDef(AstNode):
343
359
  def __init__(
344
360
  self,
345
361
  doc: Optional[Token],
346
- mod: Optional[DottedNameList],
347
- arch: ArchRef,
362
+ target: ArchRefChain,
348
363
  body: ArchBlock,
349
364
  parent: Optional[AstNode],
350
365
  mod_link: Optional[Module],
@@ -354,8 +369,7 @@ class ArchDef(AstNode):
354
369
  ) -> None:
355
370
  """Initialize arch def node."""
356
371
  self.doc = doc
357
- self.mod = mod
358
- self.arch = arch
372
+ self.target = target
359
373
  self.body = body
360
374
  super().__init__(
361
375
  parent=parent, mod_link=mod_link, kid=kid, line=line, sym_tab=sym_tab
@@ -405,7 +419,7 @@ class Ability(OOPAccessNode):
405
419
 
406
420
  def __init__(
407
421
  self,
408
- name_ref: Name | SpecialVarRef | ArchRef,
422
+ name_ref: Name | SpecialVarRef,
409
423
  is_func: bool,
410
424
  is_async: bool,
411
425
  is_static: bool,
@@ -452,14 +466,6 @@ class Ability(OOPAccessNode):
452
466
  else:
453
467
  raise NotImplementedError
454
468
 
455
- def resolve_ability_symtab_name(self) -> str:
456
- """Resolve ability name in symbol table."""
457
- return (
458
- f"{self.arch_attached.parent.name.value}.{self.py_resolve_name()}"
459
- if self.arch_attached and isinstance(self.arch_attached.parent, Architype)
460
- else self.py_resolve_name()
461
- )
462
-
463
469
 
464
470
  class AbilityDef(AstNode):
465
471
  """AbilityDef node type for Jac Ast."""
@@ -467,8 +473,7 @@ class AbilityDef(AstNode):
467
473
  def __init__(
468
474
  self,
469
475
  doc: Optional[Token],
470
- target: Optional[DottedNameList],
471
- ability: ArchRef,
476
+ target: ArchRefChain,
472
477
  signature: FuncSignature | EventSignature,
473
478
  body: CodeBlock,
474
479
  parent: Optional[AstNode],
@@ -480,26 +485,12 @@ class AbilityDef(AstNode):
480
485
  """Initialize ability def node."""
481
486
  self.doc = doc
482
487
  self.target = target
483
- self.ability = ability
484
488
  self.signature = signature
485
489
  self.body = body
486
490
  super().__init__(
487
491
  parent=parent, mod_link=mod_link, kid=kid, line=line, sym_tab=sym_tab
488
492
  )
489
493
 
490
- def py_resolve_name(self) -> str:
491
- """Resolve name."""
492
- ability_name = self.ability.py_resolve_name()
493
- if self.target:
494
- owner = self.target.names[-1]
495
- if isinstance(owner, ArchRef):
496
- owner = owner.py_resolve_name()
497
- ability_name = f"{owner}.{ability_name}"
498
- return ability_name
499
- raise Exception("Invalid AST: Expected reference to Architype!")
500
- else:
501
- return ability_name
502
-
503
494
 
504
495
  class EventSignature(AstNode):
505
496
  """EventSignature node type for Jac Ast."""
@@ -529,7 +520,7 @@ class DottedNameList(AstNode):
529
520
 
530
521
  def __init__(
531
522
  self,
532
- names: list[Token | SpecialVarRef | ArchRef | Name],
523
+ names: list[Token | SpecialVarRef | Name],
533
524
  parent: Optional[AstNode],
534
525
  mod_link: Optional[Module],
535
526
  kid: list[AstNode],
@@ -543,6 +534,31 @@ class DottedNameList(AstNode):
543
534
  )
544
535
 
545
536
 
537
+ class ArchRefChain(AstNode):
538
+ """Arch ref list node type for Jac Ast."""
539
+
540
+ def __init__(
541
+ self,
542
+ archs: list[ArchRef],
543
+ parent: Optional[AstNode],
544
+ mod_link: Optional[Module],
545
+ kid: list[AstNode],
546
+ line: int,
547
+ sym_tab: Optional[SymbolTable] = None,
548
+ ) -> None:
549
+ """Initialize name list ."""
550
+ self.archs = archs
551
+ super().__init__(
552
+ parent=parent, mod_link=mod_link, kid=kid, line=line, sym_tab=sym_tab
553
+ )
554
+
555
+ def py_resolve_name(self) -> str:
556
+ """Resolve name."""
557
+ return ".".join(
558
+ [f"({x.arch.value[1]}){x.py_resolve_name()}" for x in self.archs]
559
+ )
560
+
561
+
546
562
  class FuncSignature(AstNode):
547
563
  """FuncSignature node type for Jac Ast."""
548
564
 
@@ -615,7 +631,7 @@ class Enum(OOPAccessNode):
615
631
  self,
616
632
  name: Name,
617
633
  doc: Optional[Token],
618
- decorators: Optional["Decorators"],
634
+ decorators: Optional[Decorators],
619
635
  access: Optional[Token],
620
636
  base_classes: "BaseClasses",
621
637
  body: Optional["EnumBlock"],
@@ -647,8 +663,7 @@ class EnumDef(AstNode):
647
663
  def __init__(
648
664
  self,
649
665
  doc: Optional[Token],
650
- enum: ArchRef,
651
- mod: Optional[DottedNameList],
666
+ target: ArchRefChain,
652
667
  body: EnumBlock,
653
668
  parent: Optional[AstNode],
654
669
  mod_link: Optional[Module],
@@ -657,9 +672,8 @@ class EnumDef(AstNode):
657
672
  sym_tab: Optional[SymbolTable] = None,
658
673
  ) -> None:
659
674
  """Initialize arch def node."""
660
- self.enum = enum
661
675
  self.doc = doc
662
- self.mod = mod
676
+ self.target = target
663
677
  self.body = body
664
678
  super().__init__(
665
679
  parent=parent, mod_link=mod_link, kid=kid, line=line, sym_tab=sym_tab
@@ -690,7 +704,7 @@ class ArchBlock(AstNode):
690
704
 
691
705
  def __init__(
692
706
  self,
693
- members: list["ArchHas | Ability"],
707
+ members: list[ArchHas | Ability],
694
708
  parent: Optional[AstNode],
695
709
  mod_link: Optional[Module],
696
710
  kid: list[AstNode],
@@ -1714,8 +1728,8 @@ class AtomTrailer(AstNode):
1714
1728
 
1715
1729
  def __init__(
1716
1730
  self,
1717
- target: "AtomType",
1718
- right: "IndexSlice | ArchRef | Token",
1731
+ target: AtomType,
1732
+ right: IndexSlice | ArchRef | Token,
1719
1733
  null_ok: bool,
1720
1734
  parent: Optional[AstNode],
1721
1735
  mod_link: Optional[Module],
@@ -1996,6 +2010,10 @@ class Parse(AstNode):
1996
2010
  parent=parent, mod_link=mod_link, kid=kid, line=line, sym_tab=sym_tab
1997
2011
  )
1998
2012
 
2013
+ def __repr__(self) -> str:
2014
+ """Return string representation of parse node."""
2015
+ return super().__repr__() + f" ({self.name})" + " line: " + str(self.line)
2016
+
1999
2017
 
2000
2018
  class Token(AstNode):
2001
2019
  """Token node type for Jac Ast."""
@@ -2153,9 +2171,14 @@ def replace_node(node: AstNode, new_node: Optional[AstNode]) -> AstNode | None:
2153
2171
  return new_node
2154
2172
 
2155
2173
 
2156
- def append_node(node: AstNode, new_node: Optional[AstNode]) -> AstNode | None:
2174
+ def append_node(
2175
+ node: AstNode, new_node: Optional[AstNode], front: bool = False
2176
+ ) -> AstNode | None:
2157
2177
  """Replace node with new_node."""
2158
- node.kid.append(new_node)
2178
+ if front:
2179
+ node.kid.insert(0, new_node)
2180
+ else:
2181
+ node.kid.append(new_node)
2159
2182
  if new_node:
2160
2183
  new_node.parent = node
2161
2184
  return new_node
jaclang/jac/constant.py CHANGED
@@ -28,6 +28,9 @@ class Constants(str, Enum):
28
28
  WITH_DIR = "_jac_apply_dir_"
29
29
  EDGE_DIR = "_jac_Edge_Dir_"
30
30
 
31
+ PYNLINE = "::py::"
32
+ JAC_GEN_DIR = "__jac_gen__"
33
+
31
34
  def __str__(self) -> str:
32
35
  """Return the string representation of the token."""
33
36
  return self.value
@@ -55,6 +58,7 @@ class Tokens(str, Enum):
55
58
  FLOAT = "FLOAT"
56
59
  STRING = "STRING"
57
60
  DOC_STRING = "DOC_STRING"
61
+ PYNLINE = "PYNLINE"
58
62
  FSTRING = "FSTRING"
59
63
  BOOL = "BOOL"
60
64
  INT = "INT"
jaclang/jac/importer.py CHANGED
@@ -4,12 +4,12 @@ import marshal
4
4
  import sys
5
5
  import traceback
6
6
  import types
7
- from os import makedirs, path
7
+ from os import path
8
8
  from typing import Callable, Optional
9
9
 
10
- from jaclang.jac.constant import Constants as Con, Values as Val
10
+ from jaclang.jac.constant import Constants as Con
11
11
  from jaclang.jac.transpiler import transpile_jac_blue, transpile_jac_purple
12
- from jaclang.utils.helpers import add_line_numbers, clip_code_section
12
+ from jaclang.utils.helpers import handle_jac_error
13
13
 
14
14
 
15
15
  def import_jac_module(
@@ -20,9 +20,7 @@ def import_jac_module(
20
20
  override_name: Optional[str] = None,
21
21
  ) -> Optional[types.ModuleType]:
22
22
  """Core Import Process."""
23
- target = path.join(*(target.split("."))) + ".jac"
24
-
25
- dir_path, file_name = path.split(target)
23
+ dir_path, file_name = path.split(path.join(*(target.split("."))) + ".jac")
26
24
  module_name = path.splitext(file_name)[0]
27
25
  package_path = dir_path.replace(path.sep, ".")
28
26
 
@@ -36,11 +34,13 @@ def import_jac_module(
36
34
  else:
37
35
  frame = inspect.stack()[2]
38
36
  caller_dir = path.dirname(path.abspath(frame[0].f_code.co_filename))
39
- full_target = path.normpath(path.join(caller_dir, target))
37
+ caller_dir = path.join(caller_dir, dir_path)
38
+
39
+ gen_dir = path.join(caller_dir, Con.JAC_GEN_DIR)
40
+ full_target = path.normpath(path.join(caller_dir, file_name))
40
41
 
41
- dev_dir = path.join(caller_dir, "__jac_gen__")
42
- makedirs(dev_dir, exist_ok=True)
43
- py_file_path = path.join(dev_dir, module_name + ".py")
42
+ py_file_path = path.join(gen_dir, module_name + ".py")
43
+ pyc_file_path = path.join(gen_dir, module_name + ".pyc")
44
44
  if (
45
45
  cachable
46
46
  and path.exists(py_file_path)
@@ -48,33 +48,21 @@ def import_jac_module(
48
48
  ):
49
49
  with open(py_file_path, "r") as f:
50
50
  code_string = f.read()
51
+ with open(pyc_file_path, "rb") as f:
52
+ codeobj = marshal.load(f)
51
53
  else:
52
- code_string = transpiler_func(file_path=full_target, base_dir=caller_dir)
53
- with open(py_file_path, "w") as f:
54
- f.write(code_string)
54
+ if transpiler_func(file_path=full_target, base_dir=caller_dir):
55
+ return None
56
+ with open(py_file_path, "r") as f:
57
+ code_string = f.read()
58
+ with open(pyc_file_path, "rb") as f:
59
+ codeobj = marshal.load(f)
55
60
 
56
61
  module = types.ModuleType(module_name)
57
62
  module.__file__ = full_target
58
63
  module.__name__ = override_name if override_name else module_name
59
64
  module.__dict__["_jac_pycodestring_"] = code_string
60
65
 
61
- if (
62
- cachable
63
- and path.exists(py_file_path + "c")
64
- and path.getmtime(py_file_path + "c") > path.getmtime(full_target)
65
- ):
66
- with open(py_file_path + "c", "rb") as f:
67
- codeobj = marshal.load(f)
68
- else:
69
- try:
70
- codeobj = compile(code_string, f"_jac_py_gen ({module.__file__})", "exec")
71
- except Exception as e:
72
- tb = traceback.extract_tb(e.__traceback__)
73
- err = handle_jac_error(code_string, e, tb)
74
- raise type(e)(str(e) + "\n" + err)
75
- with open(py_file_path + "c", "wb") as f:
76
- marshal.dump(codeobj, f)
77
-
78
66
  try:
79
67
  exec(codeobj, module.__dict__)
80
68
  except Exception as e:
@@ -96,36 +84,6 @@ def import_jac_module(
96
84
  return module
97
85
 
98
86
 
99
- def handle_jac_error(code_string: str, e: Exception, tb: traceback.StackSummary) -> str:
100
- """Handle Jac Error."""
101
- except_line = e.end_lineno if isinstance(e, SyntaxError) else list(tb)[-1].lineno
102
- if not isinstance(except_line, int) or except_line == 0:
103
- return ""
104
- py_error_region = clip_code_section(
105
- add_line_numbers(code_string), except_line, Val.JAC_ERROR_LINE_RANGE
106
- )
107
- try:
108
- jac_err_line = int(code_string.splitlines()[except_line - 1].split()[-1])
109
- mod_index = int(code_string.splitlines()[except_line - 1].split()[-2])
110
- mod_paths = code_string.split(Con.JAC_DEBUG_SPLITTER)[1].strip().splitlines()
111
- target_mod = mod_paths[mod_index]
112
- with open(target_mod, "r") as file:
113
- jac_code_string = file.read()
114
- jac_error_region = clip_code_section(
115
- add_line_numbers(jac_code_string), jac_err_line, Val.JAC_ERROR_LINE_RANGE
116
- )
117
- except Exception as e:
118
- jac_error_region = str(e)
119
- target_mod = ""
120
- snippet = (
121
- f"{Con.JAC_ERROR_PREAMBLE}\n"
122
- f"{target_mod}\n"
123
- f"JacCode Snippet:\n{jac_error_region}\n"
124
- f"PyCode Snippet:\n{py_error_region}\n"
125
- )
126
- return snippet
127
-
128
-
129
87
  def jac_blue_import(
130
88
  target: str,
131
89
  base_path: Optional[str] = None,
@@ -0,0 +1,26 @@
1
+ """Example language server for JAC."""
2
+
3
+ from jaclang.vendor.pygls.lsprotocol.types import (
4
+ CompletionItem,
5
+ CompletionList,
6
+ CompletionParams,
7
+ TEXT_DOCUMENT_COMPLETION,
8
+ )
9
+ from jaclang.vendor.pygls.server import LanguageServer
10
+
11
+
12
+ server = LanguageServer("example-server", "v0.1")
13
+
14
+
15
+ @server.feature(TEXT_DOCUMENT_COMPLETION)
16
+ def completions(params: CompletionParams) -> CompletionList:
17
+ """Provide completions for the current line."""
18
+ items = []
19
+ document = server.workspace.get_document(params.text_document.uri)
20
+ current_line = document.lines[params.position.line].strip()
21
+ if current_line.endswith("hello."):
22
+ items = [
23
+ CompletionItem(label="world"),
24
+ CompletionItem(label="friend"),
25
+ ]
26
+ return CompletionList(is_incomplete=False, items=items)
jaclang/jac/lexer.py CHANGED
@@ -2,7 +2,7 @@
2
2
  from typing import Generator
3
3
 
4
4
  from jaclang.jac.transform import ABCLexerMeta, Transform
5
- from jaclang.utils.sly.lex import Lexer, Token
5
+ from jaclang.vendor.sly.lex import Lexer, Token
6
6
 
7
7
 
8
8
  class JacLexer(Lexer, Transform, metaclass=ABCLexerMeta):
@@ -25,6 +25,7 @@ class JacLexer(Lexer, Transform, metaclass=ABCLexerMeta):
25
25
  "FLOAT",
26
26
  "STRING",
27
27
  "DOC_STRING",
28
+ "PYNLINE",
28
29
  "FSTRING",
29
30
  "BOOL",
30
31
  "INT",
@@ -189,6 +190,7 @@ class JacLexer(Lexer, Transform, metaclass=ABCLexerMeta):
189
190
  # Regular expression rules for tokens
190
191
  FLOAT = r"(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?"
191
192
  DOC_STRING = r'"""(.|\n|\r)*?"""|\'\'\'(.|\n|\r)*?\'\'\'' # type: ignore
193
+ PYNLINE = r"::py::(.|\n|\r)*?::py::" # type: ignore
192
194
  FSTRING = r'f"[^"\r\n]*"|f\'[^\'\r\n]*\''
193
195
  STRING = r'"[^"\r\n]*"|\'[^\'\r\n]*\''
194
196
  BOOL = r"True|False"
@@ -372,6 +374,12 @@ class JacLexer(Lexer, Transform, metaclass=ABCLexerMeta):
372
374
  self.lineno += t.value.count("\r")
373
375
  return t
374
376
 
377
+ def PYNLINE(self, t: Token) -> Token: # noqa
378
+ """Add docstring to lexer."""
379
+ self.lineno += t.value.count("\n")
380
+ self.lineno += t.value.count("\r")
381
+ return t
382
+
375
383
  # Transform Implementations
376
384
  # -------------------------
377
385
  def transform(self, ir: str) -> Generator: