Metadata-Version: 2.4
Name: pastadd
Version: 0.1.2
Summary: Reference Attribute Grammars in Python — demand-driven evaluation with incremental consistency
Author: pASTadd contributors
License: BSD-3-Clause
Project-URL: Documentation, https://git.cs.lth.se/an8662be/pastadd
Project-URL: Repository, https://git.cs.lth.se/an8662be/pastadd
Keywords: attribute-grammars,compiler,education,RAG,JastAdd
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Education
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: BSD License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Compilers
Classifier: Topic :: Education
Requires-Python: >=3.13
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: pytracked
Dynamic: license-file

# pASTadd

A Python attribute grammar framework inspired by [JastAdd](https://jastadd.cs.lth.se/). Uses Python's dynamic mechanisms (descriptors, decorators, `__getattr__`) instead of code generation — making attribute grammar semantics visible and interactive.

## Quick Example

Define a grammar (`lang.ast`):

```
Program ::= Func*;
Func ::= <Name:String> Body:Expr;
abstract Expr;
IdUse : Expr ::= <Name:String>;
BinOp : Expr ::= Left:Expr <Op:String> Right:Expr;
Numeral : Expr ::= <Value:int>;
```

Load it and define attributes:

```python
from pastadd import load_grammar, inh, syn

# Load grammar — creates ASTNode classes dynamically
load_grammar("lang.ast")

# Synthesized attribute (lambda):
Func.qualified_name = lambda self: f"program.{self.name}"

# Inherited attribute (decorator):
@inh(Program)
def env(self, child_index):
    return {f.name: f for f in self.children}

# Or as a lambda:
# inh(Program).env = lambda self, ci: {f.name: f for f in self.children}

# Use it
prog = Program()
f = Func(name="main")
prog.children.append(f)

print(f.env)              # {'main': <Func object>}
print(f.qualified_name)   # 'program.main'
```

Inherited attributes resolve by walking up the tree — no pass-through equations needed on intermediate nodes.

Or generate a Python file from the grammar:

```bash
python -m pastadd.grammar lang.ast -o lang_ast.py
```

## Design Principles

1. **Grammar from `.ast` files** — `load_grammar()` creates node classes dynamically; `generate_grammar()` writes Python source.
2. **Equations live in modules** — all `syn`, `inh`, `circular`, `collection` definitions are external to the grammar.
3. **One mechanism per concern** — synthesized, inherited, circular, collection, NTA, forwarding — each has a clear API.
4. **Educational transparency** — every evaluation step is traceable via `observe()`.

## Features

| Feature | API | Description |
|---------|-----|-------------|
| Grammar loading | `load_grammar()` / `generate_grammar()` | Parse `.ast` files into ASTNode classes |
| Synthesized attributes | `@syn(Cls)` / `Cls.attr = lambda` | Demand-driven, cached |
| Inherited attributes | `@inh(Cls)` / `@inh(Cls.child)` | Parent provides equation for children |
| Collection attributes | `collection()` + `contribute()` | Gather values from tree via survey |
| Circular attributes | `circular(bottom=...)(fn)` / `circular(bottom=...)` | Fixed-point iteration (syn and inh) |
| Non-Terminal Attributes | `@nta(Cls)` | Computed subtrees with parent links |
| Rewrites | `@rewrite` / `@rewrite(Cls)` | Conditional tree transformations on access |
| Parameterized attributes | extra args on equation fn | Cached per argument tuple |
| Child targeting | `@inh(Cls.child)` | Target equations to specific children |
| Incremental evaluation | automatic | Dependency tracking + auto-invalidation |
| Forwarding | `forwards_to` attribute | Silver-style delegation |
| Tokens | `token(default)` | Mutable input data (triggers invalidation) |
| Tracing / Profiling | `observe()` | Observe evaluation, timing, cache hits |
| Abstract nodes | `abstract Foo : Bar;` in `.ast` | Cannot instantiate directly |

## Key Patterns

```python
# One-liner synthesized:
Cls.attr = lambda self: expr

# Multi-line synthesized:
@syn(Cls)
def attr(self):
    return complex_computation

# Inherited (all children, lambda):
inh(Parent).attr = lambda self, ci: value

# Inherited (all children, multi-line):
@inh(Parent)
def attr(self, child_index):
    return complex_lookup

# Inherited (targeted child):
inh(Parent.child_name).attr = lambda self, ci: value

# Circular synthesized:
Cls.attr = circular(bottom=True)(lambda self, t: t.supertype(self))

# Circular inherited:
Expr.target_type = circular(bottom=None)

# Collection:
Cls.errors = collection(bottom=set, combine=set.add)
contribute(SubCls.check, to=Cls.errors)

# NTA (computed subtree):
@nta(Cls)
def computed_child(self):
    return SomeNode(...)
```

## Requirements

Python 3.13+ required. Depends on [pytracked](https://github.com/pastadd/pytracked) for dependency tracking.

```bash
pip install pastadd
```

## Testing

```bash
python -m pytest tests/ -v
```

## See Also

- [JastAdd](https://jastadd.cs.lth.se/) — the Java attribute grammar system that inspired pASTadd
- [ExtendJ-pASTadd](https://github.com/pastadd/extendj-pastadd) — a Java 5 compiler frontend ported to pASTadd (504 tests passing)

## License

BSD-3-Clause
