diff --git a/.gitignore b/.gitignore index 095486d..e57f86c 100644 --- a/.gitignore +++ b/.gitignore @@ -140,5 +140,6 @@ cython_debug/ *.egg-info .pytest_cache/ +.vscode/settings.json .claude/worktrees/ diff --git a/PRD.md b/PRD.md index 133c687..d18fe30 100644 --- a/PRD.md +++ b/PRD.md @@ -3,7 +3,7 @@ ## Overview **Project:** OpenSCAD Parser -**Version:** 2.0.0 +**Version:** 2.2.0 **Status:** Beta **License:** MIT @@ -41,7 +41,7 @@ OpenSCAD is a popular programming language for creating parametric 3D CAD models | Support comments in AST for documentation tools | Medium | | Parse invalid code gracefully with useful errors | Medium | -## Current Capabilities (v2.0.0) +## Current Capabilities (v2.2.0) ### Language Support @@ -63,7 +63,9 @@ Full parsing support for OpenSCAD language constructs: | Source Mapping | Track positions across multiple files (essential for includes) | | Caching | In-memory AST cache with modification time validation | | Library Resolution | Platform-aware search paths matching OpenSCAD behavior | -| Comment Preservation | Optional inclusion of comments in AST | +| Comment Preservation | Optional inclusion of comments in AST, including inside statement blocks | +| AST Serialization | JSON and YAML export/import of full ASTs | +| Scope Resolution | Lexical scope building with three separate namespaces (variables, functions, modules) | ### API Surface @@ -77,6 +79,16 @@ from openscad_parser.ast import ( clear_ast_cache, # Clear AST cache ) +# Serialization +from openscad_parser.ast.serialization import ( + ast_to_json, ast_from_json, + ast_to_yaml, ast_from_yaml, + ast_to_dict, ast_from_dict, +) + +# Scope resolution +from openscad_parser.ast.scope import Scope, build_scopes + # Lower-level access from openscad_parser import getOpenSCADParser from openscad_parser.ast import parse_ast @@ -117,6 +129,8 @@ from openscad_parser.ast import parse_ast | AST Nodes | `ast/nodes.py` | Dataclass definitions for all node types | | Builder | `ast/builder.py` | Visitor that converts parse tree to AST | | Source Map | `ast/source_map.py` | Tracks positions across multiple origins | +| Scope | `ast/scope.py` | Lexical scope building across three namespaces | +| Serialization | `ast/serialization.py` | JSON/YAML export and import of ASTs | | API | `ast/__init__.py` | Public convenience functions | ## Quality Attributes @@ -149,10 +163,9 @@ The following are potential enhancements for contributors to consider: | Enhancement | Description | Rationale | |-------------|-------------|-----------| -| **AST Serialization** | JSON/YAML export of AST | Enable language-agnostic tooling | | **Code Generation** | AST to OpenSCAD source | Enable refactoring tools, formatters | | **Error Recovery** | Parse partial/invalid code | Better IDE integration, real-time parsing | -| **Semantic Analysis** | Type inference, scope resolution | Enable advanced linting, autocomplete | +| **Type Inference** | Infer types from scope analysis | Enable advanced linting, autocomplete | ### Medium Value @@ -186,7 +199,6 @@ For open source contributors, consider these indicators of project health: ### Good First Issues -- Add JSON serialization for AST nodes - Create utility functions for common AST traversals - Improve error messages for parse failures - Add examples for common use cases diff --git a/pyproject.toml b/pyproject.toml index 6ab1619..b396ac2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "uv_build" [project] name = "openscad_parser" -version = "2.1.0" +version = "2.3.0" description = "A PEG parser to read OpenSCAD language source code, with optional AST tree generation." readme = "README.rst" authors = [ @@ -33,12 +33,12 @@ classifiers = [ keywords = ["openscad", "openscad parser", "parser"] dependencies = [ "arpeggio>=2.0.3", - "pytest-cov>=7.1.0", ] [project.optional-dependencies] dev = [ "pytest>=7.0.0", + "pytest-cov>=7.1.0", ] yaml = [ "PyYAML>=6.0", diff --git a/tests/conftest.py b/tests/conftest.py index aa3c637..85a3637 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -16,6 +16,12 @@ def parser_reduced(): return getOpenSCADParser(reduce_tree=True) +@pytest.fixture +def parser_comments(): + """Create a parser instance with comment inclusion enabled.""" + return getOpenSCADParser(reduce_tree=False, include_comments=True) + + def parse_success(parser, code): """Helper function to parse code and assert success.""" result = parser.parse(code) diff --git a/tests/test_modules.py b/tests/test_modules.py index 7048103..fae7ce9 100644 --- a/tests/test_modules.py +++ b/tests/test_modules.py @@ -2,7 +2,6 @@ import pytest from tests.conftest import parse_success -from openscad_parser import getOpenSCADParser class TestModuleDefinition: @@ -53,12 +52,11 @@ def test_module_nested(self, parser): code = "module outer() { module inner() {} }" parse_success(parser, code) - def test_module_with_comment(self, parser): + def test_module_with_comment(self, parser, parser_comments): """Test module defined with comments""" - comment_parser = getOpenSCADParser(reduce_tree=False, include_comments=True) code = "module test() {\nsphere(5);\n// comment\ncube(10);}" parse_success(parser, code) - parse_success(comment_parser, code) + parse_success(parser_comments, code) class TestModuleInstantiation: """Test module instantiation parsing.""" diff --git a/uv.lock b/uv.lock index 1e9d1eb..9e9c4b1 100644 --- a/uv.lock +++ b/uv.lock @@ -135,7 +135,7 @@ wheels = [ [[package]] name = "openscad-parser" -version = "2.1.0" +version = "2.3.0" source = { editable = "." } dependencies = [ { name = "arpeggio" }, @@ -145,6 +145,7 @@ dependencies = [ [package.optional-dependencies] dev = [ { name = "pytest" }, + { name = "pytest-cov" }, ] yaml = [ { name = "pyyaml" }, @@ -154,7 +155,7 @@ yaml = [ requires-dist = [ { name = "arpeggio", specifier = ">=2.0.3" }, { name = "pytest", marker = "extra == 'dev'", specifier = ">=7.0.0" }, - { name = "pytest-cov", specifier = ">=7.1.0" }, + { name = "pytest-cov", marker = "extra == 'dev'", specifier = ">=7.1.0" }, { name = "pyyaml", marker = "extra == 'yaml'", specifier = ">=6.0" }, ] provides-extras = ["dev", "yaml"]