zexus 1.6.8 → 1.7.2

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 (177) hide show
  1. package/README.md +12 -5
  2. package/package.json +1 -1
  3. package/src/__init__.py +7 -0
  4. package/src/zexus/__init__.py +1 -1
  5. package/src/zexus/__pycache__/__init__.cpython-312.pyc +0 -0
  6. package/src/zexus/__pycache__/capability_system.cpython-312.pyc +0 -0
  7. package/src/zexus/__pycache__/debug_sanitizer.cpython-312.pyc +0 -0
  8. package/src/zexus/__pycache__/environment.cpython-312.pyc +0 -0
  9. package/src/zexus/__pycache__/error_reporter.cpython-312.pyc +0 -0
  10. package/src/zexus/__pycache__/input_validation.cpython-312.pyc +0 -0
  11. package/src/zexus/__pycache__/lexer.cpython-312.pyc +0 -0
  12. package/src/zexus/__pycache__/module_cache.cpython-312.pyc +0 -0
  13. package/src/zexus/__pycache__/module_manager.cpython-312.pyc +0 -0
  14. package/src/zexus/__pycache__/object.cpython-312.pyc +0 -0
  15. package/src/zexus/__pycache__/security.cpython-312.pyc +0 -0
  16. package/src/zexus/__pycache__/security_enforcement.cpython-312.pyc +0 -0
  17. package/src/zexus/__pycache__/syntax_validator.cpython-312.pyc +0 -0
  18. package/src/zexus/__pycache__/zexus_ast.cpython-312.pyc +0 -0
  19. package/src/zexus/__pycache__/zexus_token.cpython-312.pyc +0 -0
  20. package/src/zexus/access_control_system/__pycache__/__init__.cpython-312.pyc +0 -0
  21. package/src/zexus/access_control_system/__pycache__/access_control.cpython-312.pyc +0 -0
  22. package/src/zexus/advanced_types.py +17 -2
  23. package/src/zexus/blockchain/__init__.py +411 -0
  24. package/src/zexus/blockchain/accelerator.py +1160 -0
  25. package/src/zexus/blockchain/chain.py +660 -0
  26. package/src/zexus/blockchain/consensus.py +821 -0
  27. package/src/zexus/blockchain/contract_vm.py +1019 -0
  28. package/src/zexus/blockchain/crypto.py +79 -14
  29. package/src/zexus/blockchain/events.py +526 -0
  30. package/src/zexus/blockchain/loadtest.py +721 -0
  31. package/src/zexus/blockchain/monitoring.py +350 -0
  32. package/src/zexus/blockchain/mpt.py +716 -0
  33. package/src/zexus/blockchain/multichain.py +951 -0
  34. package/src/zexus/blockchain/multiprocess_executor.py +338 -0
  35. package/src/zexus/blockchain/network.py +886 -0
  36. package/src/zexus/blockchain/node.py +666 -0
  37. package/src/zexus/blockchain/rpc.py +1203 -0
  38. package/src/zexus/blockchain/rust_bridge.py +421 -0
  39. package/src/zexus/blockchain/storage.py +423 -0
  40. package/src/zexus/blockchain/tokens.py +750 -0
  41. package/src/zexus/blockchain/upgradeable.py +1004 -0
  42. package/src/zexus/blockchain/verification.py +1602 -0
  43. package/src/zexus/blockchain/wallet.py +621 -0
  44. package/src/zexus/capability_system.py +184 -9
  45. package/src/zexus/cli/__pycache__/main.cpython-312.pyc +0 -0
  46. package/src/zexus/cli/main.py +383 -34
  47. package/src/zexus/cli/zpm.py +1 -1
  48. package/src/zexus/compiler/__pycache__/bytecode.cpython-312.pyc +0 -0
  49. package/src/zexus/compiler/__pycache__/lexer.cpython-312.pyc +0 -0
  50. package/src/zexus/compiler/__pycache__/parser.cpython-312.pyc +0 -0
  51. package/src/zexus/compiler/__pycache__/semantic.cpython-312.pyc +0 -0
  52. package/src/zexus/compiler/__pycache__/zexus_ast.cpython-312.pyc +0 -0
  53. package/src/zexus/compiler/bytecode.py +124 -7
  54. package/src/zexus/compiler/compat_runtime.py +6 -2
  55. package/src/zexus/compiler/lexer.py +16 -5
  56. package/src/zexus/compiler/parser.py +108 -7
  57. package/src/zexus/compiler/semantic.py +18 -19
  58. package/src/zexus/compiler/zexus_ast.py +26 -1
  59. package/src/zexus/concurrency_system.py +79 -0
  60. package/src/zexus/config.py +54 -0
  61. package/src/zexus/crypto_bridge.py +244 -8
  62. package/src/zexus/dap/__init__.py +10 -0
  63. package/src/zexus/dap/__main__.py +4 -0
  64. package/src/zexus/dap/dap_server.py +391 -0
  65. package/src/zexus/dap/debug_engine.py +298 -0
  66. package/src/zexus/environment.py +112 -9
  67. package/src/zexus/evaluator/__pycache__/bytecode_compiler.cpython-312.pyc +0 -0
  68. package/src/zexus/evaluator/__pycache__/core.cpython-312.pyc +0 -0
  69. package/src/zexus/evaluator/__pycache__/expressions.cpython-312.pyc +0 -0
  70. package/src/zexus/evaluator/__pycache__/functions.cpython-312.pyc +0 -0
  71. package/src/zexus/evaluator/__pycache__/resource_limiter.cpython-312.pyc +0 -0
  72. package/src/zexus/evaluator/__pycache__/statements.cpython-312.pyc +0 -0
  73. package/src/zexus/evaluator/__pycache__/unified_execution.cpython-312.pyc +0 -0
  74. package/src/zexus/evaluator/__pycache__/utils.cpython-312.pyc +0 -0
  75. package/src/zexus/evaluator/bytecode_compiler.py +457 -37
  76. package/src/zexus/evaluator/core.py +644 -50
  77. package/src/zexus/evaluator/expressions.py +358 -62
  78. package/src/zexus/evaluator/functions.py +458 -20
  79. package/src/zexus/evaluator/resource_limiter.py +4 -4
  80. package/src/zexus/evaluator/statements.py +774 -122
  81. package/src/zexus/evaluator/unified_execution.py +573 -72
  82. package/src/zexus/evaluator/utils.py +14 -2
  83. package/src/zexus/evaluator_original.py +1 -1
  84. package/src/zexus/event_loop.py +186 -0
  85. package/src/zexus/lexer.py +742 -458
  86. package/src/zexus/lsp/__init__.py +1 -1
  87. package/src/zexus/lsp/definition_provider.py +163 -9
  88. package/src/zexus/lsp/server.py +22 -8
  89. package/src/zexus/lsp/symbol_provider.py +182 -9
  90. package/src/zexus/module_cache.py +239 -9
  91. package/src/zexus/module_manager.py +129 -1
  92. package/src/zexus/object.py +76 -6
  93. package/src/zexus/parser/__pycache__/parser.cpython-312.pyc +0 -0
  94. package/src/zexus/parser/__pycache__/strategy_context.cpython-312.pyc +0 -0
  95. package/src/zexus/parser/__pycache__/strategy_structural.cpython-312.pyc +0 -0
  96. package/src/zexus/parser/parser.py +1349 -408
  97. package/src/zexus/parser/strategy_context.py +755 -58
  98. package/src/zexus/parser/strategy_structural.py +121 -21
  99. package/src/zexus/persistence.py +15 -1
  100. package/src/zexus/renderer/__init__.py +61 -0
  101. package/src/zexus/renderer/__pycache__/__init__.cpython-312.pyc +0 -0
  102. package/src/zexus/renderer/__pycache__/backend.cpython-312.pyc +0 -0
  103. package/src/zexus/renderer/__pycache__/canvas.cpython-312.pyc +0 -0
  104. package/src/zexus/renderer/__pycache__/color_system.cpython-312.pyc +0 -0
  105. package/src/zexus/renderer/__pycache__/layout.cpython-312.pyc +0 -0
  106. package/src/zexus/renderer/__pycache__/main_renderer.cpython-312.pyc +0 -0
  107. package/src/zexus/renderer/__pycache__/painter.cpython-312.pyc +0 -0
  108. package/src/zexus/renderer/backend.py +261 -0
  109. package/src/zexus/renderer/canvas.py +78 -0
  110. package/src/zexus/renderer/color_system.py +201 -0
  111. package/src/zexus/renderer/graphics.py +31 -0
  112. package/src/zexus/renderer/layout.py +222 -0
  113. package/src/zexus/renderer/main_renderer.py +66 -0
  114. package/src/zexus/renderer/painter.py +30 -0
  115. package/src/zexus/renderer/tk_backend.py +208 -0
  116. package/src/zexus/renderer/web_backend.py +260 -0
  117. package/src/zexus/runtime/__init__.py +10 -2
  118. package/src/zexus/runtime/__pycache__/__init__.cpython-312.pyc +0 -0
  119. package/src/zexus/runtime/__pycache__/async_runtime.cpython-312.pyc +0 -0
  120. package/src/zexus/runtime/__pycache__/load_manager.cpython-312.pyc +0 -0
  121. package/src/zexus/runtime/file_flags.py +137 -0
  122. package/src/zexus/runtime/load_manager.py +368 -0
  123. package/src/zexus/safety/__pycache__/__init__.cpython-312.pyc +0 -0
  124. package/src/zexus/safety/__pycache__/memory_safety.cpython-312.pyc +0 -0
  125. package/src/zexus/security.py +424 -34
  126. package/src/zexus/stdlib/fs.py +23 -18
  127. package/src/zexus/stdlib/http.py +289 -186
  128. package/src/zexus/stdlib/sockets.py +207 -163
  129. package/src/zexus/stdlib/websockets.py +282 -0
  130. package/src/zexus/stdlib_integration.py +369 -2
  131. package/src/zexus/strategy_recovery.py +6 -3
  132. package/src/zexus/type_checker.py +423 -0
  133. package/src/zexus/virtual_filesystem.py +189 -2
  134. package/src/zexus/vm/__init__.py +113 -3
  135. package/src/zexus/vm/__pycache__/async_optimizer.cpython-312.pyc +0 -0
  136. package/src/zexus/vm/__pycache__/bytecode.cpython-312.pyc +0 -0
  137. package/src/zexus/vm/__pycache__/bytecode_converter.cpython-312.pyc +0 -0
  138. package/src/zexus/vm/__pycache__/cache.cpython-312.pyc +0 -0
  139. package/src/zexus/vm/__pycache__/compiler.cpython-312.pyc +0 -0
  140. package/src/zexus/vm/__pycache__/gas_metering.cpython-312.pyc +0 -0
  141. package/src/zexus/vm/__pycache__/jit.cpython-312.pyc +0 -0
  142. package/src/zexus/vm/__pycache__/parallel_vm.cpython-312.pyc +0 -0
  143. package/src/zexus/vm/__pycache__/vm.cpython-312.pyc +0 -0
  144. package/src/zexus/vm/async_optimizer.py +80 -6
  145. package/src/zexus/vm/binary_bytecode.py +659 -0
  146. package/src/zexus/vm/bytecode.py +59 -11
  147. package/src/zexus/vm/bytecode_converter.py +26 -12
  148. package/src/zexus/vm/cabi.c +1985 -0
  149. package/src/zexus/vm/cabi.cpython-312-x86_64-linux-gnu.so +0 -0
  150. package/src/zexus/vm/cabi.h +127 -0
  151. package/src/zexus/vm/cache.py +561 -17
  152. package/src/zexus/vm/compiler.py +818 -51
  153. package/src/zexus/vm/fastops.c +15743 -0
  154. package/src/zexus/vm/fastops.cpython-312-x86_64-linux-gnu.so +0 -0
  155. package/src/zexus/vm/fastops.pyx +288 -0
  156. package/src/zexus/vm/gas_metering.py +50 -9
  157. package/src/zexus/vm/jit.py +364 -20
  158. package/src/zexus/vm/native_jit_backend.py +1816 -0
  159. package/src/zexus/vm/native_runtime.cpp +1388 -0
  160. package/src/zexus/vm/native_runtime.cpython-312-x86_64-linux-gnu.so +0 -0
  161. package/src/zexus/vm/optimizer.py +161 -11
  162. package/src/zexus/vm/parallel_vm.py +140 -45
  163. package/src/zexus/vm/peephole_optimizer.py +82 -4
  164. package/src/zexus/vm/profiler.py +38 -18
  165. package/src/zexus/vm/register_allocator.py +16 -5
  166. package/src/zexus/vm/register_vm.py +8 -5
  167. package/src/zexus/vm/vm.py +3581 -531
  168. package/src/zexus/vm/wasm_compiler.py +658 -0
  169. package/src/zexus/zexus_ast.py +137 -11
  170. package/src/zexus/zexus_token.py +16 -5
  171. package/src/zexus/zpm/installer.py +55 -15
  172. package/src/zexus/zpm/package_manager.py +1 -1
  173. package/src/zexus/zpm/registry.py +257 -28
  174. package/src/zexus.egg-info/PKG-INFO +16 -6
  175. package/src/zexus.egg-info/SOURCES.txt +129 -17
  176. package/src/zexus.egg-info/entry_points.txt +1 -0
  177. package/src/zexus.egg-info/requires.txt +4 -0
@@ -4,6 +4,6 @@
4
4
  try:
5
5
  from .server import ZexusLanguageServer
6
6
  __all__ = ['ZexusLanguageServer']
7
- except ImportError:
7
+ except (ImportError, NameError, RuntimeError):
8
8
  # pygls not available, LSP features won't work
9
9
  __all__ = []
@@ -1,22 +1,176 @@
1
- """Definition provider for Zexus LSP."""
1
+ """Definition provider for Zexus LSP — go-to-definition support."""
2
2
 
3
3
  from typing import List, Dict, Any, Optional
4
+
4
5
  try:
5
- from pygls.lsp.types import Position
6
+ from pygls.lsp.types import Position, Location, Range
6
7
  PYGLS_AVAILABLE = True
7
8
  except ImportError:
8
9
  PYGLS_AVAILABLE = False
9
10
 
10
11
 
12
+ # AST node types that introduce named definitions
13
+ _DEFINITION_NODE_TYPES = (
14
+ 'ActionStatement', 'FunctionStatement', 'PureFunctionStatement',
15
+ 'LetStatement', 'ConstStatement',
16
+ 'EntityStatement', 'ContractStatement', 'DataStatement',
17
+ 'EnumStatement', 'InterfaceStatement', 'ProtocolStatement',
18
+ 'TypeAliasStatement', 'ModuleStatement', 'PackageStatement',
19
+ 'ScreenStatement', 'ComponentStatement', 'MiddlewareStatement',
20
+ 'PatternStatement', 'StreamStatement', 'ModifierDeclaration',
21
+ )
22
+
23
+
24
+ def _get_name(node) -> Optional[str]:
25
+ """Extract the string name from an AST definition node."""
26
+ name = getattr(node, 'name', None)
27
+ if name is None:
28
+ return None
29
+ if hasattr(name, 'value'):
30
+ return str(name.value)
31
+ return str(name)
32
+
33
+
34
+ def _collect_definitions(node, defs: Dict[str, list]):
35
+ """Walk the AST recursively and collect all named definitions.
36
+
37
+ ``defs`` maps name → list of AST nodes (same name may be defined
38
+ more than once, e.g. multiple ``let`` re-assignments).
39
+ """
40
+ if node is None:
41
+ return
42
+ node_type = type(node).__name__
43
+
44
+ if node_type in _DEFINITION_NODE_TYPES:
45
+ name = _get_name(node)
46
+ if name:
47
+ defs.setdefault(name, []).append(node)
48
+
49
+ # Recurse into child nodes that may contain more definitions
50
+ for attr in ('statements', 'body', 'consequence', 'alternative',
51
+ 'methods', 'properties', 'cases', 'block', 'try_block',
52
+ 'catch_block', 'finally_block', 'members'):
53
+ child = getattr(node, attr, None)
54
+ if child is None:
55
+ continue
56
+ if isinstance(child, list):
57
+ for c in child:
58
+ _collect_definitions(c, defs)
59
+ else:
60
+ # BlockStatement, Program, etc.
61
+ stmts = getattr(child, 'statements', None)
62
+ if isinstance(stmts, list):
63
+ for c in stmts:
64
+ _collect_definitions(c, defs)
65
+ else:
66
+ _collect_definitions(child, defs)
67
+
68
+
69
+ def _find_token_position(tokens, name: str, definition_keywords=None):
70
+ """Scan the token list for the *first* definition of ``name``.
71
+
72
+ We look for a keyword token (``action``, ``let``, ``const``, ``entity``,
73
+ ``contract``, ``data``, ``enum``, ``interface``, ``protocol``, ``type``,
74
+ ``module``, ``package``, ``screen``, ``component``, ``middleware``,
75
+ ``pattern``, ``stream``, ``pure``) followed (possibly after a type
76
+ annotation) by an ``IDENT`` token whose ``literal`` matches ``name``.
77
+ Returns ``(line, column)`` (0-based) or ``None``.
78
+ """
79
+ if definition_keywords is None:
80
+ definition_keywords = {
81
+ 'action', 'let', 'const', 'entity', 'contract', 'data',
82
+ 'enum', 'interface', 'protocol', 'type', 'module', 'package',
83
+ 'screen', 'component', 'middleware', 'pattern', 'stream',
84
+ 'pure', 'fn', 'function', 'def', 'class', 'struct', 'modifier',
85
+ }
86
+
87
+ want_ident = False
88
+ for tok in tokens:
89
+ lit = getattr(tok, 'literal', '')
90
+ ttype = getattr(tok, 'type', '')
91
+
92
+ if lit in definition_keywords:
93
+ want_ident = True
94
+ continue
95
+
96
+ if want_ident and ttype == 'IDENT' and lit == name:
97
+ line = getattr(tok, 'line', 0)
98
+ col = getattr(tok, 'column', 0)
99
+ # Token lines are 1-based; LSP uses 0-based
100
+ return (max(0, line - 1), max(0, col))
101
+
102
+ if want_ident and ttype not in ('IDENT', 'COLON', ':', 'LBRACKET', 'RBRACKET',
103
+ 'LT', 'GT', 'COMMA', 'STRING'):
104
+ # Not part of a type annotation — reset
105
+ want_ident = False
106
+
107
+ return None
108
+
109
+
110
+ def _word_at_position(text: str, line: int, character: int) -> Optional[str]:
111
+ """Extract the identifier under the cursor."""
112
+ lines = text.split('\n')
113
+ if line >= len(lines):
114
+ return None
115
+ row = lines[line]
116
+ if character >= len(row):
117
+ return None
118
+
119
+ # Walk left
120
+ start = character
121
+ while start > 0 and (row[start - 1].isalnum() or row[start - 1] == '_'):
122
+ start -= 1
123
+ # Walk right
124
+ end = character
125
+ while end < len(row) and (row[end].isalnum() or row[end] == '_'):
126
+ end += 1
127
+
128
+ word = row[start:end]
129
+ return word if word else None
130
+
131
+
11
132
  class DefinitionProvider:
12
133
  """Provides go-to-definition for Zexus code."""
13
134
 
14
- def get_definition(self, uri: str, position: Position, doc_info: Dict[str, Any]) -> Optional[List]:
15
- """Get definition location for symbol at position."""
135
+ def get_definition(self, uri: str, position, doc_info: Dict[str, Any]) -> Optional[List]:
136
+ """Return a list of ``Location`` objects for the definition of the
137
+ symbol under the cursor, or ``None`` if not found.
138
+ """
16
139
  if not PYGLS_AVAILABLE:
17
140
  return None
18
-
19
- # TODO: Implement go-to-definition by analyzing AST
20
- # For now, return None
21
-
22
- return None
141
+
142
+ text = doc_info.get('text', '')
143
+ ast = doc_info.get('ast')
144
+ tokens = doc_info.get('tokens', [])
145
+
146
+ if not text:
147
+ return None
148
+
149
+ line = position.line
150
+ character = position.character
151
+ word = _word_at_position(text, line, character)
152
+ if not word:
153
+ return None
154
+
155
+ # 1. Collect all definitions from the AST
156
+ defs: Dict[str, list] = {}
157
+ if ast:
158
+ _collect_definitions(ast, defs)
159
+
160
+ if word not in defs:
161
+ return None
162
+
163
+ # 2. Find the first definition position in the token stream
164
+ pos = _find_token_position(tokens, word)
165
+ if pos is None:
166
+ return None
167
+
168
+ def_line, def_col = pos
169
+ location = Location(
170
+ uri=uri,
171
+ range=Range(
172
+ start=Position(line=def_line, character=def_col),
173
+ end=Position(line=def_line, character=def_col + len(word)),
174
+ ),
175
+ )
176
+ return [location]
@@ -107,10 +107,18 @@ if PYGLS_AVAILABLE:
107
107
 
108
108
  # Parse the document
109
109
  try:
110
- lexer = Lexer(text)
111
- tokens = lexer.tokenize()
112
- parser = Parser(tokens)
113
- ast = parser.parse()
110
+ lexer = Lexer(text, filename=uri)
111
+ # Collect tokens for position mapping (used by providers)
112
+ tokens = []
113
+ while True:
114
+ tok = lexer.next_token()
115
+ tokens.append(tok)
116
+ if getattr(tok, 'type', '') == 'EOF':
117
+ break
118
+ # Re-lex for the parser (parser consumes the lexer)
119
+ parse_lexer = Lexer(text, filename=uri)
120
+ parser = Parser(parse_lexer)
121
+ ast = parser.parse_program()
114
122
  ls.documents[uri] = {
115
123
  'text': text,
116
124
  'ast': ast,
@@ -136,10 +144,16 @@ if PYGLS_AVAILABLE:
136
144
  text = changes[0].text
137
145
  # Re-parse the document
138
146
  try:
139
- lexer = Lexer(text)
140
- tokens = lexer.tokenize()
141
- parser = Parser(tokens)
142
- ast = parser.parse()
147
+ lexer = Lexer(text, filename=uri)
148
+ tokens = []
149
+ while True:
150
+ tok = lexer.next_token()
151
+ tokens.append(tok)
152
+ if getattr(tok, 'type', '') == 'EOF':
153
+ break
154
+ parse_lexer = Lexer(text, filename=uri)
155
+ parser = Parser(parse_lexer)
156
+ ast = parser.parse_program()
143
157
  ls.documents[uri] = {
144
158
  'text': text,
145
159
  'ast': ast,
@@ -1,6 +1,7 @@
1
- """Symbol provider for Zexus LSP."""
1
+ """Symbol provider for Zexus LSP — document outline and symbol search."""
2
+
3
+ from typing import List, Dict, Any, Optional
2
4
 
3
- from typing import List, Dict, Any
4
5
  try:
5
6
  from pygls.lsp.types import (DocumentSymbol, SymbolKind, Range, Position)
6
7
  PYGLS_AVAILABLE = True
@@ -8,7 +9,169 @@ except ImportError:
8
9
  PYGLS_AVAILABLE = False
9
10
  # Define minimal stubs when pygls not available
10
11
  class SymbolKind:
11
- pass
12
+ Function = 12
13
+ Variable = 13
14
+ Constant = 14
15
+ Class = 5
16
+ Struct = 23
17
+ Enum = 10
18
+ Interface = 11
19
+ Module = 2
20
+ Package = 4
21
+ TypeParameter = 26
22
+
23
+
24
+ # Map AST node type name → (SymbolKind, detail label)
25
+ _SYMBOL_MAP = {
26
+ 'ActionStatement': (SymbolKind.Function, 'action'),
27
+ 'FunctionStatement': (SymbolKind.Function, 'function'),
28
+ 'PureFunctionStatement': (SymbolKind.Function, 'pure function'),
29
+ 'LetStatement': (SymbolKind.Variable, 'let'),
30
+ 'ConstStatement': (SymbolKind.Constant, 'const'),
31
+ 'EntityStatement': (SymbolKind.Class, 'entity'),
32
+ 'ContractStatement': (SymbolKind.Class, 'contract'),
33
+ 'DataStatement': (SymbolKind.Struct, 'data'),
34
+ 'EnumStatement': (SymbolKind.Enum, 'enum'),
35
+ 'InterfaceStatement': (SymbolKind.Interface, 'interface'),
36
+ 'ProtocolStatement': (SymbolKind.Interface, 'protocol'),
37
+ 'TypeAliasStatement': (SymbolKind.TypeParameter, 'type'),
38
+ 'ModuleStatement': (SymbolKind.Module, 'module'),
39
+ 'PackageStatement': (SymbolKind.Package, 'package'),
40
+ 'ScreenStatement': (SymbolKind.Function, 'screen'),
41
+ 'ComponentStatement': (SymbolKind.Function, 'component'),
42
+ 'MiddlewareStatement': (SymbolKind.Function, 'middleware'),
43
+ 'PatternStatement': (SymbolKind.Function, 'pattern'),
44
+ 'StreamStatement': (SymbolKind.Variable, 'stream'),
45
+ 'ModifierDeclaration': (SymbolKind.Function, 'modifier'),
46
+ }
47
+
48
+
49
+ def _get_name(node) -> Optional[str]:
50
+ """Extract the string name from an AST definition node."""
51
+ name = getattr(node, 'name', None)
52
+ if name is None:
53
+ return None
54
+ if hasattr(name, 'value'):
55
+ return str(name.value)
56
+ return str(name)
57
+
58
+
59
+ def _find_name_line(tokens, name: str, start_line: int = 0) -> int:
60
+ """Return the 0-based line number where ``name`` appears as an IDENT
61
+ in the token stream, starting from ``start_line``. Returns
62
+ ``start_line`` if not found.
63
+ """
64
+ for tok in tokens:
65
+ ttype = getattr(tok, 'type', '')
66
+ lit = getattr(tok, 'literal', '')
67
+ line = getattr(tok, 'line', 0)
68
+ if ttype == 'IDENT' and lit == name and (line - 1) >= start_line:
69
+ return max(0, line - 1)
70
+ return start_line
71
+
72
+
73
+ def _make_range(line: int, col: int = 0, end_line: Optional[int] = None, length: int = 1):
74
+ """Build a pygls Range. Lines/cols are 0-based."""
75
+ if end_line is None:
76
+ end_line = line
77
+ return Range(
78
+ start=Position(line=line, character=col),
79
+ end=Position(line=end_line, character=col + length),
80
+ )
81
+
82
+
83
+ def _node_to_symbol(node, tokens, text_lines) -> Optional[DocumentSymbol]:
84
+ """Convert an AST definition node into a ``DocumentSymbol``.
85
+
86
+ Returns ``None`` if the node does not map to a symbol.
87
+ """
88
+ if not PYGLS_AVAILABLE:
89
+ return None
90
+
91
+ node_type = type(node).__name__
92
+ entry = _SYMBOL_MAP.get(node_type)
93
+ if entry is None:
94
+ return None
95
+
96
+ kind, detail = entry
97
+ name = _get_name(node)
98
+ if not name:
99
+ return None
100
+
101
+ # Locate the name in the token stream
102
+ line = _find_name_line(tokens, name)
103
+ symbol_range = _make_range(line, length=len(name))
104
+ selection_range = symbol_range
105
+
106
+ # Build children for container types (entity, contract, enum, data)
107
+ children = []
108
+
109
+ if node_type == 'EntityStatement':
110
+ # Properties
111
+ for prop in (getattr(node, 'properties', None) or []):
112
+ prop_name = _get_name(prop)
113
+ if prop_name:
114
+ pline = _find_name_line(tokens, prop_name, line)
115
+ children.append(DocumentSymbol(
116
+ name=prop_name,
117
+ kind=SymbolKind.Variable,
118
+ range=_make_range(pline, length=len(prop_name)),
119
+ selection_range=_make_range(pline, length=len(prop_name)),
120
+ detail='property',
121
+ ))
122
+ # Methods
123
+ for method in (getattr(node, 'methods', None) or []):
124
+ child_sym = _node_to_symbol(method, tokens, text_lines)
125
+ if child_sym:
126
+ children.append(child_sym)
127
+
128
+ elif node_type == 'ContractStatement':
129
+ body = getattr(node, 'body', None)
130
+ stmts = getattr(body, 'statements', None) if body else None
131
+ if isinstance(stmts, list):
132
+ for stmt in stmts:
133
+ child_sym = _node_to_symbol(stmt, tokens, text_lines)
134
+ if child_sym:
135
+ children.append(child_sym)
136
+
137
+ elif node_type == 'EnumStatement':
138
+ for member in (getattr(node, 'members', None) or []):
139
+ m_name = _get_name(member) or getattr(member, 'value', None)
140
+ if isinstance(m_name, str):
141
+ pass
142
+ elif hasattr(m_name, 'value'):
143
+ m_name = m_name.value
144
+ if m_name:
145
+ mline = _find_name_line(tokens, str(m_name), line)
146
+ children.append(DocumentSymbol(
147
+ name=str(m_name),
148
+ kind=SymbolKind.Constant,
149
+ range=_make_range(mline, length=len(str(m_name))),
150
+ selection_range=_make_range(mline, length=len(str(m_name))),
151
+ detail='member',
152
+ ))
153
+
154
+ elif node_type == 'DataStatement':
155
+ for field in (getattr(node, 'fields', None) or []):
156
+ f_name = _get_name(field)
157
+ if f_name:
158
+ fline = _find_name_line(tokens, f_name, line)
159
+ children.append(DocumentSymbol(
160
+ name=f_name,
161
+ kind=SymbolKind.Variable,
162
+ range=_make_range(fline, length=len(f_name)),
163
+ selection_range=_make_range(fline, length=len(f_name)),
164
+ detail='field',
165
+ ))
166
+
167
+ return DocumentSymbol(
168
+ name=name,
169
+ kind=kind,
170
+ range=symbol_range,
171
+ selection_range=selection_range,
172
+ detail=detail,
173
+ children=children if children else None,
174
+ )
12
175
 
13
176
 
14
177
  class SymbolProvider:
@@ -18,14 +181,24 @@ class SymbolProvider:
18
181
  """Get document symbols from AST."""
19
182
  if not PYGLS_AVAILABLE:
20
183
  return []
21
-
184
+
22
185
  symbols = []
23
186
  ast = doc_info.get('ast')
24
-
187
+ tokens = doc_info.get('tokens', [])
188
+ text = doc_info.get('text', '')
189
+ text_lines = text.split('\n') if text else []
190
+
25
191
  if not ast:
26
192
  return symbols
27
-
28
- # TODO: Walk AST and extract symbols
29
- # For now, return empty list
30
-
193
+
194
+ # Walk top-level statements
195
+ stmts = getattr(ast, 'statements', [])
196
+ if not isinstance(stmts, list):
197
+ return symbols
198
+
199
+ for stmt in stmts:
200
+ sym = _node_to_symbol(stmt, tokens, text_lines)
201
+ if sym:
202
+ symbols.append(sym)
203
+
31
204
  return symbols