python-library-ralf-model 0.1.0__tar.gz → 0.1.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.
- python_library_ralf_model-0.1.2/.gitignore +21 -0
- {python_library_ralf_model-0.1.0 → python_library_ralf_model-0.1.2}/PKG-INFO +3 -1
- {python_library_ralf_model-0.1.0 → python_library_ralf_model-0.1.2}/README.md +2 -0
- {python_library_ralf_model-0.1.0 → python_library_ralf_model-0.1.2}/pyproject.toml +1 -1
- {python_library_ralf_model-0.1.0 → python_library_ralf_model-0.1.2}/ralf_model/emit.py +4 -0
- {python_library_ralf_model-0.1.0 → python_library_ralf_model-0.1.2}/ralf_model/errors.py +14 -2
- {python_library_ralf_model-0.1.0 → python_library_ralf_model-0.1.2}/ralf_model/io.py +9 -4
- {python_library_ralf_model-0.1.0 → python_library_ralf_model-0.1.2}/ralf_model/nodes.py +8 -0
- {python_library_ralf_model-0.1.0 → python_library_ralf_model-0.1.2}/ralf_model/parse.py +35 -5
- {python_library_ralf_model-0.1.0 → python_library_ralf_model-0.1.2}/ralf_model/source_expand.py +9 -3
- {python_library_ralf_model-0.1.0 → python_library_ralf_model-0.1.2}/tests/test_ralf_model.py +90 -1
- {python_library_ralf_model-0.1.0 → python_library_ralf_model-0.1.2}/tests/test_source_include.py +1 -1
- python_library_ralf_model-0.1.0/.gitignore +0 -12
- {python_library_ralf_model-0.1.0 → python_library_ralf_model-0.1.2}/example/__main__.py +0 -0
- {python_library_ralf_model-0.1.0 → python_library_ralf_model-0.1.2}/example.bat +0 -0
- {python_library_ralf_model-0.1.0 → python_library_ralf_model-0.1.2}/ralf_model/__init__.py +0 -0
- {python_library_ralf_model-0.1.0 → python_library_ralf_model-0.1.2}/ralf_model/abc.py +0 -0
- {python_library_ralf_model-0.1.0 → python_library_ralf_model-0.1.2}/test.bat +0 -0
- {python_library_ralf_model-0.1.0 → python_library_ralf_model-0.1.2}/tests/__init__.py +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.pyc
|
|
3
|
+
*.pyo
|
|
4
|
+
*.egg-info/
|
|
5
|
+
dist/
|
|
6
|
+
build/
|
|
7
|
+
.venv/
|
|
8
|
+
.env
|
|
9
|
+
.env.*
|
|
10
|
+
!.env.example
|
|
11
|
+
# packages 示例本地密钥(*.example 为模板,可提交)
|
|
12
|
+
packages/**/examples/**/.env
|
|
13
|
+
!packages/**/examples/**/.env.example
|
|
14
|
+
packages/**/examples/**/mcp.json
|
|
15
|
+
!packages/**/examples/**/mcp.json.example
|
|
16
|
+
packages/**/examples/**/.sandbox/
|
|
17
|
+
.pytest_cache/
|
|
18
|
+
config.yaml
|
|
19
|
+
logs/
|
|
20
|
+
.cursor/
|
|
21
|
+
uv.lock
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-library-ralf-model
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Requires-Python: >=3.10
|
|
5
5
|
Requires-Dist: pydantic<3,>=2.0
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -41,6 +41,8 @@ doc = load_ralf_file(
|
|
|
41
41
|
## 能力范围
|
|
42
42
|
|
|
43
43
|
- **block**:定义 ``block 名 { ... }``;简单映射 ``block 名 @地址;``;赋值与可选路径、地址 ``block 左名 = 右名``、``block 左名 = 右名 (hdl路径)``、``... @地址``,可与 ``{ ... }`` 组合。
|
|
44
|
+
- **register**:``register 名 [ (hdl路径) ] [ @字节偏移 ] { ... }`` 或 ``register 名;`` 前向引用。
|
|
45
|
+
- **field**:``field 名 [(hdl路径)] [ @位偏移 ] { ... }``(括号路径可紧贴字段名,如 ``field f(hdl.f)``)。
|
|
44
46
|
- `field` 花括号内按**源顺序**保留各条语句(含 `enum { ... };` 等),便于往返。
|
|
45
47
|
- `@` 后的偏移在写出时统一为 Verilog 风格十六进制字面量(如 `'h5`);`bytes` 等为十进制。
|
|
46
48
|
|
|
@@ -34,6 +34,8 @@ doc = load_ralf_file(
|
|
|
34
34
|
## 能力范围
|
|
35
35
|
|
|
36
36
|
- **block**:定义 ``block 名 { ... }``;简单映射 ``block 名 @地址;``;赋值与可选路径、地址 ``block 左名 = 右名``、``block 左名 = 右名 (hdl路径)``、``... @地址``,可与 ``{ ... }`` 组合。
|
|
37
|
+
- **register**:``register 名 [ (hdl路径) ] [ @字节偏移 ] { ... }`` 或 ``register 名;`` 前向引用。
|
|
38
|
+
- **field**:``field 名 [(hdl路径)] [ @位偏移 ] { ... }``(括号路径可紧贴字段名,如 ``field f(hdl.f)``)。
|
|
37
39
|
- `field` 花括号内按**源顺序**保留各条语句(含 `enum { ... };` 等),便于往返。
|
|
38
40
|
- `@` 后的偏移在写出时统一为 Verilog 风格十六进制字面量(如 `'h5`);`bytes` 等为十进制。
|
|
39
41
|
|
|
@@ -12,6 +12,8 @@ def _fmt_at_int(v: int) -> str:
|
|
|
12
12
|
|
|
13
13
|
def _emit_field(f: FieldNode, indent: str) -> list[str]:
|
|
14
14
|
head = f"{indent}field {f.name}"
|
|
15
|
+
if f.paren_path is not None:
|
|
16
|
+
head += f"({f.paren_path})"
|
|
15
17
|
if f.offset_bits is not None:
|
|
16
18
|
head += f" @{_fmt_at_int(f.offset_bits)}"
|
|
17
19
|
head += " {"
|
|
@@ -26,6 +28,8 @@ def _emit_field(f: FieldNode, indent: str) -> list[str]:
|
|
|
26
28
|
|
|
27
29
|
def _emit_register(r: RegisterNode, indent: str) -> list[str]:
|
|
28
30
|
head = f"{indent}register {r.name}"
|
|
31
|
+
if r.paren_path is not None:
|
|
32
|
+
head += f" ({r.paren_path})"
|
|
29
33
|
if r.offset_bytes is not None:
|
|
30
34
|
head += f" @{_fmt_at_int(r.offset_bytes)}"
|
|
31
35
|
if r.declaration_only:
|
|
@@ -10,10 +10,22 @@ class RalfError(Exception):
|
|
|
10
10
|
class RalfParseError(RalfError):
|
|
11
11
|
"""文本不符合预期语法或词法。"""
|
|
12
12
|
|
|
13
|
-
def __init__(
|
|
14
|
-
|
|
13
|
+
def __init__(
|
|
14
|
+
self,
|
|
15
|
+
message: str,
|
|
16
|
+
*,
|
|
17
|
+
line: int,
|
|
18
|
+
col: int,
|
|
19
|
+
path: Path | None = None,
|
|
20
|
+
) -> None:
|
|
21
|
+
loc = f"行 {line}, 列 {col}"
|
|
22
|
+
if path is not None:
|
|
23
|
+
loc = f"{path}: {loc}"
|
|
24
|
+
super().__init__(f"{message} ({loc})")
|
|
25
|
+
self.message = message
|
|
15
26
|
self.line = line
|
|
16
27
|
self.col = col
|
|
28
|
+
self.path = path
|
|
17
29
|
|
|
18
30
|
|
|
19
31
|
class RalfSourceError(RalfError):
|
|
@@ -20,9 +20,13 @@ def load_ralf_file(
|
|
|
20
20
|
p = Path(path).resolve()
|
|
21
21
|
text = p.read_text(encoding=encoding)
|
|
22
22
|
inc = tuple(Path(x).resolve() for x in (include_paths or ()))
|
|
23
|
+
line_sources: list[Path] | None = None
|
|
23
24
|
if expand_source:
|
|
24
|
-
text = expand_ralf_sources(
|
|
25
|
-
|
|
25
|
+
text, line_sources = expand_ralf_sources(
|
|
26
|
+
text, current_file=p, include_paths=inc, encoding=encoding
|
|
27
|
+
)
|
|
28
|
+
return parse_ralf(text, line_sources=line_sources)
|
|
29
|
+
return parse_ralf(text, path=p)
|
|
26
30
|
|
|
27
31
|
|
|
28
32
|
def loads_ralf(
|
|
@@ -38,13 +42,14 @@ def loads_ralf(
|
|
|
38
42
|
virtual = bd / "__inline__.ralf"
|
|
39
43
|
inc = tuple(Path(x).resolve() for x in (include_paths or ()))
|
|
40
44
|
if expand_source:
|
|
41
|
-
text = expand_ralf_sources(
|
|
45
|
+
text, line_sources = expand_ralf_sources(
|
|
42
46
|
text,
|
|
43
47
|
current_file=virtual,
|
|
44
48
|
include_paths=inc,
|
|
45
49
|
encoding=encoding,
|
|
46
50
|
)
|
|
47
|
-
|
|
51
|
+
return parse_ralf(text, line_sources=line_sources)
|
|
52
|
+
return parse_ralf(text, path=virtual)
|
|
48
53
|
|
|
49
54
|
|
|
50
55
|
def dump_ralf_file(doc: RalfDocument, path: str | Path, *, encoding: str = "utf-8") -> None:
|
|
@@ -9,6 +9,10 @@ class FieldNode(BaseModel):
|
|
|
9
9
|
model_config = ConfigDict(extra="forbid")
|
|
10
10
|
|
|
11
11
|
name: str
|
|
12
|
+
paren_path: str | None = Field(
|
|
13
|
+
default=None,
|
|
14
|
+
description="紧跟在 field 名后的圆括号路径内容(不含括号),如 ``field f(hdl.sig)``",
|
|
15
|
+
)
|
|
12
16
|
offset_bits: int | None = Field(default=None, description="field 内 `@` 后的位偏移")
|
|
13
17
|
inner_statements: list[str] = Field(
|
|
14
18
|
default_factory=list,
|
|
@@ -22,6 +26,10 @@ class RegisterNode(BaseModel):
|
|
|
22
26
|
model_config = ConfigDict(extra="forbid")
|
|
23
27
|
|
|
24
28
|
name: str
|
|
29
|
+
paren_path: str | None = Field(
|
|
30
|
+
default=None,
|
|
31
|
+
description="紧跟在 register 名后的圆括号路径内容(不含括号),如 ``register r (dut.reg)``",
|
|
32
|
+
)
|
|
25
33
|
offset_bytes: int | None = Field(default=None, description="register 后的 `@` 字节偏移")
|
|
26
34
|
bytes_width: int | None = None
|
|
27
35
|
fields: list[FieldNode] = Field(default_factory=list)
|
|
@@ -1,18 +1,28 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import re
|
|
4
|
+
from collections.abc import Sequence
|
|
5
|
+
from pathlib import Path
|
|
4
6
|
|
|
5
7
|
from ralf_model.errors import RalfParseError
|
|
6
8
|
from ralf_model.nodes import BlockNode, FieldNode, RalfDocument, RegisterNode
|
|
7
9
|
|
|
8
10
|
|
|
9
11
|
class _Parser:
|
|
10
|
-
def __init__(
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
text: str,
|
|
15
|
+
*,
|
|
16
|
+
path: Path | None = None,
|
|
17
|
+
line_sources: Sequence[Path] | None = None,
|
|
18
|
+
) -> None:
|
|
11
19
|
self._s = text
|
|
12
20
|
self._n = len(text)
|
|
13
21
|
self._i = 0
|
|
14
22
|
self.line = 1
|
|
15
23
|
self.col = 1
|
|
24
|
+
self._path = path
|
|
25
|
+
self._line_sources = line_sources
|
|
16
26
|
|
|
17
27
|
def _advance(self, n: int = 1) -> None:
|
|
18
28
|
for _ in range(n):
|
|
@@ -30,7 +40,10 @@ class _Parser:
|
|
|
30
40
|
return self._s[j] if j < self._n else None
|
|
31
41
|
|
|
32
42
|
def _error(self, msg: str) -> None:
|
|
33
|
-
|
|
43
|
+
path = self._path
|
|
44
|
+
if self._line_sources and 0 < self.line <= len(self._line_sources):
|
|
45
|
+
path = self._line_sources[self.line - 1]
|
|
46
|
+
raise RalfParseError(msg, line=self.line, col=self.col, path=path)
|
|
34
47
|
|
|
35
48
|
def skip_ws_and_comments(self) -> None:
|
|
36
49
|
while self._i < self._n:
|
|
@@ -336,6 +349,10 @@ class _Parser:
|
|
|
336
349
|
self.expect_keyword("register")
|
|
337
350
|
name = self.read_ident()
|
|
338
351
|
self.skip_ws_and_comments()
|
|
352
|
+
paren_path: str | None = None
|
|
353
|
+
if self._peek() == "(":
|
|
354
|
+
paren_path = self.read_round_paren_inner()
|
|
355
|
+
self.skip_ws_and_comments()
|
|
339
356
|
offset: int | None = None
|
|
340
357
|
if self._peek() == "@":
|
|
341
358
|
self._advance()
|
|
@@ -345,6 +362,7 @@ class _Parser:
|
|
|
345
362
|
self._advance()
|
|
346
363
|
return RegisterNode(
|
|
347
364
|
name=name,
|
|
365
|
+
paren_path=paren_path,
|
|
348
366
|
offset_bytes=offset,
|
|
349
367
|
declaration_only=True,
|
|
350
368
|
)
|
|
@@ -354,6 +372,7 @@ class _Parser:
|
|
|
354
372
|
self.expect_char("}")
|
|
355
373
|
return RegisterNode(
|
|
356
374
|
name=name,
|
|
375
|
+
paren_path=paren_path,
|
|
357
376
|
offset_bytes=offset,
|
|
358
377
|
bytes_width=rbw,
|
|
359
378
|
fields=fields,
|
|
@@ -383,6 +402,10 @@ class _Parser:
|
|
|
383
402
|
self.expect_keyword("field")
|
|
384
403
|
name = self.read_ident()
|
|
385
404
|
self.skip_ws_and_comments()
|
|
405
|
+
paren_path: str | None = None
|
|
406
|
+
if self._peek() == "(":
|
|
407
|
+
paren_path = self.read_round_paren_inner()
|
|
408
|
+
self.skip_ws_and_comments()
|
|
386
409
|
off_bits: int | None = None
|
|
387
410
|
if self._peek() == "@":
|
|
388
411
|
self._advance()
|
|
@@ -390,7 +413,9 @@ class _Parser:
|
|
|
390
413
|
self.expect_char("{")
|
|
391
414
|
fn = self._parse_field_body()
|
|
392
415
|
self.expect_char("}")
|
|
393
|
-
return fn.model_copy(
|
|
416
|
+
return fn.model_copy(
|
|
417
|
+
update={"name": name, "paren_path": paren_path, "offset_bits": off_bits}
|
|
418
|
+
)
|
|
394
419
|
|
|
395
420
|
def _parse_field_body(self) -> FieldNode:
|
|
396
421
|
inner: list[str] = []
|
|
@@ -519,9 +544,14 @@ def _parse_verilog_sized(s: str, i: int) -> tuple[int, int]:
|
|
|
519
544
|
_VER_WS = re.compile(r"\s+")
|
|
520
545
|
|
|
521
546
|
|
|
522
|
-
def parse_ralf(
|
|
547
|
+
def parse_ralf(
|
|
548
|
+
text: str,
|
|
549
|
+
*,
|
|
550
|
+
path: Path | None = None,
|
|
551
|
+
line_sources: Sequence[Path] | None = None,
|
|
552
|
+
) -> RalfDocument:
|
|
523
553
|
"""将 RALF 源文本解析为 `RalfDocument`。"""
|
|
524
|
-
p = _Parser(text)
|
|
554
|
+
p = _Parser(text, path=path, line_sources=line_sources)
|
|
525
555
|
doc = p.parse_document()
|
|
526
556
|
return doc
|
|
527
557
|
|
{python_library_ralf_model-0.1.0 → python_library_ralf_model-0.1.2}/ralf_model/source_expand.py
RENAMED
|
@@ -109,11 +109,13 @@ def expand_ralf_sources(
|
|
|
109
109
|
include_paths: Sequence[Path] = (),
|
|
110
110
|
encoding: str = "utf-8",
|
|
111
111
|
_chain: tuple[Path, ...] = (),
|
|
112
|
-
) -> str:
|
|
112
|
+
) -> tuple[str, list[Path]]:
|
|
113
113
|
"""将 Tcl 风格 ``source path`` 递归展开为单段 RALF 文本后再交给 ``parse_ralf``。
|
|
114
114
|
|
|
115
115
|
``current_file`` 用于确定相对路径的基准目录(通常为 ``path.parent``),并参与循环检测。
|
|
116
116
|
从内存加载字符串时可使用 ``base_dir / \"__inline__.ralf\"`` 这类占位路径。
|
|
117
|
+
|
|
118
|
+
返回 ``(展开后文本, 行来源)``:``行来源[i]`` 对应展开结果第 ``i+1`` 行的源文件路径。
|
|
117
119
|
"""
|
|
118
120
|
cf = current_file.resolve()
|
|
119
121
|
if cf in _chain:
|
|
@@ -123,15 +125,17 @@ def expand_ralf_sources(
|
|
|
123
125
|
inc = tuple(Path(p).resolve() for p in include_paths)
|
|
124
126
|
|
|
125
127
|
out: list[str] = []
|
|
128
|
+
line_sources: list[Path] = []
|
|
126
129
|
for line in text.splitlines(keepends=True):
|
|
127
130
|
spec = _parse_source_argument(line)
|
|
128
131
|
if spec is None:
|
|
129
132
|
out.append(line)
|
|
133
|
+
line_sources.append(cf)
|
|
130
134
|
continue
|
|
131
135
|
|
|
132
136
|
inner_path = resolve_source_path(spec, base_dir=cf.parent, include_paths=inc)
|
|
133
137
|
inner_text = inner_path.read_text(encoding=encoding)
|
|
134
|
-
expanded_inner = expand_ralf_sources(
|
|
138
|
+
expanded_inner, inner_sources = expand_ralf_sources(
|
|
135
139
|
inner_text,
|
|
136
140
|
current_file=inner_path,
|
|
137
141
|
include_paths=inc,
|
|
@@ -139,7 +143,9 @@ def expand_ralf_sources(
|
|
|
139
143
|
_chain=chain,
|
|
140
144
|
)
|
|
141
145
|
out.append(expanded_inner)
|
|
146
|
+
line_sources.extend(inner_sources)
|
|
142
147
|
if expanded_inner and not expanded_inner.endswith("\n"):
|
|
143
148
|
out.append("\n")
|
|
149
|
+
line_sources.append(inner_sources[-1] if inner_sources else inner_path)
|
|
144
150
|
|
|
145
|
-
return "".join(out)
|
|
151
|
+
return "".join(out), line_sources
|
{python_library_ralf_model-0.1.0 → python_library_ralf_model-0.1.2}/tests/test_ralf_model.py
RENAMED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import tempfile
|
|
3
4
|
import unittest
|
|
5
|
+
from pathlib import Path
|
|
4
6
|
|
|
5
|
-
from ralf_model import dump_ralf, parse_ralf
|
|
7
|
+
from ralf_model import RalfParseError, dump_ralf, load_ralf_file, parse_ralf
|
|
6
8
|
from ralf_model.parse import normalize_ralf_whitespace
|
|
7
9
|
|
|
8
10
|
|
|
@@ -146,6 +148,69 @@ block top {
|
|
|
146
148
|
out = dump_ralf(doc)
|
|
147
149
|
self.assertIn("register R0;", out)
|
|
148
150
|
|
|
151
|
+
def test_register_paren_path_at_offset(self) -> None:
|
|
152
|
+
src = """
|
|
153
|
+
block top {
|
|
154
|
+
bytes 4;
|
|
155
|
+
register CTRL (dut.ctrl_reg) @'h0 {
|
|
156
|
+
bytes 4;
|
|
157
|
+
field ena(dut.ctrl_reg.en) @1 {
|
|
158
|
+
bits 1;
|
|
159
|
+
access rw;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
"""
|
|
164
|
+
doc = parse_ralf(src)
|
|
165
|
+
reg = doc.blocks[0].registers[0]
|
|
166
|
+
self.assertEqual(reg.name, "CTRL")
|
|
167
|
+
self.assertEqual(reg.paren_path, "dut.ctrl_reg")
|
|
168
|
+
self.assertEqual(reg.offset_bytes, 0)
|
|
169
|
+
fld = reg.fields[0]
|
|
170
|
+
self.assertEqual(fld.name, "ena")
|
|
171
|
+
self.assertEqual(fld.paren_path, "dut.ctrl_reg.en")
|
|
172
|
+
self.assertEqual(fld.offset_bits, 1)
|
|
173
|
+
out = dump_ralf(doc)
|
|
174
|
+
self.assertIn("register CTRL (dut.ctrl_reg) @'h0", out)
|
|
175
|
+
self.assertIn("field ena(dut.ctrl_reg.en) @'h1", out)
|
|
176
|
+
doc2 = parse_ralf(out)
|
|
177
|
+
self.assertEqual(doc.model_dump(), doc2.model_dump())
|
|
178
|
+
|
|
179
|
+
def test_register_paren_path_forward_decl(self) -> None:
|
|
180
|
+
src = """
|
|
181
|
+
block top {
|
|
182
|
+
register R0 (hdl.r0) @'h10;
|
|
183
|
+
}
|
|
184
|
+
"""
|
|
185
|
+
doc = parse_ralf(src)
|
|
186
|
+
r = doc.blocks[0].registers[0]
|
|
187
|
+
self.assertTrue(r.declaration_only)
|
|
188
|
+
self.assertEqual(r.paren_path, "hdl.r0")
|
|
189
|
+
self.assertEqual(r.offset_bytes, 0x10)
|
|
190
|
+
out = dump_ralf(doc)
|
|
191
|
+
self.assertIn("register R0 (hdl.r0) @'h10;", out)
|
|
192
|
+
doc2 = parse_ralf(out)
|
|
193
|
+
self.assertEqual(doc.model_dump(), doc2.model_dump())
|
|
194
|
+
|
|
195
|
+
def test_field_paren_path_no_space(self) -> None:
|
|
196
|
+
src = """
|
|
197
|
+
block top {
|
|
198
|
+
register r {
|
|
199
|
+
bytes 4;
|
|
200
|
+
field sig(hdl.sig) @0 {
|
|
201
|
+
bits 4;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
"""
|
|
206
|
+
doc = parse_ralf(src)
|
|
207
|
+
fld = doc.blocks[0].registers[0].fields[0]
|
|
208
|
+
self.assertEqual(fld.paren_path, "hdl.sig")
|
|
209
|
+
out = dump_ralf(doc)
|
|
210
|
+
self.assertIn("field sig(hdl.sig)", out)
|
|
211
|
+
doc2 = parse_ralf(out)
|
|
212
|
+
self.assertEqual(doc.model_dump(), doc2.model_dump())
|
|
213
|
+
|
|
149
214
|
def test_block_ref_rhs_with_parentheses(self) -> None:
|
|
150
215
|
src = """
|
|
151
216
|
block top {
|
|
@@ -176,5 +241,29 @@ block a {
|
|
|
176
241
|
self.assertNotIn("//", n)
|
|
177
242
|
|
|
178
243
|
|
|
244
|
+
class ParseErrorTests(unittest.TestCase):
|
|
245
|
+
def test_parse_error_includes_file_path(self) -> None:
|
|
246
|
+
with tempfile.TemporaryDirectory() as td:
|
|
247
|
+
bad = Path(td) / "bad.ralf"
|
|
248
|
+
bad.write_text("block x {\n ???\n}\n", encoding="utf-8")
|
|
249
|
+
with self.assertRaises(RalfParseError) as ctx:
|
|
250
|
+
load_ralf_file(bad, expand_source=False)
|
|
251
|
+
self.assertIn(str(bad.resolve()), str(ctx.exception))
|
|
252
|
+
|
|
253
|
+
def test_parse_error_in_included_file(self) -> None:
|
|
254
|
+
with tempfile.TemporaryDirectory() as td:
|
|
255
|
+
root = Path(td)
|
|
256
|
+
inc = root / "inc"
|
|
257
|
+
inc.mkdir()
|
|
258
|
+
(inc / "broken.ralf").write_text("block y {\n ???\n}\n", encoding="utf-8")
|
|
259
|
+
top = root / "top.ralf"
|
|
260
|
+
top.write_text('source "inc/broken.ralf"\n', encoding="utf-8")
|
|
261
|
+
broken = (inc / "broken.ralf").resolve()
|
|
262
|
+
with self.assertRaises(RalfParseError) as ctx:
|
|
263
|
+
load_ralf_file(top)
|
|
264
|
+
self.assertIn(str(broken), str(ctx.exception))
|
|
265
|
+
self.assertNotIn(str(top.resolve()), str(ctx.exception))
|
|
266
|
+
|
|
267
|
+
|
|
179
268
|
if __name__ == "__main__":
|
|
180
269
|
unittest.main()
|
{python_library_ralf_model-0.1.0 → python_library_ralf_model-0.1.2}/tests/test_source_include.py
RENAMED
|
@@ -51,7 +51,7 @@ class SourceIncludeTests(unittest.TestCase):
|
|
|
51
51
|
top = root / "top.ralf"
|
|
52
52
|
top.write_text('source "mid.ralf"\n', encoding="utf-8")
|
|
53
53
|
|
|
54
|
-
out = expand_ralf_sources(
|
|
54
|
+
out, _line_sources = expand_ralf_sources(
|
|
55
55
|
top.read_text(encoding="utf-8"),
|
|
56
56
|
current_file=top.resolve(),
|
|
57
57
|
include_paths=(),
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|