File size: 2,167 Bytes
7952f32
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
"""Parse-only validator.

Calls Python's own ``compile()`` on each materialized file and reports any
syntax / lexer errors. This is the cheapest gate; the agent receives this
feedback as part of ``materialize_and_validate``.

Heavier checks (import-resolution, ``mypy --strict``, behavioral tests) live
elsewhere in :mod:`graphforge.validator` and :mod:`graphforge.behavioral` —
intentionally separated so the agent can pay for verification incrementally.
"""

from __future__ import annotations

from dataclasses import dataclass, field


@dataclass(frozen=True)
class ParseError:
    filename: str
    line: int | None
    column: int | None
    message: str


@dataclass
class ValidationReport:
    parse_errors: list[ParseError] = field(default_factory=list)

    @property
    def ok(self) -> bool:
        return not self.parse_errors

    def to_dict(self) -> dict[str, object]:
        return {
            "ok": self.ok,
            "parse_errors": [
                {
                    "filename": e.filename,
                    "line": e.line,
                    "column": e.column,
                    "message": e.message,
                }
                for e in self.parse_errors
            ],
        }


def parse_check(files: dict[str, str]) -> list[ParseError]:
    """Compile each ``files[name]`` source. Return collected errors.

    An empty list means every file parsed cleanly.
    """
    errors: list[ParseError] = []
    for filename, source in files.items():
        try:
            compile(source, filename, "exec")
        except SyntaxError as e:
            errors.append(
                ParseError(
                    filename=filename,
                    line=e.lineno,
                    column=e.offset,
                    message=e.msg,
                )
            )
    return errors


def full_check(files: dict[str, str]) -> ValidationReport:
    """Run every validator gate that's currently implemented.

    Today: parse-only. ``mypy --strict`` and import-resolution are added in
    follow-up commits but the report shape stays the same.
    """
    return ValidationReport(parse_errors=parse_check(files))