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.
- package/README.md +123 -0
- package/dist/index.d.ts +267 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +195 -0
- package/dist/index.js.map +1 -0
- package/package.json +63 -0
- package/src/parser/Core/Bounds.mjs +61 -0
- package/src/parser/Core/Bounds.res +65 -0
- package/src/parser/Core/Grid.mjs +268 -0
- package/src/parser/Core/Grid.res +265 -0
- package/src/parser/Core/Position.mjs +83 -0
- package/src/parser/Core/Position.res +54 -0
- package/src/parser/Core/Types.mjs +435 -0
- package/src/parser/Core/Types.res +331 -0
- package/src/parser/Core/__tests__/Bounds_test.mjs +326 -0
- package/src/parser/Core/__tests__/Bounds_test.res +412 -0
- package/src/parser/Core/__tests__/Grid_test.mjs +322 -0
- package/src/parser/Core/__tests__/Grid_test.res +319 -0
- package/src/parser/Core/__tests__/Types_test.mjs +614 -0
- package/src/parser/Core/__tests__/Types_test.res +650 -0
- package/src/parser/Detector/BoxTracer.mjs +302 -0
- package/src/parser/Detector/BoxTracer.res +374 -0
- package/src/parser/Detector/HierarchyBuilder.mjs +158 -0
- package/src/parser/Detector/HierarchyBuilder.res +315 -0
- package/src/parser/Detector/ShapeDetector.mjs +134 -0
- package/src/parser/Detector/ShapeDetector.res +236 -0
- package/src/parser/Detector/__tests__/BoxTracer_test.mjs +70 -0
- package/src/parser/Detector/__tests__/BoxTracer_test.res +92 -0
- package/src/parser/Detector/__tests__/HierarchyBuilder_test.mjs +489 -0
- package/src/parser/Detector/__tests__/HierarchyBuilder_test.res +849 -0
- package/src/parser/Detector/__tests__/ShapeDetector_test.mjs +377 -0
- package/src/parser/Detector/__tests__/ShapeDetector_test.res +563 -0
- package/src/parser/Errors/ErrorContext.mjs +106 -0
- package/src/parser/Errors/ErrorContext.res +191 -0
- package/src/parser/Errors/ErrorMessages.mjs +289 -0
- package/src/parser/Errors/ErrorMessages.res +303 -0
- package/src/parser/Errors/ErrorTypes.mjs +105 -0
- package/src/parser/Errors/ErrorTypes.res +169 -0
- package/src/parser/Interactions/InteractionMerger.mjs +266 -0
- package/src/parser/Interactions/InteractionMerger.res +450 -0
- package/src/parser/Interactions/InteractionParser.mjs +88 -0
- package/src/parser/Interactions/InteractionParser.res +127 -0
- package/src/parser/Interactions/SimpleInteractionParser.mjs +278 -0
- package/src/parser/Interactions/SimpleInteractionParser.res +262 -0
- package/src/parser/Interactions/__tests__/InteractionMerger_test.mjs +576 -0
- package/src/parser/Interactions/__tests__/InteractionMerger_test.res +646 -0
- package/src/parser/Parser.gen.tsx +96 -0
- package/src/parser/Parser.mjs +212 -0
- package/src/parser/Parser.res +481 -0
- package/src/parser/Scanner/__tests__/Grid_manual.mjs +214 -0
- package/src/parser/Scanner/__tests__/Grid_manual.res +141 -0
- package/src/parser/Semantic/ASTBuilder.mjs +197 -0
- package/src/parser/Semantic/ASTBuilder.res +288 -0
- package/src/parser/Semantic/AlignmentCalc.mjs +41 -0
- package/src/parser/Semantic/AlignmentCalc.res +104 -0
- package/src/parser/Semantic/Elements/ButtonParser.mjs +58 -0
- package/src/parser/Semantic/Elements/ButtonParser.res +131 -0
- package/src/parser/Semantic/Elements/CheckboxParser.mjs +58 -0
- package/src/parser/Semantic/Elements/CheckboxParser.res +79 -0
- package/src/parser/Semantic/Elements/CodeTextParser.mjs +50 -0
- package/src/parser/Semantic/Elements/CodeTextParser.res +111 -0
- package/src/parser/Semantic/Elements/ElementParser.mjs +15 -0
- package/src/parser/Semantic/Elements/ElementParser.res +83 -0
- package/src/parser/Semantic/Elements/EmphasisParser.mjs +46 -0
- package/src/parser/Semantic/Elements/EmphasisParser.res +67 -0
- package/src/parser/Semantic/Elements/InputParser.mjs +41 -0
- package/src/parser/Semantic/Elements/InputParser.res +97 -0
- package/src/parser/Semantic/Elements/LinkParser.mjs +60 -0
- package/src/parser/Semantic/Elements/LinkParser.res +156 -0
- package/src/parser/Semantic/Elements/TextParser.mjs +19 -0
- package/src/parser/Semantic/Elements/TextParser.res +42 -0
- package/src/parser/Semantic/Elements/__tests__/ButtonParser_test.mjs +189 -0
- package/src/parser/Semantic/Elements/__tests__/ButtonParser_test.res +257 -0
- package/src/parser/Semantic/Elements/__tests__/CheckboxParser_test.mjs +202 -0
- package/src/parser/Semantic/Elements/__tests__/CheckboxParser_test.res +250 -0
- package/src/parser/Semantic/Elements/__tests__/CodeTextParser_manual.mjs +293 -0
- package/src/parser/Semantic/Elements/__tests__/CodeTextParser_manual.res +134 -0
- package/src/parser/Semantic/Elements/__tests__/InputParser_test.mjs +253 -0
- package/src/parser/Semantic/Elements/__tests__/InputParser_test.res +304 -0
- package/src/parser/Semantic/Elements/__tests__/LinkParser_test.mjs +289 -0
- package/src/parser/Semantic/Elements/__tests__/LinkParser_test.res +402 -0
- package/src/parser/Semantic/Elements/__tests__/TextParser_test.mjs +149 -0
- package/src/parser/Semantic/Elements/__tests__/TextParser_test.res +167 -0
- package/src/parser/Semantic/ParserRegistry.mjs +82 -0
- package/src/parser/Semantic/ParserRegistry.res +145 -0
- package/src/parser/Semantic/SemanticParser.mjs +850 -0
- package/src/parser/Semantic/SemanticParser.res +1368 -0
- package/src/parser/Semantic/__tests__/ASTBuilder_test.mjs +187 -0
- package/src/parser/Semantic/__tests__/ASTBuilder_test.res +192 -0
- package/src/parser/Semantic/__tests__/ParserRegistry_test.mjs +154 -0
- package/src/parser/Semantic/__tests__/ParserRegistry_test.res +191 -0
- package/src/parser/Semantic/__tests__/SemanticParser_integration_test.mjs +768 -0
- package/src/parser/Semantic/__tests__/SemanticParser_integration_test.res +1069 -0
- package/src/parser/Semantic/__tests__/SemanticParser_manual.mjs +1329 -0
- package/src/parser/Semantic/__tests__/SemanticParser_manual.res +544 -0
- package/src/parser/TestMain.mjs +21 -0
- package/src/parser/TestMain.res +14 -0
- package/src/parser/TextExtractor.mjs +179 -0
- package/src/parser/TextExtractor.res +264 -0
- package/src/parser/__tests__/GridScanner_integration.test.mjs +632 -0
- package/src/parser/__tests__/GridScanner_integration.test.res +816 -0
- package/src/parser/__tests__/Performance.test.mjs +244 -0
- package/src/parser/__tests__/Performance.test.res +371 -0
- package/src/parser/__tests__/PerformanceFixtures.mjs +200 -0
- package/src/parser/__tests__/PerformanceFixtures.res +284 -0
- package/src/parser/__tests__/WyreframeParser_integration.test.mjs +770 -0
- package/src/parser/__tests__/WyreframeParser_integration.test.res +1008 -0
- package/src/parser/__tests__/fixtures/alignment-test.txt +9 -0
- package/src/parser/__tests__/fixtures/all-elements.txt +16 -0
- package/src/parser/__tests__/fixtures/login-scene.txt +17 -0
- package/src/parser/__tests__/fixtures/multi-scene.txt +25 -0
- package/src/parser/__tests__/fixtures/nested-boxes.txt +15 -0
- package/src/parser/__tests__/fixtures/simple-box.txt +5 -0
- package/src/parser/__tests__/fixtures/with-dividers.txt +14 -0
- package/src/renderer/Renderer.gen.tsx +32 -0
- package/src/renderer/Renderer.mjs +391 -0
- 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
|
+
}
|