ZedScripts 1.0.1__tar.gz → 1.1.0__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 (45) hide show
  1. {zedscripts-1.0.1/src/ZedScripts.egg-info → zedscripts-1.1.0}/PKG-INFO +7 -1
  2. {zedscripts-1.0.1 → zedscripts-1.1.0}/README.md +4 -0
  3. {zedscripts-1.0.1 → zedscripts-1.1.0}/pyproject.toml +4 -1
  4. zedscripts-1.1.0/src/ZedScripts/__about__.py +7 -0
  5. zedscripts-1.1.0/src/ZedScripts/__init__.py +75 -0
  6. {zedscripts-1.0.1 → zedscripts-1.1.0}/src/ZedScripts/environment/__init__.py +8 -0
  7. {zedscripts-1.0.1 → zedscripts-1.1.0}/src/ZedScripts/environment/config.py +91 -8
  8. {zedscripts-1.0.1 → zedscripts-1.1.0}/src/ZedScripts/environment/document.py +2 -0
  9. {zedscripts-1.0.1 → zedscripts-1.1.0}/src/ZedScripts/environment/workspace.py +14 -1
  10. {zedscripts-1.0.1 → zedscripts-1.1.0}/src/ZedScripts/server.py +9 -1
  11. {zedscripts-1.0.1 → zedscripts-1.1.0}/src/ZedScripts/structure/lexer.py +24 -19
  12. zedscripts-1.1.0/src/ZedScripts/tests/parser/test_lexer.py +261 -0
  13. {zedscripts-1.0.1 → zedscripts-1.1.0/src/ZedScripts.egg-info}/PKG-INFO +7 -1
  14. {zedscripts-1.0.1 → zedscripts-1.1.0}/src/ZedScripts.egg-info/SOURCES.txt +3 -1
  15. {zedscripts-1.0.1 → zedscripts-1.1.0}/src/ZedScripts.egg-info/requires.txt +3 -0
  16. zedscripts-1.1.0/tests/test_config_cli.py +31 -0
  17. zedscripts-1.0.1/src/ZedScripts/__about__.py +0 -4
  18. zedscripts-1.0.1/src/ZedScripts/__init__.py +0 -38
  19. {zedscripts-1.0.1 → zedscripts-1.1.0}/LICENSE +0 -0
  20. {zedscripts-1.0.1 → zedscripts-1.1.0}/setup.cfg +0 -0
  21. {zedscripts-1.0.1 → zedscripts-1.1.0}/src/ZedScripts/enums/Diagnostic.py +0 -0
  22. {zedscripts-1.0.1 → zedscripts-1.1.0}/src/ZedScripts/enums/SyntaxErrorType.py +0 -0
  23. {zedscripts-1.0.1 → zedscripts-1.1.0}/src/ZedScripts/environment/mod.py +0 -0
  24. {zedscripts-1.0.1 → zedscripts-1.1.0}/src/ZedScripts/environment/version.py +0 -0
  25. {zedscripts-1.0.1 → zedscripts-1.1.0}/src/ZedScripts/locale/en.json +0 -0
  26. {zedscripts-1.0.1 → zedscripts-1.1.0}/src/ZedScripts/main.py +0 -0
  27. {zedscripts-1.0.1 → zedscripts-1.1.0}/src/ZedScripts/providers/actions.py +0 -0
  28. {zedscripts-1.0.1 → zedscripts-1.1.0}/src/ZedScripts/providers/capabilities.py +0 -0
  29. {zedscripts-1.0.1 → zedscripts-1.1.0}/src/ZedScripts/providers/debug.py +0 -0
  30. {zedscripts-1.0.1 → zedscripts-1.1.0}/src/ZedScripts/providers/diagnostics.py +0 -0
  31. {zedscripts-1.0.1 → zedscripts-1.1.0}/src/ZedScripts/providers/hover.py +0 -0
  32. {zedscripts-1.0.1 → zedscripts-1.1.0}/src/ZedScripts/providers/http.py +0 -0
  33. {zedscripts-1.0.1 → zedscripts-1.1.0}/src/ZedScripts/providers/locale.py +0 -0
  34. {zedscripts-1.0.1 → zedscripts-1.1.0}/src/ZedScripts/providers/notifications.py +0 -0
  35. {zedscripts-1.0.1 → zedscripts-1.1.0}/src/ZedScripts/providers/semantic_tokens.py +0 -0
  36. {zedscripts-1.0.1 → zedscripts-1.1.0}/src/ZedScripts/scripts/__init__.py +0 -0
  37. {zedscripts-1.0.1 → zedscripts-1.1.0}/src/ZedScripts/scripts/block.py +0 -0
  38. {zedscripts-1.0.1 → zedscripts-1.1.0}/src/ZedScripts/scripts/dataset.py +0 -0
  39. {zedscripts-1.0.1 → zedscripts-1.1.0}/src/ZedScripts/scripts/value.py +0 -0
  40. {zedscripts-1.0.1 → zedscripts-1.1.0}/src/ZedScripts/scripts/version.py +0 -0
  41. {zedscripts-1.0.1 → zedscripts-1.1.0}/src/ZedScripts/structure/ast.py +0 -0
  42. {zedscripts-1.0.1 → zedscripts-1.1.0}/src/ZedScripts/structure/parser.py +0 -0
  43. {zedscripts-1.0.1 → zedscripts-1.1.0}/src/ZedScripts/utils.py +0 -0
  44. {zedscripts-1.0.1 → zedscripts-1.1.0}/src/ZedScripts.egg-info/dependency_links.txt +0 -0
  45. {zedscripts-1.0.1 → zedscripts-1.1.0}/src/ZedScripts.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ZedScripts
3
- Version: 1.0.1
3
+ Version: 1.1.0
4
4
  Author: SimKDT
5
5
  License: MIT License
6
6
 
@@ -36,6 +36,8 @@ Provides-Extra: build
36
36
  Requires-Dist: pyinstaller; extra == "build"
37
37
  Provides-Extra: debug
38
38
  Requires-Dist: debugpy; extra == "debug"
39
+ Provides-Extra: test
40
+ Requires-Dist: pytest; extra == "test"
39
41
  Dynamic: license-file
40
42
 
41
43
  # ZedScripts LSP
@@ -211,6 +213,10 @@ Each diagnostic by default have a specific set of modifiers that can be overridd
211
213
  }
212
214
  ```
213
215
 
216
+ ### Server
217
+ A list of server-related configurations for the LSP. The following parameters are available:
218
+ - `logging_level` (optional, default: `INFO`): specifies the logging level for the server. This option is only available in the global configuration and accepts values such as `NOTSET`, `DEBUG`, `INFO`, `WARNING`, `ERROR`, and `CRITICAL`.
219
+
214
220
  ## IDE configuration
215
221
  Based on the IDE you use, you may need to install the ZedScripts LSP application for your OS in the [releases](https://github.com/PZ-Wiki-Modding/ZedScripts-LSP/releases) (Linux, Windows).
216
222
 
@@ -171,6 +171,10 @@ Each diagnostic by default have a specific set of modifiers that can be overridd
171
171
  }
172
172
  ```
173
173
 
174
+ ### Server
175
+ A list of server-related configurations for the LSP. The following parameters are available:
176
+ - `logging_level` (optional, default: `INFO`): specifies the logging level for the server. This option is only available in the global configuration and accepts values such as `NOTSET`, `DEBUG`, `INFO`, `WARNING`, `ERROR`, and `CRITICAL`.
177
+
174
178
  ## IDE configuration
175
179
  Based on the IDE you use, you may need to install the ZedScripts LSP application for your OS in the [releases](https://github.com/PZ-Wiki-Modding/ZedScripts-LSP/releases) (Linux, Windows).
176
180
 
@@ -3,7 +3,7 @@ name = "ZedScripts"
3
3
  description = ""
4
4
  readme = { file = "README.md", content-type = "text/markdown" }
5
5
  requires-python = ">=3.12.3"
6
- version = "1.0.1"
6
+ version = "1.1.0"
7
7
  license = {file = "LICENSE"}
8
8
  authors = [
9
9
  { name = "SimKDT" },
@@ -23,6 +23,9 @@ build = [
23
23
  debug = [
24
24
  "debugpy"
25
25
  ]
26
+ test = [
27
+ "pytest"
28
+ ]
26
29
 
27
30
  # used to load locale JSON files
28
31
  [build-system]
@@ -0,0 +1,7 @@
1
+ __version__ = "1.1.0"
2
+
3
+ def main():
4
+ print(__version__)
5
+
6
+ if __name__ == "__main__":
7
+ main()
@@ -0,0 +1,75 @@
1
+ import os
2
+ import logging
3
+ from datetime import datetime, timezone
4
+ from pathlib import Path
5
+
6
+ IDENTIFIER = "ZedScripts"
7
+ SOURCE = IDENTIFIER
8
+
9
+ IS_DEBUG = os.getenv("ZEDSCRIPTS_DEBUG_MODE") == "1"
10
+
11
+ # zedscript's cache directory
12
+ ZEDSCRIPT_CACHE_DIR = Path.home() / ".zedscripts"
13
+ """
14
+ Cache directory to hold various information related to ZedScripts.
15
+ """
16
+ ZEDSCRIPT_CACHE_DIR.mkdir(parents=True, exist_ok=True)
17
+
18
+ # config files naming
19
+ CONFIGURATION_FILE_NAME = ".zedscripts.json"
20
+ GLOBAL_CONFIGURATION_FILE = (ZEDSCRIPT_CACHE_DIR / CONFIGURATION_FILE_NAME).resolve()
21
+
22
+ # setup the logger
23
+
24
+ log_dir = Path.home() / ".zedscripts" / "logs"
25
+ log_dir.mkdir(parents=True, exist_ok=True)
26
+
27
+ # define logger
28
+ logger = logging.getLogger()
29
+
30
+ # set early level, this is changed later by the user configuration
31
+ # in the server initialization
32
+ logger.setLevel(logging.DEBUG if IS_DEBUG else logging.INFO)
33
+
34
+ formater = logging.Formatter("%(asctime)s %(levelname)s %(message)s")
35
+
36
+ # setup the time-based log handler
37
+ timestamp = datetime.now(timezone.utc).strftime("%Y-%m-%d-%H-%M-%S-%f")
38
+ time_log_file = log_dir / f"server-{timestamp}.log"
39
+ time_handler = logging.FileHandler(time_log_file, encoding="utf-8")
40
+ time_handler.setFormatter(
41
+ formater
42
+ )
43
+ logger.addHandler(time_handler)
44
+
45
+ # setup the debug log handler which stays in the same file to make it easier to view logs
46
+ # when restarting the server for example
47
+ if IS_DEBUG:
48
+ global_log_file = log_dir / f"server-debug.log"
49
+ global_handler = logging.FileHandler(
50
+ global_log_file, encoding="utf-8", mode="w"
51
+ )
52
+ global_handler.setFormatter(
53
+ formater
54
+ )
55
+ logger.addHandler(global_handler)
56
+
57
+ # filenames sort chronologically because the timestamp comes first
58
+ keep_logs = 5
59
+ log_files = sorted(log_dir.glob("server-*.log"), reverse=True)
60
+
61
+ # delete the oldest log file
62
+ for old_file in log_files[keep_logs:]:
63
+ try:
64
+ old_file.unlink()
65
+ except OSError:
66
+ logger.exception("Failed to delete old log file: %s", old_file)
67
+
68
+ logging.info("ZedScripts logger initialized. Debug mode: %s", IS_DEBUG)
69
+
70
+ # datasets
71
+ SCRIPTS_DATA_MANIFEST = "https://raw.githubusercontent.com/PZ-Wiki-Modding/pz-scripts-data/refs/heads/main/manifest.json"
72
+ SCRIPTS_BLOCKS_DATA_LINK = "https://raw.githubusercontent.com/PZ-Wiki-Modding/pz-scripts-data/refs/tags/{best_tag}/out/scriptsBlocks.json"
73
+ ROOTS_DATA_LINK = "https://raw.githubusercontent.com/PZ-Wiki-Modding/pz-scripts-data/refs/tags/{best_tag}/out/roots.json"
74
+
75
+ SCRIPTSDOCS_LINK = "https://pz-wiki-modding.github.io/PZ-API-Docs/scripts/"
@@ -1,4 +1,5 @@
1
1
  import enum
2
+ import logging
2
3
 
3
4
 
4
5
  class WorkspaceType(enum.StrEnum):
@@ -31,3 +32,10 @@ class VersionType(enum.StrEnum):
31
32
  BASE_GAME = enum.auto()
32
33
  """Base game files (e.g., `ProjectZomboid/media`)."""
33
34
 
35
+ class LoggerLevel(enum.IntEnum):
36
+ NOTSET = logging.NOTSET
37
+ DEBUG = logging.DEBUG
38
+ INFO = logging.INFO
39
+ WARNING = logging.WARNING
40
+ ERROR = logging.ERROR
41
+ CRITICAL = logging.CRITICAL
@@ -1,11 +1,19 @@
1
+ """
2
+ Provides the configuration models and schema generation for the ZedScripts environment.
3
+
4
+ Usage:
5
+ python -m ZedScripts.environment.config schema -o schema/zedscripts.json
6
+ python -m ZedScripts.environment.config default -o default_config.json
7
+ """
1
8
  import logging
2
9
  import enum
3
10
  from pathlib import Path
4
11
  from typing import Annotated, Optional
5
- from pydantic import BaseModel, BeforeValidator, Field, WithJsonSchema
12
+ from pydantic import BaseModel, BeforeValidator, Field, WithJsonSchema, ConfigDict
6
13
 
7
14
  from lsprotocol.types import DiagnosticSeverity, DiagnosticTag
8
15
 
16
+ from ..environment import LoggerLevel
9
17
  from ..scripts.version import DataVersion
10
18
  from ..enums.Diagnostic import DiagnosticType
11
19
 
@@ -68,6 +76,24 @@ DiagnosticTagName = Annotated[
68
76
  }),
69
77
  ]
70
78
 
79
+ def _parse_logger_level_name(value: object) -> object:
80
+ if isinstance(value, LoggerLevel):
81
+ return value
82
+ if isinstance(value, str):
83
+ try:
84
+ return LoggerLevel[value]
85
+ except KeyError:
86
+ raise ValueError("level must be a LoggerLevel member name") from None
87
+ return value
88
+ LoggerLevelName = Annotated[
89
+ LoggerLevel,
90
+ BeforeValidator(_parse_logger_level_name),
91
+ WithJsonSchema({
92
+ "type": "string",
93
+ "enum": [member.name for member in LoggerLevel],
94
+ }),
95
+ ]
96
+
71
97
 
72
98
 
73
99
  # define the models
@@ -77,8 +103,13 @@ class DatasetTag(enum.StrEnum):
77
103
  STABLE = "stable"
78
104
 
79
105
 
106
+ class ZedBaseModel(BaseModel):
107
+ """Base model for all ZedScripts configuration models.
108
+ Notably used to set additionalProperties to false for everything."""
109
+ model_config = ConfigDict(extra='forbid')
110
+
80
111
 
81
- class ReleaseModel(BaseModel):
112
+ class ReleaseModel(ZedBaseModel):
82
113
  major: int = Field(
83
114
  description="Build game version number.",
84
115
  ge=42,
@@ -97,7 +128,7 @@ class ReleaseModel(BaseModel):
97
128
  ge=0,
98
129
  )
99
130
 
100
- class DatasetModel(BaseModel):
131
+ class DatasetModel(ZedBaseModel):
101
132
  release: Optional[ReleaseModel] = Field(
102
133
  description="Provides a configuration for the dataset version to use for validation. This overrides the `latest` and `stable` fields if set.",
103
134
  default=None,
@@ -126,7 +157,7 @@ class DatasetModel(BaseModel):
126
157
  logging.error(f"Failed to get data version from release model: {e}. Defaulting to 'stable'.")
127
158
  return DatasetTag.STABLE
128
159
 
129
- class DiagnosticsModel(BaseModel):
160
+ class DiagnosticsModel(ZedBaseModel):
130
161
  """Represents the diagnostics configuration for the workspace environment."""
131
162
  type: DiagnosticTypeName = Field(
132
163
  description="Specifies the diagnostic member name to enable.",
@@ -144,7 +175,15 @@ class DiagnosticsModel(BaseModel):
144
175
  default=None,
145
176
  )
146
177
 
147
- class ConfigurationModel(BaseModel):
178
+ class ServerModel(ZedBaseModel):
179
+ """Represents the server configuration for the workspace environment."""
180
+ logging_level: Optional[LoggerLevelName] = Field(
181
+ description="Specifies the logging level for the server. This option only works in the global configuration.",
182
+ default=LoggerLevel.INFO,
183
+ exclude=True,
184
+ )
185
+
186
+ class ConfigurationModel(ZedBaseModel):
148
187
  """Represents the content of the configuration files for workspace environments."""
149
188
  dataset: DatasetModel = Field(
150
189
  description="Provides configuration for the dataset version to use for diagnostics.",
@@ -166,6 +205,10 @@ class ConfigurationModel(BaseModel):
166
205
  description="List of diagnostics configurations for the workspace environment.",
167
206
  default_factory=list,
168
207
  )
208
+ server: ServerModel = Field(
209
+ description="Provides configuration for the server in the workspace environment.",
210
+ default_factory=ServerModel,
211
+ )
169
212
 
170
213
  def find_diagnostic(self, type: DiagnosticType) -> DiagnosticsModel | None:
171
214
  for diagnostic in self.diagnostics:
@@ -174,6 +217,46 @@ class ConfigurationModel(BaseModel):
174
217
  return None
175
218
 
176
219
  if __name__ == "__main__":
177
- import json
178
- config = ConfigurationModel()
179
- print(json.dumps(config.model_json_schema(), indent=4))
220
+ import argparse
221
+
222
+ def print_schema(args: argparse.Namespace):
223
+ import json
224
+ config = ConfigurationModel()
225
+ out = json.dumps(config.model_json_schema(), indent=4)
226
+ print(out)
227
+ if args.output_path:
228
+ with open(args.output_path, "w") as f:
229
+ f.write(out)
230
+
231
+ def print_default_config(args: argparse.Namespace):
232
+ config = ConfigurationModel()
233
+ out = config.model_dump_json(indent=4)
234
+ print(out)
235
+ if args.output_path:
236
+ with open(args.output_path, "w") as f:
237
+ f.write(out)
238
+
239
+ main_parser = argparse.ArgumentParser(description="ZedScripts configuration tool")
240
+
241
+ # schema generation command
242
+ subparser = main_parser.add_subparsers(
243
+ dest="command",
244
+ required=True,
245
+ help="Available commands"
246
+ )
247
+ schema_parser = subparser.add_parser("schema",
248
+ help="Print the JSON schema of the configuration model.")
249
+ schema_parser.add_argument("-o", "--output-path",
250
+ help="Path to output the JSON schema.")
251
+ schema_parser.set_defaults(func=print_schema)
252
+
253
+ # default configuration command
254
+ default_config_subparser = subparser.add_parser("default",
255
+ help="Print the default configuration of the workspace environment.")
256
+ default_config_subparser.add_argument("-o", "--output-path",
257
+ help="Path to output the default configuration.")
258
+ default_config_subparser.set_defaults(func=print_default_config)
259
+
260
+ args = main_parser.parse_args()
261
+
262
+ args.func(args)
@@ -131,12 +131,14 @@ class Document:
131
131
  def make(dataset: 'Dataset', path: Path, workspace: 'Workspace') -> 'Document | None':
132
132
  # find the rootType of the document
133
133
  rootType = dataset.test_for_root(path)
134
+ logging.debug(f"Tested {path} for root, got: {rootType}")
134
135
  if rootType is None:
135
136
  return None
136
137
 
137
138
  # find the version and skip if pre 42
138
139
  # since we don't validate that
139
140
  version = Version.find_or_make_version(path)
141
+ logging.debug(f"Tested {path} for version, got: {version}")
140
142
  if version.type == VersionType.PRE_42:
141
143
  return None
142
144
 
@@ -260,7 +260,20 @@ class Workspace:
260
260
  @staticmethod
261
261
  def update_global_configuration(text: str | None = None) -> None:
262
262
  logging.info("Updating global configuration from %s", GLOBAL_CONFIGURATION_FILE)
263
- Workspace.global_configuration = Workspace.load_configuration(GLOBAL_CONFIGURATION_FILE, text=text)
263
+ # init file if it doesn't exist to default values
264
+ if not GLOBAL_CONFIGURATION_FILE.exists():
265
+ with open(GLOBAL_CONFIGURATION_FILE, "w") as f:
266
+ config = ConfigurationModel()
267
+ f.write(config.model_dump_json(
268
+ indent=4
269
+ ))
270
+
271
+ # load existing global
272
+ else:
273
+ config = Workspace.load_configuration(GLOBAL_CONFIGURATION_FILE, text=text)
274
+
275
+ # cache current snapshot of the global configuration
276
+ Workspace.global_configuration = config
264
277
  logging.debug(Workspace.global_configuration)
265
278
 
266
279
  @staticmethod
@@ -148,7 +148,7 @@ zedserver = ZedServer()
148
148
 
149
149
 
150
150
 
151
- ## EVENT HANDLERS
151
+ ## INITIALIZERS
152
152
 
153
153
  @zedserver.feature(types.INITIALIZE)
154
154
  def initialize(server: ZedServer, params: types.InitializeParams):
@@ -165,6 +165,11 @@ def initialize(server: ZedServer, params: types.InitializeParams):
165
165
  # this will store what the server can currently do
166
166
  capabilities.register_client_capabilities(params)
167
167
 
168
+ global_config = Workspace.get_global_configuration()
169
+ if global_config.server.logging_level is not None:
170
+ logging.info("Setting logging level to %s", global_config.server.logging_level)
171
+ logging.getLogger().setLevel(global_config.server.logging_level)
172
+
168
173
  # from the above, we determine what the server capabilities should be
169
174
  return types.InitializeResult(
170
175
  capabilities=capabilities.get_server_capabilities(),
@@ -217,6 +222,9 @@ def initialized(server: ZedServer, params: types.InitializedParams) -> None:
217
222
  notifications.send_notification(notifications.ZedNotification.LOADING_DOCUMENTS_DONE)
218
223
 
219
224
 
225
+
226
+ ## EVENT HANDLERS
227
+
220
228
  @zedserver.feature(types.TEXT_DOCUMENT_DID_OPEN)
221
229
  def did_open(server: ZedServer, params: types.DidOpenTextDocumentParams) -> None:
222
230
  server.on_document_changed(uri_to_path(params.text_document.uri), params.text_document.text)
@@ -194,25 +194,21 @@ WHITESPACE_NO_NEWLINE = string.whitespace.replace("\n", "")
194
194
 
195
195
 
196
196
 
197
- class ZedscriptSource:
198
- def __init__(self, text: str) -> None:
199
- self.string: str = text
200
- self.tokens: TokenCollection = TokenCollection()
201
-
202
-
203
197
  class Token:
204
198
  def __init__(
205
199
  self, type: TokenType, pos: TextPosition, end: TextPosition,
206
- source: ZedscriptSource, text: str
200
+ text: str
207
201
  ) -> None:
208
202
  self.type: TokenType = type
209
203
  self.pos: TextPosition = pos
210
204
  """Start position of the token."""
211
205
  self.end: TextPosition = end
212
206
  """End position of the token."""
213
- self.source: ZedscriptSource = source
214
207
  self.text = text
215
208
 
209
+ def __repr__(self) -> str:
210
+ return f"Token(type={self.type}, text={self.text!r}, pos={self.pos}, end={self.end})"
211
+
216
212
  def pos_of(self, i: int) -> TextPosition:
217
213
  """
218
214
  Returns the TextPosition of the character at index i in the string.
@@ -233,6 +229,16 @@ class Token:
233
229
  def __str__(self) -> str:
234
230
  return self.text
235
231
 
232
+ def __eq__(self, value: object) -> bool:
233
+ if not isinstance(value, Token):
234
+ return NotImplemented
235
+ return (
236
+ self.type == value.type and
237
+ self.pos == value.pos and
238
+ self.end == value.end and
239
+ self.text == value.text
240
+ )
241
+
236
242
 
237
243
  class TokenCollection(UserList[Token]):
238
244
  def at(self, pos: TextPosition) -> Token | None:
@@ -314,23 +320,22 @@ class TokenCollection(UserList[Token]):
314
320
 
315
321
  # TODO: this class is kinda redundant after all
316
322
  class TokenBuilder:
317
- def __init__(self, pos: TextPosition, type: TokenType, source: ZedscriptSource) -> None:
323
+ def __init__(self, pos: TextPosition, type: TokenType) -> None:
318
324
  self.type: TokenType = type
319
325
  self.pos: TextPosition = pos
320
326
  self.text: str = ""
321
- self.source: ZedscriptSource = source
322
327
 
323
328
  def add(self, char: str) -> None:
324
329
  self.text += char
325
330
 
326
331
  def build(self, end: TextPosition) -> Token:
327
- return Token(self.type, self.pos, end, self.source, self.text)
332
+ return Token(self.type, self.pos, end, self.text)
328
333
 
329
334
 
330
335
  class Lexer:
331
336
  def __init__(self, text: str) -> None:
332
337
  self.text: str = text
333
- self.source = ZedscriptSource(text)
338
+ self.tokens = TokenCollection()
334
339
  self.pos: int = -1
335
340
  self.line: int = 0
336
341
  self.offset: int = 0
@@ -364,7 +369,7 @@ class Lexer:
364
369
  return self.text[self.pos + num_characters]
365
370
 
366
371
  def start_token(self, type: TokenType) -> TokenBuilder:
367
- return TokenBuilder(TextPosition(self.line, self.offset), type, self.source)
372
+ return TokenBuilder(TextPosition(self.line, self.offset), type)
368
373
 
369
374
  def tokenize_comment(self) -> None:
370
375
  assert self.check_comment()
@@ -381,7 +386,7 @@ class Lexer:
381
386
  token.add(self.next())
382
387
  break
383
388
 
384
- self.source.tokens.append(
389
+ self.tokens.append(
385
390
  token.build(
386
391
  TextPosition(self.line, self.offset)
387
392
  )
@@ -396,7 +401,7 @@ class Lexer:
396
401
  def tokenize_punctuator(self) -> None:
397
402
  token = self.start_token(TokenType.PUNCTUATOR)
398
403
  token.add(self.next())
399
- self.source.tokens.append(
404
+ self.tokens.append(
400
405
  token.build(
401
406
  TextPosition(self.line, self.offset)
402
407
  )
@@ -414,7 +419,7 @@ class Lexer:
414
419
  break
415
420
  token.add(self.next())
416
421
 
417
- self.source.tokens.append(
422
+ self.tokens.append(
418
423
  token.build(
419
424
  TextPosition(self.line, self.offset)
420
425
  )
@@ -429,7 +434,7 @@ class Lexer:
429
434
  break
430
435
  token.add(self.next())
431
436
 
432
- self.source.tokens.append(
437
+ self.tokens.append(
433
438
  token.build(
434
439
  TextPosition(self.line, self.offset)
435
440
  )
@@ -449,7 +454,7 @@ class Lexer:
449
454
  elif char in ELEMENTS_DELIMITERS:
450
455
  token = lexer.start_token(TokenType.ELEMENT_DELIMITER)
451
456
  token.add(lexer.next())
452
- lexer.source.tokens.append(
457
+ lexer.tokens.append(
453
458
  token.build(
454
459
  TextPosition(lexer.line, lexer.offset)
455
460
  )
@@ -460,7 +465,7 @@ class Lexer:
460
465
  continue
461
466
  lexer.tokenize_text()
462
467
 
463
- return TokenCollection(lexer.source.tokens)
468
+ return TokenCollection(lexer.tokens)
464
469
 
465
470
 
466
471
  if __name__ == "__main__":
@@ -0,0 +1,261 @@
1
+
2
+ from ZedScripts.structure.lexer import Lexer, TextPosition, TokenType, Token, TokenCollection
3
+
4
+
5
+ def parse_example_content() -> TokenCollection:
6
+ EXAMPLE_SCRIPT_CONTENT = r"""
7
+ module Base {
8
+ item ItemTest {
9
+ param1 = value,
10
+ param2 = val1;val2;val3,
11
+ param3 = key1:val1;key2:val2,
12
+
13
+ param4= value,
14
+ param5 =value,
15
+ param6=value,
16
+ param7 = fuck you ?,
17
+
18
+ weird_param = key1=val1:key2=val2,
19
+
20
+ to_new_line =
21
+ value,
22
+
23
+ /* values */
24
+ value1,
25
+ value2,
26
+ value3,
27
+ value4:value5,
28
+ }
29
+
30
+ sound SoundTest {
31
+ clip = this/path/to/clip, volume = 0.8,
32
+ }
33
+
34
+ model id with spaces {
35
+
36
+ }
37
+
38
+ /* comment, different syntax block under
39
+ hello world *//* comment continued */
40
+
41
+ this_is_a_weird_block {
42
+
43
+ }
44
+
45
+ item ItemTest2
46
+ {
47
+
48
+ }
49
+ }
50
+ """.strip()
51
+ return Lexer.tokenize(EXAMPLE_SCRIPT_CONTENT)
52
+
53
+
54
+ def get_theorical_tokens():
55
+ EXPECTED_LEXICAL_TOKENS = TokenCollection(
56
+ [
57
+ Token(TokenType.TEXT, TextPosition(0, 0), TextPosition(0, 6), 'module'),
58
+ Token(TokenType.WHITESPACE, TextPosition(0, 6), TextPosition(0, 7), ' '),
59
+ Token(TokenType.TEXT, TextPosition(0, 7), TextPosition(0, 11), 'Base'),
60
+ Token(TokenType.WHITESPACE, TextPosition(0, 11), TextPosition(0, 12), ' '),
61
+ Token(TokenType.ELEMENT_DELIMITER, TextPosition(0, 12), TextPosition(0, 13), '{'),
62
+ Token(TokenType.WHITESPACE, TextPosition(0, 13), TextPosition(1, 4), '\n '),
63
+ Token(TokenType.TEXT, TextPosition(1, 4), TextPosition(1, 8), 'item'),
64
+ Token(TokenType.WHITESPACE, TextPosition(1, 8), TextPosition(1, 9), ' '),
65
+ Token(TokenType.TEXT, TextPosition(1, 9), TextPosition(1, 17), 'ItemTest'),
66
+ Token(TokenType.WHITESPACE, TextPosition(1, 17), TextPosition(1, 18), ' '),
67
+ Token(TokenType.ELEMENT_DELIMITER, TextPosition(1, 18), TextPosition(1, 19), '{'),
68
+ Token(TokenType.WHITESPACE, TextPosition(1, 19), TextPosition(2, 8), '\n '),
69
+ Token(TokenType.TEXT, TextPosition(2, 8), TextPosition(2, 14), 'param1'),
70
+ Token(TokenType.WHITESPACE, TextPosition(2, 14), TextPosition(2, 15), ' '),
71
+ Token(TokenType.PUNCTUATOR, TextPosition(2, 15), TextPosition(2, 16), '='),
72
+ Token(TokenType.WHITESPACE, TextPosition(2, 16), TextPosition(2, 17), ' '),
73
+ Token(TokenType.TEXT, TextPosition(2, 17), TextPosition(2, 22), 'value'),
74
+ Token(TokenType.ELEMENT_DELIMITER, TextPosition(2, 22), TextPosition(2, 23), ','),
75
+ Token(TokenType.WHITESPACE, TextPosition(2, 23), TextPosition(3, 8), '\n '),
76
+ Token(TokenType.TEXT, TextPosition(3, 8), TextPosition(3, 14), 'param2'),
77
+ Token(TokenType.WHITESPACE, TextPosition(3, 14), TextPosition(3, 15), ' '),
78
+ Token(TokenType.PUNCTUATOR, TextPosition(3, 15), TextPosition(3, 16), '='),
79
+ Token(TokenType.WHITESPACE, TextPosition(3, 16), TextPosition(3, 17), ' '),
80
+ Token(TokenType.TEXT, TextPosition(3, 17), TextPosition(3, 21), 'val1'),
81
+ Token(TokenType.PUNCTUATOR, TextPosition(3, 21), TextPosition(3, 22), ';'),
82
+ Token(TokenType.TEXT, TextPosition(3, 22), TextPosition(3, 26), 'val2'),
83
+ Token(TokenType.PUNCTUATOR, TextPosition(3, 26), TextPosition(3, 27), ';'),
84
+ Token(TokenType.TEXT, TextPosition(3, 27), TextPosition(3, 31), 'val3'),
85
+ Token(TokenType.ELEMENT_DELIMITER, TextPosition(3, 31), TextPosition(3, 32), ','),
86
+ Token(TokenType.WHITESPACE, TextPosition(3, 32), TextPosition(4, 8), '\n '),
87
+ Token(TokenType.TEXT, TextPosition(4, 8), TextPosition(4, 14), 'param3'),
88
+ Token(TokenType.WHITESPACE, TextPosition(4, 14), TextPosition(4, 15), ' '),
89
+ Token(TokenType.PUNCTUATOR, TextPosition(4, 15), TextPosition(4, 16), '='),
90
+ Token(TokenType.WHITESPACE, TextPosition(4, 16), TextPosition(4, 17), ' '),
91
+ Token(TokenType.TEXT, TextPosition(4, 17), TextPosition(4, 21), 'key1'),
92
+ Token(TokenType.PUNCTUATOR, TextPosition(4, 21), TextPosition(4, 22), ':'),
93
+ Token(TokenType.TEXT, TextPosition(4, 22), TextPosition(4, 26), 'val1'),
94
+ Token(TokenType.PUNCTUATOR, TextPosition(4, 26), TextPosition(4, 27), ';'),
95
+ Token(TokenType.TEXT, TextPosition(4, 27), TextPosition(4, 31), 'key2'),
96
+ Token(TokenType.PUNCTUATOR, TextPosition(4, 31), TextPosition(4, 32), ':'),
97
+ Token(TokenType.TEXT, TextPosition(4, 32), TextPosition(4, 36), 'val2'),
98
+ Token(TokenType.ELEMENT_DELIMITER, TextPosition(4, 36), TextPosition(4, 37), ','),
99
+ Token(TokenType.WHITESPACE, TextPosition(4, 37), TextPosition(6, 8), '\n\n '),
100
+ Token(TokenType.TEXT, TextPosition(6, 8), TextPosition(6, 14), 'param4'),
101
+ Token(TokenType.PUNCTUATOR, TextPosition(6, 14), TextPosition(6, 15), '='),
102
+ Token(TokenType.WHITESPACE, TextPosition(6, 15), TextPosition(6, 16), ' '),
103
+ Token(TokenType.TEXT, TextPosition(6, 16), TextPosition(6, 21), 'value'),
104
+ Token(TokenType.ELEMENT_DELIMITER, TextPosition(6, 21), TextPosition(6, 22), ','),
105
+ Token(TokenType.WHITESPACE, TextPosition(6, 22), TextPosition(7, 8), '\n '),
106
+ Token(TokenType.TEXT, TextPosition(7, 8), TextPosition(7, 14), 'param5'),
107
+ Token(TokenType.WHITESPACE, TextPosition(7, 14), TextPosition(7, 15), ' '),
108
+ Token(TokenType.PUNCTUATOR, TextPosition(7, 15), TextPosition(7, 16), '='),
109
+ Token(TokenType.TEXT, TextPosition(7, 16), TextPosition(7, 21), 'value'),
110
+ Token(TokenType.ELEMENT_DELIMITER, TextPosition(7, 21), TextPosition(7, 22), ','),
111
+ Token(TokenType.WHITESPACE, TextPosition(7, 22), TextPosition(8, 8), '\n '),
112
+ Token(TokenType.TEXT, TextPosition(8, 8), TextPosition(8, 14), 'param6'),
113
+ Token(TokenType.PUNCTUATOR, TextPosition(8, 14), TextPosition(8, 15), '='),
114
+ Token(TokenType.TEXT, TextPosition(8, 15), TextPosition(8, 20), 'value'),
115
+ Token(TokenType.ELEMENT_DELIMITER, TextPosition(8, 20), TextPosition(8, 21), ','),
116
+ Token(TokenType.WHITESPACE, TextPosition(8, 21), TextPosition(9, 8), '\n '),
117
+ Token(TokenType.TEXT, TextPosition(9, 8), TextPosition(9, 14), 'param7'),
118
+ Token(TokenType.WHITESPACE, TextPosition(9, 14), TextPosition(9, 21), ' '),
119
+ Token(TokenType.PUNCTUATOR, TextPosition(9, 21), TextPosition(9, 22), '='),
120
+ Token(TokenType.WHITESPACE, TextPosition(9, 22), TextPosition(9, 41), ' '),
121
+ Token(TokenType.TEXT, TextPosition(9, 41), TextPosition(9, 45), 'fuck'),
122
+ Token(TokenType.WHITESPACE, TextPosition(9, 45), TextPosition(9, 46), ' '),
123
+ Token(TokenType.TEXT, TextPosition(9, 46), TextPosition(9, 49), 'you'),
124
+ Token(TokenType.WHITESPACE, TextPosition(9, 49), TextPosition(9, 50), ' '),
125
+ Token(TokenType.TEXT, TextPosition(9, 50), TextPosition(9, 51), '?'),
126
+ Token(TokenType.ELEMENT_DELIMITER, TextPosition(9, 51), TextPosition(9, 52), ','),
127
+ Token(TokenType.WHITESPACE, TextPosition(9, 52), TextPosition(11, 8), '\n\n '),
128
+ Token(TokenType.TEXT, TextPosition(11, 8), TextPosition(11, 19), 'weird_param'),
129
+ Token(TokenType.WHITESPACE, TextPosition(11, 19), TextPosition(11, 20), ' '),
130
+ Token(TokenType.PUNCTUATOR, TextPosition(11, 20), TextPosition(11, 21), '='),
131
+ Token(TokenType.WHITESPACE, TextPosition(11, 21), TextPosition(11, 22), ' '),
132
+ Token(TokenType.TEXT, TextPosition(11, 22), TextPosition(11, 26), 'key1'),
133
+ Token(TokenType.PUNCTUATOR, TextPosition(11, 26), TextPosition(11, 27), '='),
134
+ Token(TokenType.TEXT, TextPosition(11, 27), TextPosition(11, 31), 'val1'),
135
+ Token(TokenType.PUNCTUATOR, TextPosition(11, 31), TextPosition(11, 32), ':'),
136
+ Token(TokenType.TEXT, TextPosition(11, 32), TextPosition(11, 36), 'key2'),
137
+ Token(TokenType.PUNCTUATOR, TextPosition(11, 36), TextPosition(11, 37), '='),
138
+ Token(TokenType.TEXT, TextPosition(11, 37), TextPosition(11, 41), 'val2'),
139
+ Token(TokenType.ELEMENT_DELIMITER, TextPosition(11, 41), TextPosition(11, 42), ','),
140
+ Token(TokenType.WHITESPACE, TextPosition(11, 42), TextPosition(13, 8), '\n\n '),
141
+ Token(TokenType.TEXT, TextPosition(13, 8), TextPosition(13, 19), 'to_new_line'),
142
+ Token(TokenType.WHITESPACE, TextPosition(13, 19), TextPosition(13, 20), ' '),
143
+ Token(TokenType.PUNCTUATOR, TextPosition(13, 20), TextPosition(13, 21), '='),
144
+ Token(TokenType.WHITESPACE, TextPosition(13, 21), TextPosition(14, 8), ' \n '),
145
+ Token(TokenType.TEXT, TextPosition(14, 8), TextPosition(14, 13), 'value'),
146
+ Token(TokenType.ELEMENT_DELIMITER, TextPosition(14, 13), TextPosition(14, 14), ','),
147
+ Token(TokenType.WHITESPACE, TextPosition(14, 14), TextPosition(16, 8), '\n\n '),
148
+ Token(TokenType.COMMENT, TextPosition(16, 8), TextPosition(16, 20), '/* values */'),
149
+ Token(TokenType.WHITESPACE, TextPosition(16, 20), TextPosition(17, 8), '\n '),
150
+ Token(TokenType.TEXT, TextPosition(17, 8), TextPosition(17, 14), 'value1'),
151
+ Token(TokenType.ELEMENT_DELIMITER, TextPosition(17, 14), TextPosition(17, 15), ','),
152
+ Token(TokenType.WHITESPACE, TextPosition(17, 15), TextPosition(18, 8), '\n '),
153
+ Token(TokenType.TEXT, TextPosition(18, 8), TextPosition(18, 14), 'value2'),
154
+ Token(TokenType.ELEMENT_DELIMITER, TextPosition(18, 14), TextPosition(18, 15), ','),
155
+ Token(TokenType.WHITESPACE, TextPosition(18, 15), TextPosition(19, 8), '\n '),
156
+ Token(TokenType.TEXT, TextPosition(19, 8), TextPosition(19, 14), 'value3'),
157
+ Token(TokenType.ELEMENT_DELIMITER, TextPosition(19, 14), TextPosition(19, 15), ','),
158
+ Token(TokenType.WHITESPACE, TextPosition(19, 15), TextPosition(20, 8), '\n '),
159
+ Token(TokenType.TEXT, TextPosition(20, 8), TextPosition(20, 14), 'value4'),
160
+ Token(TokenType.PUNCTUATOR, TextPosition(20, 14), TextPosition(20, 15), ':'),
161
+ Token(TokenType.TEXT, TextPosition(20, 15), TextPosition(20, 21), 'value5'),
162
+ Token(TokenType.ELEMENT_DELIMITER, TextPosition(20, 21), TextPosition(20, 22), ','),
163
+ Token(TokenType.WHITESPACE, TextPosition(20, 22), TextPosition(21, 4), '\n '),
164
+ Token(TokenType.ELEMENT_DELIMITER, TextPosition(21, 4), TextPosition(21, 5), '}'),
165
+ Token(TokenType.WHITESPACE, TextPosition(21, 5), TextPosition(23, 4), '\n\n '),
166
+ Token(TokenType.TEXT, TextPosition(23, 4), TextPosition(23, 9), 'sound'),
167
+ Token(TokenType.WHITESPACE, TextPosition(23, 9), TextPosition(23, 10), ' '),
168
+ Token(TokenType.TEXT, TextPosition(23, 10), TextPosition(23, 19), 'SoundTest'),
169
+ Token(TokenType.WHITESPACE, TextPosition(23, 19), TextPosition(23, 20), ' '),
170
+ Token(TokenType.ELEMENT_DELIMITER, TextPosition(23, 20), TextPosition(23, 21), '{'),
171
+ Token(TokenType.WHITESPACE, TextPosition(23, 21), TextPosition(24, 8), '\n '),
172
+ Token(TokenType.TEXT, TextPosition(24, 8), TextPosition(24, 12), 'clip'),
173
+ Token(TokenType.WHITESPACE, TextPosition(24, 12), TextPosition(24, 13), ' '),
174
+ Token(TokenType.PUNCTUATOR, TextPosition(24, 13), TextPosition(24, 14), '='),
175
+ Token(TokenType.WHITESPACE, TextPosition(24, 14), TextPosition(24, 15), ' '),
176
+ Token(TokenType.TEXT, TextPosition(24, 15), TextPosition(24, 32), 'this/path/to/clip'),
177
+ Token(TokenType.ELEMENT_DELIMITER, TextPosition(24, 32), TextPosition(24, 33), ','),
178
+ Token(TokenType.WHITESPACE, TextPosition(24, 33), TextPosition(24, 34), ' '),
179
+ Token(TokenType.TEXT, TextPosition(24, 34), TextPosition(24, 40), 'volume'),
180
+ Token(TokenType.WHITESPACE, TextPosition(24, 40), TextPosition(24, 41), ' '),
181
+ Token(TokenType.PUNCTUATOR, TextPosition(24, 41), TextPosition(24, 42), '='),
182
+ Token(TokenType.WHITESPACE, TextPosition(24, 42), TextPosition(24, 43), ' '),
183
+ Token(TokenType.TEXT, TextPosition(24, 43), TextPosition(24, 44), '0'),
184
+ Token(TokenType.PUNCTUATOR, TextPosition(24, 44), TextPosition(24, 45), '.'),
185
+ Token(TokenType.TEXT, TextPosition(24, 45), TextPosition(24, 46), '8'),
186
+ Token(TokenType.ELEMENT_DELIMITER, TextPosition(24, 46), TextPosition(24, 47), ','),
187
+ Token(TokenType.WHITESPACE, TextPosition(24, 47), TextPosition(25, 4), '\n '),
188
+ Token(TokenType.ELEMENT_DELIMITER, TextPosition(25, 4), TextPosition(25, 5), '}'),
189
+ Token(TokenType.WHITESPACE, TextPosition(25, 5), TextPosition(27, 4), '\n\n '),
190
+ Token(TokenType.TEXT, TextPosition(27, 4), TextPosition(27, 9), 'model'),
191
+ Token(TokenType.WHITESPACE, TextPosition(27, 9), TextPosition(27, 10), ' '),
192
+ Token(TokenType.TEXT, TextPosition(27, 10), TextPosition(27, 12), 'id'),
193
+ Token(TokenType.WHITESPACE, TextPosition(27, 12), TextPosition(27, 13), ' '),
194
+ Token(TokenType.TEXT, TextPosition(27, 13), TextPosition(27, 17), 'with'),
195
+ Token(TokenType.WHITESPACE, TextPosition(27, 17), TextPosition(27, 18), ' '),
196
+ Token(TokenType.TEXT, TextPosition(27, 18), TextPosition(27, 24), 'spaces'),
197
+ Token(TokenType.WHITESPACE, TextPosition(27, 24), TextPosition(27, 25), ' '),
198
+ Token(TokenType.ELEMENT_DELIMITER, TextPosition(27, 25), TextPosition(27, 26), '{'),
199
+ Token(TokenType.WHITESPACE, TextPosition(27, 26), TextPosition(29, 4), '\n \n '),
200
+ Token(TokenType.ELEMENT_DELIMITER, TextPosition(29, 4), TextPosition(29, 5), '}'),
201
+ Token(TokenType.WHITESPACE, TextPosition(29, 5), TextPosition(31, 4), '\n\n '),
202
+ Token(TokenType.COMMENT, TextPosition(31, 4), TextPosition(32, 18), '/* comment, different syntax block under\n hello world */'),
203
+ Token(TokenType.COMMENT, TextPosition(32, 18), TextPosition(32, 41), '/* comment continued */'),
204
+ Token(TokenType.WHITESPACE, TextPosition(32, 41), TextPosition(34, 4), '\n \n '),
205
+ Token(TokenType.TEXT, TextPosition(34, 4), TextPosition(34, 25), 'this_is_a_weird_block'),
206
+ Token(TokenType.WHITESPACE, TextPosition(34, 25), TextPosition(34, 26), ' '),
207
+ Token(TokenType.ELEMENT_DELIMITER, TextPosition(34, 26), TextPosition(34, 27), '{'),
208
+ Token(TokenType.WHITESPACE, TextPosition(34, 27), TextPosition(36, 4), '\n \n '),
209
+ Token(TokenType.ELEMENT_DELIMITER, TextPosition(36, 4), TextPosition(36, 5), '}'),
210
+ Token(TokenType.WHITESPACE, TextPosition(36, 5), TextPosition(38, 4), '\n\n '),
211
+ Token(TokenType.TEXT, TextPosition(38, 4), TextPosition(38, 8), 'item'),
212
+ Token(TokenType.WHITESPACE, TextPosition(38, 8), TextPosition(38, 9), ' '),
213
+ Token(TokenType.TEXT, TextPosition(38, 9), TextPosition(38, 18), 'ItemTest2'),
214
+ Token(TokenType.WHITESPACE, TextPosition(38, 18), TextPosition(39, 4), '\n '),
215
+ Token(TokenType.ELEMENT_DELIMITER, TextPosition(39, 4), TextPosition(39, 5), '{'),
216
+ Token(TokenType.WHITESPACE, TextPosition(39, 5), TextPosition(41, 4), '\n\n '),
217
+ Token(TokenType.ELEMENT_DELIMITER, TextPosition(41, 4), TextPosition(41, 5), '}'),
218
+ Token(TokenType.WHITESPACE, TextPosition(41, 5), TextPosition(42, 0), '\n'),
219
+ Token(TokenType.ELEMENT_DELIMITER, TextPosition(42, 0), TextPosition(42, 1), '}'),
220
+ ]
221
+ )
222
+ return EXPECTED_LEXICAL_TOKENS
223
+
224
+
225
+
226
+
227
+ def test_lexer():
228
+ """Test the lexer by comparing its output against the theorical tokens."""
229
+ lexical_tokens = parse_example_content()
230
+ theorical_tokens = get_theorical_tokens()
231
+
232
+ for token, theorical_token in zip(lexical_tokens, theorical_tokens):
233
+ print(repr(theorical_token).ljust(80), repr(token).ljust(80))
234
+ assert token == theorical_token, f"Theorical token: {theorical_token!r}, Actual token: {token!r}"
235
+
236
+ def generate_theorical_tokens():
237
+ """
238
+ Assuming the tokens currently being parsed are already the right ones, generate the comparison list of theorical tokens
239
+ we are supposed to compare against.
240
+ """
241
+ lexical_tokens = parse_example_content()
242
+ for token in lexical_tokens:
243
+ print(f"Token({token.type}, TextPosition({token.pos.line}, {token.pos.offset}), TextPosition({token.end.line}, {token.end.offset}), {token.text!r}),")
244
+
245
+ def main():
246
+ import argparse
247
+
248
+ parser = argparse.ArgumentParser(description="Lexer test.")
249
+ parser.add_argument("--run", action="store_true", help="Run the lexer test.")
250
+ parser.add_argument("--generate", action="store_true", help="Generate the theorical tokens.")
251
+ args = parser.parse_args()
252
+ if args.run:
253
+ test_lexer()
254
+ elif args.generate:
255
+ generate_theorical_tokens()
256
+ else:
257
+ parser.print_help()
258
+
259
+
260
+ if __name__ == "__main__":
261
+ main()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ZedScripts
3
- Version: 1.0.1
3
+ Version: 1.1.0
4
4
  Author: SimKDT
5
5
  License: MIT License
6
6
 
@@ -36,6 +36,8 @@ Provides-Extra: build
36
36
  Requires-Dist: pyinstaller; extra == "build"
37
37
  Provides-Extra: debug
38
38
  Requires-Dist: debugpy; extra == "debug"
39
+ Provides-Extra: test
40
+ Requires-Dist: pytest; extra == "test"
39
41
  Dynamic: license-file
40
42
 
41
43
  # ZedScripts LSP
@@ -211,6 +213,10 @@ Each diagnostic by default have a specific set of modifiers that can be overridd
211
213
  }
212
214
  ```
213
215
 
216
+ ### Server
217
+ A list of server-related configurations for the LSP. The following parameters are available:
218
+ - `logging_level` (optional, default: `INFO`): specifies the logging level for the server. This option is only available in the global configuration and accepts values such as `NOTSET`, `DEBUG`, `INFO`, `WARNING`, `ERROR`, and `CRITICAL`.
219
+
214
220
  ## IDE configuration
215
221
  Based on the IDE you use, you may need to install the ZedScripts LSP application for your OS in the [releases](https://github.com/PZ-Wiki-Modding/ZedScripts-LSP/releases) (Linux, Windows).
216
222
 
@@ -36,4 +36,6 @@ src/ZedScripts/scripts/value.py
36
36
  src/ZedScripts/scripts/version.py
37
37
  src/ZedScripts/structure/ast.py
38
38
  src/ZedScripts/structure/lexer.py
39
- src/ZedScripts/structure/parser.py
39
+ src/ZedScripts/structure/parser.py
40
+ src/ZedScripts/tests/parser/test_lexer.py
41
+ tests/test_config_cli.py
@@ -9,3 +9,6 @@ pyinstaller
9
9
 
10
10
  [debug]
11
11
  debugpy
12
+
13
+ [test]
14
+ pytest
@@ -0,0 +1,31 @@
1
+ import json
2
+ import subprocess
3
+ import sys
4
+
5
+ from ZedScripts.environment.config import ConfigurationModel
6
+
7
+
8
+ def test_default_config_cli():
9
+ result = subprocess.run(
10
+ [sys.executable, "-m", "ZedScripts.environment.config", "default"],
11
+ capture_output=True,
12
+ text=True,
13
+ check=True,
14
+ )
15
+
16
+ output = json.loads(result.stdout)
17
+ assert output["libraries"] == []
18
+ assert isinstance(output["ignored"], list)
19
+ assert ConfigurationModel.model_validate_json(result.stdout) == ConfigurationModel()
20
+ assert '\n "dataset": {' in result.stdout
21
+
22
+
23
+ def test_config_schema_cli():
24
+ result = subprocess.run(
25
+ [sys.executable, "-m", "ZedScripts.environment.config", "schema"],
26
+ capture_output=True,
27
+ text=True,
28
+ check=True,
29
+ )
30
+
31
+ assert json.loads(result.stdout) == ConfigurationModel.model_json_schema()
@@ -1,4 +0,0 @@
1
- __version__ = "1.0.1"
2
-
3
- if __name__ == "__main__":
4
- print(__version__)
@@ -1,38 +0,0 @@
1
- import os
2
- import logging
3
- from pathlib import Path
4
-
5
- IS_DEBUG = os.getenv("ZEDSCRIPTS_DEBUG_MODE") == "1"
6
-
7
- ZEDSCRIPT_CACHE_DIR = Path.home() / ".zedscripts"
8
- """
9
- Cache directory to hold various information related to ZedScripts.
10
- """
11
- ZEDSCRIPT_CACHE_DIR.mkdir(parents=True, exist_ok=True)
12
-
13
- # config files
14
- CONFIGURATION_FILE_NAME = ".zedscripts.json"
15
- GLOBAL_CONFIGURATION_FILE = (ZEDSCRIPT_CACHE_DIR / CONFIGURATION_FILE_NAME).resolve()
16
-
17
- # setup the logger
18
- log_file = ZEDSCRIPT_CACHE_DIR / "server.log"
19
-
20
- level = logging.DEBUG if IS_DEBUG else logging.INFO
21
- logging.basicConfig(
22
- filename=log_file,
23
- level=level,
24
- format="%(asctime)s %(levelname)s %(message)s",
25
- filemode="w",
26
- # force=True,
27
- )
28
-
29
- logging.info("ZedScripts logger initialized. Debug mode: %s", IS_DEBUG)
30
-
31
- IDENTIFIER = "ZedScripts"
32
- SOURCE = IDENTIFIER
33
-
34
- SCRIPTS_DATA_MANIFEST = "https://raw.githubusercontent.com/PZ-Wiki-Modding/pz-scripts-data/refs/heads/main/manifest.json"
35
- SCRIPTS_BLOCKS_DATA_LINK = "https://raw.githubusercontent.com/PZ-Wiki-Modding/pz-scripts-data/refs/tags/{best_tag}/out/scriptsBlocks.json"
36
- ROOTS_DATA_LINK = "https://raw.githubusercontent.com/PZ-Wiki-Modding/pz-scripts-data/refs/tags/{best_tag}/out/roots.json"
37
-
38
- SCRIPTSDOCS_LINK = "https://pz-wiki-modding.github.io/PZ-API-Docs/scripts/"
File without changes
File without changes