@rip-lang/schema 0.2.1

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.
package/grammar.rip ADDED
@@ -0,0 +1,504 @@
1
+ # ==============================================================================
2
+ # Schema Grammar - S-Expression Output for Rip Schema Definitions
3
+ #
4
+ # A unified schema language for types, validation, database models, and UI.
5
+ # Inspired by SPOT/Sage, rip-schema, TypeScript, and Zod.
6
+ #
7
+ # Author: Steve Shreeve <steve.shreeve@gmail.com>
8
+ # Date: January 2026
9
+ #
10
+ # Key syntax:
11
+ # @enum Role: admin, user, guest
12
+ # @type Address
13
+ # @model User
14
+ # name! string, [1, 100] # ! = required, [min, max]
15
+ # email!# email # # = unique
16
+ # role? Role, [user] # ? = optional, [default]
17
+ # ==============================================================================
18
+
19
+ o = (pattern, action, options) ->
20
+ pattern = pattern.trim().replace /\s{2,}/g, ' '
21
+ [pattern, action ?? 1, options]
22
+
23
+ mode = 'sexp'
24
+
25
+ grammar =
26
+
27
+ # ============================================================================
28
+ # Top-Level Structure
29
+ # ============================================================================
30
+
31
+ Schema: [
32
+ o '' , '["schema"]'
33
+ o 'Definitions', '["schema", ...1]'
34
+ ]
35
+
36
+ Definitions: [
37
+ o 'Definition' , '[1]'
38
+ o 'Definitions TERMINATOR Definition' , '[...1, 3]'
39
+ o 'Definitions TERMINATOR'
40
+ ]
41
+
42
+ Definition: [
43
+ o 'Import'
44
+ o 'EnumDef'
45
+ o 'TypeDef'
46
+ o 'ModelDef'
47
+ o 'MixinDef'
48
+ o 'WidgetDef'
49
+ o 'FormDef'
50
+ o 'StateDef'
51
+ ]
52
+
53
+ # ============================================================================
54
+ # Import Statements
55
+ # ============================================================================
56
+
57
+ Import: [
58
+ o 'IMPORT STRING', '["import", 2]'
59
+ ]
60
+
61
+ # ============================================================================
62
+ # Helper: Optional Terminator before INDENT
63
+ # ============================================================================
64
+
65
+ Block: [
66
+ o 'INDENT Body OUTDENT' , 2
67
+ o 'TERMINATOR INDENT Body OUTDENT' , 3
68
+ ]
69
+
70
+ # ============================================================================
71
+ # Enum Definitions
72
+ # ============================================================================
73
+
74
+ # @enum Role: admin, user, guest
75
+ # @enum Status
76
+ # pending: 0
77
+ # active: 1
78
+
79
+ EnumDef: [
80
+ o 'ENUM Identifier : EnumValues' , '["enum", 2, 4]'
81
+ o 'ENUM Identifier INDENT EnumMembers OUTDENT' , '["enum", 2, 4]'
82
+ o 'ENUM Identifier TERMINATOR INDENT EnumMembers OUTDENT', '["enum", 2, 5]'
83
+ ]
84
+
85
+ EnumValues: [
86
+ o 'Identifier' , '[1]'
87
+ o 'EnumValues , Identifier' , '[...1, 3]'
88
+ ]
89
+
90
+ EnumMembers: [
91
+ o 'EnumMember' , '[1]'
92
+ o 'EnumMembers TERMINATOR EnumMember' , '[...1, 3]'
93
+ o 'EnumMembers TERMINATOR'
94
+ ]
95
+
96
+ EnumMember: [
97
+ o 'Identifier : Literal', '[1, 3]'
98
+ ]
99
+
100
+ # ============================================================================
101
+ # Type Definitions (reusable structures, no DB)
102
+ # ============================================================================
103
+
104
+ TypeDef: [
105
+ o 'TYPE Identifier Block' , '["type", 2, null, 3]'
106
+ o 'TYPE Identifier : Identifier Block' , '["type", 2, 4, 5]'
107
+ ]
108
+
109
+ # ============================================================================
110
+ # Model Definitions (database-backed entities)
111
+ # ============================================================================
112
+
113
+ ModelDef: [
114
+ o 'MODEL Identifier Block' , '["model", 2, null, 3]'
115
+ o 'MODEL Identifier : Identifier Block' , '["model", 2, 4, 5]'
116
+ ]
117
+
118
+ # ============================================================================
119
+ # Mixin Definitions (reusable field groups)
120
+ # ============================================================================
121
+
122
+ MixinDef: [
123
+ o 'MIXIN Identifier Block', '["mixin", 2, 3]'
124
+ ]
125
+
126
+ # ============================================================================
127
+ # Widget Definitions (UI components)
128
+ # ============================================================================
129
+
130
+ WidgetDef: [
131
+ o 'WIDGET Identifier Block' , '["widget", 2, null, 3]'
132
+ o 'WIDGET Identifier : Identifier Block' , '["widget", 2, 4, 5]'
133
+ ]
134
+
135
+ # ============================================================================
136
+ # Form Definitions (model + layout)
137
+ # ============================================================================
138
+
139
+ FormDef: [
140
+ o 'FORM Identifier : Identifier Block' , '["form", 2, 4, 5]'
141
+ ]
142
+
143
+ # ============================================================================
144
+ # State Definitions (reactive state)
145
+ # ============================================================================
146
+
147
+ StateDef: [
148
+ o 'STATE Identifier Block', '["state", 2, 3]'
149
+ ]
150
+
151
+ # ============================================================================
152
+ # Body - List of members (fields, directives, etc.)
153
+ # ============================================================================
154
+
155
+ Body: [
156
+ o 'Member' , '[1]'
157
+ o 'Body TERMINATOR Member' , '[...1, 3]'
158
+ o 'Body TERMINATOR'
159
+ ]
160
+
161
+ Member: [
162
+ o 'Field'
163
+ o 'Directive'
164
+ o 'Relation'
165
+ o 'IndexDef'
166
+ o 'ComputedBlock'
167
+ o 'ValidateBlock'
168
+ o 'EventsDef'
169
+ o 'ActionsBlock'
170
+ o 'FormField'
171
+ ]
172
+
173
+ # ============================================================================
174
+ # Field Definitions
175
+ # ============================================================================
176
+
177
+ # Syntax: name[!#?] type[, constraints][, attributes]
178
+ #
179
+ # Examples:
180
+ # name! string Required string
181
+ # email!# email Required + unique
182
+ # bio? text, [0, 1000] Optional with max length
183
+ # role Role, [user] Enum with default
184
+ # items Item[] Array type
185
+ # data json, [{}] JSON with default
186
+ # phone string, { mask: "###" } With UI attributes
187
+
188
+ # Field definition - handles both regular fields and computed fields
189
+ Field: [
190
+ # Regular field: name type
191
+ o 'IDENTIFIER FieldType' , '["field", 1, [], 2, null, null]'
192
+ o 'IDENTIFIER FieldType , Constraints' , '["field", 1, [], 2, 4, null]'
193
+ o 'IDENTIFIER FieldType , Attrs' , '["field", 1, [], 2, null, 4]'
194
+ o 'IDENTIFIER FieldType , Constraints , Attrs' , '["field", 1, [], 2, 4, 6]'
195
+ # Field with modifiers: name! type, name!# type, etc.
196
+ o 'IDENTIFIER FieldModifiers FieldType' , '["field", 1, 2, 3, null, null]'
197
+ o 'IDENTIFIER FieldModifiers FieldType , Constraints' , '["field", 1, 2, 3, 5, null]'
198
+ o 'IDENTIFIER FieldModifiers FieldType , Attrs' , '["field", 1, 2, 3, null, 5]'
199
+ o 'IDENTIFIER FieldModifiers FieldType , Constraints , Attrs' , '["field", 1, 2, 3, 5, 7]'
200
+ # Computed field: name -> expr
201
+ o 'IDENTIFIER ArrowFunc' , '["computed", 1, 2]'
202
+ ]
203
+
204
+ FieldModifiers: [
205
+ o '!' , '["!"]'
206
+ o '#' , '["#"]'
207
+ o '?' , '["?"]'
208
+ o 'FieldModifiers !' , '[...1, "!"]'
209
+ o 'FieldModifiers #' , '[...1, "#"]'
210
+ o 'FieldModifiers ?' , '[...1, "?"]'
211
+ ]
212
+
213
+ # Field type (supports arrays via [])
214
+ FieldType: [
215
+ o 'IDENTIFIER' # string, email, User, etc.
216
+ o 'IDENTIFIER [ ]' , '["array", 1]'
217
+ ]
218
+
219
+ # Constraints in brackets: [min, max], [default], [min, max, default]
220
+ Constraints: [
221
+ o '[ ConstraintList ]', 2
222
+ ]
223
+
224
+ ConstraintList: [
225
+ o 'ConstraintValue' , '[1]'
226
+ o 'ConstraintList , ConstraintValue' , '[...1, 3]'
227
+ ]
228
+
229
+ ConstraintValue: [
230
+ o 'Literal'
231
+ o 'IDENTIFIER' # Enum values like [user]
232
+ o 'Object'
233
+ o 'Array'
234
+ o 'Regex'
235
+ o 'ArrowFunc'
236
+ ]
237
+
238
+ # Attributes in braces (for UI hints)
239
+ Attrs: [
240
+ o 'Object'
241
+ ]
242
+
243
+ # ============================================================================
244
+ # Directives (@timestamps, @softDelete, etc.)
245
+ # ============================================================================
246
+
247
+ Directive: [
248
+ o 'TIMESTAMPS' , '["timestamps"]'
249
+ o 'SOFT_DELETE' , '["softDelete"]'
250
+ o 'INCLUDE IDENTIFIER' , '["include", 2]'
251
+ o 'PATTERN Regex' , '["pattern", 2]'
252
+ ]
253
+
254
+ # ============================================================================
255
+ # Relationships
256
+ # ============================================================================
257
+
258
+ Relation: [
259
+ o 'BELONGS_TO IDENTIFIER' , '["belongs_to", 2, null]'
260
+ o 'BELONGS_TO IDENTIFIER , RelationOpts' , '["belongs_to", 2, 4]'
261
+ o 'HAS_ONE IDENTIFIER' , '["has_one", 2, null]'
262
+ o 'HAS_ONE IDENTIFIER , RelationOpts' , '["has_one", 2, 4]'
263
+ o 'HAS_MANY IDENTIFIER' , '["has_many", 2, null]'
264
+ o 'HAS_MANY IDENTIFIER , RelationOpts' , '["has_many", 2, 4]'
265
+ o 'ONE IDENTIFIER' , '["has_one", 2, null]'
266
+ o 'ONE IDENTIFIER , RelationOpts' , '["has_one", 2, 4]'
267
+ o 'MANY IDENTIFIER' , '["has_many", 2, null]'
268
+ o 'MANY IDENTIFIER , RelationOpts' , '["has_many", 2, 4]'
269
+ o 'LINK STRING , IDENTIFIER' , '["link", 2, 4, null]'
270
+ o 'LINK STRING , IDENTIFIER , RelationOpts' , '["link", 2, 4, 6]'
271
+ ]
272
+
273
+ RelationOpts: [
274
+ o 'Object'
275
+ ]
276
+
277
+ # ============================================================================
278
+ # Indexes
279
+ # ============================================================================
280
+
281
+ IndexDef: [
282
+ o 'INDEX IDENTIFIER' , '["index", [2], false]'
283
+ o 'INDEX IDENTIFIER #' , '["index", [2], true]'
284
+ o 'INDEX [ IndexFields ]' , '["index", 3, false]'
285
+ o 'INDEX [ IndexFields ] #' , '["index", 3, true]'
286
+ ]
287
+
288
+ IndexFields: [
289
+ o 'IDENTIFIER' , '[1]'
290
+ o 'IndexFields , IDENTIFIER' , '[...1, 3]'
291
+ ]
292
+
293
+ # ============================================================================
294
+ # Computed Fields Block
295
+ # ============================================================================
296
+
297
+ ComputedBlock: [
298
+ o 'COMPUTED Block', '["computed", 2]'
299
+ ]
300
+
301
+ # ============================================================================
302
+ # Validation Block
303
+ # ============================================================================
304
+
305
+ ValidateBlock: [
306
+ o 'VALIDATE Block', '["validate", 2]'
307
+ ]
308
+
309
+ # ============================================================================
310
+ # Events (for widgets)
311
+ # ============================================================================
312
+
313
+ EventsDef: [
314
+ o 'EVENTS EventList', '["events", 2]'
315
+ ]
316
+
317
+ EventList: [
318
+ o 'IDENTIFIER' , '[1]'
319
+ o 'EventList , IDENTIFIER' , '[...1, 3]'
320
+ ]
321
+
322
+ # ============================================================================
323
+ # Actions Block (for forms/state)
324
+ # ============================================================================
325
+
326
+ ActionsBlock: [
327
+ o 'ACTIONS Block', '["actions", 2]'
328
+ ]
329
+
330
+ # ============================================================================
331
+ # Form Fields (field with placement)
332
+ # ============================================================================
333
+
334
+ FormField: [
335
+ o 'IDENTIFIER Object', '["placement", 1, 2]'
336
+ ]
337
+
338
+ # ============================================================================
339
+ # Literals and Values
340
+ # ============================================================================
341
+
342
+ Identifier: [
343
+ o 'IDENTIFIER'
344
+ ]
345
+
346
+ Literal: [
347
+ o 'NUMBER'
348
+ o 'STRING'
349
+ o 'BOOL'
350
+ o 'NULL'
351
+ o 'UNDEFINED'
352
+ ]
353
+
354
+ Regex: [
355
+ o 'REGEX'
356
+ ]
357
+
358
+ # ============================================================================
359
+ # Object Literals: { key: value, ... }
360
+ # ============================================================================
361
+
362
+ Object: [
363
+ o '{ }' , '["object"]'
364
+ o '{ ObjectEntries }' , '["object", ...2]'
365
+ ]
366
+
367
+ ObjectEntries: [
368
+ o 'ObjectEntry' , '[1]'
369
+ o 'ObjectEntries , ObjectEntry' , '[...1, 3]'
370
+ o 'ObjectEntries OptComma TERMINATOR ObjectEntry' , '[...1, 4]'
371
+ o 'ObjectEntries OptComma TERMINATOR'
372
+ o 'INDENT ObjectEntries OptComma OUTDENT' , 2
373
+ ]
374
+
375
+ ObjectEntry: [
376
+ o 'IDENTIFIER' , '[1, 1]' # Shorthand { x }
377
+ o 'IDENTIFIER : Value' , '[1, 3]'
378
+ o 'STRING : Value' , '[1, 3]'
379
+ o '... IDENTIFIER' , '["...", 2]' # Spread
380
+ ]
381
+
382
+ OptComma: [
383
+ o ''
384
+ o ','
385
+ ]
386
+
387
+ # ============================================================================
388
+ # Array Literals: [a, b, c]
389
+ # ============================================================================
390
+
391
+ Array: [
392
+ o '[ ]' , '["array"]'
393
+ o '[ ArrayItems ]' , '["array", ...2]'
394
+ ]
395
+
396
+ ArrayItems: [
397
+ o 'Value' , '[1]'
398
+ o 'ArrayItems , Value' , '[...1, 3]'
399
+ o 'ArrayItems OptComma TERMINATOR Value', '[...1, 4]'
400
+ o 'ArrayItems OptComma TERMINATOR'
401
+ ]
402
+
403
+ # ============================================================================
404
+ # Arrow Functions: -> expr or (params) -> expr
405
+ # ============================================================================
406
+
407
+ ArrowFunc: [
408
+ o '-> Value' , '["->", [], 2]'
409
+ o '-> INDENT Value OUTDENT' , '["->", [], 3]'
410
+ o '( Params ) -> Value' , '["->", 2, 5]'
411
+ o '( Params ) -> INDENT Value OUTDENT' , '["->", 2, 6]'
412
+ ]
413
+
414
+ Params: [
415
+ o '' , '[]'
416
+ o 'IDENTIFIER' , '[1]'
417
+ o 'Params , IDENTIFIER' , '[...1, 3]'
418
+ ]
419
+
420
+ # ============================================================================
421
+ # Values (simplified expressions for schemas)
422
+ # ============================================================================
423
+
424
+ # Value is what can appear in constraints, defaults, computed fields, etc.
425
+ # This is intentionally simpler than full expressions to reduce conflicts.
426
+
427
+ Value: [
428
+ o 'Primary'
429
+ o 'Value . IDENTIFIER' , '[".", 1, 3]' # Property access
430
+ o 'Value ?. IDENTIFIER' , '["?.", 1, 3]' # Optional chaining
431
+ o 'Value [ Value ]' , '["[]", 1, 3]' # Index access
432
+ o 'Value ( )' , '["call", 1, []]' # Function call
433
+ o 'Value ( Args )' , '["call", 1, 3]'
434
+ o 'Value BinOp Value' , '[2, 1, 3]' # Binary operators
435
+ o 'UnaryOp Value' , '[1, 2]', prec: 'UNARY'
436
+ o '( Value )' , 2 # Grouping
437
+ ]
438
+
439
+ Primary: [
440
+ o 'Literal'
441
+ o 'IDENTIFIER'
442
+ o 'Object'
443
+ o 'Array'
444
+ o 'Regex'
445
+ o 'ArrowFunc'
446
+ o '@ IDENTIFIER' , '[".", "this", 2]' # @field → this.field
447
+ o '@' , '"this"' # @ → this
448
+ ]
449
+
450
+ Args: [
451
+ o 'Value' , '[1]'
452
+ o 'Args , Value' , '[...1, 3]'
453
+ ]
454
+
455
+ # Binary operators (single rule to reduce states)
456
+ BinOp: [
457
+ o '+' , '"+"'
458
+ o '-' , '"-"'
459
+ o '*' , '"*"'
460
+ o '/' , '"/"'
461
+ o '==' , '"=="'
462
+ o '!=' , '"!="'
463
+ o '<' , '"<"'
464
+ o '>' , '">"'
465
+ o '<=' , '"<="'
466
+ o '>=' , '">="'
467
+ o '&&' , '"&&"'
468
+ o '||' , '"||"'
469
+ o '??' , '"??"'
470
+ o 'IS' , '"is"'
471
+ o 'ISNT', '"isnt"'
472
+ ]
473
+
474
+ # Unary operators
475
+ UnaryOp: [
476
+ o 'NOT' , '"not"'
477
+ o '!' , '"!"'
478
+ o '-' , '"-"'
479
+ ]
480
+
481
+ # ==============================================================================
482
+ # Operator Precedence (low to high - reversed for solar)
483
+ # ==============================================================================
484
+
485
+ operators = """
486
+ right ,
487
+ right :
488
+ right ->
489
+ left ??
490
+ left ||
491
+ left &&
492
+ left == != IS ISNT
493
+ left < > <= >=
494
+ left + -
495
+ left * /
496
+ right UNARY NOT
497
+ left . ?. [ ] ( )
498
+ """.trim().split('\n').reverse().map (line) -> line.trim().split /\s+/
499
+
500
+ # ==============================================================================
501
+ # Export
502
+ # ==============================================================================
503
+
504
+ export default {mode, grammar, operators}
package/index.js ADDED
@@ -0,0 +1,39 @@
1
+ // ==============================================================================
2
+ // @rip-lang/schema — Unified schema language
3
+ //
4
+ // One definition → TypeScript types + SQL DDL + runtime validation + ORM models
5
+ //
6
+ // Code generation:
7
+ // import { Schema } from '@rip-lang/schema'
8
+ // const schema = Schema.load('app.schema')
9
+ // const ts = schema.toTypes()
10
+ // const sql = schema.toSQL()
11
+ //
12
+ // ORM (with database):
13
+ // import { Schema } from '@rip-lang/schema/orm'
14
+ // const schema = Schema.load('app.schema')
15
+ // schema.connect('http://localhost:4213')
16
+ // const User = schema.model('User', { ... })
17
+ //
18
+ // Author: Steve Shreeve <steve.shreeve@gmail.com>
19
+ // Date: January 2026
20
+ // ==============================================================================
21
+
22
+ import { parse as _rawParse, parser, Parser } from './parser.js'
23
+ import { formatParseError } from './errors.js'
24
+
25
+ export { parser, Parser }
26
+ export { SchemaLexer } from './lexer.js'
27
+ export { Schema } from './runtime.js'
28
+ export { generateTypes } from './emit-types.js'
29
+ export { generateSQL } from './emit-sql.js'
30
+ export { generateZod } from './emit-zod.js'
31
+ export { formatParseError } from './errors.js'
32
+
33
+ export function parse(source, filename) {
34
+ try {
35
+ return _rawParse(source)
36
+ } catch (err) {
37
+ throw formatParseError(err, source, filename)
38
+ }
39
+ }