feat(cli): dump environment after compile

This commit is contained in:
2026-05-31 18:44:41 +02:00
parent 1a1b0e8e15
commit 893e1ba190
4 changed files with 64 additions and 2 deletions

View File

@@ -273,7 +273,7 @@ class Checker(
def visit_return_stmt(self, stmt: p.ReturnStmt) -> None: def visit_return_stmt(self, stmt: p.ReturnStmt) -> None:
type: Type = stmt.value.accept(self) if stmt.value is not None else UnitType() type: Type = stmt.value.accept(self) if stmt.value is not None else UnitType()
self.env.return_types.add(type) self.env.return_types.append(type)
raise ReturnException() raise ReturnException()
def visit_binary_expr(self, expr: p.BinaryExpr) -> Type: def visit_binary_expr(self, expr: p.BinaryExpr) -> Type:

View File

@@ -15,7 +15,11 @@ class Environment:
def __init__(self, enclosing: Optional[Environment] = None) -> None: def __init__(self, enclosing: Optional[Environment] = None) -> None:
self.enclosing: Optional[Environment] = enclosing self.enclosing: Optional[Environment] = enclosing
self.values: dict[str, Type] = {} self.values: dict[str, Type] = {}
self.return_types: set[Type] = set() self.return_types: list[Type] = []
self._children: list[Environment] = []
if enclosing is not None:
enclosing._children.append(self)
def define(self, name: str, value: Type) -> None: def define(self, name: str, value: Type) -> None:
"""Define a variable in this environment """Define a variable in this environment
@@ -129,3 +133,10 @@ class Environment:
if self.enclosing is None: if self.enclosing is None:
return self.values return self.values
return self.enclosing.flat_dict() | self.values return self.enclosing.flat_dict() | self.values
def dump(self) -> dict:
return {
"values": self.values,
"return_types": self.return_types,
"children": [child.dump() for child in self._children],
}

View File

@@ -1,4 +1,5 @@
import ast import ast
import json
import logging import logging
from dataclasses import dataclass from dataclasses import dataclass
from pathlib import Path from pathlib import Path
@@ -18,6 +19,7 @@ from midas.lexer.token import Token, TokenType
from midas.parser.midas import MidasParser from midas.parser.midas import MidasParser
from midas.parser.python import PythonParser from midas.parser.python import PythonParser
from midas.resolver.resolver import Resolver from midas.resolver.resolver import Resolver
from midas.utils import UniversalJSONDumper
@click.group() @click.group()
@@ -40,6 +42,15 @@ def compile(file: TextIO):
for diagnostic in diagnostics: for diagnostic in diagnostics:
print(diagnostic) print(diagnostic)
print(
json.dumps(
UniversalJSONDumper.dump(
checker.global_env, [("Environment", "_children")]
),
indent=4,
)
)
@midas.group() @midas.group()
def utils(): def utils():

40
midas/utils.py Normal file
View File

@@ -0,0 +1,40 @@
from typing import Any, Optional
class UniversalJSONDumper:
@classmethod
def dump(
cls, obj: Any, include_keys: Optional[list[str | tuple[str, str]]] = None
) -> Any:
if include_keys is None:
include_keys = []
return cls._dump(obj, include_keys, [])
@classmethod
def _dump(
cls, obj: Any, include_keys: list[str | tuple[str, str]], visited: list[Any]
) -> Any:
if obj in visited:
return None
match obj:
case str() | int() | float() | None:
return obj
case list() | set() | tuple():
return [cls._dump(child, include_keys, visited) for child in obj]
case dict():
return {
str(k): cls._dump(v, include_keys, visited) for k, v in obj.items()
}
case object():
visited.append(obj)
return {
"_type": obj.__class__.__name__,
} | {
k: cls._dump(v, include_keys, visited)
for k, v in obj.__dict__.items()
if not k.startswith("_")
or k in include_keys
or (obj.__class__.__name__, k) in include_keys
}
case _:
raise ValueError(f"Unsupported value: {obj}")