storm-cli 1.0.0__py3-none-any.whl

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.
src/tokenizer.py ADDED
@@ -0,0 +1,238 @@
1
+ import unicodedata
2
+
3
+ from src.tok import Tok
4
+ from src.pos import Pos
5
+ from src.tok_type import TokenType
6
+ from src.keyword import Keyword
7
+ from src.error_handler import raise_error
8
+
9
+
10
+ class Tokenizer:
11
+ def __init__(self, fpath: str, fdata: str):
12
+ self.file_path = fpath
13
+ self.file_data = fdata
14
+ self.pos = 0
15
+ self.line = 1
16
+ self.column = 1
17
+
18
+ self.keywords = {k.value for k in Keyword}
19
+
20
+ self._two_char = {
21
+ '++', '--', '==', '!=', '<=', '>=', '&&', '||',
22
+ '<<', '>>', '->', '+=', '-=', '*=', '/=', '%=',
23
+ '&=', '|=', '^=', '##',
24
+ }
25
+
26
+ self._three_char = {'<<=', '>>='}
27
+
28
+ def _pos(self) -> Pos:
29
+ return Pos(self.line, self.column)
30
+
31
+ def _advance(self) -> str:
32
+ ch = self.file_data[self.pos]
33
+ if ch == '\n':
34
+ self.line += 1
35
+ self.column = 1
36
+ else:
37
+ self.column += 1
38
+ self.pos += 1
39
+ return ch
40
+
41
+ def _peek(self, n: int = 0) -> str:
42
+ idx = self.pos + n
43
+ if idx < len(self.file_data):
44
+ return self.file_data[idx]
45
+ return ''
46
+
47
+ def _skip_whitespace(self):
48
+ while self.pos < len(self.file_data):
49
+ ch = self.file_data[self.pos]
50
+ if ch in ' \t\r\n':
51
+ self._advance()
52
+ else:
53
+ break
54
+
55
+ def _skip_line_comment(self):
56
+ while self.pos < len(self.file_data) and self.file_data[self.pos] != '\n':
57
+ self._advance()
58
+
59
+ def _skip_block_comment(self):
60
+ self._advance()
61
+ self._advance()
62
+ while self.pos < len(self.file_data):
63
+ if self.file_data[self.pos] == '*' and self._peek(1) == '/':
64
+ self._advance()
65
+ self._advance()
66
+ return
67
+ self._advance()
68
+ raise_error(self.file_path, self.file_data, 'unterminated block comment', self._pos())
69
+
70
+ def _read_string(self, quote: str) -> Tok:
71
+ start = self._pos()
72
+ self._advance()
73
+ chars = []
74
+ while self.pos < len(self.file_data):
75
+ ch = self.file_data[self.pos]
76
+ if ch == '\\':
77
+ self._advance()
78
+ if self.pos >= len(self.file_data):
79
+ raise_error(self.file_path, self.file_data, 'unterminated string', start)
80
+ esc = self._advance()
81
+ if esc == 'n':
82
+ chars.append('\n')
83
+ elif esc == 't':
84
+ chars.append('\t')
85
+ elif esc == 'r':
86
+ chars.append('\r')
87
+ elif esc == '0':
88
+ chars.append('\0')
89
+ elif esc == '\\':
90
+ chars.append('\\')
91
+ elif esc == '\'':
92
+ chars.append('\'')
93
+ elif esc == '"':
94
+ chars.append('"')
95
+ elif esc == 'x':
96
+ hex_digits = []
97
+ while self.pos < len(self.file_data):
98
+ c = self.file_data[self.pos]
99
+ if c.isdigit() or c.lower() in 'abcdef':
100
+ hex_digits.append(self._advance())
101
+ else:
102
+ break
103
+ if hex_digits:
104
+ chars.append(chr(int(''.join(hex_digits), 16)))
105
+ else:
106
+ chars.append(esc)
107
+ elif ch == quote:
108
+ self._advance()
109
+ return Tok(TokenType.STRING, ''.join(chars), start)
110
+ elif ch == '\n':
111
+ raise_error(self.file_path, self.file_data, 'unterminated string', start)
112
+ else:
113
+ chars.append(self._advance())
114
+ raise_error(self.file_path, self.file_data, 'unterminated string', start)
115
+
116
+ def _read_number(self) -> Tok:
117
+ start = self._pos()
118
+ result = [self._advance()]
119
+
120
+ if result[0] == '0' and self.pos < len(self.file_data):
121
+ nxt = self.file_data[self.pos].lower()
122
+ if nxt == 'x':
123
+ result.append(self._advance())
124
+ if self.pos >= len(self.file_data) or not (self.file_data[self.pos].isdigit() or self.file_data[self.pos].lower() in 'abcdef'):
125
+ raise_error(self.file_path, self.file_data, 'invalid hex literal', start)
126
+ while self.pos < len(self.file_data):
127
+ c = self.file_data[self.pos]
128
+ if c.isdigit() or c.lower() in 'abcdef':
129
+ result.append(self._advance())
130
+ else:
131
+ break
132
+ return Tok(TokenType.HEX, ''.join(result), start)
133
+ elif nxt == 'o':
134
+ result.append(self._advance())
135
+ if self.pos >= len(self.file_data) or self.file_data[self.pos] not in '01234567':
136
+ raise_error(self.file_path, self.file_data, 'invalid octal literal', start)
137
+ while self.pos < len(self.file_data):
138
+ c = self.file_data[self.pos]
139
+ if c in '01234567':
140
+ result.append(self._advance())
141
+ else:
142
+ break
143
+ return Tok(TokenType.OCT, ''.join(result), start)
144
+ elif nxt == 'b':
145
+ result.append(self._advance())
146
+ if self.pos >= len(self.file_data) or self.file_data[self.pos] not in '01':
147
+ raise_error(self.file_path, self.file_data, 'invalid binary literal', start)
148
+ while self.pos < len(self.file_data):
149
+ c = self.file_data[self.pos]
150
+ if c in '01':
151
+ result.append(self._advance())
152
+ else:
153
+ break
154
+ return Tok(TokenType.BIN, ''.join(result), start)
155
+
156
+ while self.pos < len(self.file_data):
157
+ c = self.file_data[self.pos]
158
+ if c.isdigit():
159
+ result.append(self._advance())
160
+ else:
161
+ break
162
+ return Tok(TokenType.INT, ''.join(result), start)
163
+
164
+ def _read_identifier(self) -> Tok:
165
+ start = self._pos()
166
+ result = []
167
+ while self.pos < len(self.file_data):
168
+ ch = self.file_data[self.pos]
169
+ cat = unicodedata.category(ch)
170
+ if not result:
171
+ if ch == '_' or ch.isalpha() or cat.startswith('L'):
172
+ result.append(self._advance())
173
+ else:
174
+ break
175
+ else:
176
+ if ch == '_' or ch.isalnum():
177
+ result.append(self._advance())
178
+ elif cat in ('Nd', 'Pc', 'Mn', 'Mc'):
179
+ result.append(self._advance())
180
+ else:
181
+ break
182
+ word = ''.join(result)
183
+ if word in self.keywords:
184
+ return Tok(TokenType.KEYWORD, word, start)
185
+ return Tok(TokenType.IDENTIFIER, word, start)
186
+
187
+ def _read_symbol(self) -> Tok:
188
+ start = self._pos()
189
+
190
+ if self._peek(2) and self.file_data[self.pos:self.pos + 3] in self._three_char:
191
+ val = self.file_data[self.pos:self.pos + 3]
192
+ self._advance()
193
+ self._advance()
194
+ self._advance()
195
+ return Tok(TokenType.SYMBOL, val, start)
196
+
197
+ if self._peek(1) and self.file_data[self.pos:self.pos + 2] in self._two_char:
198
+ val = self.file_data[self.pos:self.pos + 2]
199
+ self._advance()
200
+ self._advance()
201
+ return Tok(TokenType.SYMBOL, val, start)
202
+
203
+ ch = self._advance()
204
+ if ch == '.' and self._peek() == '.':
205
+ raise_error(self.file_path, self.file_data, 'unexpected token', start)
206
+ return Tok(TokenType.SYMBOL, ch, start)
207
+
208
+ def nextToken(self) -> Tok:
209
+ while self.pos < len(self.file_data):
210
+ self._skip_whitespace()
211
+ if self.pos >= len(self.file_data):
212
+ break
213
+
214
+ ch = self.file_data[self.pos]
215
+
216
+ if ch == '/' and self._peek(1) == '/':
217
+ self._skip_line_comment()
218
+ continue
219
+
220
+ if ch == '/' and self._peek(1) == '*':
221
+ self._skip_block_comment()
222
+ continue
223
+
224
+ if ch in '"\'':
225
+ return self._read_string(ch)
226
+
227
+ if ch.isdigit():
228
+ return self._read_number()
229
+
230
+ if ch == '_' or ch.isalpha() or unicodedata.category(ch).startswith('L'):
231
+ return self._read_identifier()
232
+
233
+ if ch in '+-*/%&|^~!<>=.,;:{}[]()?#\\':
234
+ return self._read_symbol()
235
+
236
+ raise_error(self.file_path, self.file_data, f"unexpected character '{ch}'", self._pos())
237
+
238
+ return Tok(TokenType.EOF, '', self._pos())
@@ -0,0 +1,350 @@
1
+ Metadata-Version: 2.4
2
+ Name: storm-cli
3
+ Version: 1.0.0
4
+ Summary: Declarative API scaffold: define your data model in .storm format and generate complete C# (.NET) or PHP (Laravel) backends with zero boilerplate
5
+ Author-email: Philipp Andrew Redondo <hollishake@pm.me>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/HolliShake/storm-cli
8
+ Project-URL: Repository, https://github.com/HolliShake/storm-cli
9
+ Project-URL: Issues, https://github.com/HolliShake/storm-cli/issues
10
+ Keywords: api,scaffold,code-generation,dotnet,csharp,laravel,php,webapi,rest,cli,storm
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Programming Language :: Python :: 3.14
21
+ Classifier: Topic :: Software Development :: Code Generators
22
+ Classifier: Topic :: Software Development :: Build Tools
23
+ Requires-Python: >=3.10
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Requires-Dist: colorama>=0.4.0
27
+ Dynamic: license-file
28
+
29
+ <p align="center">
30
+ <img src="https://raw.githubusercontent.com/twitter/twemoji/master/assets/svg/1f4a5.svg" width="64" height="64" alt="" />
31
+ </p>
32
+
33
+ <h1 align="center">⚡ API Starter Kit</h1>
34
+ <p align="center"><strong>Declare your data model once. Generate everything.</strong></p>
35
+
36
+ <p align="center">
37
+ <img src="https://img.shields.io/badge/python-3.10+-blue.svg" alt="Python 3.10+" />
38
+ <img src="https://img.shields.io/badge/tests-148%20passed-brightgreen.svg" alt="148 tests passed" />
39
+ <img src="https://img.shields.io/badge/license-MIT-green.svg" alt="MIT License" />
40
+ <img src="https://img.shields.io/badge/templates-.NET%20%7C%20Laravel-purple.svg" alt="Templates" />
41
+ </p>
42
+
43
+ ---
44
+
45
+ A CLI tool that scaffolds full‑stack API projects from a **declarative schema file**.
46
+ Write your data model in `.storm` format and get complete, production‑ready backend code — controllers, services, models, DTOs, migrations, OpenAPI annotations — in **C# (.NET)** or **PHP (Laravel 12+)** with zero boilerplate.
47
+
48
+ ---
49
+
50
+ ## 📦 Quick Start
51
+
52
+ ```bash
53
+ # 1. Install Python dependencies
54
+ pip install -r requirements.txt
55
+
56
+ # 2. Scaffold a project (pick your template)
57
+ python main.py --init --template dotnet-csharp --name MyApi # .NET
58
+ python main.py --init --template laravel-php # Laravel
59
+
60
+ # 3. Edit schema.storm to define your data model
61
+
62
+ # 4. Generate all source files
63
+ python main.py --generate
64
+ ```
65
+
66
+ ---
67
+
68
+ ## 🧭 Commands
69
+
70
+ | Flag | Alias | Description |
71
+ |------|-------|-------------|
72
+ | `--init` | `-i` | Scaffold a new project from a template |
73
+ | `--generate` | `-g` | Regenerate all source files from `schema.storm` |
74
+ | `--template` | `-t` | Template: `dotnet-csharp`, `dotnet-csharp-clean-architecture`, or `laravel-php` |
75
+ | `--name` | `-n` | Project name (defaults to current folder name) |
76
+
77
+ ---
78
+
79
+ ## 🏗️ Templates
80
+
81
+ | Template | Stack | ORM | Auth |
82
+ |----------|-------|-----|------|
83
+ | `dotnet-csharp` | ASP.NET Core Web API | EF Core (SQL Server) | ASP.NET Identity |
84
+ | `dotnet-csharp-clean-architecture` | Clean Architecture (Domain → Application → Infrastructure → API) | EF Core (SQL Server) | ASP.NET Identity |
85
+ | `laravel-php` | Laravel 13+ · PHP 8.5+ | Eloquent | — (pkg ready) |
86
+
87
+ ---
88
+
89
+ ## 📐 Schema Language (`.storm`)
90
+
91
+ Define your entire data model in a clean, readable syntax — one file, no ceremony.
92
+
93
+ ```storm
94
+ // ═══ Enums ═══════════════════════════════════════════════════════════════
95
+
96
+ enum Status {
97
+ Active = "active",
98
+ Inactive = "inactive",
99
+ Pending = "pending"
100
+ }
101
+
102
+ enum Role {
103
+ Admin = "admin",
104
+ User = "user"
105
+ }
106
+
107
+ // ═══ Tables ═════════════════════════════════════════════════════════════
108
+
109
+ table User {
110
+ id: uuid pk;
111
+ name: string(min=2, max=100);
112
+ email: string(max=255) unique;
113
+ password: string(max=255);
114
+ role: Role;
115
+ status: Status;
116
+ createdAt: datetime;
117
+ updatedAt: datetime;
118
+ }
119
+
120
+ table Product {
121
+ id: int? pk;
122
+ name: string(min=1, max=200);
123
+ price: double(min=0) = 0;
124
+ stock: int(min=0) = 0;
125
+ status: Status;
126
+ ownerId: uuid;
127
+ owner: User; // ← FK: BelongsTo User
128
+ createdAt: datetime;
129
+ updatedAt: datetime;
130
+ }
131
+ ```
132
+
133
+ ### 🔤 Type System
134
+
135
+ | Storm Type | C# | PHP / OA | Migration | Notes |
136
+ |------------|-----|----------|-----------|-------|
137
+ | `int` | `int` | `integer` | `integer` | Supports `pk`, `unique`, `min`, `max` |
138
+ | `long` | `long` | `integer` | `bigInteger` | |
139
+ | `float` | `float` | `number` | `float` | |
140
+ | `double` | `double` | `number` | `double` | |
141
+ | `string` | `string` | `string` | `string` | Supports `min`, `max` |
142
+ | `bool` | `bool` | `boolean` | `boolean` | |
143
+ | `uuid` | `Guid` | `string` | `uuid` | |
144
+ | `datetime` | `DateTime` | `string` (date‑time) | `dateTime` | |
145
+
146
+ - Append `?` for nullable: `int?`, `uuid?`
147
+ - Reference another table → **foreign key** (BelongsTo / HasMany generated automatically)
148
+ - Reference an enum → typed enum column with casts
149
+ - Default values support **constant expressions**: `price:double = 0`, `qty:int = 5 * 4`
150
+
151
+ ### 🧮 Expressions
152
+
153
+ Constant expressions in default values are evaluated at parse time:
154
+
155
+ | Expression | Result |
156
+ |------------|--------|
157
+ | `5 * 4` | `20` |
158
+ | `(1 + 2) * 3` | `9` |
159
+ | `-5` | `-5` |
160
+ | `!true` | `false` |
161
+
162
+ ---
163
+
164
+ ## 🎯 Generated Artifacts
165
+
166
+ ### .NET (`dotnet-csharp` / clean‑architecture)
167
+
168
+ | File | Path | Description |
169
+ |------|------|-------------|
170
+ | `{Entity}.cs` | `Models/` | Entity class (User extends `IdentityUser`) |
171
+ | `{Entity}RequestDto.cs` | `Dtos/` | Create / update payload |
172
+ | `{Entity}ResponseDto.cs` | `Dtos/` | Full API response |
173
+ | `{Entity}ResponseSimplifiedDto.cs` | `Dtos/` | Lightweight response (no FK expansion) |
174
+ | `I{Entity}Service.cs` | `IServices/` | Interface: CRUD + FK + Enum pagination |
175
+ | `{Entity}Service.cs` | `Services/` | EF Core + AutoMapper implementation |
176
+ | `{Entity}Controller.cs` | `Controllers/` | REST API with Swagger + Scalar annotations |
177
+ | `{Entity}MappingProfile.cs` | `Mappers/` | AutoMapper profile |
178
+ | `Program.cs` | `/` | App bootstrap: Identity, DbContext, AutoMapper, Scrutor, Scalar |
179
+ | `AppDbContext.cs` | `Data/` | EF Core context with `DbSet<T>` |
180
+
181
+ Shared base files (once):
182
+
183
+ | File | Description |
184
+ |------|-------------|
185
+ | `IGenericService.cs` | Generic CRUD interface |
186
+ | `GenericService.cs` | Generic EF Core implementation |
187
+ | `GenericController.cs` | Generic REST controller |
188
+ | `PaginatedResult.cs` | Pagination DTO + extension |
189
+ | `PaginateQuery.cs` | Query parameters: `page`, `rows` |
190
+
191
+ **Clean Architecture** maps the same files into namespace‑aware paths:
192
+ `Domain/`, `Application/`, `Infrastructure/`, `API/`.
193
+
194
+ ### Laravel PHP
195
+
196
+ | File | Path | Description |
197
+ |------|------|-------------|
198
+ | `{Entity}.php` | `app/Models/` | Eloquent model with `#[OA\Schema]` annotations |
199
+ | `{Entity}Controller.php` | `app/Controllers/` | Full CRUD with `#[OA\Get/Post/Put/Delete]` |
200
+ | `{Entity}Service.php` | `app/Services/` | Pagination, CRUD, FK & enum filters, validation rules |
201
+ | `{Enum}.php` | `app/Static/` | PHP 8.1+ backed enum with `#[OA\Schema]` |
202
+ | `Controller.php` | `app/Controllers/` | Base controller with `ok()`, `notFound()` + shared OA schemas |
203
+ | `*_create_*_table.php` | `database/migrations/` | Schema migration with FK cascades |
204
+ | `api.php` | `routes/` | All RESTful routes including FK & enum filter endpoints |
205
+
206
+ ---
207
+
208
+ ## 🔗 Auto‑Generated Endpoints
209
+
210
+ For a `Product` table with FK `owner:User` and enum `status:Status`:
211
+
212
+ ### C#
213
+ | Method | Route | Description |
214
+ |--------|-------|-------------|
215
+ | `GET` | `/api/Product` | Paginated list (search, filter, page, rows) |
216
+ | `GET` | `/api/Product/{id}` | Single item by ID |
217
+ | `GET` | `/api/Product/user/{ownerId}` | Paginated list filtered by User |
218
+ | `GET` | `/api/Product/status/{status}` | Paginated list filtered by Status |
219
+ | `POST` | `/api/Product` | Create |
220
+ | `PUT` | `/api/Product/{id}` | Update |
221
+ | `DELETE` | `/api/Product/{id}` | Delete |
222
+
223
+ ### PHP
224
+ | Method | Route | Description |
225
+ |--------|-------|-------------|
226
+ | `GET` | `/api/product` | Paginated list |
227
+ | `GET` | `/api/product/{id}` | Single item |
228
+ | `GET` | `/api/product/user/{ownerId}` | Filtered by User |
229
+ | `GET` | `/api/product/status/{status}` | Filtered by Status |
230
+ | `POST` | `/api/product/create` | Create |
231
+ | `PUT` | `/api/product/update/{id}` | Update |
232
+ | `DELETE` | `/api/product/delete/{id}` | Delete |
233
+
234
+ ---
235
+
236
+ ## 📁 Project Structure
237
+
238
+ ```
239
+ apistarterkit/
240
+ ├── main.py # CLI entry point
241
+ ├── storm.config.json # Project config (auto‑generated)
242
+ ├── schema.storm # Your data model (edit this!)
243
+ ├── requirements.txt # Python deps
244
+
245
+ ├── src/ # Core engine
246
+ │ ├── interpreter.py # Code generator (C# + PHP)
247
+ │ ├── parser.py # Recursive‑descent parser
248
+ │ ├── tokenizer.py # Lexer
249
+ │ ├── ast.py # AST node types
250
+ │ ├── enum.py # Enum AST handler
251
+ │ ├── table.py # Table AST handler
252
+ │ ├── column.py # Column type system
253
+ │ ├── template.py # Template enum
254
+ │ ├── error_handler.py # Formatted error reporting
255
+ │ ├── keyword.py # Language keywords
256
+ │ ├── tok_type.py # Token type enum
257
+ │ ├── tok.py # Token class
258
+ │ ├── pos.py # Position tracking
259
+ │ ├── generic_controller_csharp.py
260
+ │ ├── generic_service_csharp.py
261
+ │ ├── generic_mapper_csharp.py
262
+ │ ├── generic_pagination_csharp.py
263
+ │ └── generic_query_chsarp.py
264
+
265
+ ├── tests/ # Test suite (148 tests)
266
+ │ ├── conftest.py
267
+ │ ├── test_codegen.py # Template output tests (70)
268
+ │ ├── test_parser.py # Parser correctness (33)
269
+ │ ├── test_tokenizer.py # Lexer correctness (26)
270
+ │ ├── test_error_handler.py # Error formatting (5)
271
+ │ └── test_integration.py # End‑to‑end parse (7)
272
+
273
+ └── pyvenv/ # Python virtual environment
274
+ ```
275
+
276
+ ---
277
+
278
+ ## 📦 Dependencies
279
+
280
+ ### C# (.NET)
281
+
282
+ Auto‑installed during `--init`:
283
+
284
+ | Package | Purpose |
285
+ |---------|---------|
286
+ | `Microsoft.EntityFrameworkCore` | ORM |
287
+ | `Microsoft.EntityFrameworkCore.SqlServer` | SQL Server provider |
288
+ | `Microsoft.EntityFrameworkCore.Design` | Migrations |
289
+ | `Microsoft.AspNetCore.Identity.EntityFrameworkCore` | ASP.NET Identity |
290
+ | `AutoMapper` | Object mapping |
291
+ | `Scrutor` | Assembly scanning / DI |
292
+ | `Scalar.AspNetCore` | OpenAPI UI |
293
+
294
+ ### PHP (Laravel)
295
+
296
+ Auto‑installed during `--init`:
297
+
298
+ | Package | Purpose |
299
+ |---------|---------|
300
+ | `laravel/framework` | Core framework (`^13.0`) |
301
+ | `zircote/swagger-php` | OpenAPI attribute annotations |
302
+ | `spatie/laravel-query-builder` | Eloquent query builder |
303
+
304
+ ---
305
+
306
+ ## ✅ Requirements
307
+
308
+ | Dependency | Version | Required For |
309
+ |------------|---------|--------------|
310
+ | Python | 3.10+ | CLI runtime (all templates) |
311
+ | .NET SDK | 8.0+ | `dotnet-csharp`, `dotnet-csharp-clean-architecture` |
312
+ | EF Core CLI | `dotnet-ef` | `dotnet-csharp`, `dotnet-csharp-clean-architecture` |
313
+ | PHP | 8.5+ | `laravel-php` |
314
+ | Composer | — | `laravel-php` |
315
+
316
+ ---
317
+
318
+ ## 🧪 Testing
319
+
320
+ ```bash
321
+ # Full suite (148 tests)
322
+ python -m pytest tests/ -q
323
+
324
+ # Code‑generation only
325
+ python -m pytest tests/test_codegen.py -v
326
+
327
+ # With coverage
328
+ python -m pytest tests/ --cov=src
329
+ ```
330
+
331
+ ---
332
+
333
+ ## ⚙️ Workflow
334
+
335
+ ```mermaid
336
+ flowchart LR
337
+ A["<b>schema.storm</b><br/>Declarative schema"] --> B("<b>main.py</b><br/>Storm CLI")
338
+ B --> C["<b>Parser</b><br/>AST + validation"]
339
+ C --> D["<b>Interpreter</b><br/>Dependency graph"]
340
+ D --> E{Template}
341
+ E -->|dotnet-csharp| F["<b>C#/.NET</b><br/>Models, DTOs, Services<br/>Controllers, Mappers<br/>DbContext, Program.cs"]
342
+ E -->|clean-arch| G["<b>C# Clean Arch</b><br/>Domain / Application<br/>Infrastructure / API"]
343
+ E -->|laravel-php| H["<b>PHP / Laravel</b><br/>Models + OA Schemas<br/>Controllers + OA Routes<br/>Services, Migrations<br/>Routes"]
344
+ ```
345
+
346
+ ---
347
+
348
+ ## 📄 License
349
+
350
+ MIT — see [LICENSE](./LICENSE).
@@ -0,0 +1,26 @@
1
+ main.py,sha256=pniCMQLb0o8Ysg8eajvGFAqkaMqbnw08Y6gFNCRQShw,27687
2
+ src/__init__.py,sha256=mpDvbK-NQ8BFC5jxvsOwgO7Yiv981iEBawgakO8V1nA,106
3
+ src/ast.py,sha256=gh2LZ-ftNidmQ9xTfWXo9TY5fhD-CvXpZ4DmOfQ4ZIk,2759
4
+ src/column.py,sha256=rotMxxjFnJJwfH-2z65v4MV0Z_RI4PMG6MST28nfG_U,1661
5
+ src/enum.py,sha256=s9vuYEO3ymC4jK7XSDm125nrSTfrGzOi6EinZyRRBo4,428
6
+ src/error_handler.py,sha256=7xDWk4OTM-wls7GE_rA4FrAwx7Cbe5XC3gaOTPLiKbA,1073
7
+ src/generic_controller_csharp.py,sha256=PAaOIxAifdOtpuk4jWH-q54Zi_zCv9QuD9wx9hFqyfs,3778
8
+ src/generic_mapper_csharp.py,sha256=9ffZDqBSBj1l_NvJiW3CrLVh5vHb4oig-agceI84R6Y,769
9
+ src/generic_pagination_csharp.py,sha256=BspY1HSDqYdWJAHFec0GYXBVif2of-iUqhijcqY9eag,1151
10
+ src/generic_query_chsarp.py,sha256=0oBJpj0n5jJrPwuGvaDAmUZpqSfD7y1i9ROvbq6gKlU,245
11
+ src/generic_service_csharp.py,sha256=mP-W0_qxXd2D3yh21nuXujN_AOpCvoDmPjcsLhfV1MU,3439
12
+ src/interpreter.py,sha256=GKsXDV69J-a2MEoTO-Dens2xSwBk2hbIVjGVykX6VwM,90689
13
+ src/keyword.py,sha256=U8Rtbhp0rPleUFG-M6ZNQpMEs6P63mqQqdo4sHKC_Tg,317
14
+ src/parser.py,sha256=jlJrNl-WomFB7RgYbiR5nzLQbrAL4dazlUWEtAcJ_3g,17528
15
+ src/pos.py,sha256=nT8xHkcLotHZOIkcA3DXtPMzL3j1SUd9iC0HNhUUZnY,193
16
+ src/table.py,sha256=Q1rIc6NqRuniPQCPRSdq3N3l2PmJvvPFnjp2iJEzppI,1044
17
+ src/template.py,sha256=fUXoRlstCakmE-qBmXeF8rxjUTuOdDfhBqCuyBxUuEw,248
18
+ src/tok.py,sha256=rQEDwC0PpjZk3jfVsx-czVInoeachpXwOBW3symVbvE,333
19
+ src/tok_type.py,sha256=DDd7PEvLK7smxHBtjcD09B86rtBAZCle703mXd7mME0,225
20
+ src/tokenizer.py,sha256=6SwzB8Vpm9TAufrr6fuVbvM8YCAvMWUBskByXGE1Mb8,8755
21
+ storm_cli-1.0.0.dist-info/licenses/LICENSE,sha256=SKXpqq-gsi9MLS_Esr3EurgrXMSRpJ-souhlBwSvGH0,1100
22
+ storm_cli-1.0.0.dist-info/METADATA,sha256=RMBdFpVVN8Hwh9FqT9Pw8X_GARVt5lobLw-qjKc5h78,13166
23
+ storm_cli-1.0.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
24
+ storm_cli-1.0.0.dist-info/entry_points.txt,sha256=FklyLtoCjIpodgWpT2LW99CzgKnXqQKlncyVeLgHjkQ,36
25
+ storm_cli-1.0.0.dist-info/top_level.txt,sha256=7dQR-Fa4VIhfnQo3p1CubPBIGo7YRYTo2kAuwuj_HuI,9
26
+ storm_cli-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ storm = main:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Philipp Andrew Redondo
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.
@@ -0,0 +1,2 @@
1
+ main
2
+ src