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,303 @@
1
+ // ErrorMessages.res
2
+ // Natural language error message templates for all parser error codes
3
+
4
+ // Import error types
5
+ open ErrorTypes
6
+
7
+ // Template type containing structured error message parts
8
+ type template = {
9
+ title: string,
10
+ message: string,
11
+ solution: string,
12
+ }
13
+
14
+ // Helper function to format position as "row X, column Y" (1-indexed for user display)
15
+ let formatPosition = (pos: Types.Position.t): string => {
16
+ `row ${Int.toString(pos.row + 1)}, column ${Int.toString(pos.col + 1)}`
17
+ }
18
+
19
+ // Helper function to format optional box name
20
+ let formatBoxName = (name: option<string>): string => {
21
+ switch name {
22
+ | Some(n) => `"${n}"`
23
+ | None => "unnamed box"
24
+ }
25
+ }
26
+
27
+ // Get error message template for a specific error code
28
+ let getTemplate = (code: errorCode): template => {
29
+ switch code {
30
+ // Structural Errors
31
+ | UncloseBox({corner, direction}) => {
32
+ title: "❌ Box is not closed",
33
+ message: `Box opened at ${formatPosition(corner)} but never closed on the ${direction} side.
34
+
35
+ The parser detected a '+' corner character that starts a box, but couldn't find the matching closing border. This usually happens when:
36
+ • The closing corner '+' is missing
37
+ • The border characters ('-' or '|') are broken or incomplete
38
+ • The box structure is malformed`,
39
+ solution: `💡 Solution:
40
+ Add the missing ${direction} border to close the box:
41
+ • If ${direction} is "top" or "bottom": use '+' corners and '-' characters
42
+ • If ${direction} is "left" or "right": use '+' corners and '|' characters
43
+ • Ensure all four corners are present and borders are continuous`,
44
+ }
45
+
46
+ | MismatchedWidth({topLeft, topWidth, bottomWidth}) => {
47
+ title: "❌ Box width mismatch",
48
+ message: `Box starting at ${formatPosition(topLeft)} has different widths on top and bottom borders:
49
+ • Top border: ${Int.toString(topWidth)} characters wide
50
+ • Bottom border: ${Int.toString(bottomWidth)} characters wide
51
+
52
+ All boxes must have matching top and bottom border widths to form a valid rectangle.`,
53
+ solution: `💡 Solution:
54
+ Make both borders the same width. Adjust the ${topWidth > bottomWidth ? "bottom" : "top"} border to match:
55
+ • Add ${Int.toString(Js.Math.abs_int(topWidth - bottomWidth))} ${topWidth > bottomWidth ? "dashes" : "dashes"} to make them equal
56
+ • Count carefully: include both corner '+' characters in the width
57
+ • Example: "+----+" is 6 characters wide (including corners)`,
58
+ }
59
+
60
+ | MisalignedPipe({position, expectedCol, actualCol}) => {
61
+ title: "❌ Vertical border misaligned",
62
+ message: `The '|' character at ${formatPosition(position)} is not aligned with the box edge:
63
+ • Expected column: ${Int.toString(expectedCol + 1)}
64
+ • Actual column: ${Int.toString(actualCol + 1)}
65
+ • Off by: ${Int.toString(Js.Math.abs_int(expectedCol - actualCol))} ${Js.Math.abs_int(expectedCol - actualCol) === 1 ? "space" : "spaces"}
66
+
67
+ Vertical borders must be perfectly aligned to form valid box sides.`,
68
+ solution: `💡 Solution:
69
+ Move the '|' character to column ${Int.toString(expectedCol + 1)}:
70
+ • ${expectedCol > actualCol ? "Add" : "Remove"} ${Int.toString(Js.Math.abs_int(expectedCol - actualCol))} space${Js.Math.abs_int(expectedCol - actualCol) === 1 ? "" : "s"} ${expectedCol > actualCol ? "before" : "after"} the '|' character
71
+ • Use a monospace font editor to ensure proper alignment
72
+ • Check that all '|' characters in this box are in the same column`,
73
+ }
74
+
75
+ | OverlappingBoxes({box1Name, box2Name, position}) => {
76
+ title: "❌ Overlapping boxes detected",
77
+ message: `Two boxes overlap at ${formatPosition(position)} but neither completely contains the other:
78
+ • Box 1: ${formatBoxName(box1Name)}
79
+ • Box 2: ${formatBoxName(box2Name)}
80
+
81
+ Boxes must either:
82
+ • Be completely nested (one fully inside the other), OR
83
+ • Be completely separate (no overlap at all)`,
84
+ solution: `💡 Solution:
85
+ Fix the overlap by either:
86
+ 1. Nesting: Make one box completely inside the other
87
+ • Ensure all four borders of the inner box are inside the outer box
88
+ 2. Separating: Move the boxes so they don't touch
89
+ • Add space between the boxes
90
+ • Or place them on different rows
91
+
92
+ Partial overlaps are not allowed in the wireframe syntax.`,
93
+ }
94
+
95
+ // Syntax Errors
96
+ | InvalidElement({content, position}) => {
97
+ title: "❌ Invalid element syntax",
98
+ message: `Unrecognized element syntax at ${formatPosition(position)}:
99
+ "${content}"
100
+
101
+ The parser couldn't match this content to any known element pattern:
102
+ • Buttons: [ Text ]
103
+ • Inputs: #fieldname
104
+ • Links: "Link Text"
105
+ • Checkboxes: [x] or [ ]
106
+ • Emphasis: * Text`,
107
+ solution: `💡 Solution:
108
+ Check the element syntax and fix any typos:
109
+ • Make sure brackets match: [ and ]
110
+ • Ensure quotes are paired: "text"
111
+ • Verify input fields start with #
112
+ • Use supported element patterns from the documentation
113
+
114
+ If this is plain text, it will be treated as a text element automatically.`,
115
+ }
116
+
117
+ | UnclosedBracket({opening}) => {
118
+ title: "❌ Unclosed bracket",
119
+ message: `Opening bracket '[' at ${formatPosition(opening)} is never closed.
120
+
121
+ This bracket starts a button or checkbox but has no matching closing ']' bracket.`,
122
+ solution: `💡 Solution:
123
+ Add the closing ']' bracket to complete the element:
124
+ • For buttons: [ Button Text ]
125
+ • For checkboxes: [x] or [ ]
126
+
127
+ Make sure both brackets are on the same line.`,
128
+ }
129
+
130
+ | EmptyButton({position}) => {
131
+ title: "❌ Empty button",
132
+ message: `Button at ${formatPosition(position)} has no text content.
133
+
134
+ Buttons must have descriptive text between the brackets: [ Text ]`,
135
+ solution: `💡 Solution:
136
+ Add text between the brackets:
137
+ • Bad: [ ]
138
+ • Good: [ Submit ]
139
+ • Good: [ Click Here ]
140
+
141
+ Button text should clearly describe the action.`,
142
+ }
143
+
144
+ | InvalidInteractionDSL({message, position}) => {
145
+ let posInfo = switch position {
146
+ | Some(pos) => ` at ${formatPosition(pos)}`
147
+ | None => ""
148
+ }
149
+
150
+ {
151
+ title: "❌ Invalid interaction syntax",
152
+ message: `Failed to parse interaction DSL${posInfo}:
153
+ ${message}
154
+
155
+ The interaction definition doesn't match the expected YAML-like syntax.`,
156
+ solution: `💡 Solution:
157
+ Check your interaction syntax:
158
+ • Scene declarations: @scene: sceneName
159
+ • Element selectors: #input: or [ button ]:
160
+ • Properties: indent with 2 spaces, use "key: value"
161
+ • Actions: @click -> goto(target)
162
+
163
+ Refer to the interaction DSL documentation for examples.`,
164
+ }
165
+ }
166
+
167
+ // Warnings
168
+ | UnusualSpacing({position, issue}) => {
169
+ title: "⚠️ Unusual spacing detected",
170
+ message: `Spacing issue at ${formatPosition(position)}:
171
+ ${issue}
172
+
173
+ While this may still parse correctly, it could cause alignment problems in some environments.`,
174
+ solution: `💡 Solution:
175
+ Normalize the spacing:
176
+ • Use spaces instead of tabs for alignment
177
+ • Use consistent spacing throughout the wireframe
178
+ • Use a monospace font to verify alignment visually
179
+
180
+ This is a warning - parsing will continue.`,
181
+ }
182
+
183
+ | DeepNesting({depth, position}) => {
184
+ title: "⚠️ Deep nesting detected",
185
+ message: `Box at ${formatPosition(position)} is nested ${Int.toString(depth)} levels deep.
186
+
187
+ While technically valid, deeply nested boxes can:
188
+ • Reduce readability
189
+ • Make maintenance difficult
190
+ • Indicate overly complex UI structure`,
191
+ solution: `💡 Solution:
192
+ Consider simplifying the structure:
193
+ • Flatten the hierarchy where possible
194
+ • Split complex sections into separate scenes
195
+ • Use dividers (===) instead of nested boxes for simple grouping
196
+ • Keep nesting to 3-4 levels maximum for best readability
197
+
198
+ This is a warning - parsing will continue.`,
199
+ }
200
+
201
+ | InvalidInput({message}) => {
202
+ title: "❌ Invalid input",
203
+ message: `${message}
204
+
205
+ The input could not be processed due to formatting or content issues.`,
206
+ solution: `💡 Solution:
207
+ Check your input format:
208
+ • Ensure the wireframe text is properly formatted
209
+ • Use ASCII characters for box drawing
210
+ • Check for encoding issues if pasting from external sources`,
211
+ }
212
+
213
+ | InvalidStartPosition(position) => {
214
+ title: "❌ Invalid starting position",
215
+ message: `Position ${formatPosition(position)} is not a valid corner for box tracing.
216
+
217
+ Box tracing must start from a '+' character that forms a valid corner.`,
218
+ solution: `💡 Solution:
219
+ Ensure the starting position:
220
+ • Contains a '+' character
221
+ • Is part of a complete box structure
222
+ • Has valid border characters adjacent to it`,
223
+ }
224
+ }
225
+ }
226
+
227
+ // Format complete error message from error code
228
+ let format = (code: errorCode): string => {
229
+ let template = getTemplate(code)
230
+
231
+ `${template.title}
232
+
233
+ ${template.message}
234
+
235
+ ${template.solution}`
236
+ }
237
+
238
+ // Format error message from ParseError type
239
+ let formatError = (error: t): string => {
240
+ format(error.code)
241
+ }
242
+
243
+ // Format complete error message from ParseError with code snippet context
244
+ // Includes: title, message, code snippet (if available), and solution
245
+ let formatWithContext = (error: t): string => {
246
+ let template = getTemplate(error.code)
247
+
248
+ // Build the formatted message parts
249
+ let parts = []
250
+
251
+ // 1. Title
252
+ parts->Js.Array2.push(template.title)->ignore
253
+ parts->Js.Array2.push("")->ignore
254
+
255
+ // 2. Message
256
+ parts->Js.Array2.push(template.message)->ignore
257
+ parts->Js.Array2.push("")->ignore
258
+
259
+ // 3. Code Snippet (if context is available)
260
+ switch error.context {
261
+ | Some(ctx) => {
262
+ let snippet = ErrorContext.getSnippet(ctx)
263
+ parts->Js.Array2.push("📍 Location:")->ignore
264
+ parts->Js.Array2.push("")->ignore
265
+ parts->Js.Array2.push(snippet)->ignore
266
+ parts->Js.Array2.push("")->ignore
267
+ }
268
+ | None => ()
269
+ }
270
+
271
+ // 4. Solution
272
+ parts->Js.Array2.push(template.solution)->ignore
273
+
274
+ // Join all parts with newlines
275
+ parts->Js.Array2.joinWith("\n")
276
+ }
277
+
278
+ // Format complete error message from ParseError type
279
+ // Uses formatWithContext if context is available, otherwise uses simple format
280
+ let formatComplete = (error: t): string => {
281
+ switch error.context {
282
+ | Some(_) => formatWithContext(error)
283
+ | None => formatError(error)
284
+ }
285
+ }
286
+
287
+ // Get just the title from an error code
288
+ let getTitle = (code: errorCode): string => {
289
+ let template = getTemplate(code)
290
+ template.title
291
+ }
292
+
293
+ // Get just the message from an error code
294
+ let getMessage = (code: errorCode): string => {
295
+ let template = getTemplate(code)
296
+ template.message
297
+ }
298
+
299
+ // Get just the solution from an error code
300
+ let getSolution = (code: errorCode): string => {
301
+ let template = getTemplate(code)
302
+ template.solution
303
+ }
@@ -0,0 +1,105 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Types from "../Core/Types.mjs";
4
+ import * as ErrorContext from "./ErrorContext.mjs";
5
+
6
+ function getSeverity(code) {
7
+ switch (code.TAG) {
8
+ case "UnusualSpacing" :
9
+ case "DeepNesting" :
10
+ return "Warning";
11
+ default:
12
+ return "Error";
13
+ }
14
+ }
15
+
16
+ function make(code, context) {
17
+ return {
18
+ code: code,
19
+ severity: getSeverity(code),
20
+ context: context
21
+ };
22
+ }
23
+
24
+ function makeSimple(code) {
25
+ return make(code, undefined);
26
+ }
27
+
28
+ function makeWithGrid(code, grid, position) {
29
+ let typesPosition = Types.Position.make(position.row, position.col);
30
+ let context = ErrorContext.make(grid, typesPosition, undefined);
31
+ return make(code, context);
32
+ }
33
+
34
+ function getPosition(code) {
35
+ switch (code.TAG) {
36
+ case "InvalidInput" :
37
+ return;
38
+ case "InvalidStartPosition" :
39
+ return code._0;
40
+ case "UncloseBox" :
41
+ return code.corner;
42
+ case "MismatchedWidth" :
43
+ return code.topLeft;
44
+ case "OverlappingBoxes" :
45
+ return code.position;
46
+ case "UnclosedBracket" :
47
+ return code.opening;
48
+ case "InvalidInteractionDSL" :
49
+ return code.position;
50
+ case "InvalidElement" :
51
+ case "DeepNesting" :
52
+ return code.position;
53
+ default:
54
+ return code.position;
55
+ }
56
+ }
57
+
58
+ function isWarning(error) {
59
+ return error.severity === "Warning";
60
+ }
61
+
62
+ function isError(error) {
63
+ return error.severity === "Error";
64
+ }
65
+
66
+ function getCodeName(code) {
67
+ switch (code.TAG) {
68
+ case "InvalidInput" :
69
+ return "InvalidInput";
70
+ case "InvalidStartPosition" :
71
+ return "InvalidStartPosition";
72
+ case "UncloseBox" :
73
+ return "UncloseBox";
74
+ case "MismatchedWidth" :
75
+ return "MismatchedWidth";
76
+ case "MisalignedPipe" :
77
+ return "MisalignedPipe";
78
+ case "OverlappingBoxes" :
79
+ return "OverlappingBoxes";
80
+ case "InvalidElement" :
81
+ return "InvalidElement";
82
+ case "UnclosedBracket" :
83
+ return "UnclosedBracket";
84
+ case "EmptyButton" :
85
+ return "EmptyButton";
86
+ case "InvalidInteractionDSL" :
87
+ return "InvalidInteractionDSL";
88
+ case "UnusualSpacing" :
89
+ return "UnusualSpacing";
90
+ case "DeepNesting" :
91
+ return "DeepNesting";
92
+ }
93
+ }
94
+
95
+ export {
96
+ getSeverity,
97
+ make,
98
+ makeSimple,
99
+ makeWithGrid,
100
+ getPosition,
101
+ isWarning,
102
+ isError,
103
+ getCodeName,
104
+ }
105
+ /* Types Not a pure module */
@@ -0,0 +1,169 @@
1
+ // ErrorTypes.res
2
+ // Structured error types for the Wyreframe parser
3
+ // Provides comprehensive error classification with contextual information
4
+
5
+ open Types
6
+
7
+ /**
8
+ * Error severity levels for categorizing parser errors
9
+ */
10
+ type severity =
11
+ | Error // Fatal errors that prevent parsing
12
+ | Warning // Non-fatal issues that should be addressed
13
+
14
+ /**
15
+ * Comprehensive error code variants for all parser stages
16
+ * Each variant carries specific context for helpful error messages
17
+ */
18
+ type errorCode =
19
+ // Grid Scanner Errors
20
+ | InvalidInput({message: string}) // Invalid input format
21
+ // Shape Detector Errors - Box Tracing (REQ-3, REQ-7)
22
+ | InvalidStartPosition(Position.t) // Starting position is not a corner
23
+ | UncloseBox({
24
+ corner: Position.t,
25
+ direction: string, // "top", "right", "bottom", or "left"
26
+ }) // Box edge not closed - consolidated variant
27
+ | MismatchedWidth({
28
+ topLeft: Position.t,
29
+ topWidth: int,
30
+ bottomWidth: int,
31
+ }) // Top and bottom widths don't match (REQ-7)
32
+ | MisalignedPipe({
33
+ position: Position.t,
34
+ expectedCol: int,
35
+ actualCol: int,
36
+ }) // Vertical pipe not aligned with box edge (REQ-7)
37
+ | OverlappingBoxes({
38
+ box1Name: option<string>,
39
+ box2Name: option<string>,
40
+ position: Position.t,
41
+ }) // Boxes overlap incorrectly (REQ-6)
42
+ // Semantic Parser Errors
43
+ | InvalidElement({
44
+ content: string,
45
+ position: Position.t,
46
+ }) // Unknown element syntax (REQ-19)
47
+ | UnclosedBracket({opening: Position.t}) // Bracket not closed (REQ-19)
48
+ | EmptyButton({position: Position.t}) // Button has no text (REQ-19)
49
+ | InvalidInteractionDSL({
50
+ message: string,
51
+ position: option<Position.t>,
52
+ }) // Interaction DSL parsing failed
53
+ // Warnings (REQ-19)
54
+ | UnusualSpacing({
55
+ position: Position.t,
56
+ issue: string,
57
+ }) // Tabs instead of spaces, etc.
58
+ | DeepNesting({
59
+ depth: int,
60
+ position: Position.t,
61
+ }) // Nesting depth exceeds recommended level
62
+
63
+ /**
64
+ * Complete parse error with error code, severity, and context
65
+ */
66
+ type t = {
67
+ code: errorCode,
68
+ severity: severity,
69
+ context: option<ErrorContext.t>,
70
+ }
71
+
72
+ /**
73
+ * Determine severity from error code
74
+ * Warnings start with "Unusual" or "Deep", all others are Errors
75
+ */
76
+ let getSeverity = (code: errorCode): severity => {
77
+ switch code {
78
+ | UnusualSpacing(_) | DeepNesting(_) => Warning
79
+ | _ => Error
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Create a ParseError from an error code
85
+ * Automatically determines severity based on error type
86
+ */
87
+ let make = (code: errorCode, context: option<ErrorContext.t>): t => {
88
+ {
89
+ code: code,
90
+ severity: getSeverity(code),
91
+ context: context,
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Create a ParseError without context (simple errors)
97
+ * Useful when grid context is not available or needed
98
+ * REQ-16: Structured error objects
99
+ */
100
+ let makeSimple = (code: errorCode): t => {
101
+ make(code, None)
102
+ }
103
+
104
+ /**
105
+ * Create a ParseError from an error code with grid context
106
+ * Builds error context from grid and position
107
+ * REQ-18: Contextual code snippets
108
+ */
109
+ let makeWithGrid = (code: errorCode, grid: Grid.t, position: Position.t): t => {
110
+ let typesPosition = Types.Position.make(position.row, position.col)
111
+ let context = ErrorContext.make(grid, typesPosition)
112
+ make(code, Some(context))
113
+ }
114
+
115
+ /**
116
+ * Get position from error code if available
117
+ * Returns the primary position associated with the error
118
+ */
119
+ let getPosition = (code: errorCode): option<Position.t> => {
120
+ switch code {
121
+ | InvalidInput(_) => None
122
+ | InvalidStartPosition(pos) => Some(pos)
123
+ | UncloseBox({corner}) => Some(corner)
124
+ | MismatchedWidth({topLeft}) => Some(topLeft)
125
+ | MisalignedPipe({position}) => Some(position)
126
+ | OverlappingBoxes({position}) => Some(position)
127
+ | InvalidElement({position}) => Some(position)
128
+ | UnclosedBracket({opening}) => Some(opening)
129
+ | EmptyButton({position}) => Some(position)
130
+ | InvalidInteractionDSL({position}) => position
131
+ | UnusualSpacing({position}) => Some(position)
132
+ | DeepNesting({position}) => Some(position)
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Check if error is a warning
138
+ */
139
+ let isWarning = (error: t): bool => {
140
+ error.severity == Warning
141
+ }
142
+
143
+ /**
144
+ * Check if error is an error (not a warning)
145
+ */
146
+ let isError = (error: t): bool => {
147
+ error.severity == Error
148
+ }
149
+
150
+ /**
151
+ * Get error code name as string (useful for logging/debugging)
152
+ * REQ-16: Error codes for different types of problems
153
+ */
154
+ let getCodeName = (code: errorCode): string => {
155
+ switch code {
156
+ | InvalidInput(_) => "InvalidInput"
157
+ | InvalidStartPosition(_) => "InvalidStartPosition"
158
+ | UncloseBox(_) => "UncloseBox"
159
+ | MismatchedWidth(_) => "MismatchedWidth"
160
+ | MisalignedPipe(_) => "MisalignedPipe"
161
+ | OverlappingBoxes(_) => "OverlappingBoxes"
162
+ | InvalidElement(_) => "InvalidElement"
163
+ | UnclosedBracket(_) => "UnclosedBracket"
164
+ | EmptyButton(_) => "EmptyButton"
165
+ | InvalidInteractionDSL(_) => "InvalidInteractionDSL"
166
+ | UnusualSpacing(_) => "UnusualSpacing"
167
+ | DeepNesting(_) => "DeepNesting"
168
+ }
169
+ }