hassl 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hassl-0.2.0/LICENSE +21 -0
- hassl-0.2.0/PKG-INFO +167 -0
- hassl-0.2.0/README.md +152 -0
- hassl-0.2.0/hassl/__init__.py +0 -0
- hassl-0.2.0/hassl/ast/__init__.py +0 -0
- hassl-0.2.0/hassl/ast/nodes.py +34 -0
- hassl-0.2.0/hassl/cli.py +42 -0
- hassl-0.2.0/hassl/codegen/__init__.py +22 -0
- hassl-0.2.0/hassl/codegen/package.py +335 -0
- hassl-0.2.0/hassl/codegen/rules_min.py +663 -0
- hassl-0.2.0/hassl/codegen/yaml_emit.py +77 -0
- hassl-0.2.0/hassl/parser/__init__.py +0 -0
- hassl-0.2.0/hassl/parser/transform.py +272 -0
- hassl-0.2.0/hassl/semantics/__init__.py +0 -0
- hassl-0.2.0/hassl/semantics/analyzer.py +145 -0
- hassl-0.2.0/hassl/semantics/domains.py +8 -0
- hassl-0.2.0/hassl.egg-info/PKG-INFO +167 -0
- hassl-0.2.0/hassl.egg-info/SOURCES.txt +38 -0
- hassl-0.2.0/hassl.egg-info/dependency_links.txt +1 -0
- hassl-0.2.0/hassl.egg-info/entry_points.txt +2 -0
- hassl-0.2.0/hassl.egg-info/requires.txt +3 -0
- hassl-0.2.0/hassl.egg-info/top_level.txt +1 -0
- hassl-0.2.0/pyproject.toml +23 -0
- hassl-0.2.0/setup.cfg +28 -0
- hassl-0.2.0/tests/test_codegen_sync_basic.py +21 -0
- hassl-0.2.0/tests/test_golden_ir_sync_shared.py +18 -0
hassl-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Andrew Danowitz
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
hassl-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hassl
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: HASSL: Home Assistant Simple Scripting Language
|
|
5
|
+
Home-page: https://github.com/adanowitz/hassl
|
|
6
|
+
Author: adanowitz
|
|
7
|
+
Author-email: adanowitz@gmail.com
|
|
8
|
+
Requires-Python: >=3.11
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Requires-Dist: lark-parser
|
|
12
|
+
Requires-Dist: jinja2
|
|
13
|
+
Requires-Dist: pyyaml
|
|
14
|
+
Dynamic: license-file
|
|
15
|
+
|
|
16
|
+
# HASSL
|
|
17
|
+
|
|
18
|
+
> **Home Assistant Simple Scripting Language**
|
|
19
|
+
|
|
20
|
+
HASSL is a human-friendly domain-specific language (DSL) for building **loop-safe**, **deterministic**, and **composable** automations for [Home Assistant](https://www.home-assistant.io/).
|
|
21
|
+
|
|
22
|
+
It compiles lightweight `.hassl` scripts into fully functional YAML packages that plug directly into Home Assistant, replacing complex automations with a clean, readable syntax.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## 🚀 Features
|
|
27
|
+
|
|
28
|
+
- **Readable DSL** → write logic like natural language (`if motion && lux < 50 then light = on`)
|
|
29
|
+
- **Sync devices** → keep switches, dimmers, and fans perfectly in sync
|
|
30
|
+
- **Schedules** → declare time-based gates (`enable from 08:00 until 19:00`)
|
|
31
|
+
- **Loop-safe** → context ID tracking prevents feedback loops
|
|
32
|
+
- **Per-rule enable gates** → `disable rule` or `enable rule` dynamically
|
|
33
|
+
- **Inline waits** → `wait (!motion for 10m)` works like native HA triggers
|
|
34
|
+
- **Color temperature in Kelvin** → `light.kelvin = 2700`
|
|
35
|
+
- **Auto-reload resilience** → schedules re-evaluate automatically on HA restart
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## 🧰 Example
|
|
40
|
+
|
|
41
|
+
```hassl
|
|
42
|
+
alias light = light.wesley_lamp
|
|
43
|
+
alias motion = binary_sensor.wesley_motion_motion
|
|
44
|
+
alias lux = sensor.wesley_motion_illuminance
|
|
45
|
+
|
|
46
|
+
schedule wake_hours:
|
|
47
|
+
enable from 08:00 until 19:00;
|
|
48
|
+
|
|
49
|
+
rule wesley_motion_light:
|
|
50
|
+
schedule use wake_hours;
|
|
51
|
+
if (motion && lux < 50)
|
|
52
|
+
then light = on;
|
|
53
|
+
wait (!motion for 10m) light = off
|
|
54
|
+
|
|
55
|
+
rule landing_manual_off:
|
|
56
|
+
if (light == off) not_by any_hassl
|
|
57
|
+
then disable rule wesley_motion_light for 3m
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Produces a complete Home Assistant package with:
|
|
61
|
+
|
|
62
|
+
- Helpers (`input_boolean`, `input_text`, `input_number`)
|
|
63
|
+
- Context-aware writer scripts
|
|
64
|
+
- Sync automations for linked devices
|
|
65
|
+
- Rule-based automations with schedules and `not_by` guards
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## 🏗 Installation
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
git clone https://github.com/adanowitz/hassl.git
|
|
73
|
+
cd hassl
|
|
74
|
+
pip install -e .
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Verify:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
hasslc --help
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## ⚙️ Usage
|
|
86
|
+
|
|
87
|
+
1. Create a `.hassl` script (e.g., `living_room.hassl`).
|
|
88
|
+
2. Compile it into a Home Assistant package:
|
|
89
|
+
```bash
|
|
90
|
+
hasslc living_room.hassl -o ./packages/living_room/
|
|
91
|
+
```
|
|
92
|
+
3. Copy the package into `/config/packages/` and reload automations.
|
|
93
|
+
|
|
94
|
+
Each `.hassl` file compiles into an isolated package — no naming collisions, no shared helpers.
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## 📦 Output Files
|
|
99
|
+
|
|
100
|
+
| File | Description |
|
|
101
|
+
| -------------------------- | --------------------------------------------- |
|
|
102
|
+
| `helpers_<pkg>.yaml` | Defines all helpers (booleans, text, numbers) |
|
|
103
|
+
| `scripts_<pkg>.yaml` | Writer scripts with context stamping |
|
|
104
|
+
| `sync_<pkg>_*.yaml` | Sync automations for each property |
|
|
105
|
+
| `rules_bundled_<pkg>.yaml` | Rule logic automations + schedules |
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## 🧠 Concepts
|
|
110
|
+
|
|
111
|
+
| Concept | Description |
|
|
112
|
+
| ------------ | --------------------------------------------------------------- |
|
|
113
|
+
| **Alias** | Maps short names to HA entities (`alias light = light.kitchen`) |
|
|
114
|
+
| **Sync** | Keeps multiple devices aligned across on/off, brightness, etc. |
|
|
115
|
+
| **Rule** | Defines reactive logic with guards, waits, and control flow. |
|
|
116
|
+
| **Schedule** | Defines active time windows, reusable across rules. |
|
|
117
|
+
| **Tag** | Lightweight metadata stored in `input_text` helpers. |
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## 🔒 Loop Safety & Context Tracking
|
|
122
|
+
|
|
123
|
+
HASSL automatically writes the **parent context ID** into helper entities before performing actions.\
|
|
124
|
+
This ensures `not_by any_hassl` and `not_by rule("name")` guards work flawlessly, preventing infinite feedback.
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## 🕒 Schedules That Survive Restarts
|
|
129
|
+
|
|
130
|
+
All schedules are restart-safe:
|
|
131
|
+
|
|
132
|
+
- `input_boolean.hassl_schedule_<name>` automatically re-evaluates on startup.
|
|
133
|
+
- Triggers are set for both start and end times.
|
|
134
|
+
- Missed events (like mid-day restarts) are recovered automatically.
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## 📚 Documentation
|
|
139
|
+
|
|
140
|
+
For full grammar and detailed semantics, see the [HASSL Language Specification](./HASSL_Specification.md).
|
|
141
|
+
|
|
142
|
+
For a hands-on guide, check out the [Quickstart](./quickstart.md).
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## 🧩 Contributing
|
|
147
|
+
|
|
148
|
+
Contributions, tests, and ideas welcome!\
|
|
149
|
+
To run tests locally:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
pytest tests/
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Please open pull requests for grammar improvements, new device domains, or scheduling logic.
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## 📄 License
|
|
160
|
+
|
|
161
|
+
MIT License © 2025\
|
|
162
|
+
Created and maintained by [@adanowitz](https://github.com/adanowitz)
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
**HASSL** — simple, reliable, human-readable automations for Home Assistant.
|
|
167
|
+
|
hassl-0.2.0/README.md
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# HASSL
|
|
2
|
+
|
|
3
|
+
> **Home Assistant Simple Scripting Language**
|
|
4
|
+
|
|
5
|
+
HASSL is a human-friendly domain-specific language (DSL) for building **loop-safe**, **deterministic**, and **composable** automations for [Home Assistant](https://www.home-assistant.io/).
|
|
6
|
+
|
|
7
|
+
It compiles lightweight `.hassl` scripts into fully functional YAML packages that plug directly into Home Assistant, replacing complex automations with a clean, readable syntax.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 🚀 Features
|
|
12
|
+
|
|
13
|
+
- **Readable DSL** → write logic like natural language (`if motion && lux < 50 then light = on`)
|
|
14
|
+
- **Sync devices** → keep switches, dimmers, and fans perfectly in sync
|
|
15
|
+
- **Schedules** → declare time-based gates (`enable from 08:00 until 19:00`)
|
|
16
|
+
- **Loop-safe** → context ID tracking prevents feedback loops
|
|
17
|
+
- **Per-rule enable gates** → `disable rule` or `enable rule` dynamically
|
|
18
|
+
- **Inline waits** → `wait (!motion for 10m)` works like native HA triggers
|
|
19
|
+
- **Color temperature in Kelvin** → `light.kelvin = 2700`
|
|
20
|
+
- **Auto-reload resilience** → schedules re-evaluate automatically on HA restart
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 🧰 Example
|
|
25
|
+
|
|
26
|
+
```hassl
|
|
27
|
+
alias light = light.wesley_lamp
|
|
28
|
+
alias motion = binary_sensor.wesley_motion_motion
|
|
29
|
+
alias lux = sensor.wesley_motion_illuminance
|
|
30
|
+
|
|
31
|
+
schedule wake_hours:
|
|
32
|
+
enable from 08:00 until 19:00;
|
|
33
|
+
|
|
34
|
+
rule wesley_motion_light:
|
|
35
|
+
schedule use wake_hours;
|
|
36
|
+
if (motion && lux < 50)
|
|
37
|
+
then light = on;
|
|
38
|
+
wait (!motion for 10m) light = off
|
|
39
|
+
|
|
40
|
+
rule landing_manual_off:
|
|
41
|
+
if (light == off) not_by any_hassl
|
|
42
|
+
then disable rule wesley_motion_light for 3m
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Produces a complete Home Assistant package with:
|
|
46
|
+
|
|
47
|
+
- Helpers (`input_boolean`, `input_text`, `input_number`)
|
|
48
|
+
- Context-aware writer scripts
|
|
49
|
+
- Sync automations for linked devices
|
|
50
|
+
- Rule-based automations with schedules and `not_by` guards
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## 🏗 Installation
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
git clone https://github.com/adanowitz/hassl.git
|
|
58
|
+
cd hassl
|
|
59
|
+
pip install -e .
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Verify:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
hasslc --help
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## ⚙️ Usage
|
|
71
|
+
|
|
72
|
+
1. Create a `.hassl` script (e.g., `living_room.hassl`).
|
|
73
|
+
2. Compile it into a Home Assistant package:
|
|
74
|
+
```bash
|
|
75
|
+
hasslc living_room.hassl -o ./packages/living_room/
|
|
76
|
+
```
|
|
77
|
+
3. Copy the package into `/config/packages/` and reload automations.
|
|
78
|
+
|
|
79
|
+
Each `.hassl` file compiles into an isolated package — no naming collisions, no shared helpers.
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## 📦 Output Files
|
|
84
|
+
|
|
85
|
+
| File | Description |
|
|
86
|
+
| -------------------------- | --------------------------------------------- |
|
|
87
|
+
| `helpers_<pkg>.yaml` | Defines all helpers (booleans, text, numbers) |
|
|
88
|
+
| `scripts_<pkg>.yaml` | Writer scripts with context stamping |
|
|
89
|
+
| `sync_<pkg>_*.yaml` | Sync automations for each property |
|
|
90
|
+
| `rules_bundled_<pkg>.yaml` | Rule logic automations + schedules |
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## 🧠 Concepts
|
|
95
|
+
|
|
96
|
+
| Concept | Description |
|
|
97
|
+
| ------------ | --------------------------------------------------------------- |
|
|
98
|
+
| **Alias** | Maps short names to HA entities (`alias light = light.kitchen`) |
|
|
99
|
+
| **Sync** | Keeps multiple devices aligned across on/off, brightness, etc. |
|
|
100
|
+
| **Rule** | Defines reactive logic with guards, waits, and control flow. |
|
|
101
|
+
| **Schedule** | Defines active time windows, reusable across rules. |
|
|
102
|
+
| **Tag** | Lightweight metadata stored in `input_text` helpers. |
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## 🔒 Loop Safety & Context Tracking
|
|
107
|
+
|
|
108
|
+
HASSL automatically writes the **parent context ID** into helper entities before performing actions.\
|
|
109
|
+
This ensures `not_by any_hassl` and `not_by rule("name")` guards work flawlessly, preventing infinite feedback.
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## 🕒 Schedules That Survive Restarts
|
|
114
|
+
|
|
115
|
+
All schedules are restart-safe:
|
|
116
|
+
|
|
117
|
+
- `input_boolean.hassl_schedule_<name>` automatically re-evaluates on startup.
|
|
118
|
+
- Triggers are set for both start and end times.
|
|
119
|
+
- Missed events (like mid-day restarts) are recovered automatically.
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## 📚 Documentation
|
|
124
|
+
|
|
125
|
+
For full grammar and detailed semantics, see the [HASSL Language Specification](./HASSL_Specification.md).
|
|
126
|
+
|
|
127
|
+
For a hands-on guide, check out the [Quickstart](./quickstart.md).
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## 🧩 Contributing
|
|
132
|
+
|
|
133
|
+
Contributions, tests, and ideas welcome!\
|
|
134
|
+
To run tests locally:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
pytest tests/
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Please open pull requests for grammar improvements, new device domains, or scheduling logic.
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## 📄 License
|
|
145
|
+
|
|
146
|
+
MIT License © 2025\
|
|
147
|
+
Created and maintained by [@adanowitz](https://github.com/adanowitz)
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
**HASSL** — simple, reliable, human-readable automations for Home Assistant.
|
|
152
|
+
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from dataclasses import dataclass, asdict, field
|
|
2
|
+
from typing import List, Any, Dict
|
|
3
|
+
|
|
4
|
+
@dataclass
|
|
5
|
+
class Alias:
|
|
6
|
+
name: str
|
|
7
|
+
entity: str
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class Sync:
|
|
11
|
+
kind: str
|
|
12
|
+
members: List[str]
|
|
13
|
+
name: str
|
|
14
|
+
invert: List[str] = field(default_factory=list)
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class IfClause:
|
|
18
|
+
condition: Dict[str, Any]
|
|
19
|
+
actions: List[Dict[str, Any]]
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class Rule:
|
|
23
|
+
name: str
|
|
24
|
+
clauses: List[IfClause]
|
|
25
|
+
|
|
26
|
+
@dataclass
|
|
27
|
+
class Program:
|
|
28
|
+
statements: List[object]
|
|
29
|
+
def to_dict(self):
|
|
30
|
+
def enc(x):
|
|
31
|
+
if isinstance(x, (Alias, Sync, Rule, IfClause)):
|
|
32
|
+
d = asdict(x); d["type"] = x.__class__.__name__; return d
|
|
33
|
+
return x
|
|
34
|
+
return {"type": "Program","statements": [enc(s) for s in self.statements]}
|
hassl-0.2.0/hassl/cli.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import os, json
|
|
3
|
+
from .parser.transform import HasslTransformer
|
|
4
|
+
from .ast.nodes import Program
|
|
5
|
+
from lark import Lark
|
|
6
|
+
from .semantics.analyzer import analyze
|
|
7
|
+
from .codegen.package import emit_package
|
|
8
|
+
from .codegen import generate as codegen_generate
|
|
9
|
+
|
|
10
|
+
GRAMMAR_PATH = os.path.join(os.path.dirname(__file__), "parser", "hassl.lark")
|
|
11
|
+
|
|
12
|
+
def parse_hassl(text: str) -> Program:
|
|
13
|
+
with open(GRAMMAR_PATH) as f:
|
|
14
|
+
grammar = f.read()
|
|
15
|
+
parser = Lark(grammar, start="start", parser="lalr", maybe_placeholders=False)
|
|
16
|
+
tree = parser.parse(text)
|
|
17
|
+
program = HasslTransformer().transform(tree)
|
|
18
|
+
return program
|
|
19
|
+
|
|
20
|
+
def main():
|
|
21
|
+
ap = argparse.ArgumentParser(prog="hasslc", description="HASSL Compiler")
|
|
22
|
+
ap.add_argument("input", help="Input .hassl file")
|
|
23
|
+
ap.add_argument("-o", "--out", default="./packages/out", help="Output directory for HA package")
|
|
24
|
+
args = ap.parse_args()
|
|
25
|
+
|
|
26
|
+
with open(args.input) as f:
|
|
27
|
+
src = f.read()
|
|
28
|
+
|
|
29
|
+
program = parse_hassl(src)
|
|
30
|
+
print("[hasslc] AST:", program.to_dict())
|
|
31
|
+
ir = analyze(program)
|
|
32
|
+
print("[hasslc] IR:", ir.to_dict())
|
|
33
|
+
|
|
34
|
+
ir_dict = ir.to_dict() if hasattr(ir, "to_dict") else ir
|
|
35
|
+
codegen_generate(ir_dict, args.out)
|
|
36
|
+
print(f"[hasslc] Package written to {args.out}")
|
|
37
|
+
|
|
38
|
+
os.makedirs(args.out, exist_ok=True)
|
|
39
|
+
emit_package(ir, args.out)
|
|
40
|
+
with open(os.path.join(args.out, "DEBUG_ir.json"), "w") as dbg:
|
|
41
|
+
dbg.write(json.dumps(ir.to_dict(), indent=2))
|
|
42
|
+
print(f"[hasslc] Package written to {args.out}")
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from .package import emit_package
|
|
3
|
+
from .rules_min import generate_rules
|
|
4
|
+
|
|
5
|
+
def generate(ir_obj, outdir):
|
|
6
|
+
"""
|
|
7
|
+
Orchestrate codegen in a merge-safe order:
|
|
8
|
+
1) emit_package: writes/merges helpers, scripts, and sync automations
|
|
9
|
+
2) generate_rules: writes rules automations & merges gate booleans into helpers.yaml
|
|
10
|
+
+ """
|
|
11
|
+
Path(outdir).mkdir(parents=True, exist_ok=True)
|
|
12
|
+
|
|
13
|
+
# 1) Sync & helpers first (merge-safe via yaml_emit._dump_yaml)
|
|
14
|
+
try:
|
|
15
|
+
emit_package(ir_obj if hasattr(ir_obj, "syncs") else ir_obj, outdir)
|
|
16
|
+
except Exception:
|
|
17
|
+
# keep going to still emit rules even if sync pass fails
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
# 2) Rules last (adds gate booleans; also merge-safe)
|
|
21
|
+
generate_rules(ir_obj if isinstance(ir_obj, dict) else getattr(ir_obj, "to_dict", lambda: ir_obj)(), outdir)
|
|
22
|
+
return True
|