Skip to content
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
b8be80c
transformer overhaul
kkozik-amplify Mar 21, 2025
e39b429
reorganize code
kkozik-amplify Mar 26, 2025
d9c2eca
batch of different changes
kkozik-amplify Apr 2, 2025
448ffd4
comments
kkozik-amplify Apr 4, 2025
65f88bc
various changes
kkozik-amplify Jul 2, 2025
5a10fec
batch of changes
kkozik-amplify Jul 23, 2025
f0f6fc9
add JSON -> LarkElement deserializer;
kkozik-amplify Aug 12, 2025
d8ac92d
add heredoc rules and deserialization;
kkozik-amplify Aug 27, 2025
5932662
add `for` expressions rules
kkozik-amplify Sep 15, 2025
107fcb2
add Lark AST -> HCL2 reconstructor and LarkTree formatter; various ot…
kkozik-amplify Sep 29, 2025
5ccfa65
* HCLReconstructor._reconstruct_token - handle 0 length tokens
kkozik-amplify Dec 12, 2025
ca19232
fix operator precedence
kkozik-amplify Feb 21, 2026
fc49bad
reorganize new and old code
kkozik-amplify Feb 22, 2026
ba80334
minor improvements to deserializer.py and formatter.py
kkozik-amplify Feb 22, 2026
e32d3e3
add round-trip test suite
kkozik-amplify Feb 22, 2026
e32a540
removed old unused file
kkozik-amplify Feb 22, 2026
210e3cd
fix - dont add spaces add the end of the line (before newline rule); …
kkozik-amplify Feb 22, 2026
b235ec9
use unittest subTest to fix noise in test results ("The type of the N…
kkozik-amplify Feb 22, 2026
a3fe326
remove files for WIP features
kkozik-amplify Feb 22, 2026
4054fc9
add new unit tests, exclude some files from coverage report
kkozik-amplify Feb 22, 2026
7662a5e
rewrite api.py, update builder.py, add unit tests for them
kkozik-amplify Feb 22, 2026
c05273d
reorganize "round-trip" tests into integration tests
kkozik-amplify Feb 22, 2026
e33b728
increase coverage failure threshold
kkozik-amplify Feb 22, 2026
df62cc9
add CLAUDE.md
kkozik-amplify Feb 22, 2026
cf33fb3
increase coverage failure threshold
kkozik-amplify Feb 22, 2026
501e8aa
Merge branch 'transformer-overhaul' into transformer-overhaul-claudemd
kkozik-amplify Feb 22, 2026
020d141
migrate some of existing round-trip tests to the new style, fix some …
kkozik-amplify Feb 23, 2026
1ab1f0d
add unit tests for
kkozik-amplify Feb 23, 2026
0a6b996
exclude abstract methods from test coverage report
kkozik-amplify Feb 23, 2026
be1e4f1
fix scientific notation preservation, function argument lookup during…
kkozik-amplify Feb 23, 2026
13ae15a
more robust heredocs serialization, add option to deserialize strings…
kkozik-amplify Feb 23, 2026
1df894b
CLI rework
kkozik-amplify Mar 2, 2026
776a3f0
minor fixes
kkozik-amplify Mar 2, 2026
745b1c7
fixes to for/tuple expressions formatting;
kkozik-amplify Mar 2, 2026
648696e
hcl2/rules - fix pre-commit errors
kkozik-amplify Mar 7, 2026
72078f0
fix pre-commit errors
kkozik-amplify Mar 7, 2026
b887656
update docs
kkozik-amplify Mar 7, 2026
4a65479
fix the limitation of using expressions as object keys
kkozik-amplify Mar 7, 2026
a066002
`Formatter._vertically_align_object_elems` - fix alignment for expres…
kkozik-amplify Mar 7, 2026
deaf093
remove unused test files
kkozik-amplify Mar 7, 2026
4c08d6e
fix some minor issues; add more cli tests
kkozik-amplify Mar 7, 2026
783b603
fix another bunch of issues
kkozik-amplify Mar 7, 2026
e893e7d
fix another bunch of issues
kkozik-amplify Mar 7, 2026
0c8a1c3
fix another bunch of issues
kkozik-amplify Mar 7, 2026
5ce94f8
increase minimum test coverage
kkozik-amplify Mar 7, 2026
4af1b0f
update CLAUDE.md
kkozik-amplify Mar 9, 2026
6defc68
Merge branch 'transformer-overhaul' into transformer-overhaul-claudemd
kkozik-amplify Mar 9, 2026
630efd2
Merge branch 'release/8.x' into transformer-overhaul-claudemd
kkozik-amplify Mar 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ branch = true
omit =
hcl2/__main__.py
hcl2/lark_parser.py
hcl2/version.py
hcl2/__init__.py
hcl2/rules/__init__.py

[report]
show_missing = true
fail_under = 80
fail_under = 95
226 changes: 226 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
# HCL2 Parser Development Guidelines

When working with this HCL2 parser codebase, follow these architectural principles and patterns.

## Core Architecture Rules

**ALWAYS** understand the bidirectional pipeline:

```
Forward: HCL2 Text → Lark Parse Tree → LarkElement Tree → Python Dict/JSON
Reverse: Python Dict/JSON → LarkElement Tree → Lark Tree → HCL2 Text
```

**NEVER** bypass the LarkElement intermediate representation. It provides type safety and enables bidirectional transformations.

**REMEMBER** that separation of concerns is key:

- Grammar definition (`hcl2.lark`) — syntax rules
- Transformer (`transformer.py`) — Lark parse tree → LarkElement tree
- Serialization (`rules/*.serialize()`) — LarkElement tree → Python dict
- Deserializer (`deserializer.py`) — Python dict → LarkElement tree
- Formatter (`formatter.py`) — whitespace alignment and spacing on LarkElement trees
- Reconstructor (`reconstructor.py`) — LarkElement tree → HCL2 text via Lark

### Public API Design

**FOLLOW** the `json` module convention in `api.py`:

- `load/loads` — HCL2 text → Python dict
- `dump/dumps` — Python dict → HCL2 text
- Intermediate stages for advanced usage: `parse/parses`, `parse_to_tree/parses_to_tree`, `transform`, `serialize`, `from_dict`, `from_json`, `reconstruct`
- All option parameters are keyword-only

## Design Pattern Guidelines

### Rule-Based Transformation Pattern

**FOLLOW** the one-to-one mapping: each Lark grammar rule corresponds to exactly one `LarkRule` class.

**ENSURE** every rule class:

- Mirrors lark grammar definition
- Inherits from appropriate base class (`LarkRule` or `LarkToken`)
- Implements `lark_name()` returning the grammar rule name
- Provides typed property accessors for child elements
- Handles its own serialization logic via `serialize()`
- Defines `_children` static field with appropriate type hinting

**LOCATE** transformation logic in `hcl2/transformer.py`

### Type Safety Requirements

**USE** abstract base classes from `hcl2/rules/abstract.py` to define contracts.

**PROVIDE** comprehensive type hints for all rule children structures.

**LEVERAGE** the generic token system in `hcl2/rules/tokens.py` for dynamic token creation with caching.

### Modular Organization Rules

**ORGANIZE** rules by domain responsibility:

- **Structural rules** → `rules/base.py`
- **Container rules** → `rules/containers.py`
- **Expression rules** → `rules/expressions.py`
- **Literal rules** → `rules/literal_rules.py`
- **String rules** → `rules/strings.py`
- **Function rules** → `rules/functions.py`
- **Indexing rules** → `rules/indexing.py`
- **For-expression rules** → `rules/for_expressions.py`
- **Metadata rules** → `rules/whitespace.py`

**NEVER** mix concerns across these domains.

### Serialization Strategy Guidelines

**IMPLEMENT** context-aware serialization using:

- `SerializationOptions` for configuration
- `SerializationContext` for state tracking
- Context managers for temporary state changes

**REFERENCE** implementation patterns in `hcl2/utils.py`

**ENSURE** each rule type follows its serialization strategy:

- Structural rules create nested dictionaries
- Container rules handle collections with optional wrapping
- Expression rules generate `${...}` interpolation when needed
- Literal rules convert to appropriate Python types

## Critical Implementation Rules

### Block vs Object Distinction

**ALWAYS** preserve the semantic difference between HCL2 blocks and data objects.

**USE** `__is_block__` markers to maintain semantic intent during round-trips.

**IMPLEMENT** block recognition logic in deserializer that can distinguish blocks from regular objects.

**HANDLE** multi-label blocks by implementing recursive label extraction algorithms.

### Bidirectional Requirements

**ENSURE** every serialization operation has a corresponding deserialization counterpart.

**TEST** round-trip integrity: Parse → Serialize → Deserialize → Serialize should produce identical results.

**REFERENCE** deserialization patterns in `hcl2/deserializer.py`

### String Interpolation Handling

**SUPPORT** nested expression evaluation within `${expression}` syntax.

**HANDLE** escape sequences and literal text segments properly.

**MAINTAIN** context awareness when generating interpolation strings.

## Extension Guidelines

### Adding New Language Constructs

**FOLLOW** this exact sequence:

1. Add grammar rules to `hcl2.lark`
1. Create rule classes following existing patterns
1. Add transformer methods to map grammar to rules
1. Implement serialization logic in rule classes
1. Update deserializer for round-trip support

### Rule Implementation Conventions

**ALWAYS** implement these methods/properties:

- `lark_name()` static method
- Property accessors for child elements
- `serialize()` method with context support
- Type hints for `_children` structure

**FOLLOW** naming conventions consistent with existing rules.

### Testing Requirements

**USE** `unittest.TestCase` as the test framework (not pytest).

**ORGANIZE** tests into two directories:

- `test/unit/` — granular tests that instantiate rule objects directly (no parsing)
- `test/unit/rules/` — one file per rules module (e.g., `test_expressions.py` covers `hcl2/rules/expressions.py`)
- `test/unit/test_api.py`, `test/unit/test_builder.py`, etc. — other module tests
- `test/integration/` — full-pipeline tests using golden files
- `test_round_trip.py` — suite-based step tests (HCL→JSON, JSON→JSON, JSON→HCL, full round-trip) that iterate over all suites in `hcl2_original/`
- `test_specialized.py` — feature-specific integration tests (operator precedence, Builder round-trip) with golden files in `specialized/`

**USE** concrete stubs when testing ABCs (e.g., `StubExpression(ExpressionRule)` for testing `_wrap_into_parentheses` logic without the parser).

**RUN** tests with: `python -m unittest discover -s test -p "test_*.py" -v`

## Code Quality Rules

### Type Safety Requirements

**PROVIDE** full type hints to enable static analysis.

**USE** proper inheritance hierarchies to catch errors at runtime.

**IMPLEMENT** property-based access to prevent structural errors.

### Performance Considerations

**LEVERAGE** cached token creation to prevent duplicate instantiation.

**IMPLEMENT** lazy evaluation for context-sensitive processing.

**OPTIMIZE** tree traversal using parent-child references.

### Maintainability Standards

**ENSURE** each rule has single responsibility for one grammar construct.

**FOLLOW** open/closed principle: extend via new rules, don't modify existing ones.

**MAINTAIN** clear import dependencies and type relationships.

## File Organization Standards

**KEEP** core abstractions in `rules/abstract.py`

**GROUP** domain-specific rules by functionality in separate files

**SEPARATE** utility functions into dedicated modules

**MAINTAIN** grammar definition independence from implementation

**STRUCTURE** test infrastructure to support incremental validation

## Common Pitfalls to Avoid

**DO NOT** create direct transformations from parse tree to Python dict - always use LarkElement intermediate representation.

**DO NOT** mix serialization concerns across rule types - each rule handles its own format.

**DO NOT** ignore context when generating expressions - interpolation behavior depends on nesting.

**DO NOT** forget to update both serialization and deserialization when adding new constructs.

**DO NOT** bypass the factory pattern for token creation - use the cached `StringToken` system.

## When Making Changes

**ALWAYS** run round-trip tests after any modifications.

**VERIFY** that new rules follow existing patterns and conventions.

**UPDATE** both transformer and deserializer when adding language features.

**MAINTAIN** type safety and proper inheritance relationships.

**DOCUMENT** any new patterns or conventions introduced.

This architecture enables robust HCL2 parsing with full round-trip fidelity while maintaining code quality and extensibility.

## Keeping This File Current

**PROACTIVELY** update this file when your work changes the architecture, file organization, module responsibilities, public API surface, or testing conventions described above. If you add, rename, move, or delete modules, rules files, test directories, or pipeline stages — reflect those changes here before finishing the task. Stale documentation is worse than no documentation.
14 changes: 12 additions & 2 deletions hcl2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,21 @@
from .api import (
load,
loads,
dump,
dumps,
parse,
parses,
parse_to_tree,
parses_to_tree,
from_dict,
from_json,
reconstruct,
transform,
reverse_transform,
writes,
serialize,
)

from .builder import Builder
from .deserializer import DeserializerOptions
from .formatter import FormatterOptions
from .rules.base import StartRule
from .utils import SerializationOptions
4 changes: 3 additions & 1 deletion hcl2/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from lark import UnexpectedCharacters, UnexpectedToken

from . import load
from .utils import SerializationOptions
from .version import __version__


Expand Down Expand Up @@ -58,7 +59,8 @@ def main():
else open(args.OUT_PATH, "w", encoding="utf-8")
)
print(args.PATH, file=sys.stderr, flush=True)
json.dump(load(in_file, with_meta=args.with_meta), out_file)
options = SerializationOptions(with_meta=True) if args.with_meta else None
json.dump(load(in_file, serialization_options=options), out_file)
if args.OUT_PATH is None:
out_file.write("\n")
out_file.close()
Expand Down
Loading
Loading