wyreframe 0.1.0

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.
Files changed (117) hide show
  1. package/README.md +123 -0
  2. package/dist/index.d.ts +267 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +195 -0
  5. package/dist/index.js.map +1 -0
  6. package/package.json +63 -0
  7. package/src/parser/Core/Bounds.mjs +61 -0
  8. package/src/parser/Core/Bounds.res +65 -0
  9. package/src/parser/Core/Grid.mjs +268 -0
  10. package/src/parser/Core/Grid.res +265 -0
  11. package/src/parser/Core/Position.mjs +83 -0
  12. package/src/parser/Core/Position.res +54 -0
  13. package/src/parser/Core/Types.mjs +435 -0
  14. package/src/parser/Core/Types.res +331 -0
  15. package/src/parser/Core/__tests__/Bounds_test.mjs +326 -0
  16. package/src/parser/Core/__tests__/Bounds_test.res +412 -0
  17. package/src/parser/Core/__tests__/Grid_test.mjs +322 -0
  18. package/src/parser/Core/__tests__/Grid_test.res +319 -0
  19. package/src/parser/Core/__tests__/Types_test.mjs +614 -0
  20. package/src/parser/Core/__tests__/Types_test.res +650 -0
  21. package/src/parser/Detector/BoxTracer.mjs +302 -0
  22. package/src/parser/Detector/BoxTracer.res +374 -0
  23. package/src/parser/Detector/HierarchyBuilder.mjs +158 -0
  24. package/src/parser/Detector/HierarchyBuilder.res +315 -0
  25. package/src/parser/Detector/ShapeDetector.mjs +134 -0
  26. package/src/parser/Detector/ShapeDetector.res +236 -0
  27. package/src/parser/Detector/__tests__/BoxTracer_test.mjs +70 -0
  28. package/src/parser/Detector/__tests__/BoxTracer_test.res +92 -0
  29. package/src/parser/Detector/__tests__/HierarchyBuilder_test.mjs +489 -0
  30. package/src/parser/Detector/__tests__/HierarchyBuilder_test.res +849 -0
  31. package/src/parser/Detector/__tests__/ShapeDetector_test.mjs +377 -0
  32. package/src/parser/Detector/__tests__/ShapeDetector_test.res +563 -0
  33. package/src/parser/Errors/ErrorContext.mjs +106 -0
  34. package/src/parser/Errors/ErrorContext.res +191 -0
  35. package/src/parser/Errors/ErrorMessages.mjs +289 -0
  36. package/src/parser/Errors/ErrorMessages.res +303 -0
  37. package/src/parser/Errors/ErrorTypes.mjs +105 -0
  38. package/src/parser/Errors/ErrorTypes.res +169 -0
  39. package/src/parser/Interactions/InteractionMerger.mjs +266 -0
  40. package/src/parser/Interactions/InteractionMerger.res +450 -0
  41. package/src/parser/Interactions/InteractionParser.mjs +88 -0
  42. package/src/parser/Interactions/InteractionParser.res +127 -0
  43. package/src/parser/Interactions/SimpleInteractionParser.mjs +278 -0
  44. package/src/parser/Interactions/SimpleInteractionParser.res +262 -0
  45. package/src/parser/Interactions/__tests__/InteractionMerger_test.mjs +576 -0
  46. package/src/parser/Interactions/__tests__/InteractionMerger_test.res +646 -0
  47. package/src/parser/Parser.gen.tsx +96 -0
  48. package/src/parser/Parser.mjs +212 -0
  49. package/src/parser/Parser.res +481 -0
  50. package/src/parser/Scanner/__tests__/Grid_manual.mjs +214 -0
  51. package/src/parser/Scanner/__tests__/Grid_manual.res +141 -0
  52. package/src/parser/Semantic/ASTBuilder.mjs +197 -0
  53. package/src/parser/Semantic/ASTBuilder.res +288 -0
  54. package/src/parser/Semantic/AlignmentCalc.mjs +41 -0
  55. package/src/parser/Semantic/AlignmentCalc.res +104 -0
  56. package/src/parser/Semantic/Elements/ButtonParser.mjs +58 -0
  57. package/src/parser/Semantic/Elements/ButtonParser.res +131 -0
  58. package/src/parser/Semantic/Elements/CheckboxParser.mjs +58 -0
  59. package/src/parser/Semantic/Elements/CheckboxParser.res +79 -0
  60. package/src/parser/Semantic/Elements/CodeTextParser.mjs +50 -0
  61. package/src/parser/Semantic/Elements/CodeTextParser.res +111 -0
  62. package/src/parser/Semantic/Elements/ElementParser.mjs +15 -0
  63. package/src/parser/Semantic/Elements/ElementParser.res +83 -0
  64. package/src/parser/Semantic/Elements/EmphasisParser.mjs +46 -0
  65. package/src/parser/Semantic/Elements/EmphasisParser.res +67 -0
  66. package/src/parser/Semantic/Elements/InputParser.mjs +41 -0
  67. package/src/parser/Semantic/Elements/InputParser.res +97 -0
  68. package/src/parser/Semantic/Elements/LinkParser.mjs +60 -0
  69. package/src/parser/Semantic/Elements/LinkParser.res +156 -0
  70. package/src/parser/Semantic/Elements/TextParser.mjs +19 -0
  71. package/src/parser/Semantic/Elements/TextParser.res +42 -0
  72. package/src/parser/Semantic/Elements/__tests__/ButtonParser_test.mjs +189 -0
  73. package/src/parser/Semantic/Elements/__tests__/ButtonParser_test.res +257 -0
  74. package/src/parser/Semantic/Elements/__tests__/CheckboxParser_test.mjs +202 -0
  75. package/src/parser/Semantic/Elements/__tests__/CheckboxParser_test.res +250 -0
  76. package/src/parser/Semantic/Elements/__tests__/CodeTextParser_manual.mjs +293 -0
  77. package/src/parser/Semantic/Elements/__tests__/CodeTextParser_manual.res +134 -0
  78. package/src/parser/Semantic/Elements/__tests__/InputParser_test.mjs +253 -0
  79. package/src/parser/Semantic/Elements/__tests__/InputParser_test.res +304 -0
  80. package/src/parser/Semantic/Elements/__tests__/LinkParser_test.mjs +289 -0
  81. package/src/parser/Semantic/Elements/__tests__/LinkParser_test.res +402 -0
  82. package/src/parser/Semantic/Elements/__tests__/TextParser_test.mjs +149 -0
  83. package/src/parser/Semantic/Elements/__tests__/TextParser_test.res +167 -0
  84. package/src/parser/Semantic/ParserRegistry.mjs +82 -0
  85. package/src/parser/Semantic/ParserRegistry.res +145 -0
  86. package/src/parser/Semantic/SemanticParser.mjs +850 -0
  87. package/src/parser/Semantic/SemanticParser.res +1368 -0
  88. package/src/parser/Semantic/__tests__/ASTBuilder_test.mjs +187 -0
  89. package/src/parser/Semantic/__tests__/ASTBuilder_test.res +192 -0
  90. package/src/parser/Semantic/__tests__/ParserRegistry_test.mjs +154 -0
  91. package/src/parser/Semantic/__tests__/ParserRegistry_test.res +191 -0
  92. package/src/parser/Semantic/__tests__/SemanticParser_integration_test.mjs +768 -0
  93. package/src/parser/Semantic/__tests__/SemanticParser_integration_test.res +1069 -0
  94. package/src/parser/Semantic/__tests__/SemanticParser_manual.mjs +1329 -0
  95. package/src/parser/Semantic/__tests__/SemanticParser_manual.res +544 -0
  96. package/src/parser/TestMain.mjs +21 -0
  97. package/src/parser/TestMain.res +14 -0
  98. package/src/parser/TextExtractor.mjs +179 -0
  99. package/src/parser/TextExtractor.res +264 -0
  100. package/src/parser/__tests__/GridScanner_integration.test.mjs +632 -0
  101. package/src/parser/__tests__/GridScanner_integration.test.res +816 -0
  102. package/src/parser/__tests__/Performance.test.mjs +244 -0
  103. package/src/parser/__tests__/Performance.test.res +371 -0
  104. package/src/parser/__tests__/PerformanceFixtures.mjs +200 -0
  105. package/src/parser/__tests__/PerformanceFixtures.res +284 -0
  106. package/src/parser/__tests__/WyreframeParser_integration.test.mjs +770 -0
  107. package/src/parser/__tests__/WyreframeParser_integration.test.res +1008 -0
  108. package/src/parser/__tests__/fixtures/alignment-test.txt +9 -0
  109. package/src/parser/__tests__/fixtures/all-elements.txt +16 -0
  110. package/src/parser/__tests__/fixtures/login-scene.txt +17 -0
  111. package/src/parser/__tests__/fixtures/multi-scene.txt +25 -0
  112. package/src/parser/__tests__/fixtures/nested-boxes.txt +15 -0
  113. package/src/parser/__tests__/fixtures/simple-box.txt +5 -0
  114. package/src/parser/__tests__/fixtures/with-dividers.txt +14 -0
  115. package/src/renderer/Renderer.gen.tsx +32 -0
  116. package/src/renderer/Renderer.mjs +391 -0
  117. package/src/renderer/Renderer.res +558 -0
@@ -0,0 +1,262 @@
1
+ /**
2
+ * SimpleInteractionParser.res
3
+ *
4
+ * A simple line-based parser for the Interaction DSL.
5
+ * No external dependencies - pure ReScript implementation.
6
+ */
7
+
8
+ open Types
9
+
10
+ // Regex Patterns
11
+ let scenePattern = %re("/^@scene:\s*(\w+)\s*$/")
12
+ let inputSelectorPattern = %re("/^#(\w+):\s*$/")
13
+ let buttonSelectorPattern = %re("/^\[([^\]]+)\]:\s*$/")
14
+ let linkSelectorPattern = %re("/^\"([^\"]+)\":\s*$/")
15
+ let propertyPattern = %re("/^\s{2}(\w+):\s*(.+?)\s*$/")
16
+ let actionPattern = %re("/^\s{2}@(\w+)\s*->\s*(.+?)\s*$/")
17
+ let sceneSeparator = %re("/^---\s*$/")
18
+
19
+ let parseStringValue = (value: string): string => {
20
+ let trimmed = value->String.trim
21
+ if trimmed->String.startsWith("\"") && trimmed->String.endsWith("\"") {
22
+ trimmed->String.slice(~start=1, ~end=-1)
23
+ } else {
24
+ trimmed
25
+ }
26
+ }
27
+
28
+ let getCapture = (result: RegExp.Result.t, index: int): option<string> => {
29
+ result->RegExp.Result.matches->Array.get(index)
30
+ }
31
+
32
+ let parseActionExpr = (expr: string): option<interactionAction> => {
33
+ let gotoPattern = %re("/^goto\s*\(\s*(\w+)(?:\s*,\s*([\w-]+))?\s*\)$/")
34
+ let backPattern = %re("/^back\s*\(\s*\)$/")
35
+ let forwardPattern = %re("/^forward\s*\(\s*\)$/")
36
+
37
+ switch gotoPattern->RegExp.exec(expr) {
38
+ | Some(result) => {
39
+ let target = getCapture(result, 0)
40
+ let transition = getCapture(result, 1)->Option.getOr("fade")
41
+ switch target {
42
+ | Some(t) => Some(Goto({target: t, transition, condition: None}))
43
+ | None => None
44
+ }
45
+ }
46
+ | None => {
47
+ if backPattern->RegExp.test(expr) {
48
+ Some(Back)
49
+ } else if forwardPattern->RegExp.test(expr) {
50
+ Some(Forward)
51
+ } else {
52
+ let callPattern = %re("/^(\w+)\s*\(([^)]*)\)$/")
53
+ switch callPattern->RegExp.exec(expr) {
54
+ | Some(result) => {
55
+ let funcName = getCapture(result, 0)
56
+ let argsStr = getCapture(result, 1)->Option.getOr("")
57
+ switch funcName {
58
+ | Some(name) => {
59
+ let args = if argsStr->String.trim === "" {
60
+ []
61
+ } else {
62
+ argsStr->String.split(",")->Array.map(String.trim)
63
+ }
64
+ Some(Call({function: name, args, condition: None}))
65
+ }
66
+ | None => None
67
+ }
68
+ }
69
+ | None => None
70
+ }
71
+ }
72
+ }
73
+ }
74
+ }
75
+
76
+ let selectorToId = (selectorType: string, text: string): string => {
77
+ switch selectorType {
78
+ | "input" => text
79
+ | "button" | "link" => {
80
+ text
81
+ ->String.trim
82
+ ->String.toLowerCase
83
+ ->String.replaceRegExp(%re("/[^a-z0-9]+/g"), "-")
84
+ ->String.replaceRegExp(%re("/^-|-$/g"), "")
85
+ }
86
+ | _ => text
87
+ }
88
+ }
89
+
90
+ type parserState = {
91
+ mutable currentScene: option<string>,
92
+ mutable currentElementId: option<string>,
93
+ mutable currentProperties: Js.Dict.t<Js.Json.t>,
94
+ mutable currentActions: array<interactionAction>,
95
+ mutable sceneInteractions: array<sceneInteractions>,
96
+ mutable currentInteractions: array<interaction>,
97
+ }
98
+
99
+ let createState = (): parserState => {
100
+ currentScene: None,
101
+ currentElementId: None,
102
+ currentProperties: Js.Dict.empty(),
103
+ currentActions: [],
104
+ sceneInteractions: [],
105
+ currentInteractions: [],
106
+ }
107
+
108
+ let flushElement = (state: parserState): unit => {
109
+ switch state.currentElementId {
110
+ | Some(elementId) => {
111
+ let interaction: interaction = {
112
+ elementId,
113
+ properties: state.currentProperties,
114
+ actions: state.currentActions,
115
+ }
116
+ state.currentInteractions->Array.push(interaction)->ignore
117
+ state.currentElementId = None
118
+ state.currentProperties = Js.Dict.empty()
119
+ state.currentActions = []
120
+ }
121
+ | None => ()
122
+ }
123
+ }
124
+
125
+ let flushScene = (state: parserState): unit => {
126
+ flushElement(state)
127
+ switch state.currentScene {
128
+ | Some(sceneId) => {
129
+ if state.currentInteractions->Array.length > 0 {
130
+ let si: sceneInteractions = {
131
+ sceneId,
132
+ interactions: state.currentInteractions,
133
+ }
134
+ state.sceneInteractions->Array.push(si)->ignore
135
+ }
136
+ state.currentScene = None
137
+ state.currentInteractions = []
138
+ }
139
+ | None => ()
140
+ }
141
+ }
142
+
143
+ let parse = (input: string): result<array<sceneInteractions>, string> => {
144
+ let state = createState()
145
+ let lines = input->String.split("\n")
146
+ let lineNum = ref(0)
147
+ let error = ref(None)
148
+
149
+ lines->Array.forEach(line => {
150
+ lineNum := lineNum.contents + 1
151
+
152
+ if error.contents->Option.isSome {
153
+ ()
154
+ } else {
155
+ let trimmed = line->String.trim
156
+ if trimmed === "" || trimmed->String.startsWith("//") {
157
+ ()
158
+ } else if sceneSeparator->RegExp.test(trimmed) {
159
+ flushScene(state)
160
+ } else {
161
+ switch scenePattern->RegExp.exec(trimmed) {
162
+ | Some(result) => {
163
+ flushScene(state)
164
+ let matches = result->RegExp.Result.matches
165
+ switch matches->Array.get(0) {
166
+ | Some(sceneId) => state.currentScene = Some(sceneId)
167
+ | None => ()
168
+ }
169
+ }
170
+ | None => {
171
+ switch inputSelectorPattern->RegExp.exec(trimmed) {
172
+ | Some(result) => {
173
+ flushElement(state)
174
+ let matches = result->RegExp.Result.matches
175
+ switch matches->Array.get(0) {
176
+ | Some(id) => state.currentElementId = Some(id)
177
+ | None => ()
178
+ }
179
+ }
180
+ | None => {
181
+ switch buttonSelectorPattern->RegExp.exec(trimmed) {
182
+ | Some(result) => {
183
+ flushElement(state)
184
+ let matches = result->RegExp.Result.matches
185
+ switch matches->Array.get(0) {
186
+ | Some(text) => {
187
+ let id = selectorToId("button", text)
188
+ state.currentElementId = Some(id)
189
+ }
190
+ | None => ()
191
+ }
192
+ }
193
+ | None => {
194
+ switch linkSelectorPattern->RegExp.exec(trimmed) {
195
+ | Some(result) => {
196
+ flushElement(state)
197
+ let matches = result->RegExp.Result.matches
198
+ switch matches->Array.get(0) {
199
+ | Some(text) => {
200
+ let id = selectorToId("link", text)
201
+ state.currentElementId = Some(id)
202
+ }
203
+ | None => ()
204
+ }
205
+ }
206
+ | None => {
207
+ switch propertyPattern->RegExp.exec(line) {
208
+ | Some(result) => {
209
+ let matches = result->RegExp.Result.matches
210
+ let key = matches->Array.get(0)
211
+ let value = matches->Array.get(1)
212
+ switch (key, value, state.currentElementId) {
213
+ | (Some(k), Some(v), Some(_)) => {
214
+ let parsedValue = parseStringValue(v)
215
+ state.currentProperties->Js.Dict.set(k, Js.Json.string(parsedValue))
216
+ }
217
+ | _ => ()
218
+ }
219
+ }
220
+ | None => {
221
+ switch actionPattern->RegExp.exec(line) {
222
+ | Some(result) => {
223
+ let matches = result->RegExp.Result.matches
224
+ let _event = matches->Array.get(0)
225
+ let actionExpr = matches->Array.get(1)
226
+ switch (actionExpr, state.currentElementId) {
227
+ | (Some(expr), Some(_)) => {
228
+ switch parseActionExpr(expr) {
229
+ | Some(action) => {
230
+ state.currentActions->Array.push(action)->ignore
231
+ }
232
+ | None => {
233
+ error := Some(`Line ${Int.toString(lineNum.contents)}: Invalid action`)
234
+ }
235
+ }
236
+ }
237
+ | _ => ()
238
+ }
239
+ }
240
+ | None => ()
241
+ }
242
+ }
243
+ }
244
+ }
245
+ }
246
+ }
247
+ }
248
+ }
249
+ }
250
+ }
251
+ }
252
+ }
253
+ }
254
+ })
255
+
256
+ flushScene(state)
257
+
258
+ switch error.contents {
259
+ | Some(msg) => Error(msg)
260
+ | None => Ok(state.sceneInteractions)
261
+ }
262
+ }