From 893e1ba19073705a0924993c7686d1a3d738457b Mon Sep 17 00:00:00 2001 From: LordBaryhobal Date: Sun, 31 May 2026 18:44:41 +0200 Subject: [PATCH] feat(cli): dump environment after compile --- midas/checker/checker.py | 2 +- midas/checker/environment.py | 13 +++++++++++- midas/cli/main.py | 11 ++++++++++ midas/utils.py | 40 ++++++++++++++++++++++++++++++++++++ 4 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 midas/utils.py diff --git a/midas/checker/checker.py b/midas/checker/checker.py index f83dd14..4ebedd5 100644 --- a/midas/checker/checker.py +++ b/midas/checker/checker.py @@ -273,7 +273,7 @@ class Checker( def visit_return_stmt(self, stmt: p.ReturnStmt) -> None: 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() def visit_binary_expr(self, expr: p.BinaryExpr) -> Type: diff --git a/midas/checker/environment.py b/midas/checker/environment.py index 39e53e3..7727f6e 100644 --- a/midas/checker/environment.py +++ b/midas/checker/environment.py @@ -15,7 +15,11 @@ class Environment: def __init__(self, enclosing: Optional[Environment] = None) -> None: self.enclosing: Optional[Environment] = enclosing 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: """Define a variable in this environment @@ -129,3 +133,10 @@ class Environment: if self.enclosing is None: return 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], + } diff --git a/midas/cli/main.py b/midas/cli/main.py index d17b4eb..9119a07 100644 --- a/midas/cli/main.py +++ b/midas/cli/main.py @@ -1,4 +1,5 @@ import ast +import json import logging from dataclasses import dataclass from pathlib import Path @@ -18,6 +19,7 @@ from midas.lexer.token import Token, TokenType from midas.parser.midas import MidasParser from midas.parser.python import PythonParser from midas.resolver.resolver import Resolver +from midas.utils import UniversalJSONDumper @click.group() @@ -40,6 +42,15 @@ def compile(file: TextIO): for diagnostic in diagnostics: print(diagnostic) + print( + json.dumps( + UniversalJSONDumper.dump( + checker.global_env, [("Environment", "_children")] + ), + indent=4, + ) + ) + @midas.group() def utils(): diff --git a/midas/utils.py b/midas/utils.py new file mode 100644 index 0000000..0c387e2 --- /dev/null +++ b/midas/utils.py @@ -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}")