diff --git a/docs/kcl/std.json b/docs/kcl/std.json index 170794122..9c4230194 100644 --- a/docs/kcl/std.json +++ b/docs/kcl/std.json @@ -744,8 +744,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "ExtrudeSurface": { "description": "An extrude surface.", @@ -1596,8 +1596,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "ExtrudeSurface": { "description": "An extrude surface.", @@ -2499,8 +2499,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "ExtrudeSurface": { "description": "An extrude surface.", @@ -3859,8 +3859,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "ExtrudeSurface": { "description": "An extrude surface.", @@ -4711,8 +4711,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "ExtrudeSurface": { "description": "An extrude surface.", @@ -5614,8 +5614,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "ExtrudeSurface": { "description": "An extrude surface.", @@ -7007,8 +7007,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -8325,8 +8325,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -9691,8 +9691,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -11110,8 +11110,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -12428,8 +12428,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -13794,8 +13794,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -15213,8 +15213,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -16531,8 +16531,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -17897,8 +17897,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -19312,8 +19312,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "ExtrudeSurface": { "description": "An extrude surface.", @@ -20241,8 +20241,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "ExtrudeSurface": { "description": "An extrude surface.", @@ -21559,8 +21559,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "ExtrudeSurface": { "description": "An extrude surface.", @@ -22861,8 +22861,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -24265,8 +24265,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -25583,8 +25583,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -26949,8 +26949,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -28353,8 +28353,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -29671,8 +29671,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -31037,8 +31037,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -32492,8 +32492,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -33810,8 +33810,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -35176,8 +35176,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -37004,8 +37004,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -38322,8 +38322,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -39688,8 +39688,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -41151,8 +41151,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "ExtrudeSurface": { "description": "An extrude surface.", @@ -42107,8 +42107,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "ExtrudeSurface": { "description": "An extrude surface.", @@ -43443,8 +43443,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "ExtrudeSurface": { "description": "An extrude surface.", @@ -44382,8 +44382,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "Sketch": { "description": "A sketch is a collection of paths.", @@ -45732,8 +45732,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "Solid": { "description": "An solid is a collection of extrude surfaces.", @@ -47050,8 +47050,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "Solid": { "description": "An solid is a collection of extrude surfaces.", @@ -48804,8 +48804,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -50182,8 +50182,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -51500,8 +51500,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -52866,8 +52866,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -54374,8 +54374,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -55409,8 +55409,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "Sketch": { "description": "A sketch is a collection of paths.", @@ -57215,8 +57215,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "ExtrudeSurface": { "description": "An extrude surface.", @@ -58171,8 +58171,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "ExtrudeSurface": { "description": "An extrude surface.", @@ -59507,8 +59507,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "ExtrudeSurface": { "description": "An extrude surface.", @@ -60446,8 +60446,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "Sketch": { "description": "A sketch is a collection of paths.", @@ -62267,8 +62267,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "ExtrudeSurface": { "description": "An extrude surface.", @@ -63165,8 +63165,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "ExtrudeSurface": { "description": "An extrude surface.", @@ -64063,8 +64063,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "ExtrudeSurface": { "description": "An extrude surface.", @@ -64627,8 +64627,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "Sketch": { "description": "A sketch is a collection of paths.", @@ -66002,8 +66002,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "Sketch": { "description": "A sketch is a collection of paths.", @@ -67783,8 +67783,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -69148,8 +69148,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -70514,8 +70514,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -71540,8 +71540,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "Sketch": { "description": "A sketch is a collection of paths.", @@ -72915,8 +72915,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "Sketch": { "description": "A sketch is a collection of paths.", @@ -74397,8 +74397,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 } } }, @@ -75138,8 +75138,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -76527,8 +76527,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -78076,8 +78076,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -79394,8 +79394,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -80760,8 +80760,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -82154,8 +82154,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -83472,8 +83472,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -84838,8 +84838,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -86258,8 +86258,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -87576,8 +87576,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -88558,8 +88558,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "Sketch": { "description": "A sketch is a collection of paths.", @@ -90229,8 +90229,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "TagEngineInfo": { "description": "Engine information for a tag.", @@ -92509,8 +92509,6 @@ "description": "KCL value for an optional parameter which was not given an argument. (remember, parameters are in the function declaration, arguments are in the function call/application).", "type": "object", "required": [ - "end", - "start", "type" ], "properties": { @@ -93973,8 +93971,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "TagEngineInfo": { "description": "Engine information for a tag.", @@ -96253,8 +96251,6 @@ "description": "KCL value for an optional parameter which was not given an argument. (remember, parameters are in the function declaration, arguments are in the function call/application).", "type": "object", "required": [ - "end", - "start", "type" ], "properties": { @@ -97721,8 +97717,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "TagEngineInfo": { "description": "Engine information for a tag.", @@ -100001,8 +99997,6 @@ "description": "KCL value for an optional parameter which was not given an argument. (remember, parameters are in the function declaration, arguments are in the function call/application).", "type": "object", "required": [ - "end", - "start", "type" ], "properties": { @@ -101924,8 +101918,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "ExtrudeSurface": { "description": "An extrude surface.", @@ -102995,8 +102989,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "ExtrudeSurface": { "description": "An extrude surface.", @@ -104303,8 +104297,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -105955,8 +105949,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -107327,8 +107321,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -108387,8 +108381,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "Sketch": { "description": "A sketch is a collection of paths.", @@ -109768,8 +109762,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "Sketch": { "description": "A sketch is a collection of paths.", @@ -111538,8 +111532,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -112910,8 +112904,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -113954,8 +113948,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "Sketch": { "description": "A sketch is a collection of paths.", @@ -115335,8 +115329,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "Sketch": { "description": "A sketch is a collection of paths.", @@ -116711,8 +116705,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "Sketch": { "description": "A sketch is a collection of paths.", @@ -118092,8 +118086,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "Sketch": { "description": "A sketch is a collection of paths.", @@ -119474,8 +119468,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "Solid": { "description": "An solid is a collection of extrude surfaces.", @@ -120792,8 +120786,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "Solid": { "description": "An solid is a collection of extrude surfaces.", @@ -122546,8 +122540,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -123972,8 +123966,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -125366,8 +125360,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -126755,8 +126749,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -127917,8 +127911,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "TagEngineInfo": { "description": "Engine information for a tag.", @@ -130197,8 +130191,6 @@ "description": "KCL value for an optional parameter which was not given an argument. (remember, parameters are in the function declaration, arguments are in the function call/application).", "type": "object", "required": [ - "end", - "start", "type" ], "properties": { @@ -132052,8 +132044,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "TagEngineInfo": { "description": "Engine information for a tag.", @@ -134332,8 +134324,6 @@ "description": "KCL value for an optional parameter which was not given an argument. (remember, parameters are in the function declaration, arguments are in the function call/application).", "type": "object", "required": [ - "end", - "start", "type" ], "properties": { @@ -135794,8 +135784,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "TagEngineInfo": { "description": "Engine information for a tag.", @@ -138074,8 +138064,6 @@ "description": "KCL value for an optional parameter which was not given an argument. (remember, parameters are in the function declaration, arguments are in the function call/application).", "type": "object", "required": [ - "end", - "start", "type" ], "properties": { @@ -139947,8 +139935,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "TagEngineInfo": { "description": "Engine information for a tag.", @@ -142227,8 +142215,6 @@ "description": "KCL value for an optional parameter which was not given an argument. (remember, parameters are in the function declaration, arguments are in the function call/application).", "type": "object", "required": [ - "end", - "start", "type" ], "properties": { @@ -144082,8 +144068,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "TagEngineInfo": { "description": "Engine information for a tag.", @@ -146362,8 +146348,6 @@ "description": "KCL value for an optional parameter which was not given an argument. (remember, parameters are in the function declaration, arguments are in the function call/application).", "type": "object", "required": [ - "end", - "start", "type" ], "properties": { @@ -147826,8 +147810,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "TagEngineInfo": { "description": "Engine information for a tag.", @@ -150106,8 +150090,6 @@ "description": "KCL value for an optional parameter which was not given an argument. (remember, parameters are in the function declaration, arguments are in the function call/application).", "type": "object", "required": [ - "end", - "start", "type" ], "properties": { @@ -151568,8 +151550,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "TagEngineInfo": { "description": "Engine information for a tag.", @@ -153848,8 +153830,6 @@ "description": "KCL value for an optional parameter which was not given an argument. (remember, parameters are in the function declaration, arguments are in the function call/application).", "type": "object", "required": [ - "end", - "start", "type" ], "properties": { @@ -156150,8 +156130,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "ExtrudeSurface": { "description": "An extrude surface.", @@ -157189,8 +157169,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "ExtrudeSurface": { "description": "An extrude surface.", @@ -158128,8 +158108,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "Sketch": { "description": "A sketch is a collection of paths.", @@ -159893,8 +159873,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "ExtrudeSurface": { "description": "An extrude surface.", @@ -160791,8 +160771,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "ExtrudeSurface": { "description": "An extrude surface.", @@ -161694,8 +161674,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "ExtrudeSurface": { "description": "An extrude surface.", @@ -162592,8 +162572,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "ExtrudeSurface": { "description": "An extrude surface.", @@ -163490,8 +163470,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "ExtrudeSurface": { "description": "An extrude surface.", @@ -164388,8 +164368,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "ExtrudeSurface": { "description": "An extrude surface.", @@ -165291,8 +165271,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "ExtrudeSurface": { "description": "An extrude surface.", @@ -166189,8 +166169,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "ExtrudeSurface": { "description": "An extrude surface.", @@ -167143,8 +167123,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "ExtrudeSurface": { "description": "An extrude surface.", @@ -168147,8 +168127,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "ExtrudeSurface": { "description": "An extrude surface.", @@ -169118,8 +169098,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "Sketch": { "description": "A sketch is a collection of paths.", @@ -170520,8 +170500,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "Solid": { "description": "An solid is a collection of extrude surfaces.", @@ -171838,8 +171818,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "Solid": { "description": "An solid is a collection of extrude surfaces.", @@ -173734,8 +173714,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -175130,8 +175110,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -176279,8 +176259,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "Sketch": { "description": "A sketch is a collection of paths.", @@ -177697,8 +177677,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "Sketch": { "description": "A sketch is a collection of paths.", @@ -178940,8 +178920,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "Solid": { "description": "An solid is a collection of extrude surfaces.", @@ -180918,8 +180898,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -182236,8 +182216,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -183602,8 +183582,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -184995,8 +184975,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -186313,8 +186293,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -187679,8 +187659,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -189072,8 +189052,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -190390,8 +190370,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -191756,8 +191736,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -193243,8 +193223,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -194561,8 +194541,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -195927,8 +195907,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -197315,8 +197295,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -198633,8 +198613,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -199999,8 +199979,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -201387,8 +201367,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -202705,8 +202685,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -204071,8 +204051,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -205459,8 +205439,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -206777,8 +206757,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", @@ -208143,8 +208123,8 @@ "format": "uint", "minimum": 0.0 }, - "maxItems": 2, - "minItems": 2 + "maxItems": 3, + "minItems": 3 }, "SketchSurface": { "description": "A sketch type.", diff --git a/e2e/playwright/lib/console-error-whitelist.ts b/e2e/playwright/lib/console-error-whitelist.ts index 721f1683a..176c335ef 100644 --- a/e2e/playwright/lib/console-error-whitelist.ts +++ b/e2e/playwright/lib/console-error-whitelist.ts @@ -18,7 +18,7 @@ export const isErrorWhitelisted = (exception: Error) => { { name: '"{"kind"', message: - '"engine","sourceRanges":[[0,0]],"msg":"Failed to get string from response from engine: `JsValue(undefined)`"}"', + '"engine","sourceRanges":[[0,0,0]],"msg":"Failed to get string from response from engine: `JsValue(undefined)`"}"', stack: '', foundInSpec: 'e2e/playwright/testing-settings.spec.ts', project: 'Google Chrome', @@ -156,8 +156,8 @@ export const isErrorWhitelisted = (exception: Error) => { { name: 'Unhandled Promise Rejection', message: - '{"kind":"engine","sourceRanges":[[0,0]],"msg":"Failed to get string from response from engine: `JsValue(undefined)`"}', - stack: `Unhandled Promise Rejection: {"kind":"engine","sourceRanges":[[0,0]],"msg":"Failed to get string from response from engine: \`JsValue(undefined)\`"} + '{"kind":"engine","sourceRanges":[[0,0,0]],"msg":"Failed to get string from response from engine: `JsValue(undefined)`"}', + stack: `Unhandled Promise Rejection: {"kind":"engine","sourceRanges":[[0,0,0]],"msg":"Failed to get string from response from engine: \`JsValue(undefined)\`"} at unknown (http://localhost:3000/src/lang/std/engineConnection.ts:1245:26)`, foundInSpec: 'e2e/playwright/onboarding-tests.spec.ts Click through each onboarding step', @@ -253,7 +253,7 @@ export const isErrorWhitelisted = (exception: Error) => { { name: '{"kind"', stack: ``, - message: `engine","sourceRanges":[[0,0]],"msg":"Failed to wait for promise from engine: JsValue(\\"Force interrupt, executionIsStale, new AST requested\\")"}`, + message: `engine","sourceRanges":[[0,0,0]],"msg":"Failed to wait for promise from engine: JsValue(\\"Force interrupt, executionIsStale, new AST requested\\")"}`, project: 'Google Chrome', foundInSpec: 'e2e/playwright/testing-settings.spec.ts', }, diff --git a/src/components/ModelingSidebar/ModelingPanes/MemoryPane.test.tsx b/src/components/ModelingSidebar/ModelingPanes/MemoryPane.test.tsx index 6b1931875..6b3adfbb2 100644 --- a/src/components/ModelingSidebar/ModelingPanes/MemoryPane.test.tsx +++ b/src/components/ModelingSidebar/ModelingPanes/MemoryPane.test.tsx @@ -43,14 +43,14 @@ describe('processMemory', () => { tag: null, id: expect.any(String), faceId: expect.any(String), - sourceRange: [170, 194], + sourceRange: [170, 194, 0], }, { type: 'extrudePlane', tag: null, id: expect.any(String), faceId: expect.any(String), - sourceRange: [202, 230], + sourceRange: [202, 230, 0], }, ], theSketch: [ diff --git a/src/lang/KclSingleton.ts b/src/lang/KclSingleton.ts index 1a3c3e066..ce5ed236b 100644 --- a/src/lang/KclSingleton.ts +++ b/src/lang/KclSingleton.ts @@ -38,6 +38,7 @@ export class KclManager { body: [], start: 0, end: 0, + moduleId: 0, nonCodeMeta: { nonCodeNodes: {}, startNodes: [], @@ -204,6 +205,7 @@ export class KclManager { body: [], start: 0, end: 0, + moduleId: 0, nonCodeMeta: { nonCodeNodes: {}, startNodes: [], diff --git a/src/lang/abstractSyntaxTree.test.ts b/src/lang/abstractSyntaxTree.test.ts index 6fc30dd39..b77a27936 100644 --- a/src/lang/abstractSyntaxTree.test.ts +++ b/src/lang/abstractSyntaxTree.test.ts @@ -1903,6 +1903,6 @@ describe('parsing errors', () => { const error = result as KCLError expect(error.kind).toBe('syntax') expect(error.msg).toBe('Unexpected token: (') - expect(error.sourceRanges).toEqual([[27, 28]]) + expect(error.sourceRanges).toEqual([[27, 28, 0]]) }) }) diff --git a/src/lang/artifact.test.ts b/src/lang/artifact.test.ts index 5a6060703..52a8746de 100644 --- a/src/lang/artifact.test.ts +++ b/src/lang/artifact.test.ts @@ -19,7 +19,7 @@ const mySketch001 = startSketchOn('XY') const sketch001 = execState.memory.get('mySketch001') expect(sketch001).toEqual({ type: 'UserVal', - __meta: [{ sourceRange: [46, 71] }], + __meta: [{ sourceRange: [46, 71, 0] }], value: { type: 'Sketch', on: expect.any(Object), @@ -29,7 +29,7 @@ const mySketch001 = startSketchOn('XY') tag: null, __geoMeta: { id: expect.any(String), - sourceRange: [46, 71], + sourceRange: [46, 71, 0], }, }, paths: [ @@ -39,7 +39,7 @@ const mySketch001 = startSketchOn('XY') to: [-1.59, -1.54], from: [0, 0], __geoMeta: { - sourceRange: [77, 102], + sourceRange: [77, 102, 0], id: expect.any(String), }, }, @@ -49,13 +49,13 @@ const mySketch001 = startSketchOn('XY') from: [-1.59, -1.54], tag: null, __geoMeta: { - sourceRange: [108, 132], + sourceRange: [108, 132, 0], id: expect.any(String), }, }, ], id: expect.any(String), - __meta: [{ sourceRange: [46, 71] }], + __meta: [{ sourceRange: [46, 71, 0] }], }, }) }) @@ -80,14 +80,14 @@ const mySketch001 = startSketchOn('XY') faceId: expect.any(String), tag: null, id: expect.any(String), - sourceRange: [77, 102], + sourceRange: [77, 102, 0], }, { type: 'extrudePlane', faceId: expect.any(String), tag: null, id: expect.any(String), - sourceRange: [108, 132], + sourceRange: [108, 132, 0], }, ], sketch: { @@ -104,7 +104,7 @@ const mySketch001 = startSketchOn('XY') tag: null, __geoMeta: { id: expect.any(String), - sourceRange: [77, 102], + sourceRange: [77, 102, 0], }, }, { @@ -114,7 +114,7 @@ const mySketch001 = startSketchOn('XY') tag: null, __geoMeta: { id: expect.any(String), - sourceRange: [108, 132], + sourceRange: [108, 132, 0], }, }, ], @@ -122,7 +122,7 @@ const mySketch001 = startSketchOn('XY') height: 2, startCapId: expect.any(String), endCapId: expect.any(String), - __meta: [{ sourceRange: [46, 71] }], + __meta: [{ sourceRange: [46, 71, 0] }], }) }) test('sketch extrude and sketch on one of the faces', async () => { @@ -162,7 +162,7 @@ const sk2 = startSketchOn('XY') faceId: expect.any(String), tag: null, id: expect.any(String), - sourceRange: [69, 89], + sourceRange: [69, 89, 0], }, { type: 'extrudePlane', @@ -174,14 +174,14 @@ const sk2 = startSketchOn('XY') value: 'p', }, id: expect.any(String), - sourceRange: [95, 117], + sourceRange: [95, 117, 0], }, { type: 'extrudePlane', faceId: expect.any(String), tag: null, id: expect.any(String), - sourceRange: [123, 142], + sourceRange: [123, 142, 0], }, ], sketch: { @@ -194,7 +194,7 @@ const sk2 = startSketchOn('XY') p: { __meta: [ { - sourceRange: [114, 116], + sourceRange: [114, 116, 0], }, ], type: 'TagIdentifier', @@ -210,7 +210,7 @@ const sk2 = startSketchOn('XY') tag: null, __geoMeta: { id: expect.any(String), - sourceRange: [69, 89], + sourceRange: [69, 89, 0], }, }, { @@ -225,7 +225,7 @@ const sk2 = startSketchOn('XY') }, __geoMeta: { id: expect.any(String), - sourceRange: [95, 117], + sourceRange: [95, 117, 0], }, }, { @@ -235,7 +235,7 @@ const sk2 = startSketchOn('XY') tag: null, __geoMeta: { id: expect.any(String), - sourceRange: [123, 142], + sourceRange: [123, 142, 0], }, }, ], @@ -243,7 +243,7 @@ const sk2 = startSketchOn('XY') height: 2, startCapId: expect.any(String), endCapId: expect.any(String), - __meta: [{ sourceRange: [38, 63] }], + __meta: [{ sourceRange: [38, 63, 0] }], }, { type: 'Solid', @@ -254,7 +254,7 @@ const sk2 = startSketchOn('XY') faceId: expect.any(String), tag: null, id: expect.any(String), - sourceRange: [373, 393], + sourceRange: [373, 393, 0], }, { type: 'extrudePlane', @@ -266,14 +266,14 @@ const sk2 = startSketchOn('XY') value: 'o', }, id: expect.any(String), - sourceRange: [399, 420], + sourceRange: [399, 420, 0], }, { type: 'extrudePlane', faceId: expect.any(String), tag: null, id: expect.any(String), - sourceRange: [426, 445], + sourceRange: [426, 445, 0], }, ], sketch: { @@ -286,7 +286,7 @@ const sk2 = startSketchOn('XY') o: { __meta: [ { - sourceRange: [417, 419], + sourceRange: [417, 419, 0], }, ], type: 'TagIdentifier', @@ -302,7 +302,7 @@ const sk2 = startSketchOn('XY') tag: null, __geoMeta: { id: expect.any(String), - sourceRange: [373, 393], + sourceRange: [373, 393, 0], }, }, { @@ -317,7 +317,7 @@ const sk2 = startSketchOn('XY') }, __geoMeta: { id: expect.any(String), - sourceRange: [399, 420], + sourceRange: [399, 420, 0], }, }, { @@ -327,7 +327,7 @@ const sk2 = startSketchOn('XY') tag: null, __geoMeta: { id: expect.any(String), - sourceRange: [426, 445], + sourceRange: [426, 445, 0], }, }, ], @@ -335,7 +335,7 @@ const sk2 = startSketchOn('XY') height: 2, startCapId: expect.any(String), endCapId: expect.any(String), - __meta: [{ sourceRange: [342, 367] }], + __meta: [{ sourceRange: [342, 367, 0] }], }, ]) }) diff --git a/src/lang/errors.test.ts b/src/lang/errors.test.ts index ec1bf7089..bcb4302ba 100644 --- a/src/lang/errors.test.ts +++ b/src/lang/errors.test.ts @@ -9,8 +9,8 @@ describe('test kclErrToDiagnostic', () => { kind: 'semantic', msg: 'Semantic error', sourceRanges: [ - [0, 1], - [2, 3], + [0, 1, 0], + [2, 3, 0], ], }, { @@ -19,8 +19,8 @@ describe('test kclErrToDiagnostic', () => { kind: 'type', msg: 'Type error', sourceRanges: [ - [4, 5], - [6, 7], + [4, 5, 0], + [6, 7, 0], ], }, ] diff --git a/src/lang/errors.ts b/src/lang/errors.ts index 9ee093395..c1c8f14d7 100644 --- a/src/lang/errors.ts +++ b/src/lang/errors.ts @@ -4,15 +4,17 @@ import { posToOffset } from '@kittycad/codemirror-lsp-client' import { Diagnostic as LspDiagnostic } from 'vscode-languageserver-protocol' import { Text } from '@codemirror/state' +const TOP_LEVEL_MODULE_ID = 0 + type ExtractKind = T extends { kind: infer K } ? K : never export class KCLError extends Error { kind: ExtractKind | 'name' - sourceRanges: [number, number][] + sourceRanges: [number, number, number][] msg: string constructor( kind: ExtractKind | 'name', msg: string, - sourceRanges: [number, number][] + sourceRanges: [number, number, number][] ) { super() this.kind = kind @@ -23,63 +25,63 @@ export class KCLError extends Error { } export class KCLLexicalError extends KCLError { - constructor(msg: string, sourceRanges: [number, number][]) { + constructor(msg: string, sourceRanges: [number, number, number][]) { super('lexical', msg, sourceRanges) Object.setPrototypeOf(this, KCLSyntaxError.prototype) } } export class KCLInternalError extends KCLError { - constructor(msg: string, sourceRanges: [number, number][]) { + constructor(msg: string, sourceRanges: [number, number, number][]) { super('internal', msg, sourceRanges) Object.setPrototypeOf(this, KCLSyntaxError.prototype) } } export class KCLSyntaxError extends KCLError { - constructor(msg: string, sourceRanges: [number, number][]) { + constructor(msg: string, sourceRanges: [number, number, number][]) { super('syntax', msg, sourceRanges) Object.setPrototypeOf(this, KCLSyntaxError.prototype) } } export class KCLSemanticError extends KCLError { - constructor(msg: string, sourceRanges: [number, number][]) { + constructor(msg: string, sourceRanges: [number, number, number][]) { super('semantic', msg, sourceRanges) Object.setPrototypeOf(this, KCLSemanticError.prototype) } } export class KCLTypeError extends KCLError { - constructor(msg: string, sourceRanges: [number, number][]) { + constructor(msg: string, sourceRanges: [number, number, number][]) { super('type', msg, sourceRanges) Object.setPrototypeOf(this, KCLTypeError.prototype) } } export class KCLUnimplementedError extends KCLError { - constructor(msg: string, sourceRanges: [number, number][]) { + constructor(msg: string, sourceRanges: [number, number, number][]) { super('unimplemented', msg, sourceRanges) Object.setPrototypeOf(this, KCLUnimplementedError.prototype) } } export class KCLUnexpectedError extends KCLError { - constructor(msg: string, sourceRanges: [number, number][]) { + constructor(msg: string, sourceRanges: [number, number, number][]) { super('unexpected', msg, sourceRanges) Object.setPrototypeOf(this, KCLUnexpectedError.prototype) } } export class KCLValueAlreadyDefined extends KCLError { - constructor(key: string, sourceRanges: [number, number][]) { + constructor(key: string, sourceRanges: [number, number, number][]) { super('name', `Key ${key} was already defined elsewhere`, sourceRanges) Object.setPrototypeOf(this, KCLValueAlreadyDefined.prototype) } } export class KCLUndefinedValueError extends KCLError { - constructor(key: string, sourceRanges: [number, number][]) { + constructor(key: string, sourceRanges: [number, number, number][]) { super('name', `Key ${key} has not been defined`, sourceRanges) Object.setPrototypeOf(this, KCLUndefinedValueError.prototype) } @@ -97,13 +99,22 @@ export function lspDiagnosticsToKclErrors( .flatMap( ({ range, message }) => new KCLError('unexpected', message, [ - [posToOffset(doc, range.start)!, posToOffset(doc, range.end)!], + [ + posToOffset(doc, range.start)!, + posToOffset(doc, range.end)!, + TOP_LEVEL_MODULE_ID, + ], ]) ) .filter(({ sourceRanges }) => { - const [from, to] = sourceRanges[0] + const [from, to, moduleId] = sourceRanges[0] return ( - from !== null && to !== null && from !== undefined && to !== undefined + from !== null && + to !== null && + from !== undefined && + to !== undefined && + // Filter out errors that are not from the top-level module. + moduleId === TOP_LEVEL_MODULE_ID ) }) .sort((a, b) => { @@ -127,8 +138,16 @@ export function kclErrorsToDiagnostics( errors: KCLError[] ): CodeMirrorDiagnostic[] { return errors?.flatMap((err) => { - return err.sourceRanges.map(([from, to]) => { - return { from, to, message: err.msg, severity: 'error' } - }) + const sourceRanges: CodeMirrorDiagnostic[] = err.sourceRanges + // Filter out errors that are not from the top-level module. + .filter(([_start, _end, moduleId]) => moduleId === TOP_LEVEL_MODULE_ID) + .map(([from, to]) => { + return { from, to, message: err.msg, severity: 'error' } + }) + // Make sure we didn't filter out all the source ranges. + if (sourceRanges.length === 0) { + sourceRanges.push({ from: 0, to: 0, message: err.msg, severity: 'error' }) + } + return sourceRanges }) } diff --git a/src/lang/executor.test.ts b/src/lang/executor.test.ts index 825f84819..39773481b 100644 --- a/src/lang/executor.test.ts +++ b/src/lang/executor.test.ts @@ -65,7 +65,7 @@ const newVar = myVar + 1` to: [0, 2], from: [0, 0], __geoMeta: { - sourceRange: [72, 97], + sourceRange: [72, 97, 0], id: expect.any(String), }, tag: { @@ -81,7 +81,7 @@ const newVar = myVar + 1` from: [0, 2], tag: null, __geoMeta: { - sourceRange: [103, 119], + sourceRange: [103, 119, 0], id: expect.any(String), }, }, @@ -90,7 +90,7 @@ const newVar = myVar + 1` to: [5, -1], from: [2, 3], __geoMeta: { - sourceRange: [125, 154], + sourceRange: [125, 154, 0], id: expect.any(String), }, tag: { @@ -160,14 +160,14 @@ const newVar = myVar + 1` tag: null, __geoMeta: { id: expect.any(String), - sourceRange: [39, 63], + sourceRange: [39, 63, 0], }, }, tags: { myPath: { __meta: [ { - sourceRange: [109, 116], + sourceRange: [109, 116, 0], }, ], type: 'TagIdentifier', @@ -182,7 +182,7 @@ const newVar = myVar + 1` from: [0, 0], tag: null, __geoMeta: { - sourceRange: [69, 85], + sourceRange: [69, 85, 0], id: expect.any(String), }, }, @@ -191,7 +191,7 @@ const newVar = myVar + 1` to: [0, 1], from: [1, 1], __geoMeta: { - sourceRange: [91, 117], + sourceRange: [91, 117, 0], id: expect.any(String), }, tag: { @@ -207,15 +207,15 @@ const newVar = myVar + 1` from: [0, 1], tag: null, __geoMeta: { - sourceRange: [123, 139], + sourceRange: [123, 139, 0], id: expect.any(String), }, }, ], id: expect.any(String), - __meta: [{ sourceRange: [39, 63] }], + __meta: [{ sourceRange: [39, 63, 0] }], }, - __meta: [{ sourceRange: [39, 63] }], + __meta: [{ sourceRange: [39, 63, 0] }], }) }) it('execute array expression', async () => { @@ -229,7 +229,7 @@ const newVar = myVar + 1` value: 3, __meta: [ { - sourceRange: [14, 15], + sourceRange: [14, 15, 0], }, ], }) @@ -238,7 +238,7 @@ const newVar = myVar + 1` value: [1, '2', 3, 9], __meta: [ { - sourceRange: [27, 49], + sourceRange: [27, 49, 0], }, ], }) @@ -257,7 +257,7 @@ const newVar = myVar + 1` value: { aStr: 'str', anum: 2, identifier: 3, binExp: 9 }, __meta: [ { - sourceRange: [27, 83], + sourceRange: [27, 83, 0], }, ], }) @@ -272,7 +272,7 @@ const newVar = myVar + 1` value: '123', __meta: [ { - sourceRange: [41, 50], + sourceRange: [41, 50, 0], }, ], }) @@ -426,7 +426,7 @@ const theExtrude = startSketchOn('XY') new KCLError( 'undefined_value', 'memory item key `myVarZ` is not defined', - [[129, 135]] + [[129, 135, 0]] ) ) }) diff --git a/src/lang/modifyAst.test.ts b/src/lang/modifyAst.test.ts index e57402e5d..7dd459261 100644 --- a/src/lang/modifyAst.test.ts +++ b/src/lang/modifyAst.test.ts @@ -101,15 +101,15 @@ describe('Testing findUniqueName', () => { it('should find a unique name', () => { const result = findUniqueName( JSON.stringify([ - { type: 'Identifier', name: 'yo01', start: 0, end: 0 }, - { type: 'Identifier', name: 'yo02', start: 0, end: 0 }, - { type: 'Identifier', name: 'yo03', start: 0, end: 0 }, - { type: 'Identifier', name: 'yo04', start: 0, end: 0 }, - { type: 'Identifier', name: 'yo05', start: 0, end: 0 }, - { type: 'Identifier', name: 'yo06', start: 0, end: 0 }, - { type: 'Identifier', name: 'yo07', start: 0, end: 0 }, - { type: 'Identifier', name: 'yo08', start: 0, end: 0 }, - { type: 'Identifier', name: 'yo09', start: 0, end: 0 }, + { type: 'Identifier', name: 'yo01', start: 0, end: 0, moduleId: 0 }, + { type: 'Identifier', name: 'yo02', start: 0, end: 0, moduleId: 0 }, + { type: 'Identifier', name: 'yo03', start: 0, end: 0, moduleId: 0 }, + { type: 'Identifier', name: 'yo04', start: 0, end: 0, moduleId: 0 }, + { type: 'Identifier', name: 'yo05', start: 0, end: 0, moduleId: 0 }, + { type: 'Identifier', name: 'yo06', start: 0, end: 0, moduleId: 0 }, + { type: 'Identifier', name: 'yo07', start: 0, end: 0, moduleId: 0 }, + { type: 'Identifier', name: 'yo08', start: 0, end: 0, moduleId: 0 }, + { type: 'Identifier', name: 'yo09', start: 0, end: 0, moduleId: 0 }, ] satisfies Node[]), 'yo', 2 @@ -124,6 +124,7 @@ describe('Testing addSketchTo', () => { body: [], start: 0, end: 0, + moduleId: 0, nonCodeMeta: { nonCodeNodes: {}, startNodes: [] }, }, 'yz' diff --git a/src/lang/modifyAst.ts b/src/lang/modifyAst.ts index aec67b79c..8ec8ae57d 100644 --- a/src/lang/modifyAst.ts +++ b/src/lang/modifyAst.ts @@ -242,6 +242,7 @@ export function mutateObjExpProp( value: updateWith, start: 0, end: 0, + moduleId: 0, }) } } @@ -577,6 +578,7 @@ export function createLiteral(value: string | number): Node { type: 'Literal', start: 0, end: 0, + moduleId: 0, value, raw: `${value}`, } @@ -587,6 +589,7 @@ export function createTagDeclarator(value: string): Node { type: 'TagDeclarator', start: 0, end: 0, + moduleId: 0, value, } @@ -597,6 +600,7 @@ export function createIdentifier(name: string): Node { type: 'Identifier', start: 0, end: 0, + moduleId: 0, name, } @@ -607,6 +611,7 @@ export function createPipeSubstitution(): Node { type: 'PipeSubstitution', start: 0, end: 0, + moduleId: 0, } } @@ -618,10 +623,12 @@ export function createCallExpressionStdLib( type: 'CallExpression', start: 0, end: 0, + moduleId: 0, callee: { type: 'Identifier', start: 0, end: 0, + moduleId: 0, name, }, @@ -638,10 +645,12 @@ export function createCallExpression( type: 'CallExpression', start: 0, end: 0, + moduleId: 0, callee: { type: 'Identifier', start: 0, end: 0, + moduleId: 0, name, }, @@ -657,6 +666,7 @@ export function createArrayExpression( type: 'ArrayExpression', start: 0, end: 0, + moduleId: 0, nonCodeMeta: nonCodeMetaEmpty(), elements, @@ -670,6 +680,7 @@ export function createPipeExpression( type: 'PipeExpression', start: 0, end: 0, + moduleId: 0, body, nonCodeMeta: nonCodeMetaEmpty(), @@ -686,12 +697,14 @@ export function createVariableDeclaration( type: 'VariableDeclaration', start: 0, end: 0, + moduleId: 0, declarations: [ { type: 'VariableDeclarator', start: 0, end: 0, + moduleId: 0, id: createIdentifier(varName), init, @@ -709,12 +722,14 @@ export function createObjectExpression(properties: { type: 'ObjectExpression', start: 0, end: 0, + moduleId: 0, nonCodeMeta: nonCodeMetaEmpty(), properties: Object.entries(properties).map(([key, value]) => ({ type: 'ObjectProperty', start: 0, end: 0, + moduleId: 0, key: createIdentifier(key), value, @@ -730,6 +745,7 @@ export function createUnaryExpression( type: 'UnaryExpression', start: 0, end: 0, + moduleId: 0, operator, argument, @@ -745,6 +761,7 @@ export function createBinaryExpression([left, operator, right]: [ type: 'BinaryExpression', start: 0, end: 0, + moduleId: 0, operator, left, diff --git a/src/lang/std/__snapshots__/artifactGraph.test.ts.snap b/src/lang/std/__snapshots__/artifactGraph.test.ts.snap index d0a9a13e6..af03d4c30 100644 --- a/src/lang/std/__snapshots__/artifactGraph.test.ts.snap +++ b/src/lang/std/__snapshots__/artifactGraph.test.ts.snap @@ -13,6 +13,7 @@ Map { "range": [ 37, 64, + 0, ], }, "pathIds": [ @@ -31,6 +32,7 @@ Map { "range": [ 37, 64, + 0, ], }, "planeId": "UUID", @@ -56,6 +58,7 @@ Map { "range": [ 70, 86, + 0, ], }, "edgeIds": [ @@ -77,6 +80,7 @@ Map { "range": [ 92, 119, + 0, ], }, "edgeCutId": "UUID", @@ -99,6 +103,7 @@ Map { "range": [ 125, 150, + 0, ], }, "edgeIds": [ @@ -120,6 +125,7 @@ Map { "range": [ 156, 203, + 0, ], }, "edgeIds": [ @@ -141,6 +147,7 @@ Map { "range": [ 209, 217, + 0, ], }, "edgeIds": [], @@ -162,6 +169,7 @@ Map { "range": [ 231, 254, + 0, ], }, "edgeIds": [ @@ -289,6 +297,7 @@ Map { "range": [ 260, 299, + 0, ], }, "consumedEdgeId": "UUID", @@ -307,6 +316,7 @@ Map { "range": [ 350, 377, + 0, ], }, "planeId": "UUID", @@ -331,6 +341,7 @@ Map { "range": [ 383, 398, + 0, ], }, "edgeIds": [ @@ -352,6 +363,7 @@ Map { "range": [ 404, 420, + 0, ], }, "edgeIds": [ @@ -373,6 +385,7 @@ Map { "range": [ 426, 473, + 0, ], }, "edgeIds": [ @@ -394,6 +407,7 @@ Map { "range": [ 479, 487, + 0, ], }, "edgeIds": [], @@ -415,6 +429,7 @@ Map { "range": [ 501, 522, + 0, ], }, "edgeIds": [ diff --git a/src/lang/std/artifactGraph.test.ts b/src/lang/std/artifactGraph.test.ts index 996876f0e..837acc894 100644 --- a/src/lang/std/artifactGraph.test.ts +++ b/src/lang/std/artifactGraph.test.ts @@ -610,7 +610,7 @@ describe('testing getArtifactsToUpdate', () => { sweepId: '', codeRef: { pathToNode: [['body', '']], - range: [37, 64], + range: [37, 64, 0], }, }, ]) @@ -622,7 +622,7 @@ describe('testing getArtifactsToUpdate', () => { surfaceIds: [], edgeIds: [], codeRef: { - range: [231, 254], + range: [231, 254, 0], pathToNode: [['body', '']], }, }, @@ -632,7 +632,7 @@ describe('testing getArtifactsToUpdate', () => { planeId: expect.any(String), sweepId: expect.any(String), codeRef: { - range: [37, 64], + range: [37, 64, 0], pathToNode: [['body', '']], }, solid2dId: expect.any(String), @@ -645,7 +645,7 @@ describe('testing getArtifactsToUpdate', () => { surfaceId: '', edgeIds: [], codeRef: { - range: [70, 86], + range: [70, 86, 0], pathToNode: [['body', '']], }, }, @@ -655,7 +655,7 @@ describe('testing getArtifactsToUpdate', () => { planeId: expect.any(String), sweepId: expect.any(String), codeRef: { - range: [37, 64], + range: [37, 64, 0], pathToNode: [['body', '']], }, solid2dId: expect.any(String), @@ -669,7 +669,7 @@ describe('testing getArtifactsToUpdate', () => { edgeIds: [], surfaceId: '', codeRef: { - range: [260, 299], + range: [260, 299, 0], pathToNode: [['body', '']], }, }, @@ -679,7 +679,7 @@ describe('testing getArtifactsToUpdate', () => { surfaceId: expect.any(String), edgeIds: expect.any(Array), codeRef: { - range: [92, 119], + range: [92, 119, 0], pathToNode: [['body', '']], }, edgeCutId: expect.any(String), @@ -699,7 +699,7 @@ describe('testing getArtifactsToUpdate', () => { surfaceId: expect.any(String), edgeIds: expect.any(Array), codeRef: { - range: [156, 203], + range: [156, 203, 0], pathToNode: [['body', '']], }, }, @@ -710,7 +710,7 @@ describe('testing getArtifactsToUpdate', () => { surfaceIds: expect.any(Array), edgeIds: expect.any(Array), codeRef: { - range: [231, 254], + range: [231, 254, 0], pathToNode: [['body', '']], }, }, @@ -727,7 +727,7 @@ describe('testing getArtifactsToUpdate', () => { surfaceId: expect.any(String), edgeIds: expect.any(Array), codeRef: { - range: [125, 150], + range: [125, 150, 0], pathToNode: [['body', '']], }, }, @@ -738,7 +738,7 @@ describe('testing getArtifactsToUpdate', () => { surfaceIds: expect.any(Array), edgeIds: expect.any(Array), codeRef: { - range: [231, 254], + range: [231, 254, 0], pathToNode: [['body', '']], }, }, @@ -755,7 +755,7 @@ describe('testing getArtifactsToUpdate', () => { surfaceId: expect.any(String), edgeIds: expect.any(Array), codeRef: { - range: [92, 119], + range: [92, 119, 0], pathToNode: [['body', '']], }, edgeCutId: expect.any(String), @@ -767,7 +767,7 @@ describe('testing getArtifactsToUpdate', () => { surfaceIds: expect.any(Array), edgeIds: expect.any(Array), codeRef: { - range: [231, 254], + range: [231, 254, 0], pathToNode: [['body', '']], }, }, @@ -784,7 +784,7 @@ describe('testing getArtifactsToUpdate', () => { surfaceId: expect.any(String), edgeIds: expect.any(Array), codeRef: { - range: [70, 86], + range: [70, 86, 0], pathToNode: [['body', '']], }, }, @@ -795,7 +795,7 @@ describe('testing getArtifactsToUpdate', () => { surfaceIds: expect.any(Array), edgeIds: expect.any(Array), codeRef: { - range: [231, 254], + range: [231, 254, 0], pathToNode: [['body', '']], }, }, @@ -813,7 +813,7 @@ describe('testing getArtifactsToUpdate', () => { surfaceIds: expect.any(Array), edgeIds: expect.any(Array), codeRef: { - range: [231, 254], + range: [231, 254, 0], pathToNode: [['body', '']], }, }, @@ -831,7 +831,7 @@ describe('testing getArtifactsToUpdate', () => { surfaceIds: expect.any(Array), edgeIds: expect.any(Array), codeRef: { - range: [231, 254], + range: [231, 254, 0], pathToNode: [['body', '']], }, }, diff --git a/src/lang/std/sketch.ts b/src/lang/std/sketch.ts index db64c5e03..871c0b753 100644 --- a/src/lang/std/sketch.ts +++ b/src/lang/std/sketch.ts @@ -1823,11 +1823,13 @@ export const updateStartProfileAtArgs: SketchLineHelper['updateArgs'] = ({ modifiedAst: { start: 0, end: 0, + moduleId: 0, body: [], nonCodeMeta: { start: 0, end: 0, + moduleId: 0, startNodes: [], nonCodeNodes: [], }, diff --git a/src/lang/wasm.ts b/src/lang/wasm.ts index adacac41d..89e4b9ff8 100644 --- a/src/lang/wasm.ts +++ b/src/lang/wasm.ts @@ -120,8 +120,8 @@ const initialise = async () => { export const initPromise = initialise() -export const rangeTypeFix = (ranges: number[][]): [number, number][] => - ranges.map(([start, end]) => [start, end]) +export const rangeTypeFix = (ranges: number[][]): [number, number, number][] => + ranges.map(([start, end, moduleId]) => [start, end, moduleId]) export const parse = (code: string | Error): Node | Error => { if (err(code)) return code diff --git a/src/wasm-lib/Cargo.lock b/src/wasm-lib/Cargo.lock index 1fea31bd2..bdc8fc5b7 100644 --- a/src/wasm-lib/Cargo.lock +++ b/src/wasm-lib/Cargo.lock @@ -3083,6 +3083,7 @@ dependencies = [ "chrono", "dyn-clone", "indexmap 1.9.3", + "indexmap 2.6.0", "schemars_derive", "serde", "serde_json", @@ -3883,6 +3884,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a2f31991cee3dce1ca4f929a8a04fdd11fd8801aac0f2030b0fa8a0a3fef6b9" dependencies = [ "chrono", + "indexmap 2.6.0", "lazy_static", "serde_json", "thiserror 1.0.68", diff --git a/src/wasm-lib/derive-docs/src/lib.rs b/src/wasm-lib/derive-docs/src/lib.rs index 8281ed24b..d9887c9be 100644 --- a/src/wasm-lib/derive-docs/src/lib.rs +++ b/src/wasm-lib/derive-docs/src/lib.rs @@ -173,9 +173,7 @@ fn do_stdlib_inner( quote! { let code_blocks = vec![#(#cb),*]; code_blocks.iter().map(|cb| { - let tokens = crate::token::lexer(cb).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(cb).unwrap(); let mut options: crate::ast::types::FormatOptions = Default::default(); options.insert_final_newline = false; @@ -750,9 +748,7 @@ fn generate_code_block_test(fn_name: &str, code_block: &str, index: usize) -> pr quote! { #[tokio::test(flavor = "multi_thread")] async fn #test_name_mock() { - let tokens = crate::token::lexer(#code_block).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(#code_block).unwrap(); let id_generator = crate::executor::IdGenerator::default(); let ctx = crate::executor::ExecutorContext { engine: std::sync::Arc::new(Box::new(crate::engine::conn_mock::EngineConnection::new().await.unwrap())), diff --git a/src/wasm-lib/derive-docs/tests/args_with_lifetime.gen b/src/wasm-lib/derive-docs/tests/args_with_lifetime.gen index a826f845a..9a3f6cef7 100644 --- a/src/wasm-lib/derive-docs/tests/args_with_lifetime.gen +++ b/src/wasm-lib/derive-docs/tests/args_with_lifetime.gen @@ -2,9 +2,7 @@ mod test_examples_someFn { #[tokio::test(flavor = "multi_thread")] async fn test_mock_example_someFn0() { - let tokens = crate::token::lexer("someFn()").unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse("someFn()").unwrap(); let id_generator = crate::executor::IdGenerator::default(); let ctx = crate::executor::ExecutorContext { engine: std::sync::Arc::new(Box::new( @@ -113,9 +111,7 @@ impl crate::docs::StdLibFn for SomeFn { code_blocks .iter() .map(|cb| { - let tokens = crate::token::lexer(cb).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(cb).unwrap(); let mut options: crate::ast::types::FormatOptions = Default::default(); options.insert_final_newline = false; program.recast(&options, 0) diff --git a/src/wasm-lib/derive-docs/tests/args_with_refs.gen b/src/wasm-lib/derive-docs/tests/args_with_refs.gen index f75f6dff1..7a7f70e34 100644 --- a/src/wasm-lib/derive-docs/tests/args_with_refs.gen +++ b/src/wasm-lib/derive-docs/tests/args_with_refs.gen @@ -2,9 +2,7 @@ mod test_examples_someFn { #[tokio::test(flavor = "multi_thread")] async fn test_mock_example_someFn0() { - let tokens = crate::token::lexer("someFn()").unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse("someFn()").unwrap(); let id_generator = crate::executor::IdGenerator::default(); let ctx = crate::executor::ExecutorContext { engine: std::sync::Arc::new(Box::new( @@ -113,9 +111,7 @@ impl crate::docs::StdLibFn for SomeFn { code_blocks .iter() .map(|cb| { - let tokens = crate::token::lexer(cb).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(cb).unwrap(); let mut options: crate::ast::types::FormatOptions = Default::default(); options.insert_final_newline = false; program.recast(&options, 0) diff --git a/src/wasm-lib/derive-docs/tests/array.gen b/src/wasm-lib/derive-docs/tests/array.gen index d9dec25e8..2f6069e55 100644 --- a/src/wasm-lib/derive-docs/tests/array.gen +++ b/src/wasm-lib/derive-docs/tests/array.gen @@ -2,9 +2,9 @@ mod test_examples_show { #[tokio::test(flavor = "multi_thread")] async fn test_mock_example_show0() { - let tokens = crate::token::lexer("This is another code block.\nyes sirrr.\nshow").unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = + crate::parser::top_level_parse("This is another code block.\nyes sirrr.\nshow") + .unwrap(); let id_generator = crate::executor::IdGenerator::default(); let ctx = crate::executor::ExecutorContext { engine: std::sync::Arc::new(Box::new( @@ -36,9 +36,8 @@ mod test_examples_show { #[tokio::test(flavor = "multi_thread")] async fn test_mock_example_show1() { - let tokens = crate::token::lexer("This is code.\nIt does other shit.\nshow").unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = + crate::parser::top_level_parse("This is code.\nIt does other shit.\nshow").unwrap(); let id_generator = crate::executor::IdGenerator::default(); let ctx = crate::executor::ExecutorContext { engine: std::sync::Arc::new(Box::new( @@ -150,9 +149,7 @@ impl crate::docs::StdLibFn for Show { code_blocks .iter() .map(|cb| { - let tokens = crate::token::lexer(cb).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(cb).unwrap(); let mut options: crate::ast::types::FormatOptions = Default::default(); options.insert_final_newline = false; program.recast(&options, 0) diff --git a/src/wasm-lib/derive-docs/tests/box.gen b/src/wasm-lib/derive-docs/tests/box.gen index 1ec101210..6ff618161 100644 --- a/src/wasm-lib/derive-docs/tests/box.gen +++ b/src/wasm-lib/derive-docs/tests/box.gen @@ -2,9 +2,8 @@ mod test_examples_show { #[tokio::test(flavor = "multi_thread")] async fn test_mock_example_show0() { - let tokens = crate::token::lexer("This is code.\nIt does other shit.\nshow").unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = + crate::parser::top_level_parse("This is code.\nIt does other shit.\nshow").unwrap(); let id_generator = crate::executor::IdGenerator::default(); let ctx = crate::executor::ExecutorContext { engine: std::sync::Arc::new(Box::new( @@ -113,9 +112,7 @@ impl crate::docs::StdLibFn for Show { code_blocks .iter() .map(|cb| { - let tokens = crate::token::lexer(cb).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(cb).unwrap(); let mut options: crate::ast::types::FormatOptions = Default::default(); options.insert_final_newline = false; program.recast(&options, 0) diff --git a/src/wasm-lib/derive-docs/tests/doc_comment_with_code.gen b/src/wasm-lib/derive-docs/tests/doc_comment_with_code.gen index 03bf0f224..3c5d416cf 100644 --- a/src/wasm-lib/derive-docs/tests/doc_comment_with_code.gen +++ b/src/wasm-lib/derive-docs/tests/doc_comment_with_code.gen @@ -2,10 +2,9 @@ mod test_examples_my_func { #[tokio::test(flavor = "multi_thread")] async fn test_mock_example_my_func0() { - let tokens = - crate::token::lexer("This is another code block.\nyes sirrr.\nmyFunc").unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = + crate::parser::top_level_parse("This is another code block.\nyes sirrr.\nmyFunc") + .unwrap(); let id_generator = crate::executor::IdGenerator::default(); let ctx = crate::executor::ExecutorContext { engine: std::sync::Arc::new(Box::new( @@ -37,9 +36,8 @@ mod test_examples_my_func { #[tokio::test(flavor = "multi_thread")] async fn test_mock_example_my_func1() { - let tokens = crate::token::lexer("This is code.\nIt does other shit.\nmyFunc").unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = + crate::parser::top_level_parse("This is code.\nIt does other shit.\nmyFunc").unwrap(); let id_generator = crate::executor::IdGenerator::default(); let ctx = crate::executor::ExecutorContext { engine: std::sync::Arc::new(Box::new( @@ -151,9 +149,7 @@ impl crate::docs::StdLibFn for MyFunc { code_blocks .iter() .map(|cb| { - let tokens = crate::token::lexer(cb).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(cb).unwrap(); let mut options: crate::ast::types::FormatOptions = Default::default(); options.insert_final_newline = false; program.recast(&options, 0) diff --git a/src/wasm-lib/derive-docs/tests/lineTo.gen b/src/wasm-lib/derive-docs/tests/lineTo.gen index c699a8a49..013b722f2 100644 --- a/src/wasm-lib/derive-docs/tests/lineTo.gen +++ b/src/wasm-lib/derive-docs/tests/lineTo.gen @@ -2,10 +2,9 @@ mod test_examples_line_to { #[tokio::test(flavor = "multi_thread")] async fn test_mock_example_line_to0() { - let tokens = - crate::token::lexer("This is another code block.\nyes sirrr.\nlineTo").unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = + crate::parser::top_level_parse("This is another code block.\nyes sirrr.\nlineTo") + .unwrap(); let id_generator = crate::executor::IdGenerator::default(); let ctx = crate::executor::ExecutorContext { engine: std::sync::Arc::new(Box::new( @@ -37,9 +36,8 @@ mod test_examples_line_to { #[tokio::test(flavor = "multi_thread")] async fn test_mock_example_line_to1() { - let tokens = crate::token::lexer("This is code.\nIt does other shit.\nlineTo").unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = + crate::parser::top_level_parse("This is code.\nIt does other shit.\nlineTo").unwrap(); let id_generator = crate::executor::IdGenerator::default(); let ctx = crate::executor::ExecutorContext { engine: std::sync::Arc::new(Box::new( @@ -159,9 +157,7 @@ impl crate::docs::StdLibFn for LineTo { code_blocks .iter() .map(|cb| { - let tokens = crate::token::lexer(cb).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(cb).unwrap(); let mut options: crate::ast::types::FormatOptions = Default::default(); options.insert_final_newline = false; program.recast(&options, 0) diff --git a/src/wasm-lib/derive-docs/tests/min.gen b/src/wasm-lib/derive-docs/tests/min.gen index 63c776961..076679a87 100644 --- a/src/wasm-lib/derive-docs/tests/min.gen +++ b/src/wasm-lib/derive-docs/tests/min.gen @@ -2,9 +2,8 @@ mod test_examples_min { #[tokio::test(flavor = "multi_thread")] async fn test_mock_example_min0() { - let tokens = crate::token::lexer("This is another code block.\nyes sirrr.\nmin").unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = + crate::parser::top_level_parse("This is another code block.\nyes sirrr.\nmin").unwrap(); let id_generator = crate::executor::IdGenerator::default(); let ctx = crate::executor::ExecutorContext { engine: std::sync::Arc::new(Box::new( @@ -36,9 +35,8 @@ mod test_examples_min { #[tokio::test(flavor = "multi_thread")] async fn test_mock_example_min1() { - let tokens = crate::token::lexer("This is code.\nIt does other shit.\nmin").unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = + crate::parser::top_level_parse("This is code.\nIt does other shit.\nmin").unwrap(); let id_generator = crate::executor::IdGenerator::default(); let ctx = crate::executor::ExecutorContext { engine: std::sync::Arc::new(Box::new( @@ -150,9 +148,7 @@ impl crate::docs::StdLibFn for Min { code_blocks .iter() .map(|cb| { - let tokens = crate::token::lexer(cb).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(cb).unwrap(); let mut options: crate::ast::types::FormatOptions = Default::default(); options.insert_final_newline = false; program.recast(&options, 0) diff --git a/src/wasm-lib/derive-docs/tests/option.gen b/src/wasm-lib/derive-docs/tests/option.gen index a3624d9ee..23208fda8 100644 --- a/src/wasm-lib/derive-docs/tests/option.gen +++ b/src/wasm-lib/derive-docs/tests/option.gen @@ -2,9 +2,8 @@ mod test_examples_show { #[tokio::test(flavor = "multi_thread")] async fn test_mock_example_show0() { - let tokens = crate::token::lexer("This is code.\nIt does other shit.\nshow").unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = + crate::parser::top_level_parse("This is code.\nIt does other shit.\nshow").unwrap(); let id_generator = crate::executor::IdGenerator::default(); let ctx = crate::executor::ExecutorContext { engine: std::sync::Arc::new(Box::new( @@ -113,9 +112,7 @@ impl crate::docs::StdLibFn for Show { code_blocks .iter() .map(|cb| { - let tokens = crate::token::lexer(cb).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(cb).unwrap(); let mut options: crate::ast::types::FormatOptions = Default::default(); options.insert_final_newline = false; program.recast(&options, 0) diff --git a/src/wasm-lib/derive-docs/tests/option_input_format.gen b/src/wasm-lib/derive-docs/tests/option_input_format.gen index 0473f1ebe..5ae970f03 100644 --- a/src/wasm-lib/derive-docs/tests/option_input_format.gen +++ b/src/wasm-lib/derive-docs/tests/option_input_format.gen @@ -2,9 +2,8 @@ mod test_examples_import { #[tokio::test(flavor = "multi_thread")] async fn test_mock_example_import0() { - let tokens = crate::token::lexer("This is code.\nIt does other shit.\nimport").unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = + crate::parser::top_level_parse("This is code.\nIt does other shit.\nimport").unwrap(); let id_generator = crate::executor::IdGenerator::default(); let ctx = crate::executor::ExecutorContext { engine: std::sync::Arc::new(Box::new( @@ -113,9 +112,7 @@ impl crate::docs::StdLibFn for Import { code_blocks .iter() .map(|cb| { - let tokens = crate::token::lexer(cb).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(cb).unwrap(); let mut options: crate::ast::types::FormatOptions = Default::default(); options.insert_final_newline = false; program.recast(&options, 0) diff --git a/src/wasm-lib/derive-docs/tests/return_vec_box_sketch.gen b/src/wasm-lib/derive-docs/tests/return_vec_box_sketch.gen index 83b21aa2a..ce8838efe 100644 --- a/src/wasm-lib/derive-docs/tests/return_vec_box_sketch.gen +++ b/src/wasm-lib/derive-docs/tests/return_vec_box_sketch.gen @@ -2,9 +2,8 @@ mod test_examples_import { #[tokio::test(flavor = "multi_thread")] async fn test_mock_example_import0() { - let tokens = crate::token::lexer("This is code.\nIt does other shit.\nimport").unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = + crate::parser::top_level_parse("This is code.\nIt does other shit.\nimport").unwrap(); let id_generator = crate::executor::IdGenerator::default(); let ctx = crate::executor::ExecutorContext { engine: std::sync::Arc::new(Box::new( @@ -113,9 +112,7 @@ impl crate::docs::StdLibFn for Import { code_blocks .iter() .map(|cb| { - let tokens = crate::token::lexer(cb).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(cb).unwrap(); let mut options: crate::ast::types::FormatOptions = Default::default(); options.insert_final_newline = false; program.recast(&options, 0) diff --git a/src/wasm-lib/derive-docs/tests/return_vec_sketch.gen b/src/wasm-lib/derive-docs/tests/return_vec_sketch.gen index 1f0670cd5..c7075331a 100644 --- a/src/wasm-lib/derive-docs/tests/return_vec_sketch.gen +++ b/src/wasm-lib/derive-docs/tests/return_vec_sketch.gen @@ -2,9 +2,8 @@ mod test_examples_import { #[tokio::test(flavor = "multi_thread")] async fn test_mock_example_import0() { - let tokens = crate::token::lexer("This is code.\nIt does other shit.\nimport").unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = + crate::parser::top_level_parse("This is code.\nIt does other shit.\nimport").unwrap(); let id_generator = crate::executor::IdGenerator::default(); let ctx = crate::executor::ExecutorContext { engine: std::sync::Arc::new(Box::new( @@ -113,9 +112,7 @@ impl crate::docs::StdLibFn for Import { code_blocks .iter() .map(|cb| { - let tokens = crate::token::lexer(cb).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(cb).unwrap(); let mut options: crate::ast::types::FormatOptions = Default::default(); options.insert_final_newline = false; program.recast(&options, 0) diff --git a/src/wasm-lib/derive-docs/tests/show.gen b/src/wasm-lib/derive-docs/tests/show.gen index 822c93c2a..600f581a2 100644 --- a/src/wasm-lib/derive-docs/tests/show.gen +++ b/src/wasm-lib/derive-docs/tests/show.gen @@ -2,9 +2,8 @@ mod test_examples_show { #[tokio::test(flavor = "multi_thread")] async fn test_mock_example_show0() { - let tokens = crate::token::lexer("This is code.\nIt does other shit.\nshow").unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = + crate::parser::top_level_parse("This is code.\nIt does other shit.\nshow").unwrap(); let id_generator = crate::executor::IdGenerator::default(); let ctx = crate::executor::ExecutorContext { engine: std::sync::Arc::new(Box::new( @@ -113,9 +112,7 @@ impl crate::docs::StdLibFn for Show { code_blocks .iter() .map(|cb| { - let tokens = crate::token::lexer(cb).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(cb).unwrap(); let mut options: crate::ast::types::FormatOptions = Default::default(); options.insert_final_newline = false; program.recast(&options, 0) diff --git a/src/wasm-lib/derive-docs/tests/test_args_with_exec_state.gen b/src/wasm-lib/derive-docs/tests/test_args_with_exec_state.gen index 87d2c4b3c..8e4661bbe 100644 --- a/src/wasm-lib/derive-docs/tests/test_args_with_exec_state.gen +++ b/src/wasm-lib/derive-docs/tests/test_args_with_exec_state.gen @@ -2,9 +2,7 @@ mod test_examples_some_function { #[tokio::test(flavor = "multi_thread")] async fn test_mock_example_some_function0() { - let tokens = crate::token::lexer("someFunction()").unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse("someFunction()").unwrap(); let id_generator = crate::executor::IdGenerator::default(); let ctx = crate::executor::ExecutorContext { engine: std::sync::Arc::new(Box::new( @@ -108,9 +106,7 @@ impl crate::docs::StdLibFn for SomeFunction { code_blocks .iter() .map(|cb| { - let tokens = crate::token::lexer(cb).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(cb).unwrap(); let mut options: crate::ast::types::FormatOptions = Default::default(); options.insert_final_newline = false; program.recast(&options, 0) diff --git a/src/wasm-lib/kcl-macros/src/lib.rs b/src/wasm-lib/kcl-macros/src/lib.rs index 611731325..d19cf98e4 100644 --- a/src/wasm-lib/kcl-macros/src/lib.rs +++ b/src/wasm-lib/kcl-macros/src/lib.rs @@ -16,8 +16,7 @@ use syn::{parse_macro_input, LitStr}; pub fn parse(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as LitStr); let kcl_src = input.value(); - let tokens = kcl_lib::token::lexer(&kcl_src).unwrap(); - let ast = kcl_lib::parser::Parser::new(tokens).ast().unwrap(); + let ast = kcl_lib::parser::top_level_parse(&kcl_src).unwrap(); let ast_struct = ast.bake(&Default::default()); quote!(#ast_struct).into() } diff --git a/src/wasm-lib/kcl-macros/tests/macro_test.rs b/src/wasm-lib/kcl-macros/tests/macro_test.rs index 19408eb7e..bf39ea1eb 100644 --- a/src/wasm-lib/kcl-macros/tests/macro_test.rs +++ b/src/wasm-lib/kcl-macros/tests/macro_test.rs @@ -1,6 +1,6 @@ extern crate alloc; use kcl_lib::ast::types::{ - BodyItem, Expr, Identifier, ItemVisibility, Literal, LiteralValue, Node, Program, VariableDeclaration, + BodyItem, Expr, Identifier, ItemVisibility, Literal, LiteralValue, ModuleId, Node, Program, VariableDeclaration, VariableDeclarator, VariableKind, }; use kcl_macros::parse; @@ -9,6 +9,7 @@ use pretty_assertions::assert_eq; #[test] fn basic() { let actual = parse!("const y = 4"); + let module_id = ModuleId::default(); let expected = Node { inner: Program { body: vec![BodyItem::VariableDeclaration(Box::new(Node::new( @@ -22,6 +23,7 @@ fn basic() { }, 6, 7, + module_id, ), init: Expr::Literal(Box::new(Node::new( Literal { @@ -31,11 +33,13 @@ fn basic() { }, 10, 11, + module_id, ))), digest: None, }, 6, 11, + module_id, )], visibility: ItemVisibility::Default, kind: VariableKind::Const, @@ -43,12 +47,14 @@ fn basic() { }, 0, 11, + module_id, )))], non_code_meta: Default::default(), digest: None, }, start: 0, end: 11, + module_id, }; assert_eq!(expected, actual); } diff --git a/src/wasm-lib/kcl-test-server/src/lib.rs b/src/wasm-lib/kcl-test-server/src/lib.rs index 45de4eec2..54e1dbfa9 100644 --- a/src/wasm-lib/kcl-test-server/src/lib.rs +++ b/src/wasm-lib/kcl-test-server/src/lib.rs @@ -15,7 +15,7 @@ use hyper::{ service::{make_service_fn, service_fn}, Body, Error, Response, Server, }; -use kcl_lib::{executor::ExecutorContext, settings::types::UnitLength, test_server::RequestBody}; +use kcl_lib::{ast::types::ModuleId, executor::ExecutorContext, settings::types::UnitLength, test_server::RequestBody}; use tokio::{ sync::{mpsc, oneshot}, task::JoinHandle, @@ -157,7 +157,8 @@ async fn snapshot_endpoint(body: Bytes, state: ExecutorContext) -> Response return bad_request(format!("Invalid request JSON: {e}")), }; let RequestBody { kcl_program, test_name } = body; - let parser = match kcl_lib::token::lexer(&kcl_program) { + let module_id = ModuleId::default(); + let parser = match kcl_lib::token::lexer(&kcl_program, module_id) { Ok(ts) => kcl_lib::parser::Parser::new(ts), Err(e) => return bad_request(format!("tokenization error: {e}")), }; diff --git a/src/wasm-lib/kcl-to-core/src/lib.rs b/src/wasm-lib/kcl-to-core/src/lib.rs index ce007d177..1b6b19a16 100644 --- a/src/wasm-lib/kcl-to-core/src/lib.rs +++ b/src/wasm-lib/kcl-to-core/src/lib.rs @@ -7,9 +7,7 @@ mod conn_mock_core; ///Converts the given kcl code to an engine test pub async fn kcl_to_engine_core(code: &str) -> Result { - let tokens = kcl_lib::token::lexer(code)?; - let parser = kcl_lib::parser::Parser::new(tokens); - let program = parser.ast()?; + let program = kcl_lib::parser::top_level_parse(code)?; let result = Arc::new(Mutex::new("".into())); let ref_result = Arc::clone(&result); diff --git a/src/wasm-lib/kcl/Cargo.toml b/src/wasm-lib/kcl/Cargo.toml index 0773f2e32..ce50a68ae 100644 --- a/src/wasm-lib/kcl/Cargo.toml +++ b/src/wasm-lib/kcl/Cargo.toml @@ -37,14 +37,14 @@ parse-display = "0.9.1" pyo3 = { version = "0.22.6", optional = true } reqwest = { version = "0.12", default-features = false, features = ["stream", "rustls-tls"] } ropey = "1.6.1" -schemars = { version = "0.8.17", features = ["impl_json_schema", "url", "uuid1", "preserve_order"] } +schemars = { version = "0.8.17", features = ["impl_json_schema", "indexmap2", "url", "uuid1", "preserve_order"] } serde = { version = "1.0.214", features = ["derive"] } serde_json = "1.0.128" sha2 = "0.10.8" tabled = { version = "0.15.0", optional = true } thiserror = "2.0.0" toml = "0.8.19" -ts-rs = { version = "10.0.0", features = ["uuid-impl", "url-impl", "chrono-impl", "no-serde-warnings", "serde-json-impl"] } +ts-rs = { version = "10.0.0", features = ["uuid-impl", "url-impl", "chrono-impl", "indexmap-impl", "no-serde-warnings", "serde-json-impl"] } url = { version = "2.5.3", features = ["serde"] } urlencoding = "2.1.3" uuid = { version = "1.11.0", features = ["v4", "js", "serde"] } diff --git a/src/wasm-lib/kcl/benches/compiler_benchmark_criterion.rs b/src/wasm-lib/kcl/benches/compiler_benchmark_criterion.rs index f913a2665..6205afc6f 100644 --- a/src/wasm-lib/kcl/benches/compiler_benchmark_criterion.rs +++ b/src/wasm-lib/kcl/benches/compiler_benchmark_criterion.rs @@ -1,9 +1,10 @@ use criterion::{black_box, criterion_group, criterion_main, Criterion}; pub fn bench_lex(c: &mut Criterion) { - c.bench_function("lex_cube", |b| b.iter(|| lex(CUBE_PROGRAM))); - c.bench_function("lex_big_kitt", |b| b.iter(|| lex(KITT_PROGRAM))); - c.bench_function("lex_pipes_on_pipes", |b| b.iter(|| lex(PIPES_PROGRAM))); + let module_id = kcl_lib::ast::types::ModuleId::default(); + c.bench_function("lex_cube", |b| b.iter(|| lex(CUBE_PROGRAM, module_id))); + c.bench_function("lex_big_kitt", |b| b.iter(|| lex(KITT_PROGRAM, module_id))); + c.bench_function("lex_pipes_on_pipes", |b| b.iter(|| lex(PIPES_PROGRAM, module_id))); } pub fn bench_parse(c: &mut Criterion) { @@ -15,7 +16,8 @@ pub fn bench_parse(c: &mut Criterion) { ("mike_stress_test", MIKE_STRESS_TEST_PROGRAM), ("koch snowflake", LSYSTEM_KOCH_SNOWFLAKE_PROGRAM), ] { - let tokens = kcl_lib::token::lexer(file).unwrap(); + let module_id = kcl_lib::ast::types::ModuleId::default(); + let tokens = kcl_lib::token::lexer(file, module_id).unwrap(); c.bench_function(&format!("parse_{name}"), move |b| { let tok = tokens.clone(); b.iter(move || { @@ -26,8 +28,8 @@ pub fn bench_parse(c: &mut Criterion) { } } -fn lex(program: &str) { - black_box(kcl_lib::token::lexer(program).unwrap()); +fn lex(program: &str, module_id: kcl_lib::ast::types::ModuleId) { + black_box(kcl_lib::token::lexer(program, module_id).unwrap()); } criterion_group!(benches, bench_lex, bench_parse); diff --git a/src/wasm-lib/kcl/benches/compiler_benchmark_iai.rs b/src/wasm-lib/kcl/benches/compiler_benchmark_iai.rs index c1d27c8d4..ca28a1fec 100644 --- a/src/wasm-lib/kcl/benches/compiler_benchmark_iai.rs +++ b/src/wasm-lib/kcl/benches/compiler_benchmark_iai.rs @@ -1,26 +1,32 @@ use iai::black_box; pub fn parse(program: &str) { - let tokens = kcl_lib::token::lexer(program).unwrap(); + let module_id = kcl_lib::ast::types::ModuleId::default(); + let tokens = kcl_lib::token::lexer(program, module_id).unwrap(); let tok = tokens.clone(); let parser = kcl_lib::parser::Parser::new(tok.clone()); black_box(parser.ast().unwrap()); } fn lex_kitt() { - black_box(kcl_lib::token::lexer(KITT_PROGRAM).unwrap()); + let module_id = kcl_lib::ast::types::ModuleId::default(); + black_box(kcl_lib::token::lexer(KITT_PROGRAM, module_id).unwrap()); } fn lex_pipes() { - black_box(kcl_lib::token::lexer(PIPES_PROGRAM).unwrap()); + let module_id = kcl_lib::ast::types::ModuleId::default(); + black_box(kcl_lib::token::lexer(PIPES_PROGRAM, module_id).unwrap()); } fn lex_cube() { - black_box(kcl_lib::token::lexer(CUBE_PROGRAM).unwrap()); + let module_id = kcl_lib::ast::types::ModuleId::default(); + black_box(kcl_lib::token::lexer(CUBE_PROGRAM, module_id).unwrap()); } fn lex_math() { - black_box(kcl_lib::token::lexer(MATH_PROGRAM).unwrap()); + let module_id = kcl_lib::ast::types::ModuleId::default(); + black_box(kcl_lib::token::lexer(MATH_PROGRAM, module_id).unwrap()); } fn lex_lsystem() { - black_box(kcl_lib::token::lexer(LSYSTEM_PROGRAM).unwrap()); + let module_id = kcl_lib::ast::types::ModuleId::default(); + black_box(kcl_lib::token::lexer(LSYSTEM_PROGRAM, module_id).unwrap()); } fn parse_kitt() { diff --git a/src/wasm-lib/kcl/benches/digest_benchmark.rs b/src/wasm-lib/kcl/benches/digest_benchmark.rs index 91b8e949c..33bdbd14a 100644 --- a/src/wasm-lib/kcl/benches/digest_benchmark.rs +++ b/src/wasm-lib/kcl/benches/digest_benchmark.rs @@ -9,8 +9,7 @@ pub fn bench_digest(c: &mut Criterion) { ("mike_stress_test", MIKE_STRESS_TEST_PROGRAM), ("lsystem", LSYSTEM_PROGRAM), ] { - let tokens = kcl_lib::token::lexer(file).unwrap(); - let prog = kcl_lib::parser::Parser::new(tokens).ast().unwrap(); + let prog = kcl_lib::parser::top_level_parse(file).unwrap(); c.bench_function(&format!("digest_{name}"), move |b| { let prog = prog.clone(); diff --git a/src/wasm-lib/kcl/src/ast/modify.rs b/src/wasm-lib/kcl/src/ast/modify.rs index 432bb8cc6..35f00c9c0 100644 --- a/src/wasm-lib/kcl/src/ast/modify.rs +++ b/src/wasm-lib/kcl/src/ast/modify.rs @@ -16,7 +16,7 @@ use crate::{ executor::{Point2d, SourceRange}, }; -use super::types::Node; +use super::types::{ModuleId, Node}; type Point3d = kcmc::shared::Point3d; @@ -38,6 +38,7 @@ const EPSILON: f64 = 0.015625; // or 2^-6 pub async fn modify_ast_for_sketch( engine: &Arc>, program: &mut Node, + module_id: ModuleId, // The name of the sketch. sketch_name: &str, // The type of plane the sketch is on. `XY` or `XZ`, etc @@ -183,9 +184,7 @@ pub async fn modify_ast_for_sketch( let recasted = program.recast(&FormatOptions::default(), 0); // Re-parse the ast so we get the correct source ranges. - let tokens = crate::token::lexer(&recasted)?; - let parser = crate::parser::Parser::new(tokens); - *program = parser.ast()?; + *program = crate::parser::parse(&recasted, module_id)?; Ok(recasted) } diff --git a/src/wasm-lib/kcl/src/ast/types.rs b/src/wasm-lib/kcl/src/ast/types.rs index 93840b717..01a2d9937 100644 --- a/src/wasm-lib/kcl/src/ast/types.rs +++ b/src/wasm-lib/kcl/src/ast/types.rs @@ -37,6 +37,7 @@ pub(crate) mod digest; pub(crate) mod execute; mod literal_value; mod none; +pub(crate) mod source_range; use digest::Digest; @@ -48,11 +49,14 @@ pub enum Definition<'a> { #[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, Eq, ts_rs::TS, Bake)] #[databake(path = kcl_lib::ast::types)] #[ts(export)] +#[serde(rename_all = "camelCase")] pub struct Node { #[serde(flatten)] pub inner: T, pub start: usize, pub end: usize, + #[serde(default, skip_serializing_if = "ModuleId::is_top_level")] + pub module_id: ModuleId, } impl schemars::JsonSchema for Node { @@ -78,8 +82,13 @@ impl schemars::JsonSchema for Node { } impl Node { - pub fn new(inner: T, start: usize, end: usize) -> Self { - Self { inner, start, end } + pub fn new(inner: T, start: usize, end: usize, module_id: ModuleId) -> Self { + Self { + inner, + start, + end, + module_id, + } } pub fn no_src(inner: T) -> Self { @@ -87,15 +96,21 @@ impl Node { inner, start: 0, end: 0, + module_id: ModuleId::default(), } } - pub fn boxed(inner: T, start: usize, end: usize) -> BoxNode { - Box::new(Node { inner, start, end }) + pub fn boxed(inner: T, start: usize, end: usize, module_id: ModuleId) -> BoxNode { + Box::new(Node { + inner, + start, + end, + module_id, + }) } pub fn as_source_ranges(&self) -> Vec { - vec![SourceRange([self.start, self.end])] + vec![SourceRange([self.start, self.end, self.module_id.as_usize()])] } } @@ -121,19 +136,19 @@ impl fmt::Display for Node { impl From> for crate::executor::SourceRange { fn from(v: Node) -> Self { - Self([v.start, v.end]) + Self([v.start, v.end, v.module_id.as_usize()]) } } impl From<&Node> for crate::executor::SourceRange { fn from(v: &Node) -> Self { - Self([v.start, v.end]) + Self([v.start, v.end, v.module_id.as_usize()]) } } impl From<&BoxNode> for crate::executor::SourceRange { fn from(v: &BoxNode) -> Self { - Self([v.start, v.end]) + Self([v.start, v.end, v.module_id.as_usize()]) } } @@ -505,6 +520,29 @@ impl Program { } } +/// Identifier of a source file. Uses a u32 to keep the size small. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize, ts_rs::TS, JsonSchema, Bake)] +#[cfg_attr(feature = "pyo3", pyo3::pyclass)] +#[databake(path = kcl_lib::ast::types)] +#[ts(export)] +pub struct ModuleId(pub u32); + +impl ModuleId { + pub fn from_usize(id: usize) -> Self { + Self(u32::try_from(id).expect("module ID should fit in a u32")) + } + + pub fn as_usize(&self) -> usize { + usize::try_from(self.0).expect("module ID should fit in a usize") + } + + /// Top-level file is the one being executed. + /// Represented by module ID of 0, i.e. the default value. + pub fn is_top_level(&self) -> bool { + *self == Self::default() + } +} + #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Bake)] #[databake(path = kcl_lib::ast::types)] #[ts(export)] @@ -538,13 +576,13 @@ impl BodyItem { impl From for SourceRange { fn from(item: BodyItem) -> Self { - Self([item.start(), item.end()]) + Self([item.start(), item.end(), item.module_id().as_usize()]) } } impl From<&BodyItem> for SourceRange { fn from(item: &BodyItem) -> Self { - Self([item.start(), item.end()]) + Self([item.start(), item.end(), item.module_id().as_usize()]) } } @@ -568,7 +606,7 @@ pub enum Expr { MemberExpression(BoxNode), UnaryExpression(BoxNode), IfExpression(BoxNode), - None(KclNone), + None(Node), } impl Expr { @@ -758,13 +796,13 @@ impl Expr { impl From for SourceRange { fn from(value: Expr) -> Self { - Self([value.start(), value.end()]) + Self([value.start(), value.end(), value.module_id().as_usize()]) } } impl From<&Expr> for SourceRange { fn from(value: &Expr) -> Self { - Self([value.start(), value.end()]) + Self([value.start(), value.end(), value.module_id().as_usize()]) } } @@ -784,13 +822,13 @@ pub enum BinaryPart { impl From for SourceRange { fn from(value: BinaryPart) -> Self { - Self([value.start(), value.end()]) + Self([value.start(), value.end(), value.module_id().as_usize()]) } } impl From<&BinaryPart> for SourceRange { fn from(value: &BinaryPart) -> Self { - Self([value.start(), value.end()]) + Self([value.start(), value.end(), value.module_id().as_usize()]) } } @@ -2154,13 +2192,13 @@ impl MemberObject { impl From for SourceRange { fn from(obj: MemberObject) -> Self { - Self([obj.start(), obj.end()]) + Self([obj.start(), obj.end(), obj.module_id().as_usize()]) } } impl From<&MemberObject> for SourceRange { fn from(obj: &MemberObject) -> Self { - Self([obj.start(), obj.end()]) + Self([obj.start(), obj.end(), obj.module_id().as_usize()]) } } @@ -2191,13 +2229,13 @@ impl LiteralIdentifier { impl From for SourceRange { fn from(id: LiteralIdentifier) -> Self { - Self([id.start(), id.end()]) + Self([id.start(), id.end(), id.module_id().as_usize()]) } } impl From<&LiteralIdentifier> for SourceRange { fn from(id: &LiteralIdentifier) -> Self { - Self([id.start(), id.end()]) + Self([id.start(), id.end(), id.module_id().as_usize()]) } } @@ -3018,9 +3056,7 @@ fn ghi = (x) => { ghi("things") "#; - let tokens = crate::token::lexer(code).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(code).unwrap(); let folding_ranges = program.get_lsp_folding_ranges(); assert_eq!(folding_ranges.len(), 3); assert_eq!(folding_ranges[0].start_line, 29); @@ -3056,9 +3092,7 @@ fn ghi = (x) => { return x } "#; - let tokens = crate::token::lexer(code).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(code).unwrap(); let symbols = program.get_lsp_symbols(code).unwrap(); assert_eq!(symbols.len(), 7); } @@ -3078,9 +3112,7 @@ const cylinder = startSketchOn('-XZ') }, %) |> extrude(h, %) "#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(some_program_string).unwrap(); let value = program.get_non_code_meta_for_position(50); @@ -3103,9 +3135,7 @@ const cylinder = startSketchOn('-XZ') }, %) |> extrude(h, %) "#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(some_program_string).unwrap(); let value = program.get_non_code_meta_for_position(124); @@ -3118,9 +3148,7 @@ const cylinder = startSketchOn('-XZ') |> startProfileAt([0,0], %) |> xLine(5, %) // lin "#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(some_program_string).unwrap(); let value = program.get_non_code_meta_for_position(86); @@ -3132,9 +3160,7 @@ const cylinder = startSketchOn('-XZ') let some_program_string = r#"fn thing = (arg0: number, arg1: string, tag?: string) => { return arg0 }"#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(some_program_string).unwrap(); // Check the program output for the types of the parameters. let function = program.body.first().unwrap(); @@ -3156,9 +3182,7 @@ const cylinder = startSketchOn('-XZ') let some_program_string = r#"fn thing = (arg0: number[], arg1: string[], tag?: string) => { return arg0 }"#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(some_program_string).unwrap(); // Check the program output for the types of the parameters. let function = program.body.first().unwrap(); @@ -3180,9 +3204,8 @@ const cylinder = startSketchOn('-XZ') let some_program_string = r#"fn thing = (arg0: number[], arg1: {thing: number, things: string[], more?: string}, tag?: string) => { return arg0 }"#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let module_id = ModuleId::default(); + let program = crate::parser::parse(some_program_string, module_id).unwrap(); // Check the program output for the types of the parameters. let function = program.body.first().unwrap(); @@ -3207,6 +3230,7 @@ const cylinder = startSketchOn('-XZ') }, 35, 40, + module_id, ), type_: Some(FnArgType::Primitive(FnArgPrimitive::Number)), optional: false, @@ -3220,6 +3244,7 @@ const cylinder = startSketchOn('-XZ') }, 50, 56, + module_id, ), type_: Some(FnArgType::Array(FnArgPrimitive::String)), optional: false, @@ -3233,6 +3258,7 @@ const cylinder = startSketchOn('-XZ') }, 68, 72, + module_id, ), type_: Some(FnArgType::Primitive(FnArgPrimitive::String)), optional: true, @@ -3249,9 +3275,8 @@ const cylinder = startSketchOn('-XZ') let some_program_string = r#"fn thing = () => {thing: number, things: string[], more?: string} { return 1 }"#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let module_id = ModuleId::default(); + let program = crate::parser::parse(some_program_string, module_id).unwrap(); // Check the program output for the types of the parameters. let function = program.body.first().unwrap(); @@ -3275,6 +3300,7 @@ const cylinder = startSketchOn('-XZ') }, 18, 23, + module_id, ), type_: Some(FnArgType::Primitive(FnArgPrimitive::Number)), optional: false, @@ -3288,6 +3314,7 @@ const cylinder = startSketchOn('-XZ') }, 33, 39, + module_id, ), type_: Some(FnArgType::Array(FnArgPrimitive::String)), optional: false, @@ -3301,6 +3328,7 @@ const cylinder = startSketchOn('-XZ') }, 51, 55, + module_id, ), type_: Some(FnArgType::Primitive(FnArgPrimitive::String)), optional: true, @@ -3349,6 +3377,7 @@ const cylinder = startSketchOn('-XZ') }, start: 0, end: 0, + module_id: ModuleId::default(), }, return_type: None, digest: None, @@ -3375,6 +3404,7 @@ const cylinder = startSketchOn('-XZ') }, start: 0, end: 0, + module_id: ModuleId::default(), }, return_type: None, digest: None, @@ -3412,6 +3442,7 @@ const cylinder = startSketchOn('-XZ') }, start: 0, end: 0, + module_id: ModuleId::default(), }, return_type: None, digest: None, @@ -3429,9 +3460,7 @@ const cylinder = startSketchOn('-XZ') #[tokio::test(flavor = "multi_thread")] async fn test_parse_object_bool() { let some_program_string = r#"some_func({thing: true, other_thing: false})"#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(some_program_string).unwrap(); // We want to get the bool and verify it is a bool. @@ -3479,14 +3508,12 @@ const cylinder = startSketchOn('-XZ') |> startProfileAt([0, 0], %) |> line([5, 5], %, $xLine) "#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let result = parser.ast(); + let result = crate::parser::top_level_parse(some_program_string); assert!(result.is_err()); assert_eq!( result.unwrap_err().to_string(), - r#"syntax: KclErrorDetails { source_ranges: [SourceRange([76, 82])], message: "Cannot assign a tag to a reserved keyword: xLine" }"# + r#"syntax: KclErrorDetails { source_ranges: [SourceRange([76, 82, 0])], message: "Cannot assign a tag to a reserved keyword: xLine" }"# ); } @@ -3496,14 +3523,12 @@ const cylinder = startSketchOn('-XZ') |> startProfileAt([0, 0], %) |> line([5, 5], %, $) "#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let result = parser.ast(); + let result = crate::parser::top_level_parse(some_program_string); assert!(result.is_err()); assert_eq!( result.unwrap_err().to_string(), - r#"syntax: KclErrorDetails { source_ranges: [SourceRange([57, 59])], message: "Unexpected token: |>" }"# + r#"syntax: KclErrorDetails { source_ranges: [SourceRange([57, 59, 0])], message: "Unexpected token: |>" }"# ); } @@ -3513,17 +3538,13 @@ const cylinder = startSketchOn('-XZ') |> startProfileAt([0, 0], %) |> line([5, 5], %) "#; - let prog1_tokens = crate::token::lexer(prog1_string).unwrap(); - let prog1_parser = crate::parser::Parser::new(prog1_tokens); - let prog1_digest = prog1_parser.ast().unwrap().compute_digest(); + let prog1_digest = crate::parser::top_level_parse(prog1_string).unwrap().compute_digest(); let prog2_string = r#"startSketchOn('XY') |> startProfileAt([0, 2], %) |> line([5, 5], %) "#; - let prog2_tokens = crate::token::lexer(prog2_string).unwrap(); - let prog2_parser = crate::parser::Parser::new(prog2_tokens); - let prog2_digest = prog2_parser.ast().unwrap().compute_digest(); + let prog2_digest = crate::parser::top_level_parse(prog2_string).unwrap().compute_digest(); assert!(prog1_digest != prog2_digest); @@ -3531,9 +3552,7 @@ const cylinder = startSketchOn('-XZ') |> startProfileAt([0, 0], %) |> line([5, 5], %) "#; - let prog3_tokens = crate::token::lexer(prog3_string).unwrap(); - let prog3_parser = crate::parser::Parser::new(prog3_tokens); - let prog3_digest = prog3_parser.ast().unwrap().compute_digest(); + let prog3_digest = crate::parser::top_level_parse(prog3_string).unwrap().compute_digest(); assert_eq!(prog1_digest, prog3_digest); } diff --git a/src/wasm-lib/kcl/src/ast/types/condition.rs b/src/wasm-lib/kcl/src/ast/types/condition.rs index 9244a4130..ce6e0c90a 100644 --- a/src/wasm-lib/kcl/src/ast/types/condition.rs +++ b/src/wasm-lib/kcl/src/ast/types/condition.rs @@ -50,7 +50,7 @@ impl Node { impl Node { #[allow(dead_code)] fn source_ranges(&self) -> Vec { - vec![SourceRange([self.start, self.end])] + vec![SourceRange([self.start, self.end, self.module_id.as_usize()])] } } diff --git a/src/wasm-lib/kcl/src/ast/types/execute.rs b/src/wasm-lib/kcl/src/ast/types/execute.rs index d2934305d..b90ee9da7 100644 --- a/src/wasm-lib/kcl/src/ast/types/execute.rs +++ b/src/wasm-lib/kcl/src/ast/types/execute.rs @@ -235,7 +235,7 @@ pub(crate) async fn execute_pipe_body( // they use the % from the parent. After all, this pipe expression hasn't been executed yet, so it doesn't have any % value // of its own. let meta = Metadata { - source_range: SourceRange([first.start(), first.end()]), + source_range: SourceRange::from(first), }; let output = ctx .execute_expr(first, exec_state, &meta, StatementKind::Expression) @@ -285,7 +285,7 @@ async fn inner_execute_pipe_body( | Expr::None(_) => {} }; let metadata = Metadata { - source_range: SourceRange([expression.start(), expression.end()]), + source_range: SourceRange::from(expression), }; let output = ctx .execute_expr(expression, exec_state, &metadata, StatementKind::Expression) diff --git a/src/wasm-lib/kcl/src/ast/types/none.rs b/src/wasm-lib/kcl/src/ast/types/none.rs index ea4bd7fc8..6e6024331 100644 --- a/src/wasm-lib/kcl/src/ast/types/none.rs +++ b/src/wasm-lib/kcl/src/ast/types/none.rs @@ -6,9 +6,11 @@ use serde::{Deserialize, Serialize}; use crate::{ ast::types::ConstraintLevel, - executor::{KclValue, SourceRange, UserVal}, + executor::{KclValue, UserVal}, }; +use super::Node; + const KCL_NONE_ID: &str = "KCL_NONE_ID"; /// KCL value for an optional parameter which was not given an argument. @@ -19,9 +21,6 @@ const KCL_NONE_ID: &str = "KCL_NONE_ID"; #[ts(export)] #[serde(tag = "type")] pub struct KclNone { - // TODO: Convert this to be an Option. - pub start: usize, - pub end: usize, #[serde(deserialize_with = "deser_private")] #[ts(skip)] #[schemars(skip)] @@ -29,12 +28,8 @@ pub struct KclNone { } impl KclNone { - pub fn new(start: usize, end: usize) -> Self { - Self { - start, - end, - __private: Private {}, - } + pub fn new() -> Self { + Self { __private: Private {} } } } @@ -63,12 +58,6 @@ where } } -impl From<&KclNone> for SourceRange { - fn from(v: &KclNone) -> Self { - Self([v.start, v.end]) - } -} - impl From<&KclNone> for UserVal { fn from(none: &KclNone) -> Self { UserVal { @@ -85,16 +74,18 @@ impl From<&KclNone> for KclValue { } } -impl KclNone { - pub fn source_range(&self) -> SourceRange { - SourceRange([self.start, self.end]) +impl From<&Node> for KclValue { + fn from(none: &Node) -> Self { + Self::from(&none.inner) } +} +impl Node { /// Get the constraint level. /// KCL None is never constrained. pub fn get_constraint_level(&self) -> ConstraintLevel { ConstraintLevel::None { - source_ranges: vec![self.source_range()], + source_ranges: self.as_source_ranges(), } } } diff --git a/src/wasm-lib/kcl/src/ast/types/source_range.rs b/src/wasm-lib/kcl/src/ast/types/source_range.rs new file mode 100644 index 000000000..f68210d03 --- /dev/null +++ b/src/wasm-lib/kcl/src/ast/types/source_range.rs @@ -0,0 +1,66 @@ +use super::{BinaryPart, BodyItem, Expr, LiteralIdentifier, MemberObject, ModuleId}; + +impl BodyItem { + pub fn module_id(&self) -> ModuleId { + match self { + BodyItem::ImportStatement(stmt) => stmt.module_id, + BodyItem::ExpressionStatement(expression_statement) => expression_statement.module_id, + BodyItem::VariableDeclaration(variable_declaration) => variable_declaration.module_id, + BodyItem::ReturnStatement(return_statement) => return_statement.module_id, + } + } +} + +impl Expr { + pub fn module_id(&self) -> ModuleId { + match self { + Expr::Literal(literal) => literal.module_id, + Expr::Identifier(identifier) => identifier.module_id, + Expr::TagDeclarator(tag) => tag.module_id, + Expr::BinaryExpression(binary_expression) => binary_expression.module_id, + Expr::FunctionExpression(function_expression) => function_expression.module_id, + Expr::CallExpression(call_expression) => call_expression.module_id, + Expr::PipeExpression(pipe_expression) => pipe_expression.module_id, + Expr::PipeSubstitution(pipe_substitution) => pipe_substitution.module_id, + Expr::ArrayExpression(array_expression) => array_expression.module_id, + Expr::ArrayRangeExpression(array_range) => array_range.module_id, + Expr::ObjectExpression(object_expression) => object_expression.module_id, + Expr::MemberExpression(member_expression) => member_expression.module_id, + Expr::UnaryExpression(unary_expression) => unary_expression.module_id, + Expr::IfExpression(expr) => expr.module_id, + Expr::None(none) => none.module_id, + } + } +} + +impl BinaryPart { + pub fn module_id(&self) -> ModuleId { + match self { + BinaryPart::Literal(literal) => literal.module_id, + BinaryPart::Identifier(identifier) => identifier.module_id, + BinaryPart::BinaryExpression(binary_expression) => binary_expression.module_id, + BinaryPart::CallExpression(call_expression) => call_expression.module_id, + BinaryPart::UnaryExpression(unary_expression) => unary_expression.module_id, + BinaryPart::MemberExpression(member_expression) => member_expression.module_id, + BinaryPart::IfExpression(e) => e.module_id, + } + } +} + +impl MemberObject { + pub fn module_id(&self) -> ModuleId { + match self { + MemberObject::MemberExpression(member_expression) => member_expression.module_id, + MemberObject::Identifier(identifier) => identifier.module_id, + } + } +} + +impl LiteralIdentifier { + pub fn module_id(&self) -> ModuleId { + match self { + LiteralIdentifier::Identifier(identifier) => identifier.module_id, + LiteralIdentifier::Literal(literal) => literal.module_id, + } + } +} diff --git a/src/wasm-lib/kcl/src/errors.rs b/src/wasm-lib/kcl/src/errors.rs index 9f8dea691..b0caaeb3b 100644 --- a/src/wasm-lib/kcl/src/errors.rs +++ b/src/wasm-lib/kcl/src/errors.rs @@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize}; use thiserror::Error; use tower_lsp::lsp_types::{Diagnostic, DiagnosticSeverity}; -use crate::{executor::SourceRange, lsp::IntoDiagnostic}; +use crate::{ast::types::ModuleId, executor::SourceRange, lsp::IntoDiagnostic}; #[derive(Error, Debug, Serialize, Deserialize, ts_rs::TS, Clone, PartialEq, Eq)] #[ts(export)] @@ -147,6 +147,13 @@ impl IntoDiagnostic for KclError { let message = self.get_message(); let source_ranges = self.source_ranges(); + // Limit to only errors in the top-level file. + let module_id = ModuleId::default(); + let source_ranges = source_ranges + .iter() + .filter(|r| r.module_id() == module_id) + .collect::>(); + Diagnostic { range: source_ranges.first().map(|r| r.to_lsp_range(code)).unwrap_or_default(), severity: Some(self.severity()), diff --git a/src/wasm-lib/kcl/src/executor.rs b/src/wasm-lib/kcl/src/executor.rs index 3e817d445..ecbd4f4b9 100644 --- a/src/wasm-lib/kcl/src/executor.rs +++ b/src/wasm-lib/kcl/src/executor.rs @@ -7,6 +7,7 @@ use std::{ use anyhow::Result; use async_recursion::async_recursion; +use indexmap::IndexMap; use kcmc::{ each_cmd as mcmd, ok_response::{output::TakeSnapshot, OkModelingCmdResponse}, @@ -26,8 +27,8 @@ type Point3D = kcmc::shared::Point3d; use crate::{ ast::types::{ - human_friendly_type, BodyItem, Expr, FunctionExpression, ItemVisibility, KclNone, Node, NodeRef, Program, - TagDeclarator, TagNode, + human_friendly_type, BodyItem, Expr, FunctionExpression, ItemVisibility, KclNone, ModuleId, Node, NodeRef, + Program, TagDeclarator, TagNode, }, engine::{EngineManager, ExecutionKind}, errors::{KclError, KclErrorDetails}, @@ -55,11 +56,32 @@ pub struct ExecState { /// The stack of import statements for detecting circular module imports. /// If this is empty, we're not currently executing an import statement. pub import_stack: Vec, + /// Map from source file absolute path to module ID. + pub path_to_source_id: IndexMap, + /// Map from module ID to module info. + pub module_infos: IndexMap, /// The directory of the current project. This is used for resolving import /// paths. If None is given, the current working directory is used. pub project_directory: Option, } +impl ExecState { + pub fn add_module(&mut self, path: std::path::PathBuf) -> ModuleId { + // Need to avoid borrowing self in the closure. + let new_module_id = ModuleId::from_usize(self.path_to_source_id.len()); + let mut is_new = false; + let id = *self.path_to_source_id.entry(path.clone()).or_insert_with(|| { + is_new = true; + new_module_id + }); + if is_new { + let module_info = ModuleInfo { id, path }; + self.module_infos.insert(id, module_info); + } + id + } +} + #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)] #[ts(export)] #[serde(rename_all = "camelCase")] @@ -1373,21 +1395,33 @@ pub enum BodyType { Block, } +/// Info about a module. Right now, this is pretty minimal. We hope to cache +/// modules here in the future. +#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize, ts_rs::TS, JsonSchema)] +#[cfg_attr(feature = "pyo3", pyo3::pyclass)] +#[ts(export)] +pub struct ModuleInfo { + /// The ID of the module. + id: ModuleId, + /// Absolute path of the module's source file. + path: std::path::PathBuf, +} + #[derive(Debug, Default, Deserialize, Serialize, PartialEq, Copy, Clone, ts_rs::TS, JsonSchema, Hash, Eq)] #[cfg_attr(feature = "pyo3", pyo3::pyclass)] #[ts(export)] -pub struct SourceRange(#[ts(type = "[number, number]")] pub [usize; 2]); +pub struct SourceRange(#[ts(type = "[number, number]")] pub [usize; 3]); -impl From<[usize; 2]> for SourceRange { - fn from(value: [usize; 2]) -> Self { +impl From<[usize; 3]> for SourceRange { + fn from(value: [usize; 3]) -> Self { Self(value) } } impl SourceRange { /// Create a new source range. - pub fn new(start: usize, end: usize) -> Self { - Self([start, end]) + pub fn new(start: usize, end: usize, module_id: ModuleId) -> Self { + Self([start, end, module_id.as_usize()]) } /// Get the start of the range. @@ -1400,6 +1434,11 @@ impl SourceRange { self.0[1] } + /// Get the module ID of the range. + pub fn module_id(&self) -> ModuleId { + ModuleId::from_usize(self.0[2]) + } + /// Check if the range contains a position. pub fn contains(&self, pos: usize) -> bool { pos >= self.start() && pos <= self.end() @@ -1533,7 +1572,7 @@ impl From for Metadata { impl From> for Metadata { fn from(node: NodeRef<'_, T>) -> Self { Self { - source_range: SourceRange::new(node.start, node.end), + source_range: SourceRange::new(node.start, node.end, node.module_id), } } } @@ -2171,6 +2210,8 @@ impl ExecutorContext { project_directory, ..Default::default() }; + // TODO: Use the top-level file's path. + exec_state.add_module(std::path::PathBuf::from("")); // Before we even start executing the program, set the units. self.engine .batch_modeling_cmd( @@ -2210,6 +2251,13 @@ impl ExecutorContext { BodyItem::ImportStatement(import_stmt) => { let source_range = SourceRange::from(import_stmt); let path = import_stmt.path.clone(); + // Empty path is used by the top-level module. + if path.is_empty() { + return Err(KclError::Semantic(KclErrorDetails { + message: "import path cannot be empty".to_owned(), + source_ranges: vec![source_range], + })); + } let resolved_path = if let Some(project_dir) = &exec_state.project_directory { std::path::PathBuf::from(project_dir).join(&path) } else { @@ -2230,8 +2278,9 @@ impl ExecutorContext { source_ranges: vec![import_stmt.into()], })); } + let module_id = exec_state.add_module(resolved_path.clone()); let source = self.fs.read_to_string(&resolved_path, source_range).await?; - let program = crate::parser::parse(&source)?; + let program = crate::parser::parse(&source, module_id)?; let (module_memory, module_exports) = { exec_state.import_stack.push(resolved_path.clone()); let original_execution = self.engine.replace_execution_kind(ExecutionKind::Isolated); @@ -2359,7 +2408,7 @@ impl ExecutorContext { // True here tells the engine to flush all the end commands as well like fillets // and chamfers where the engine would otherwise eat the ID of the segments. true, - SourceRange([program.end, program.end]), + SourceRange([program.end, program.end, program.module_id.as_usize()]), ) .await?; } @@ -2525,7 +2574,12 @@ fn assign_args_to_params( if param.optional { // If the corresponding parameter is optional, // then it's fine, the user doesn't need to supply it. - let none = KclNone::new(param.identifier.start, param.identifier.end); + let none = Node { + inner: KclNone::new(), + start: param.identifier.start, + end: param.identifier.end, + module_id: param.identifier.module_id, + }; fn_memory.add( ¶m.identifier.name, KclValue::from(&none), @@ -2586,9 +2640,8 @@ mod tests { use crate::ast::types::{Identifier, Node, Parameter}; pub async fn parse_execute(code: &str) -> Result { - let tokens = crate::token::lexer(code)?; - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast()?; + let program = crate::parser::top_level_parse(code)?; + let ctx = ExecutorContext { engine: Arc::new(Box::new(crate::engine::conn_mock::EngineConnection::new().await?)), fs: Arc::new(crate::fs::FileManager::new()), @@ -3027,7 +3080,7 @@ const answer = returnX()"#; err, KclError::UndefinedValue(KclErrorDetails { message: "memory item key `x` is not defined".to_owned(), - source_ranges: vec![SourceRange([64, 65]), SourceRange([97, 106])], + source_ranges: vec![SourceRange([64, 65, 0]), SourceRange([97, 106, 0])], }), ); } @@ -3062,7 +3115,7 @@ let shape = layer() |> patternTransform(10, transform, %) err, KclError::UndefinedValue(KclErrorDetails { message: "memory item key `x` is not defined".to_owned(), - source_ranges: vec![SourceRange([80, 81])], + source_ranges: vec![SourceRange([80, 81, 0])], }), ); } @@ -3317,7 +3370,7 @@ let notNull = !myNull parse_execute(code1).await.unwrap_err().downcast::().unwrap(), KclError::Semantic(KclErrorDetails { message: "Cannot apply unary operator ! to non-boolean value: null".to_owned(), - source_ranges: vec![SourceRange([56, 63])], + source_ranges: vec![SourceRange([56, 63, 0])], }) ); @@ -3326,7 +3379,7 @@ let notNull = !myNull parse_execute(code2).await.unwrap_err().downcast::().unwrap(), KclError::Semantic(KclErrorDetails { message: "Cannot apply unary operator ! to non-boolean value: 0".to_owned(), - source_ranges: vec![SourceRange([14, 16])], + source_ranges: vec![SourceRange([14, 16, 0])], }) ); @@ -3337,7 +3390,7 @@ let notEmptyString = !"" parse_execute(code3).await.unwrap_err().downcast::().unwrap(), KclError::Semantic(KclErrorDetails { message: "Cannot apply unary operator ! to non-boolean value: \"\"".to_owned(), - source_ranges: vec![SourceRange([22, 25])], + source_ranges: vec![SourceRange([22, 25, 0])], }) ); @@ -3349,7 +3402,7 @@ let notMember = !obj.a parse_execute(code4).await.unwrap_err().downcast::().unwrap(), KclError::Semantic(KclErrorDetails { message: "Cannot apply unary operator ! to non-boolean value: 1".to_owned(), - source_ranges: vec![SourceRange([36, 42])], + source_ranges: vec![SourceRange([36, 42, 0])], }) ); @@ -3360,7 +3413,7 @@ let notArray = !a"; parse_execute(code5).await.unwrap_err().downcast::().unwrap(), KclError::Semantic(KclErrorDetails { message: "Cannot apply unary operator ! to non-boolean value: []".to_owned(), - source_ranges: vec![SourceRange([27, 29])], + source_ranges: vec![SourceRange([27, 29, 0])], }) ); @@ -3371,7 +3424,7 @@ let notObject = !x"; parse_execute(code6).await.unwrap_err().downcast::().unwrap(), KclError::Semantic(KclErrorDetails { message: "Cannot apply unary operator ! to non-boolean value: {}".to_owned(), - source_ranges: vec![SourceRange([28, 30])], + source_ranges: vec![SourceRange([28, 30, 0])], }) ); @@ -3424,7 +3477,7 @@ let notTagIdentifier = !myTag"; parse_execute(code10).await.unwrap_err().downcast::().unwrap(), KclError::Syntax(KclErrorDetails { message: "Unexpected token: !".to_owned(), - source_ranges: vec![SourceRange([14, 15])], + source_ranges: vec![SourceRange([14, 15, 0])], }) ); @@ -3437,7 +3490,7 @@ let notPipeSub = 1 |> identity(!%))"; parse_execute(code11).await.unwrap_err().downcast::().unwrap(), KclError::Syntax(KclErrorDetails { message: "Unexpected token: |>".to_owned(), - source_ranges: vec![SourceRange([54, 56])], + source_ranges: vec![SourceRange([54, 56, 0])], }) ); @@ -3483,7 +3536,7 @@ test([0, 0]) assert!(result.is_err()); assert_eq!( result.unwrap_err().to_string(), - r#"undefined value: KclErrorDetails { source_ranges: [SourceRange([10, 34])], message: "Result of user-defined function test is undefined" }"#.to_owned() + r#"undefined value: KclErrorDetails { source_ranges: [SourceRange([10, 34, 0])], message: "Result of user-defined function test is undefined" }"#.to_owned() ); } @@ -3600,7 +3653,7 @@ let w = f() + f() vec![req_param("x")], vec![], Err(KclError::Semantic(KclErrorDetails { - source_ranges: vec![SourceRange([0, 0])], + source_ranges: vec![SourceRange([0, 0, 0])], message: "Expected 1 arguments, got 0".to_owned(), })), ), @@ -3618,7 +3671,7 @@ let w = f() + f() vec![req_param("x"), opt_param("y")], vec![], Err(KclError::Semantic(KclErrorDetails { - source_ranges: vec![SourceRange([0, 0])], + source_ranges: vec![SourceRange([0, 0, 0])], message: "Expected 1-2 arguments, got 0".to_owned(), })), ), @@ -3645,7 +3698,7 @@ let w = f() + f() vec![req_param("x"), opt_param("y")], vec![mem(1), mem(2), mem(3)], Err(KclError::Semantic(KclErrorDetails { - source_ranges: vec![SourceRange([0, 0])], + source_ranges: vec![SourceRange([0, 0, 0])], message: "Expected 1-2 arguments, got 3".to_owned(), })), ), @@ -3661,6 +3714,7 @@ let w = f() + f() }, start: 0, end: 0, + module_id: ModuleId::default(), }, return_type: None, digest: None, diff --git a/src/wasm-lib/kcl/src/lint/checks/camel_case.rs b/src/wasm-lib/kcl/src/lint/checks/camel_case.rs index 872a39539..1f68c1eff 100644 --- a/src/wasm-lib/kcl/src/lint/checks/camel_case.rs +++ b/src/wasm-lib/kcl/src/lint/checks/camel_case.rs @@ -29,7 +29,10 @@ fn lint_lower_camel_case_var(decl: &VariableDeclarator) -> Result Result Result> { return Ok(vec![]); }; - let call_source_range = SourceRange::new(call.start, call.end); + let call_source_range = SourceRange::new(call.start, call.end, call.module_id); Ok(vec![Z0003.at( format!( "custom plane in startSketchOn; offsetPlane from {} would work here", diff --git a/src/wasm-lib/kcl/src/lint/checks/std_lib_args.rs b/src/wasm-lib/kcl/src/lint/checks/std_lib_args.rs index 6b12626f2..0a8f185e7 100644 --- a/src/wasm-lib/kcl/src/lint/checks/std_lib_args.rs +++ b/src/wasm-lib/kcl/src/lint/checks/std_lib_args.rs @@ -28,7 +28,7 @@ fn lint_too_many_args_std_lib_function( if exp.arguments.len() != 2 { findings.push(Z0002.at( format!("expected 2 arguments, found {}", exp.arguments.len()), - SourceRange::new(exp.start, exp.end), + SourceRange::new(exp.start, exp.end, exp.module_id), )); } return Ok(findings); @@ -38,7 +38,7 @@ fn lint_too_many_args_std_lib_function( if exp.arguments.len() < 2 { findings.push(Z0002.at( format!("expected at least 2 arguments, found {}", exp.arguments.len()), - SourceRange::new(exp.start, exp.end), + SourceRange::new(exp.start, exp.end, exp.module_id), )); } return Ok(findings); @@ -48,7 +48,7 @@ fn lint_too_many_args_std_lib_function( if exp.arguments.len() > fn_args_len { findings.push(Z0002.at( format!("expected {} arguments, found {}", fn_args_len, exp.arguments.len()), - SourceRange::new(exp.start, exp.end), + SourceRange::new(exp.start, exp.end, exp.module_id), )); } diff --git a/src/wasm-lib/kcl/src/lint/rule.rs b/src/wasm-lib/kcl/src/lint/rule.rs index 1b11e605f..014d90f6f 100644 --- a/src/wasm-lib/kcl/src/lint/rule.rs +++ b/src/wasm-lib/kcl/src/lint/rule.rs @@ -182,9 +182,7 @@ mod test { macro_rules! assert_no_finding { ( $check:expr, $finding:expr, $kcl:expr ) => { - let tokens = $crate::token::lexer($kcl).unwrap(); - let parser = $crate::parser::Parser::new(tokens); - let prog = parser.ast().unwrap(); + let prog = $crate::parser::top_level_parse($kcl).unwrap(); for discovered_finding in prog.lint($check).unwrap() { if discovered_finding.finding == $finding { assert!(false, "Finding {:?} was emitted", $finding.code); @@ -195,9 +193,7 @@ mod test { macro_rules! assert_finding { ( $check:expr, $finding:expr, $kcl:expr ) => { - let tokens = $crate::token::lexer($kcl).unwrap(); - let parser = $crate::parser::Parser::new(tokens); - let prog = parser.ast().unwrap(); + let prog = $crate::parser::top_level_parse($kcl).unwrap(); for discovered_finding in prog.lint($check).unwrap() { if discovered_finding.finding == $finding { diff --git a/src/wasm-lib/kcl/src/lsp/kcl/mod.rs b/src/wasm-lib/kcl/src/lsp/kcl/mod.rs index 3d3948e15..44c50ab6d 100644 --- a/src/wasm-lib/kcl/src/lsp/kcl/mod.rs +++ b/src/wasm-lib/kcl/src/lsp/kcl/mod.rs @@ -40,7 +40,7 @@ use tower_lsp::{ }; use crate::{ - ast::types::{Expr, Node, NodeRef, VariableKind}, + ast::types::{Expr, ModuleId, Node, NodeRef, VariableKind}, executor::{IdGenerator, SourceRange}, lsp::{backend::Backend as _, util::IntoDiagnostic}, parser::PIPE_OPERATOR, @@ -188,7 +188,8 @@ impl crate::lsp::backend::Backend for Backend { // We already updated the code map in the shared backend. // Lets update the tokens. - let tokens = match crate::token::lexer(¶ms.text) { + let module_id = ModuleId::default(); + let tokens = match crate::token::lexer(¶ms.text, module_id) { Ok(tokens) => tokens, Err(err) => { self.add_to_diagnostics(¶ms, &[err], true).await; @@ -1235,7 +1236,8 @@ impl LanguageServer for Backend { // Parse the ast. // I don't know if we need to do this again since it should be updated in the context. // But I figure better safe than sorry since this will write back out to the file. - let Ok(tokens) = crate::token::lexer(current_code) else { + let module_id = ModuleId::default(); + let Ok(tokens) = crate::token::lexer(current_code, module_id) else { return Ok(None); }; let parser = crate::parser::Parser::new(tokens); @@ -1251,7 +1253,7 @@ impl LanguageServer for Backend { }, 0, ); - let source_range = SourceRange([0, current_code.len()]); + let source_range = SourceRange::new(0, current_code.len(), module_id); let range = source_range.to_lsp_range(current_code); Ok(Some(vec![TextEdit { new_text: recast, @@ -1272,7 +1274,8 @@ impl LanguageServer for Backend { // Parse the ast. // I don't know if we need to do this again since it should be updated in the context. // But I figure better safe than sorry since this will write back out to the file. - let Ok(tokens) = crate::token::lexer(current_code) else { + let module_id = ModuleId::default(); + let Ok(tokens) = crate::token::lexer(current_code, module_id) else { return Ok(None); }; let parser = crate::parser::Parser::new(tokens); @@ -1286,7 +1289,7 @@ impl LanguageServer for Backend { ast.rename_symbol(¶ms.new_name, pos); // Now recast it. let recast = ast.recast(&Default::default(), 0); - let source_range = SourceRange([0, current_code.len() - 1]); + let source_range = SourceRange::new(0, current_code.len() - 1, module_id); let range = source_range.to_lsp_range(current_code); Ok(Some(WorkspaceEdit { changes: Some(HashMap::from([( diff --git a/src/wasm-lib/kcl/src/parser.rs b/src/wasm-lib/kcl/src/parser.rs index 9fa74fcf9..f0de970b3 100644 --- a/src/wasm-lib/kcl/src/parser.rs +++ b/src/wasm-lib/kcl/src/parser.rs @@ -1,5 +1,5 @@ use crate::{ - ast::types::{Node, Program}, + ast::types::{ModuleId, Node, Program}, errors::{KclError, KclErrorDetails}, executor::SourceRange, token::{Token, TokenType}, @@ -12,9 +12,15 @@ pub(crate) mod parser_impl; pub const PIPE_SUBSTITUTION_OPERATOR: &str = "%"; pub const PIPE_OPERATOR: &str = "|>"; +/// Parse the given KCL code into an AST. This is the top-level. +pub fn top_level_parse(code: &str) -> Result, KclError> { + let module_id = ModuleId::default(); + parse(code, module_id) +} + /// Parse the given KCL code into an AST. -pub fn parse(code: &str) -> Result, KclError> { - let tokens = crate::token::lexer(code)?; +pub fn parse(code: &str, module_id: ModuleId) -> Result, KclError> { + let tokens = crate::token::lexer(code, module_id)?; let parser = Parser::new(tokens); parser.ast() } diff --git a/src/wasm-lib/kcl/src/parser/bad_inputs.rs b/src/wasm-lib/kcl/src/parser/bad_inputs.rs index 8fa31fb4f..e41656466 100644 --- a/src/wasm-lib/kcl/src/parser/bad_inputs.rs +++ b/src/wasm-lib/kcl/src/parser/bad_inputs.rs @@ -5,7 +5,8 @@ mod tests { ($func_name:ident, $test_kcl_program:expr) => { #[test] fn $func_name() { - if let Ok(v) = $crate::token::lexer($test_kcl_program) { + let module_id = $crate::parser::ModuleId::default(); + if let Ok(v) = $crate::token::lexer($test_kcl_program, module_id) { let _ = $crate::parser::Parser::new(v).ast(); } } diff --git a/src/wasm-lib/kcl/src/parser/math.rs b/src/wasm-lib/kcl/src/parser/math.rs index e4744ed3d..cec3cbd97 100644 --- a/src/wasm-lib/kcl/src/parser/math.rs +++ b/src/wasm-lib/kcl/src/parser/math.rs @@ -30,6 +30,7 @@ fn evaluate(rpn: Vec) -> Result, K }; let start = left.start(); let end = right.end(); + let module_id = left.module_id(); BinaryPart::BinaryExpression(Node::boxed( BinaryExpression { @@ -40,6 +41,7 @@ fn evaluate(rpn: Vec) -> Result, K }, start, end, + module_id, )) } BinaryExpressionToken::Operand(o) => o, @@ -60,11 +62,11 @@ fn source_range(tokens: &[BinaryExpressionToken]) -> Vec { .iter() .filter_map(|op| match op { BinaryExpressionToken::Operator(_) => None, - BinaryExpressionToken::Operand(o) => Some((o.start(), o.end())), + BinaryExpressionToken::Operand(o) => Some((o.start(), o.end(), o.module_id())), }) .collect(); match (sources.first(), sources.last()) { - (Some((start, _)), Some((_, end))) => vec![SourceRange([*start, *end])], + (Some((start, _, module_id)), Some((_, end, _))) => vec![SourceRange([*start, *end, module_id.as_usize()])], _ => Vec::new(), } } @@ -124,7 +126,7 @@ impl From for BinaryExpressionToken { #[cfg(test)] mod tests { use super::*; - use crate::ast::types::Literal; + use crate::ast::types::{Literal, ModuleId}; #[test] fn parse_and_evaluate() { @@ -138,6 +140,7 @@ mod tests { }, 0, 0, + ModuleId::default(), ))) } let tests: Vec> = vec![ @@ -158,6 +161,7 @@ mod tests { }, 0, 0, + ModuleId::default(), )) .into(), BinaryOperator::Pow.into(), diff --git a/src/wasm-lib/kcl/src/parser/parser_impl.rs b/src/wasm-lib/kcl/src/parser/parser_impl.rs index 9e63a8f91..339eef9d4 100644 --- a/src/wasm-lib/kcl/src/parser/parser_impl.rs +++ b/src/wasm-lib/kcl/src/parser/parser_impl.rs @@ -91,6 +91,7 @@ fn non_code_node(i: TokenSlice) -> PResult> { }, leading_whitespace.start, node.end + 1, + node.module_id, )), _ => None, }) @@ -124,7 +125,12 @@ fn non_code_node_no_leading_whitespace(i: TokenSlice) -> PResult return None, }; - Some(Node::new(NonCodeNode { value, digest: None }, token.start, token.end)) + Some(Node::new( + NonCodeNode { value, digest: None }, + token.start, + token.end, + token.module_id, + )) } }) .context(expected("Non-code token (comments or whitespace)")) @@ -194,6 +200,7 @@ fn pipe_expression(i: TokenSlice) -> PResult> { Ok(Node { start: values.first().unwrap().start(), end: values.last().unwrap().end().max(max_noncode_end), + module_id: values.first().unwrap().module_id(), inner: PipeExpression { body: values, non_code_meta, @@ -222,6 +229,7 @@ fn bool_value(i: TokenSlice) -> PResult> { }, token.start, token.end, + token.module_id, ))) } @@ -255,6 +263,7 @@ pub fn string_literal(i: TokenSlice) -> PResult> { }, token.start, token.end, + token.module_id, )) } @@ -290,6 +299,7 @@ pub(crate) fn unsigned_number_literal(i: TokenSlice) -> PResult> { }, token.start, token.end, + token.module_id, )) } @@ -333,7 +343,7 @@ fn operand(i: TokenSlice) -> PResult { const TODO_783: &str = "found a value, but this kind of value cannot be used as the operand to an operator yet (see https://github.com/KittyCAD/modeling-app/issues/783)"; let op = possible_operands .try_map(|part| { - let source_ranges = vec![SourceRange([part.start(), part.end()])]; + let source_ranges = vec![SourceRange::from(&part)]; let expr = match part { // TODO: these should be valid operands eventually, // users should be able to run "let x = f() + g()" @@ -458,6 +468,7 @@ fn shebang(i: TokenSlice) -> PResult> { }, 0, tokens.last().unwrap().end, + tokens.first().unwrap().module_id, )) } @@ -486,7 +497,8 @@ fn array(i: TokenSlice) -> PResult { /// Match an empty array. fn array_empty(i: TokenSlice) -> PResult> { - let start = open_bracket(i)?.start; + let open = open_bracket(i)?; + let start = open.start; ignore_whitespace(i); let end = close_bracket(i)?.end; Ok(Node::new( @@ -497,6 +509,7 @@ fn array_empty(i: TokenSlice) -> PResult> { }, start, end, + open.module_id, )) } @@ -512,7 +525,8 @@ fn array_separator(i: TokenSlice) -> PResult<()> { } pub(crate) fn array_elem_by_elem(i: TokenSlice) -> PResult> { - let start = open_bracket(i)?.start; + let open = open_bracket(i)?; + let start = open.start; ignore_whitespace(i); let elements: Vec<_> = repeat( 0.., @@ -554,11 +568,13 @@ pub(crate) fn array_elem_by_elem(i: TokenSlice) -> PResult }, start, end, + open.module_id, )) } fn array_end_start(i: TokenSlice) -> PResult> { - let start = open_bracket(i)?.start; + let open = open_bracket(i)?; + let start = open.start; ignore_whitespace(i); let start_element = expression.parse_next(i)?; ignore_whitespace(i); @@ -576,6 +592,7 @@ fn array_end_start(i: TokenSlice) -> PResult> { }, start, end, + open.module_id, )) } @@ -596,6 +613,7 @@ fn object_property(i: TokenSlice) -> PResult> { Ok(Node { start: key.start, end: expr.end(), + module_id: key.module_id, inner: ObjectProperty { key, value: expr, @@ -617,7 +635,8 @@ fn property_separator(i: TokenSlice) -> PResult<()> { /// Parse a KCL object value. pub(crate) fn object(i: TokenSlice) -> PResult> { - let start = open_brace(i)?.start; + let open = open_brace(i)?; + let start = open.start; ignore_whitespace(i); let properties: Vec<_> = repeat( 0.., @@ -661,6 +680,7 @@ pub(crate) fn object(i: TokenSlice) -> PResult> { }, start, end, + open.module_id, )) } @@ -668,7 +688,12 @@ pub(crate) fn object(i: TokenSlice) -> PResult> { fn pipe_sub(i: TokenSlice) -> PResult> { any.try_map(|token: Token| { if matches!(token.token_type, TokenType::Operator) && token.value == PIPE_SUBSTITUTION_OPERATOR { - Ok(Node::new(PipeSubstitution { digest: None }, token.start, token.end)) + Ok(Node::new( + PipeSubstitution { digest: None }, + token.start, + token.end, + token.module_id, + )) } else { Err(KclError::Syntax(KclErrorDetails { source_ranges: token.as_source_ranges(), @@ -684,10 +709,10 @@ fn pipe_sub(i: TokenSlice) -> PResult> { } fn else_if(i: TokenSlice) -> PResult> { - let start = any + let else_ = any .try_map(|token: Token| { if matches!(token.token_type, TokenType::Keyword) && token.value == "else" { - Ok(token.start) + Ok(token) } else { Err(KclError::Syntax(KclErrorDetails { source_ranges: token.as_source_ranges(), @@ -728,16 +753,17 @@ fn else_if(i: TokenSlice) -> PResult> { then_val, digest: Default::default(), }, - start, + else_.start, end, + else_.module_id, )) } fn if_expr(i: TokenSlice) -> PResult> { - let start = any + let if_ = any .try_map(|token: Token| { if matches!(token.token_type, TokenType::Keyword) && token.value == "if" { - Ok(token.start) + Ok(token) } else { Err(KclError::Syntax(KclErrorDetails { source_ranges: token.as_source_ranges(), @@ -795,8 +821,9 @@ fn if_expr(i: TokenSlice) -> PResult> { final_else, digest: Default::default(), }, - start, + if_.start, end, + if_.module_id, )) } @@ -806,7 +833,8 @@ fn if_expr(i: TokenSlice) -> PResult> { // return x // } fn function_expression(i: TokenSlice) -> PResult> { - let start = open_paren(i)?.start; + let open = open_paren(i)?; + let start = open.start; let params = parameters(i)?; close_paren(i)?; ignore_whitespace(i); @@ -827,6 +855,7 @@ fn function_expression(i: TokenSlice) -> PResult> { }, start, end, + open.module_id, )) } @@ -874,6 +903,7 @@ fn member_expression(i: TokenSlice) -> PResult> { // which is guaranteed to have >=1 elements. let (property, end, computed) = members.remove(0); let start = id.start; + let module_id = id.module_id; let initial_member_expression = Node::new( MemberExpression { object: MemberObject::Identifier(Box::new(id)), @@ -883,6 +913,7 @@ fn member_expression(i: TokenSlice) -> PResult> { }, start, end, + module_id, ); // Each remaining member wraps the current member expression inside another member expression. @@ -900,6 +931,7 @@ fn member_expression(i: TokenSlice) -> PResult> { }, start, end, + module_id, ) })) } @@ -934,7 +966,12 @@ fn noncode_just_after_code(i: TokenSlice) -> PResult> { x @ NonCodeValue::NewLineBlockComment { .. } => x, x @ NonCodeValue::NewLine => x, }; - Node::new(NonCodeNode { value, ..nc.inner }, nc.start.saturating_sub(1), nc.end) + Node::new( + NonCodeNode { value, ..nc.inner }, + nc.start.saturating_sub(1), + nc.end, + nc.module_id, + ) } else if has_newline { // Nothing has to change, a single newline does not need preserving. nc @@ -950,10 +987,10 @@ fn noncode_just_after_code(i: TokenSlice) -> PResult> { x @ NonCodeValue::NewLineBlockComment { .. } => x, x @ NonCodeValue::NewLine => x, }; - Node::new(NonCodeNode { value, ..nc.inner }, nc.start, nc.end) + Node::new(NonCodeNode { value, ..nc.inner }, nc.start, nc.end, nc.module_id) } }) - .map(|nc| Node::new(nc.inner, nc.start.saturating_sub(1), nc.end)) + .map(|nc| Node::new(nc.inner, nc.start.saturating_sub(1), nc.end, nc.module_id)) .parse_next(i)?; Ok(nc) } @@ -1015,7 +1052,7 @@ pub fn function_body(i: TokenSlice) -> PResult> { // Subtract 1 from `t.start` to match behaviour of the old parser. // Consider removing the -1 in the future because I think it's inaccurate, but for now, // I prefer to match the old parser exactly when I can. - opt(whitespace).map(|tok| tok.and_then(|t| t.first().map(|t| t.start.saturating_sub(1)))), + opt(whitespace).map(|tok| tok.and_then(|t| t.first().map(|t| (t.start.saturating_sub(1), t.module_id)))), )) .parse_next(i)?; @@ -1061,6 +1098,7 @@ pub fn function_body(i: TokenSlice) -> PResult> { }, ws_token.start, ws_token.end, + ws_token.module_id, ))); } } @@ -1103,7 +1141,7 @@ pub fn function_body(i: TokenSlice) -> PResult> { match thing_in_body { WithinFunction::BodyItem((b, maybe_noncode)) => { if start.is_none() { - start = Some(b.start()); + start = Some((b.start(), b.module_id())); } end = b.end(); body.push(b); @@ -1114,7 +1152,7 @@ pub fn function_body(i: TokenSlice) -> PResult> { } WithinFunction::NonCode(nc) => { if start.is_none() { - start = Some(nc.start); + start = Some((nc.start, nc.module_id)); } end = nc.end; if body.is_empty() { @@ -1143,8 +1181,9 @@ pub fn function_body(i: TokenSlice) -> PResult> { non_code_meta, digest: None, }, - start, + start.0, end, + start.1, )) } @@ -1200,7 +1239,7 @@ fn import_stmt(i: TokenSlice) -> PResult> { { return Err(ErrMode::Cut( KclError::Syntax(KclErrorDetails { - source_ranges: vec![SourceRange::new(path.start, path.end)], + source_ranges: vec![SourceRange::new(path.start, path.end, path.module_id)], message: "import path may only contain alphanumeric characters, underscore, hyphen, and period. Files in other directories are not yet supported.".to_owned(), }) .into(), @@ -1215,12 +1254,14 @@ fn import_stmt(i: TokenSlice) -> PResult> { }, start, end, + import_token.module_id, )) } fn import_item(i: TokenSlice) -> PResult> { let name = identifier.context(expected("an identifier to import")).parse_next(i)?; let start = name.start; + let module_id = name.module_id; let alias = opt(preceded( (whitespace, import_as_keyword, whitespace), identifier.context(expected("an identifier to alias the import")), @@ -1239,6 +1280,7 @@ fn import_item(i: TokenSlice) -> PResult> { }, start, end, + module_id, )) } @@ -1259,10 +1301,10 @@ fn import_as_keyword(i: TokenSlice) -> PResult { /// Parse a return statement of a user-defined function, e.g. `return x`. pub fn return_stmt(i: TokenSlice) -> PResult> { - let start = any + let ret = any .try_map(|token: Token| { if matches!(token.token_type, TokenType::Keyword) && token.value == "return" { - Ok(token.start) + Ok(token) } else { Err(KclError::Syntax(KclErrorDetails { source_ranges: token.as_source_ranges(), @@ -1277,8 +1319,9 @@ pub fn return_stmt(i: TokenSlice) -> PResult> { require_whitespace(i)?; let argument = expression(i)?; Ok(Node { - start, + start: ret.start, end: argument.end(), + module_id: ret.module_id, inner: ReturnStatement { argument, digest: None }, }) } @@ -1385,10 +1428,10 @@ fn declaration(i: TokenSlice) -> PResult> { "an identifier, which becomes name you're binding the value to", )) .parse_next(i)?; - let (kind, mut start, dec_end) = if let Some((kind, token)) = &decl_token { - (*kind, token.start, token.end) + let (kind, mut start, dec_end, module_id) = if let Some((kind, token)) = &decl_token { + (*kind, token.start, token.end, token.module_id) } else { - (VariableKind::Const, id.start, id.end) + (VariableKind::Const, id.start, id.end, id.module_id) }; if let Some(token) = visibility_token { start = token.start; @@ -1419,7 +1462,7 @@ fn declaration(i: TokenSlice) -> PResult> { // Check the 'if' direction: if matches!(val, Expr::FunctionExpression(_)) { return Err(KclError::Syntax(KclErrorDetails { - source_ranges: vec![SourceRange([start, dec_end])], + source_ranges: vec![SourceRange([start, dec_end, module_id.as_usize()])], message: format!("Expected a `fn` variable kind, found: `{}`", kind), })); } @@ -1436,6 +1479,7 @@ fn declaration(i: TokenSlice) -> PResult> { declarations: vec![Node { start: id.start, end, + module_id, inner: VariableDeclarator { id, init: val, @@ -1448,6 +1492,7 @@ fn declaration(i: TokenSlice) -> PResult> { }, start, end, + module_id, })) } @@ -1463,6 +1508,7 @@ impl TryFrom for Node { }, token.start, token.end, + token.module_id, )) } else { Err(KclError::Syntax(KclErrorDetails { @@ -1493,6 +1539,7 @@ fn sketch_keyword(i: TokenSlice) -> PResult> { }, token.start, token.end, + token.module_id, )) } else { Err(KclError::Syntax(KclErrorDetails { @@ -1518,6 +1565,7 @@ impl TryFrom for Node { }, token.start - 1, token.end, + token.module_id, )) } else { Err(KclError::Syntax(KclErrorDetails { @@ -1533,7 +1581,7 @@ impl Node { // Make sure they are not assigning a variable to a stdlib function. if crate::std::name_in_stdlib(&self.name) { return Err(KclError::Syntax(KclErrorDetails { - source_ranges: vec![SourceRange([self.start, self.end])], + source_ranges: vec![SourceRange::from(&self)], message: format!("Cannot assign a tag to a reserved keyword: {}", self.name), })); } @@ -1588,6 +1636,7 @@ fn unary_expression(i: TokenSlice) -> PResult> { Ok(Node { start: op_token.start, end: argument.end(), + module_id: op_token.module_id, inner: UnaryExpression { operator, argument, @@ -1669,6 +1718,7 @@ fn expression_stmt(i: TokenSlice) -> PResult> { Ok(Node { start: val.start(), end: val.end(), + module_id: val.module_id(), inner: ExpressionStatement { expression: val, digest: None, @@ -1907,7 +1957,7 @@ impl Node { // Make sure they are not assigning a variable to a stdlib function. if crate::std::name_in_stdlib(&self.name) { return Err(KclError::Syntax(KclErrorDetails { - source_ranges: vec![SourceRange([self.start, self.end])], + source_ranges: vec![SourceRange::from(&self)], message: format!("Cannot assign a variable to a reserved keyword: {}", self.name), })); } @@ -1950,7 +2000,7 @@ fn fn_call(i: TokenSlice) -> PResult> { e => { return Err(ErrMode::Cut( KclError::Syntax(KclErrorDetails { - source_ranges: vec![SourceRange([arg.start(), arg.end()])], + source_ranges: vec![SourceRange::from(*arg)], message: format!("Expected a tag declarator like `$name`, found {:?}", e), }) .into(), @@ -1963,7 +2013,7 @@ fn fn_call(i: TokenSlice) -> PResult> { e => { return Err(ErrMode::Cut( KclError::Syntax(KclErrorDetails { - source_ranges: vec![SourceRange([arg.start(), arg.end()])], + source_ranges: vec![SourceRange::from(*arg)], message: format!("Expected a tag identifier like `tagName`, found {:?}", e), }) .into(), @@ -1978,6 +2028,7 @@ fn fn_call(i: TokenSlice) -> PResult> { Ok(Node { start: fn_name.start, end, + module_id: fn_name.module_id, inner: CallExpression { callee: fn_name, arguments: args, @@ -1992,12 +2043,12 @@ mod tests { use pretty_assertions::assert_eq; use super::*; - use crate::ast::types::{BodyItem, Expr, VariableKind}; + use crate::ast::types::{BodyItem, Expr, ModuleId, VariableKind}; #[test] fn parse_args() { for (i, (test, expected_len)) in [("someVar", 1), ("5, 3", 2), (r#""a""#, 1)].into_iter().enumerate() { - let tokens = crate::token::lexer(test).unwrap(); + let tokens = crate::token::lexer(test, ModuleId::default()).unwrap(); let actual = match arguments.parse(&tokens) { Ok(x) => x, Err(e) => panic!("Failed test {i}, could not parse function arguments from \"{test}\": {e:?}"), @@ -2008,7 +2059,7 @@ mod tests { #[test] fn weird_program_unclosed_paren() { - let tokens = crate::token::lexer("fn firstPrime=(").unwrap(); + let tokens = crate::token::lexer("fn firstPrime=(", ModuleId::default()).unwrap(); let last = tokens.last().unwrap(); let err: KclError = program.parse(&tokens).unwrap_err().into(); assert_eq!(err.source_ranges(), last.as_source_ranges()); @@ -2019,16 +2070,16 @@ mod tests { #[test] fn weird_program_just_a_pipe() { - let tokens = crate::token::lexer("|").unwrap(); + let tokens = crate::token::lexer("|", ModuleId::default()).unwrap(); let err: KclError = program.parse(&tokens).unwrap_err().into(); - assert_eq!(err.source_ranges(), vec![SourceRange([0, 1])]); + assert_eq!(err.source_ranges(), vec![SourceRange([0, 1, 0])]); assert_eq!(err.message(), "Unexpected token: |"); } #[test] fn parse_binary_expressions() { for (i, test_program) in ["1 + 2 + 3"].into_iter().enumerate() { - let tokens = crate::token::lexer(test_program).unwrap(); + let tokens = crate::token::lexer(test_program, ModuleId::default()).unwrap(); let mut slice = tokens.as_slice(); let _actual = match binary_expression.parse_next(&mut slice) { Ok(x) => x, @@ -2039,7 +2090,7 @@ mod tests { #[test] fn test_vardec_no_keyword() { - let tokens = crate::token::lexer("x = 4").unwrap(); + let tokens = crate::token::lexer("x = 4", ModuleId::default()).unwrap(); let vardec = declaration(&mut tokens.as_slice()).unwrap(); assert_eq!(vardec.inner.kind, VariableKind::Const); let vardec = vardec.declarations.first().unwrap(); @@ -2052,7 +2103,7 @@ mod tests { #[test] fn test_negative_operands() { - let tokens = crate::token::lexer("-leg2").unwrap(); + let tokens = crate::token::lexer("-leg2", ModuleId::default()).unwrap(); let _s = operand.parse_next(&mut tokens.as_slice()).unwrap(); } @@ -2066,7 +2117,7 @@ mod tests { // comment 2 return 1 }"#; - let tokens = crate::token::lexer(test_program).unwrap(); + let tokens = crate::token::lexer(test_program, ModuleId::default()).unwrap(); let mut slice = tokens.as_slice(); let expr = function_expression.parse_next(&mut slice).unwrap(); assert_eq!(expr.params, vec![]); @@ -2084,7 +2135,7 @@ mod tests { const yo = { a: { b: { c: '123' } } } /* block comment */ }"#; - let tokens = crate::token::lexer(test_program).unwrap(); + let tokens = crate::token::lexer(test_program, ModuleId::default()).unwrap(); let mut slice = tokens.as_slice(); let expr = function_expression.parse_next(&mut slice).unwrap(); let comment0 = &expr.body.non_code_meta.non_code_nodes.get(&0).unwrap()[0]; @@ -2097,7 +2148,7 @@ comment */ /* comment at start */ const mySk1 = startSketchAt([0, 0])"#; - let tokens = crate::token::lexer(test_program).unwrap(); + let tokens = crate::token::lexer(test_program, ModuleId::default()).unwrap(); let program = program.parse(&tokens).unwrap(); let mut starting_comments = program.inner.non_code_meta.start_nodes; assert_eq!(starting_comments.len(), 2); @@ -2115,7 +2166,7 @@ const mySk1 = startSketchAt([0, 0])"#; #[test] fn test_comment_in_pipe() { - let tokens = crate::token::lexer(r#"const x = y() |> /*hi*/ z(%)"#).unwrap(); + let tokens = crate::token::lexer(r#"const x = y() |> /*hi*/ z(%)"#, ModuleId::default()).unwrap(); let mut body = program.parse(&tokens).unwrap().inner.body; let BodyItem::VariableDeclaration(mut item) = body.remove(0) else { panic!("expected vardec"); @@ -2142,7 +2193,7 @@ const mySk1 = startSketchAt([0, 0])"#; return sg return sg }"#; - let tokens = crate::token::lexer(test_program).unwrap(); + let tokens = crate::token::lexer(test_program, ModuleId::default()).unwrap(); let mut slice = tokens.as_slice(); let _expr = function_expression.parse_next(&mut slice).unwrap(); } @@ -2153,7 +2204,8 @@ const mySk1 = startSketchAt([0, 0])"#; return 2 }"; - let tokens = crate::token::lexer(test_program).unwrap(); + let module_id = ModuleId::from_usize(1); + let tokens = crate::token::lexer(test_program, module_id).unwrap(); let mut slice = tokens.as_slice(); let expr = function_expression.parse_next(&mut slice).unwrap(); assert_eq!( @@ -2173,11 +2225,13 @@ const mySk1 = startSketchAt([0, 0])"#; }, 32, 33, + module_id, ))), digest: None, }, 25, 33, + module_id, ))], non_code_meta: NonCodeMeta { non_code_nodes: Default::default(), @@ -2188,6 +2242,7 @@ const mySk1 = startSketchAt([0, 0])"#; }, 7, 25, + module_id, )], digest: None, }, @@ -2195,12 +2250,14 @@ const mySk1 = startSketchAt([0, 0])"#; }, 7, 47, + module_id, ), return_type: None, digest: None, }, 0, 47, + module_id, ) ); } @@ -2212,7 +2269,7 @@ const mySk1 = startSketchAt([0, 0])"#; |> c(%) // inline-comment |> d(%)"#; - let tokens = crate::token::lexer(test_input).unwrap(); + let tokens = crate::token::lexer(test_input, ModuleId::default()).unwrap(); let mut slice = tokens.as_slice(); let Node { inner: PipeExpression { @@ -2242,7 +2299,8 @@ const mySk1 = startSketchAt([0, 0])"#; return things "#; - let tokens = crate::token::lexer(test_program).unwrap(); + let module_id = ModuleId::default(); + let tokens = crate::token::lexer(test_program, module_id).unwrap(); let Program { non_code_meta, .. } = function_body.parse(&tokens).unwrap().inner; assert_eq!( vec![Node::new( @@ -2255,6 +2313,7 @@ const mySk1 = startSketchAt([0, 0])"#; }, 0, 20, + module_id, )], non_code_meta.start_nodes, ); @@ -2271,6 +2330,7 @@ const mySk1 = startSketchAt([0, 0])"#; }, 60, 82, + module_id, ), Node::new( NonCodeNode { @@ -2279,6 +2339,7 @@ const mySk1 = startSketchAt([0, 0])"#; }, 82, 86, + module_id, ) ]), non_code_meta.non_code_nodes.get(&0), @@ -2295,6 +2356,7 @@ const mySk1 = startSketchAt([0, 0])"#; }, 103, 129, + module_id, )]), non_code_meta.non_code_nodes.get(&1), ); @@ -2306,7 +2368,7 @@ const mySk1 = startSketchAt([0, 0])"#; comment */ return 1"#; - let tokens = crate::token::lexer(test_program).unwrap(); + let tokens = crate::token::lexer(test_program, ModuleId::default()).unwrap(); let actual = program.parse(&tokens).unwrap(); assert_eq!(actual.non_code_meta.non_code_nodes.len(), 1); assert_eq!( @@ -2321,7 +2383,7 @@ const mySk1 = startSketchAt([0, 0])"#; #[test] fn test_bracketed_binary_expression() { let input = "(2 - 3)"; - let tokens = crate::token::lexer(input).unwrap(); + let tokens = crate::token::lexer(input, ModuleId::default()).unwrap(); let actual = match binary_expr_in_parens.parse(&tokens) { Ok(x) => x, Err(e) => panic!("{e:?}"), @@ -2336,7 +2398,7 @@ const mySk1 = startSketchAt([0, 0])"#; "6 / ( sigmaAllow * width )", "sqrt(distance * p * FOS * 6 / ( sigmaAllow * width ))", ] { - let tokens = crate::token::lexer(input).unwrap(); + let tokens = crate::token::lexer(input, ModuleId::default()).unwrap(); let _actual = match expression.parse(&tokens) { Ok(x) => x, Err(e) => panic!("{e:?}"), @@ -2347,7 +2409,7 @@ const mySk1 = startSketchAt([0, 0])"#; #[test] fn test_arithmetic() { let input = "1 * (2 - 3)"; - let tokens = crate::token::lexer(input).unwrap(); + let tokens = crate::token::lexer(input, ModuleId::default()).unwrap(); // The RHS should be a binary expression. let actual = binary_expression.parse(&tokens).unwrap(); assert_eq!(actual.operator, BinaryOperator::Mul); @@ -2375,7 +2437,7 @@ const mySk1 = startSketchAt([0, 0])"#; .into_iter() .enumerate() { - let tokens = crate::token::lexer(test_input).unwrap(); + let tokens = crate::token::lexer(test_input, ModuleId::default()).unwrap(); let mut actual = match declaration.parse(&tokens) { Err(e) => panic!("Could not parse test {i}: {e:#?}"), Ok(a) => a, @@ -2393,7 +2455,7 @@ const mySk1 = startSketchAt([0, 0])"#; #[test] fn test_function_call() { for (i, test_input) in ["const x = f(1)", "const x = f( 1 )"].into_iter().enumerate() { - let tokens = crate::token::lexer(test_input).unwrap(); + let tokens = crate::token::lexer(test_input, ModuleId::default()).unwrap(); let _actual = match declaration.parse(&tokens) { Err(e) => panic!("Could not parse test {i}: {e:#?}"), Ok(a) => a, @@ -2404,7 +2466,7 @@ const mySk1 = startSketchAt([0, 0])"#; #[test] fn test_nested_arithmetic() { let input = "1 * ((2 - 3) / 4)"; - let tokens = crate::token::lexer(input).unwrap(); + let tokens = crate::token::lexer(input, ModuleId::default()).unwrap(); // The RHS should be a binary expression. let outer = binary_expression.parse(&tokens).unwrap(); assert_eq!(outer.operator, BinaryOperator::Mul); @@ -2423,7 +2485,7 @@ const mySk1 = startSketchAt([0, 0])"#; fn binary_expression_ignores_whitespace() { let tests = ["1 - 2", "1- 2", "1 -2", "1-2"]; for test in tests { - let tokens = crate::token::lexer(test).unwrap(); + let tokens = crate::token::lexer(test, ModuleId::default()).unwrap(); let actual = binary_expression.parse(&tokens).unwrap(); assert_eq!(actual.operator, BinaryOperator::Sub); let BinaryPart::Literal(left) = actual.inner.left else { @@ -2444,7 +2506,7 @@ const mySk1 = startSketchAt([0, 0])"#; a comment spanning a few lines */ |> z(%)"#; - let tokens = crate::token::lexer(test_program).unwrap(); + let tokens = crate::token::lexer(test_program, ModuleId::default()).unwrap(); let actual = pipe_expression.parse(&tokens).unwrap(); let n = actual.non_code_meta.non_code_nodes.len(); assert_eq!(n, 1, "expected one comment in pipe expression but found {n}"); @@ -2472,7 +2534,7 @@ const mySk1 = startSketchAt([0, 0])"#; .into_iter() .enumerate() { - let tokens = crate::token::lexer(test_program).unwrap(); + let tokens = crate::token::lexer(test_program, ModuleId::default()).unwrap(); let actual = pipe_expression.parse(&tokens); assert!(actual.is_ok(), "could not parse test {i}, '{test_program}'"); let actual = actual.unwrap(); @@ -2483,6 +2545,7 @@ const mySk1 = startSketchAt([0, 0])"#; #[test] fn comments() { + let module_id = ModuleId::from_usize(1); for (i, (test_program, expected)) in [ ( "//hi", @@ -2496,6 +2559,7 @@ const mySk1 = startSketchAt([0, 0])"#; }, 0, 4, + module_id, ), ), ( @@ -2510,6 +2574,7 @@ const mySk1 = startSketchAt([0, 0])"#; }, 0, 9, + module_id, ), ), ( @@ -2524,6 +2589,7 @@ const mySk1 = startSketchAt([0, 0])"#; }, 0, 11, + module_id, ), ), ( @@ -2538,6 +2604,7 @@ const mySk1 = startSketchAt([0, 0])"#; }, 0, 12, + module_id, ), ), ( @@ -2553,6 +2620,7 @@ const mySk1 = startSketchAt([0, 0])"#; }, 0, 29, + module_id, ), ), ( @@ -2570,6 +2638,7 @@ const mySk1 = startSketchAt([0, 0])"#; }, 0, 32, + module_id, ), ), ( @@ -2587,6 +2656,7 @@ const mySk1 = startSketchAt([0, 0])"#; }, 0, 30, + module_id, ), ), ( @@ -2602,13 +2672,14 @@ const mySk1 = startSketchAt([0, 0])"#; }, 0, 39, + module_id, ), ), ] .into_iter() .enumerate() { - let tokens = crate::token::lexer(test_program).unwrap(); + let tokens = crate::token::lexer(test_program, module_id).unwrap(); let actual = non_code_node.parse(&tokens); assert!(actual.is_ok(), "could not parse test {i}: {actual:#?}"); let actual = actual.unwrap(); @@ -2619,11 +2690,12 @@ const mySk1 = startSketchAt([0, 0])"#; #[test] fn recognize_invalid_params() { let test_fn = "(let) => { return 1 }"; - let tokens = crate::token::lexer(test_fn).unwrap(); + let module_id = ModuleId::from_usize(2); + let tokens = crate::token::lexer(test_fn, module_id).unwrap(); let err = function_expression.parse(&tokens).unwrap_err().into_inner(); let cause = err.cause.unwrap(); // This is the token `let` - assert_eq!(cause.source_ranges(), vec![SourceRange([1, 4])]); + assert_eq!(cause.source_ranges(), vec![SourceRange([1, 4, 2])]); assert_eq!(cause.message(), "Cannot assign a variable to a reserved keyword: let"); } @@ -2632,7 +2704,7 @@ const mySk1 = startSketchAt([0, 0])"#; let string_literal = r#"" // a comment ""#; - let tokens = crate::token::lexer(string_literal).unwrap(); + let tokens = crate::token::lexer(string_literal, ModuleId::default()).unwrap(); let parsed_literal = literal.parse(&tokens).unwrap(); assert_eq!( parsed_literal.value, @@ -2649,7 +2721,7 @@ const mySk1 = startSketchAt([0, 0])"#; |> lineTo([0, -0], %) // MoveRelative "#; - let tokens = crate::token::lexer(test_program).unwrap(); + let tokens = crate::token::lexer(test_program, ModuleId::default()).unwrap(); let mut slice = &tokens[..]; let _actual = pipe_expression.parse_next(&mut slice).unwrap(); assert_eq!(slice[0].token_type, TokenType::Whitespace); @@ -2658,14 +2730,14 @@ const mySk1 = startSketchAt([0, 0])"#; #[test] fn test_pipes_on_pipes() { let test_program = include_str!("../../../tests/executor/inputs/pipes_on_pipes.kcl"); - let tokens = crate::token::lexer(test_program).unwrap(); + let tokens = crate::token::lexer(test_program, ModuleId::default()).unwrap(); let _actual = program.parse(&tokens).unwrap(); } #[test] fn test_cube() { let test_program = include_str!("../../../tests/executor/inputs/cube.kcl"); - let tokens = crate::token::lexer(test_program).unwrap(); + let tokens = crate::token::lexer(test_program, ModuleId::default()).unwrap(); match program.parse(&tokens) { Ok(_) => {} Err(e) => { @@ -2683,7 +2755,7 @@ const mySk1 = startSketchAt([0, 0])"#; ("a,b", vec!["a", "b"]), ]; for (i, (input, expected)) in tests.into_iter().enumerate() { - let tokens = crate::token::lexer(input).unwrap(); + let tokens = crate::token::lexer(input, ModuleId::default()).unwrap(); let actual = parameters.parse(&tokens); assert!(actual.is_ok(), "could not parse test {i}"); let actual_ids: Vec<_> = actual.unwrap().into_iter().map(|p| p.identifier.inner.name).collect(); @@ -2697,7 +2769,7 @@ const mySk1 = startSketchAt([0, 0])"#; return 2 }"; - let tokens = crate::token::lexer(input).unwrap(); + let tokens = crate::token::lexer(input, ModuleId::default()).unwrap(); let actual = function_expression.parse(&tokens); assert!(actual.is_ok(), "could not parse test function"); } @@ -2707,7 +2779,7 @@ const mySk1 = startSketchAt([0, 0])"#; let tests = ["const myVar = 5", "const myVar=5", "const myVar =5", "const myVar= 5"]; for test in tests { // Run the original parser - let tokens = crate::token::lexer(test).unwrap(); + let tokens = crate::token::lexer(test, ModuleId::default()).unwrap(); let mut expected_body = crate::parser::Parser::new(tokens.clone()).ast().unwrap().inner.body; assert_eq!(expected_body.len(), 1); let BodyItem::VariableDeclaration(expected) = expected_body.pop().unwrap() else { @@ -2734,7 +2806,8 @@ const mySk1 = startSketchAt([0, 0])"#; #[test] fn test_math_parse() { - let tokens = crate::token::lexer(r#"5 + "a""#).unwrap(); + let module_id = ModuleId::default(); + let tokens = crate::token::lexer(r#"5 + "a""#, module_id).unwrap(); let actual = crate::parser::Parser::new(tokens).ast().unwrap().inner.body; let expr = Node::boxed( BinaryExpression { @@ -2747,6 +2820,7 @@ const mySk1 = startSketchAt([0, 0])"#; }, 0, 1, + module_id, ))), right: BinaryPart::Literal(Box::new(Node::new( Literal { @@ -2756,11 +2830,13 @@ const mySk1 = startSketchAt([0, 0])"#; }, 4, 7, + module_id, ))), digest: None, }, 0, 7, + module_id, ); let expected = vec![BodyItem::ExpressionStatement(Node::new( ExpressionStatement { @@ -2769,53 +2845,62 @@ const mySk1 = startSketchAt([0, 0])"#; }, 0, 7, + module_id, ))]; assert_eq!(expected, actual); } #[test] fn test_is_code_token() { + let module_id = ModuleId::default(); let tokens = [ Token { token_type: TokenType::Word, start: 0, end: 3, + module_id, value: "log".to_string(), }, Token { token_type: TokenType::Brace, start: 3, end: 4, + module_id, value: "(".to_string(), }, Token { token_type: TokenType::Number, start: 4, end: 5, + module_id, value: "5".to_string(), }, Token { token_type: TokenType::Comma, start: 5, end: 6, + module_id, value: ",".to_string(), }, Token { token_type: TokenType::String, start: 7, end: 14, + module_id, value: "\"hello\"".to_string(), }, Token { token_type: TokenType::Word, start: 16, end: 27, + module_id, value: "aIdentifier".to_string(), }, Token { token_type: TokenType::Brace, start: 27, end: 28, + module_id, value: ")".to_string(), }, ]; @@ -2826,23 +2911,27 @@ const mySk1 = startSketchAt([0, 0])"#; #[test] fn test_is_not_code_token() { + let module_id = ModuleId::default(); let tokens = [ Token { token_type: TokenType::Whitespace, start: 6, end: 7, + module_id, value: " ".to_string(), }, Token { token_type: TokenType::BlockComment, start: 28, end: 30, + module_id, value: "/* abte */".to_string(), }, Token { token_type: TokenType::LineComment, start: 30, end: 33, + module_id, value: "// yoyo a line".to_string(), }, ]; @@ -2854,7 +2943,8 @@ const mySk1 = startSketchAt([0, 0])"#; #[test] fn test_abstract_syntax_tree() { let code = "5 +6"; - let parser = crate::parser::Parser::new(crate::token::lexer(code).unwrap()); + let module_id = ModuleId::default(); + let parser = crate::parser::Parser::new(crate::token::lexer(code, module_id).unwrap()); let result = parser.ast().unwrap(); let expected_result = Node::new( Program { @@ -2870,6 +2960,7 @@ const mySk1 = startSketchAt([0, 0])"#; }, 0, 1, + module_id, ))), operator: BinaryOperator::Add, right: BinaryPart::Literal(Box::new(Node::new( @@ -2880,22 +2971,26 @@ const mySk1 = startSketchAt([0, 0])"#; }, 3, 4, + module_id, ))), digest: None, }, 0, 4, + module_id, )), digest: None, }, 0, 4, + module_id, ))], non_code_meta: NonCodeMeta::default(), digest: None, }, 0, 4, + module_id, ); assert_eq!(result, expected_result); @@ -2904,22 +2999,16 @@ const mySk1 = startSketchAt([0, 0])"#; #[test] fn test_empty_file() { let some_program_string = r#""#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let result = parser.ast(); + let result = crate::parser::top_level_parse(some_program_string); assert!(result.is_ok()); } #[test] fn test_parse_half_pipe_small() { - let tokens = crate::token::lexer( - "const secondExtrude = startSketchOn('XY') + let code = "const secondExtrude = startSketchOn('XY') |> startProfileAt([0,0], %) - |", - ) - .unwrap(); - let parser = crate::parser::Parser::new(tokens); - let result = parser.ast(); + |"; + let result = crate::parser::top_level_parse(code); assert!(result.is_err()); let actual = result.err().unwrap().to_string(); assert!(actual.contains("Unexpected token: |"), "actual={actual:?}"); @@ -2927,93 +3016,63 @@ const mySk1 = startSketchAt([0, 0])"#; #[test] fn test_parse_member_expression_double_nested_braces() { - let tokens = crate::token::lexer(r#"const prop = yo["one"][two]"#).unwrap(); - let parser = crate::parser::Parser::new(tokens); - parser.ast().unwrap(); + let code = r#"const prop = yo["one"][two]"#; + crate::parser::top_level_parse(code).unwrap(); } #[test] fn test_parse_member_expression_binary_expression_period_number_first() { - let tokens = crate::token::lexer( - r#"const obj = { a: 1, b: 2 } -const height = 1 - obj.a"#, - ) - .unwrap(); - let parser = crate::parser::Parser::new(tokens); - parser.ast().unwrap(); + let code = r#"const obj = { a: 1, b: 2 } +const height = 1 - obj.a"#; + crate::parser::top_level_parse(code).unwrap(); } #[test] fn test_parse_member_expression_allowed_type_in_expression() { - let tokens = crate::token::lexer( - r#"const obj = { thing: 1 } -startSketchOn(obj.sketch)"#, - ) - .unwrap(); + let code = r#"const obj = { thing: 1 } +startSketchOn(obj.sketch)"#; - let parser = crate::parser::Parser::new(tokens); - parser.ast().unwrap(); + crate::parser::top_level_parse(code).unwrap(); } #[test] fn test_parse_member_expression_binary_expression_brace_number_first() { - let tokens = crate::token::lexer( - r#"const obj = { a: 1, b: 2 } -const height = 1 - obj["a"]"#, - ) - .unwrap(); - let parser = crate::parser::Parser::new(tokens); - parser.ast().unwrap(); + let code = r#"const obj = { a: 1, b: 2 } +const height = 1 - obj["a"]"#; + crate::parser::top_level_parse(code).unwrap(); } #[test] fn test_parse_member_expression_binary_expression_brace_number_second() { - let tokens = crate::token::lexer( - r#"const obj = { a: 1, b: 2 } -const height = obj["a"] - 1"#, - ) - .unwrap(); - let parser = crate::parser::Parser::new(tokens); - parser.ast().unwrap(); + let code = r#"const obj = { a: 1, b: 2 } +const height = obj["a"] - 1"#; + crate::parser::top_level_parse(code).unwrap(); } #[test] fn test_parse_member_expression_binary_expression_in_array_number_first() { - let tokens = crate::token::lexer( - r#"const obj = { a: 1, b: 2 } -const height = [1 - obj["a"], 0]"#, - ) - .unwrap(); - let parser = crate::parser::Parser::new(tokens); - parser.ast().unwrap(); + let code = r#"const obj = { a: 1, b: 2 } +const height = [1 - obj["a"], 0]"#; + crate::parser::top_level_parse(code).unwrap(); } #[test] fn test_parse_member_expression_binary_expression_in_array_number_second() { - let tokens = crate::token::lexer( - r#"const obj = { a: 1, b: 2 } -const height = [obj["a"] - 1, 0]"#, - ) - .unwrap(); - let parser = crate::parser::Parser::new(tokens); - parser.ast().unwrap(); + let code = r#"const obj = { a: 1, b: 2 } +const height = [obj["a"] - 1, 0]"#; + crate::parser::top_level_parse(code).unwrap(); } #[test] fn test_parse_member_expression_binary_expression_in_array_number_second_missing_space() { - let tokens = crate::token::lexer( - r#"const obj = { a: 1, b: 2 } -const height = [obj["a"] -1, 0]"#, - ) - .unwrap(); - let parser = crate::parser::Parser::new(tokens); - parser.ast().unwrap(); + let code = r#"const obj = { a: 1, b: 2 } +const height = [obj["a"] -1, 0]"#; + crate::parser::top_level_parse(code).unwrap(); } #[test] fn test_parse_half_pipe() { - let tokens = crate::token::lexer( - "const height = 10 + let code = "const height = 10 const firstExtrude = startSketchOn('XY') |> startProfileAt([0,0], %) @@ -3025,70 +3084,67 @@ const firstExtrude = startSketchOn('XY') const secondExtrude = startSketchOn('XY') |> startProfileAt([0,0], %) - |", - ) - .unwrap(); - let parser = crate::parser::Parser::new(tokens); - let result = parser.ast(); + |"; + let result = crate::parser::top_level_parse(code); assert!(result.is_err()); assert!(result.err().unwrap().to_string().contains("Unexpected token: |")); } #[test] fn test_parse_greater_bang() { - let tokens = crate::token::lexer(">!").unwrap(); + let module_id = ModuleId::default(); + let tokens = crate::token::lexer(">!", module_id).unwrap(); let parser = crate::parser::Parser::new(tokens); let err = parser.ast().unwrap_err(); assert_eq!( err.to_string(), - r#"syntax: KclErrorDetails { source_ranges: [SourceRange([0, 1])], message: "Unexpected token: >" }"# + r#"syntax: KclErrorDetails { source_ranges: [SourceRange([0, 1, 0])], message: "Unexpected token: >" }"# ); } #[test] fn test_parse_z_percent_parens() { - let tokens = crate::token::lexer("z%)").unwrap(); + let module_id = ModuleId::default(); + let tokens = crate::token::lexer("z%)", module_id).unwrap(); let parser = crate::parser::Parser::new(tokens); let result = parser.ast(); assert!(result.is_err()); assert_eq!( result.err().unwrap().to_string(), - r#"syntax: KclErrorDetails { source_ranges: [SourceRange([1, 2])], message: "Unexpected token: %" }"# + r#"syntax: KclErrorDetails { source_ranges: [SourceRange([1, 2, 0])], message: "Unexpected token: %" }"# ); } #[test] fn test_parse_parens_unicode() { - let result = crate::token::lexer("(Þœ"); + let module_id = ModuleId::default(); + let result = crate::token::lexer("(Þœ", module_id); // TODO: Better errors when program cannot tokenize. // https://github.com/KittyCAD/modeling-app/issues/696 assert!(result.is_err()); assert_eq!( result.err().unwrap().to_string(), - r#"lexical: KclErrorDetails { source_ranges: [SourceRange([1, 2])], message: "found unknown token 'Þœ'" }"# + r#"lexical: KclErrorDetails { source_ranges: [SourceRange([1, 2, 0])], message: "found unknown token 'Þœ'" }"# ); } #[test] fn test_parse_negative_in_array_binary_expression() { - let tokens = crate::token::lexer( - r#"const leg1 = 5 + let code = r#"const leg1 = 5 const thickness = 0.56 const bracket = [-leg2 + thickness, 0] -"#, - ) - .unwrap(); - let parser = crate::parser::Parser::new(tokens); - let result = parser.ast(); - assert!(result.is_ok()); +"#; + crate::parser::top_level_parse(code).unwrap(); } #[test] fn test_parse_nested_open_brackets() { + let module_id = ModuleId::default(); let tokens = crate::token::lexer( r#" z(-[["#, + module_id, ) .unwrap(); let parser = crate::parser::Parser::new(tokens); @@ -3098,9 +3154,11 @@ z(-[["#, #[test] fn test_parse_weird_new_line_function() { + let module_id = ModuleId::default(); let tokens = crate::token::lexer( r#"z (--#"#, + module_id, ) .unwrap(); let parser = crate::parser::Parser::new(tokens); @@ -3108,28 +3166,31 @@ z(-[["#, assert!(result.is_err()); assert_eq!( result.err().unwrap().to_string(), - r#"syntax: KclErrorDetails { source_ranges: [SourceRange([3, 4])], message: "Unexpected token: (" }"# + r#"syntax: KclErrorDetails { source_ranges: [SourceRange([3, 4, 0])], message: "Unexpected token: (" }"# ); } #[test] fn test_parse_weird_lots_of_fancy_brackets() { - let tokens = crate::token::lexer(r#"zz({{{{{{{{)iegAng{{{{{{{##"#).unwrap(); + let module_id = ModuleId::default(); + let tokens = crate::token::lexer(r#"zz({{{{{{{{)iegAng{{{{{{{##"#, module_id).unwrap(); let parser = crate::parser::Parser::new(tokens); let result = parser.ast(); assert!(result.is_err()); assert_eq!( result.err().unwrap().to_string(), - r#"syntax: KclErrorDetails { source_ranges: [SourceRange([2, 3])], message: "Unexpected token: (" }"# + r#"syntax: KclErrorDetails { source_ranges: [SourceRange([2, 3, 0])], message: "Unexpected token: (" }"# ); } #[test] fn test_parse_weird_close_before_open() { + let module_id = ModuleId::default(); let tokens = crate::token::lexer( r#"fn)n e ["#, + module_id, ) .unwrap(); let parser = crate::parser::Parser::new(tokens); @@ -3144,7 +3205,8 @@ e #[test] fn test_parse_weird_close_before_nada() { - let tokens = crate::token::lexer(r#"fn)n-"#).unwrap(); + let module_id = ModuleId::default(); + let tokens = crate::token::lexer(r#"fn)n-"#, module_id).unwrap(); let parser = crate::parser::Parser::new(tokens); let result = parser.ast(); assert!(result.is_err()); @@ -3157,9 +3219,11 @@ e #[test] fn test_parse_weird_lots_of_slashes() { + let module_id = ModuleId::default(); let tokens = crate::token::lexer( r#"J///////////o//+///////////P++++*++++++P///////ËŸ ++4"#, + module_id, ) .unwrap(); let parser = crate::parser::Parser::new(tokens); @@ -3254,26 +3318,28 @@ e #[test] fn test_error_keyword_in_variable() { let some_program_string = r#"const let = "thing""#; - let tokens = crate::token::lexer(some_program_string).unwrap(); + let module_id = ModuleId::default(); + let tokens = crate::token::lexer(some_program_string, module_id).unwrap(); let parser = crate::parser::Parser::new(tokens); let result = parser.ast(); assert!(result.is_err()); assert_eq!( result.err().unwrap().to_string(), - r#"syntax: KclErrorDetails { source_ranges: [SourceRange([6, 9])], message: "Cannot assign a variable to a reserved keyword: let" }"# + r#"syntax: KclErrorDetails { source_ranges: [SourceRange([6, 9, 0])], message: "Cannot assign a variable to a reserved keyword: let" }"# ); } #[test] fn test_error_keyword_in_fn_name() { let some_program_string = r#"fn let = () {}"#; - let tokens = crate::token::lexer(some_program_string).unwrap(); + let module_id = ModuleId::default(); + let tokens = crate::token::lexer(some_program_string, module_id).unwrap(); let parser = crate::parser::Parser::new(tokens); let result = parser.ast(); assert!(result.is_err()); assert_eq!( result.err().unwrap().to_string(), - r#"syntax: KclErrorDetails { source_ranges: [SourceRange([3, 6])], message: "Cannot assign a variable to a reserved keyword: let" }"# + r#"syntax: KclErrorDetails { source_ranges: [SourceRange([3, 6, 0])], message: "Cannot assign a variable to a reserved keyword: let" }"# ); } @@ -3282,13 +3348,14 @@ e let some_program_string = r#"fn cos = () => { return 1 }"#; - let tokens = crate::token::lexer(some_program_string).unwrap(); + let module_id = ModuleId::default(); + let tokens = crate::token::lexer(some_program_string, module_id).unwrap(); let parser = crate::parser::Parser::new(tokens); let result = parser.ast(); assert!(result.is_err()); assert_eq!( result.err().unwrap().to_string(), - r#"syntax: KclErrorDetails { source_ranges: [SourceRange([3, 6])], message: "Cannot assign a variable to a reserved keyword: cos" }"# + r#"syntax: KclErrorDetails { source_ranges: [SourceRange([3, 6, 0])], message: "Cannot assign a variable to a reserved keyword: cos" }"# ); } @@ -3297,13 +3364,14 @@ e let some_program_string = r#"fn thing = (let) => { return 1 }"#; - let tokens = crate::token::lexer(some_program_string).unwrap(); + let module_id = ModuleId::default(); + let tokens = crate::token::lexer(some_program_string, module_id).unwrap(); let parser = crate::parser::Parser::new(tokens); let result = parser.ast(); assert!(result.is_err()); assert_eq!( result.err().unwrap().to_string(), - r#"syntax: KclErrorDetails { source_ranges: [SourceRange([12, 15])], message: "Cannot assign a variable to a reserved keyword: let" }"# + r#"syntax: KclErrorDetails { source_ranges: [SourceRange([12, 15, 0])], message: "Cannot assign a variable to a reserved keyword: let" }"# ); } @@ -3312,33 +3380,33 @@ e let some_program_string = r#"fn thing = (cos) => { return 1 }"#; - let tokens = crate::token::lexer(some_program_string).unwrap(); + let module_id = ModuleId::default(); + let tokens = crate::token::lexer(some_program_string, module_id).unwrap(); let parser = crate::parser::Parser::new(tokens); let result = parser.ast(); assert!(result.is_err()); assert_eq!( result.err().unwrap().to_string(), - r#"syntax: KclErrorDetails { source_ranges: [SourceRange([12, 15])], message: "Cannot assign a variable to a reserved keyword: cos" }"# + r#"syntax: KclErrorDetails { source_ranges: [SourceRange([12, 15, 0])], message: "Cannot assign a variable to a reserved keyword: cos" }"# ); } #[test] fn zero_param_function() { - let program = r#" + let code = r#" fn firstPrimeNumber = () => { return 2 } firstPrimeNumber() "#; - let tokens = crate::token::lexer(program).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let _ast = parser.ast().unwrap(); + let _ast = crate::parser::top_level_parse(code).unwrap(); } #[test] fn array() { let program = r#"[1, 2, 3]"#; - let tokens = crate::token::lexer(program).unwrap(); + let module_id = ModuleId::default(); + let tokens = crate::token::lexer(program, module_id).unwrap(); let mut sl: &[Token] = &tokens; let _arr = array_elem_by_elem(&mut sl).unwrap(); } @@ -3350,7 +3418,8 @@ e 2, 3, ]"#; - let tokens = crate::token::lexer(program).unwrap(); + let module_id = ModuleId::default(); + let tokens = crate::token::lexer(program, module_id).unwrap(); let mut sl: &[Token] = &tokens; let _arr = array_elem_by_elem(&mut sl).unwrap(); } @@ -3363,7 +3432,8 @@ e 2, 3 ]"#; - let tokens = crate::token::lexer(program).unwrap(); + let module_id = ModuleId::default(); + let tokens = crate::token::lexer(program, module_id).unwrap(); let mut sl: &[Token] = &tokens; let _arr = array_elem_by_elem(&mut sl).unwrap(); } @@ -3375,7 +3445,8 @@ e } else { 4 }"; - let tokens = crate::token::lexer(some_program_string).unwrap(); + let module_id = ModuleId::default(); + let tokens = crate::token::lexer(some_program_string, module_id).unwrap(); let mut sl: &[Token] = &tokens; let _res = if_expr(&mut sl).unwrap(); } @@ -3385,7 +3456,8 @@ e let some_program_string = "else if true { 4 }"; - let tokens = crate::token::lexer(some_program_string).unwrap(); + let module_id = ModuleId::default(); + let tokens = crate::token::lexer(some_program_string, module_id).unwrap(); let mut sl: &[Token] = &tokens; let _res = else_if(&mut sl).unwrap(); } @@ -3399,7 +3471,8 @@ e } else { 5 }"; - let tokens = crate::token::lexer(some_program_string).unwrap(); + let module_id = ModuleId::default(); + let tokens = crate::token::lexer(some_program_string, module_id).unwrap(); let mut sl: &[Token] = &tokens; let _res = if_expr(&mut sl).unwrap(); } @@ -3412,9 +3485,7 @@ e thing(false) "#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - parser.ast().unwrap(); + crate::parser::top_level_parse(some_program_string).unwrap(); } #[test] @@ -3429,14 +3500,15 @@ thing(false) "#, name ); - let tokens = crate::token::lexer(&some_program_string).unwrap(); + let module_id = ModuleId::default(); + let tokens = crate::token::lexer(&some_program_string, module_id).unwrap(); let parser = crate::parser::Parser::new(tokens); let result = parser.ast(); assert!(result.is_err()); assert_eq!( result.err().unwrap().to_string(), format!( - r#"syntax: KclErrorDetails {{ source_ranges: [SourceRange([0, {}])], message: "Expected a `fn` variable kind, found: `const`" }}"#, + r#"syntax: KclErrorDetails {{ source_ranges: [SourceRange([0, {}, 0])], message: "Expected a `fn` variable kind, found: `const`" }}"#, name.len(), ) ); @@ -3446,7 +3518,8 @@ thing(false) #[test] fn test_error_define_var_as_function() { let some_program_string = r#"fn thing = "thing""#; - let tokens = crate::token::lexer(some_program_string).unwrap(); + let module_id = ModuleId::default(); + let tokens = crate::token::lexer(some_program_string, module_id).unwrap(); let parser = crate::parser::Parser::new(tokens); let result = parser.ast(); assert!(result.is_err()); @@ -3455,7 +3528,7 @@ thing(false) // It should say that the compiler is expecting a function expression on the RHS. assert_eq!( result.err().unwrap().to_string(), - r#"syntax: KclErrorDetails { source_ranges: [SourceRange([11, 18])], message: "Unexpected token: \"thing\"" }"# + r#"syntax: KclErrorDetails { source_ranges: [SourceRange([11, 18, 0])], message: "Unexpected token: \"thing\"" }"# ); } @@ -3469,7 +3542,8 @@ thing(false) |> line([-5.09, 12.33], %) asdasd "#; - let tokens = crate::token::lexer(test_program).unwrap(); + let module_id = ModuleId::default(); + let tokens = crate::token::lexer(test_program, module_id).unwrap(); let parser = crate::parser::Parser::new(tokens); let result = parser.ast(); let _e = result.unwrap_err(); @@ -3493,18 +3567,14 @@ const b2 = cube([3,3], 4) const pt1 = b1[0] const pt2 = b2[0] "#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - parser.ast().unwrap(); + crate::parser::top_level_parse(some_program_string).unwrap(); } #[test] fn test_math_with_stdlib() { let some_program_string = r#"const d2r = pi() / 2 let other_thing = 2 * cos(3)"#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - parser.ast().unwrap(); + crate::parser::top_level_parse(some_program_string).unwrap(); } #[test] @@ -3522,9 +3592,7 @@ let other_thing = 2 * cos(3)"#; } let myBox = box([0,0], -3, -16, -10) "#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - parser.ast().unwrap(); + crate::parser::top_level_parse(some_program_string).unwrap(); } #[test] fn must_use_percent_in_pipeline_fn() { @@ -3532,12 +3600,13 @@ let myBox = box([0,0], -3, -16, -10) foo() |> bar(2) "#; - let tokens = crate::token::lexer(some_program_string).unwrap(); + let module_id = ModuleId::default(); + let tokens = crate::token::lexer(some_program_string, module_id).unwrap(); let parser = crate::parser::Parser::new(tokens); let err = parser.ast().unwrap_err(); assert_eq!( err.to_string(), - r#"syntax: KclErrorDetails { source_ranges: [SourceRange([30, 36])], message: "All expressions in a pipeline must use the % (substitution operator)" }"# + r#"syntax: KclErrorDetails { source_ranges: [SourceRange([30, 36, 0])], message: "All expressions in a pipeline must use the % (substitution operator)" }"# ); } } @@ -3553,7 +3622,8 @@ mod snapshot_math_tests { ($func_name:ident, $test_kcl_program:expr) => { #[test] fn $func_name() { - let tokens = crate::token::lexer($test_kcl_program).unwrap(); + let module_id = crate::ast::types::ModuleId::default(); + let tokens = crate::token::lexer($test_kcl_program, module_id).unwrap(); let actual = match binary_expression.parse(&tokens) { Ok(x) => x, Err(_e) => panic!("could not parse test"), @@ -3587,7 +3657,8 @@ mod snapshot_tests { ($func_name:ident, $test_kcl_program:expr) => { #[test] fn $func_name() { - let tokens = crate::token::lexer($test_kcl_program).unwrap(); + let module_id = crate::ast::types::ModuleId::default(); + let tokens = crate::token::lexer($test_kcl_program, module_id).unwrap(); print_tokens(&tokens); let actual = match program.parse(&tokens) { Ok(x) => x, diff --git a/src/wasm-lib/kcl/src/parser/parser_impl/error.rs b/src/wasm-lib/kcl/src/parser/parser_impl/error.rs index 11dea7c3f..a4697076b 100644 --- a/src/wasm-lib/kcl/src/parser/parser_impl/error.rs +++ b/src/wasm-lib/kcl/src/parser/parser_impl/error.rs @@ -1,13 +1,12 @@ use winnow::{ error::{ErrorKind, ParseError, StrContext}, stream::Stream, - Located, }; use crate::{ errors::{KclError, KclErrorDetails}, executor::SourceRange, - token::Token, + token::{Input, Token}, }; /// Accumulate context while backtracking errors @@ -20,9 +19,10 @@ pub struct ContextError { pub cause: Option, } -impl From, winnow::error::ContextError>> for KclError { - fn from(err: ParseError, winnow::error::ContextError>) -> Self { +impl From, winnow::error::ContextError>> for KclError { + fn from(err: ParseError, winnow::error::ContextError>) -> Self { let (input, offset): (Vec, usize) = (err.input().chars().collect(), err.offset()); + let module_id = err.input().state.module_id; if offset >= input.len() { // From the winnow docs: @@ -31,7 +31,7 @@ impl From, winnow::error::ContextError>> for KclError { // the end of input (input.len()) on eof errors. return KclError::Lexical(KclErrorDetails { - source_ranges: vec![SourceRange([offset, offset])], + source_ranges: vec![SourceRange([offset, offset, module_id.as_usize()])], message: "unexpected EOF while parsing".to_string(), }); } @@ -42,7 +42,7 @@ impl From, winnow::error::ContextError>> for KclError { // TODO: Add the Winnow parser context to the error. // See https://github.com/KittyCAD/modeling-app/issues/784 KclError::Lexical(KclErrorDetails { - source_ranges: vec![SourceRange([offset, offset + 1])], + source_ranges: vec![SourceRange([offset, offset + 1, module_id.as_usize()])], message: format!("found unknown token '{}'", bad_token), }) } diff --git a/src/wasm-lib/kcl/src/simulation_tests.rs b/src/wasm-lib/kcl/src/simulation_tests.rs index cd7b75aa9..4a7c67e00 100644 --- a/src/wasm-lib/kcl/src/simulation_tests.rs +++ b/src/wasm-lib/kcl/src/simulation_tests.rs @@ -1,5 +1,5 @@ use crate::{ - ast::types::{Node, Program}, + ast::types::{ModuleId, Node, Program}, errors::KclError, parser::Parser, token::Token, @@ -44,7 +44,7 @@ fn read(filename: &'static str, test_name: &str) -> String { fn tokenize(test_name: &str) { let input = read("input.kcl", test_name); - let token_res = crate::token::lexer(&input); + let token_res = crate::token::lexer(&input, ModuleId::default()); assert_snapshot(test_name, "Result of tokenizing", || { insta::assert_json_snapshot!("tokens", token_res); diff --git a/src/wasm-lib/kcl/src/std/kcl_stdlib.rs b/src/wasm-lib/kcl/src/std/kcl_stdlib.rs index e724a4697..78cd204ca 100644 --- a/src/wasm-lib/kcl/src/std/kcl_stdlib.rs +++ b/src/wasm-lib/kcl/src/std/kcl_stdlib.rs @@ -6,7 +6,6 @@ use serde::{Deserialize, Serialize}; use crate::{ ast::types::{BodyItem, Expr, FunctionExpression, Node, Program}, docs::{StdLibFn, StdLibFnData}, - token::lexer, }; pub trait KclStdLibFn: StdLibFn { @@ -83,8 +82,7 @@ impl Serialize for Box { /// Return the program and its single function. /// Return None if those expectations aren't met. pub fn extract_function(source: &str) -> Option<(Node, crate::ast::types::BoxNode)> { - let tokens = lexer(source).unwrap(); - let src = crate::parser::Parser::new(tokens).ast().ok()?; + let src = crate::parser::top_level_parse(source).ok()?; assert_eq!(src.body.len(), 1); let BodyItem::ExpressionStatement(expr) = src.body.last()? else { panic!("expected expression statement"); diff --git a/src/wasm-lib/kcl/src/test_server.rs b/src/wasm-lib/kcl/src/test_server.rs index d54bc511e..c75780a36 100644 --- a/src/wasm-lib/kcl/src/test_server.rs +++ b/src/wasm-lib/kcl/src/test_server.rs @@ -17,9 +17,7 @@ pub struct RequestBody { /// This returns the bytes of the snapshot. pub async fn execute_and_snapshot(code: &str, units: UnitLength) -> anyhow::Result { let ctx = new_context(units, true).await?; - let tokens = crate::token::lexer(code)?; - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast()?; + let program = crate::parser::top_level_parse(code)?; do_execute_and_snapshot(&ctx, program).await.map(|(_state, snap)| snap) } @@ -37,9 +35,7 @@ pub async fn execute_and_snapshot_ast( pub async fn execute_and_snapshot_no_auth(code: &str, units: UnitLength) -> anyhow::Result { let ctx = new_context(units, false).await?; - let tokens = crate::token::lexer(code)?; - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast()?; + let program = crate::parser::top_level_parse(code)?; do_execute_and_snapshot(&ctx, program).await.map(|(_state, snap)| snap) } diff --git a/src/wasm-lib/kcl/src/token.rs b/src/wasm-lib/kcl/src/token.rs index 7533ffda3..a4b7131b7 100644 --- a/src/wasm-lib/kcl/src/token.rs +++ b/src/wasm-lib/kcl/src/token.rs @@ -8,13 +8,16 @@ use tower_lsp::lsp_types::SemanticTokenType; use winnow::stream::ContainsToken; use crate::{ - ast::types::{ItemVisibility, VariableKind}, + ast::types::{ItemVisibility, ModuleId, VariableKind}, errors::KclError, executor::SourceRange, }; mod tokeniser; +// Re-export +pub use tokeniser::Input; + /// The types of tokens. #[derive(Debug, PartialEq, Eq, Copy, Clone, Deserialize, Serialize, ts_rs::TS, JsonSchema, FromStr, Display)] #[cfg_attr(feature = "pyo3", pyo3::pyclass(eq, eq_int))] @@ -161,6 +164,8 @@ pub struct Token { pub start: usize, /// Offset in the source code where this token ends. pub end: usize, + #[serde(default, skip_serializing_if = "ModuleId::is_top_level")] + pub module_id: ModuleId, pub value: String, } @@ -177,10 +182,16 @@ impl ContainsToken for TokenType { } impl Token { - pub fn from_range(range: std::ops::Range, token_type: TokenType, value: String) -> Self { + pub fn from_range( + range: std::ops::Range, + module_id: ModuleId, + token_type: TokenType, + value: String, + ) -> Self { Self { start: range.start, end: range.end, + module_id, value, token_type, } @@ -193,7 +204,7 @@ impl Token { } pub fn as_source_range(&self) -> SourceRange { - SourceRange([self.start, self.end]) + SourceRange([self.start, self.end, self.module_id.as_usize()]) } pub fn as_source_ranges(&self) -> Vec { @@ -227,18 +238,18 @@ impl Token { impl From for SourceRange { fn from(token: Token) -> Self { - Self([token.start, token.end]) + Self([token.start, token.end, token.module_id.as_usize()]) } } impl From<&Token> for SourceRange { fn from(token: &Token) -> Self { - Self([token.start, token.end]) + Self([token.start, token.end, token.module_id.as_usize()]) } } -pub fn lexer(s: &str) -> Result, KclError> { - tokeniser::lexer(s).map_err(From::from) +pub fn lexer(s: &str, module_id: ModuleId) -> Result, KclError> { + tokeniser::lexer(s, module_id).map_err(From::from) } #[cfg(test)] diff --git a/src/wasm-lib/kcl/src/token/tokeniser.rs b/src/wasm-lib/kcl/src/token/tokeniser.rs index 589a8d478..883705595 100644 --- a/src/wasm-lib/kcl/src/token/tokeniser.rs +++ b/src/wasm-lib/kcl/src/token/tokeniser.rs @@ -5,16 +5,37 @@ use winnow::{ prelude::*, stream::{Location, Stream}, token::{any, none_of, one_of, take_till, take_until}, - Located, + Located, Stateful, }; -use crate::token::{Token, TokenType}; +use crate::{ + ast::types::ModuleId, + token::{Token, TokenType}, +}; -pub fn lexer(i: &str) -> Result, ParseError, ContextError>> { - repeat(0.., token).parse(Located::new(i)) +pub fn lexer(i: &str, module_id: ModuleId) -> Result, ParseError, ContextError>> { + let state = State::new(module_id); + let input = Input { + input: Located::new(i), + state, + }; + repeat(0.., token).parse(input) } -pub fn token(i: &mut Located<&str>) -> PResult { +pub type Input<'a> = Stateful, State>; + +#[derive(Debug, Clone)] +pub struct State { + pub module_id: ModuleId, +} + +impl State { + fn new(module_id: ModuleId) -> Self { + Self { module_id } + } +} + +pub fn token(i: &mut Input<'_>) -> PResult { match winnow::combinator::dispatch! {peek(any); '"' | '\'' => string, '/' => alt((line_comment, block_comment, operator)), @@ -42,6 +63,7 @@ pub fn token(i: &mut Located<&str>) -> PResult { Ok(Token::from_range( i.location()..i.location() + 1, + i.state.module_id, TokenType::Unknown, i.next_slice(1).to_string(), )) @@ -49,19 +71,29 @@ pub fn token(i: &mut Located<&str>) -> PResult { } } -fn block_comment(i: &mut Located<&str>) -> PResult { +fn block_comment(i: &mut Input<'_>) -> PResult { let inner = ("/*", take_until(0.., "*/"), "*/").take(); let (value, range) = inner.with_span().parse_next(i)?; - Ok(Token::from_range(range, TokenType::BlockComment, value.to_string())) + Ok(Token::from_range( + range, + i.state.module_id, + TokenType::BlockComment, + value.to_string(), + )) } -fn line_comment(i: &mut Located<&str>) -> PResult { +fn line_comment(i: &mut Input<'_>) -> PResult { let inner = (r#"//"#, take_till(0.., ['\n', '\r'])).take(); let (value, range) = inner.with_span().parse_next(i)?; - Ok(Token::from_range(range, TokenType::LineComment, value.to_string())) + Ok(Token::from_range( + range, + i.state.module_id, + TokenType::LineComment, + value.to_string(), + )) } -fn number(i: &mut Located<&str>) -> PResult { +fn number(i: &mut Input<'_>) -> PResult { let number_parser = alt(( // Digits before the decimal point. (digit1, opt(('.', digit1))).map(|_| ()), @@ -69,113 +101,193 @@ fn number(i: &mut Located<&str>) -> PResult { ('.', digit1).map(|_| ()), )); let (value, range) = number_parser.take().with_span().parse_next(i)?; - Ok(Token::from_range(range, TokenType::Number, value.to_string())) + Ok(Token::from_range( + range, + i.state.module_id, + TokenType::Number, + value.to_string(), + )) } -fn whitespace(i: &mut Located<&str>) -> PResult { +fn whitespace(i: &mut Input<'_>) -> PResult { let (value, range) = multispace1.with_span().parse_next(i)?; - Ok(Token::from_range(range, TokenType::Whitespace, value.to_string())) + Ok(Token::from_range( + range, + i.state.module_id, + TokenType::Whitespace, + value.to_string(), + )) } -fn inner_word(i: &mut Located<&str>) -> PResult<()> { +fn inner_word(i: &mut Input<'_>) -> PResult<()> { one_of(('a'..='z', 'A'..='Z', '_')).parse_next(i)?; repeat::<_, _, (), _, _>(0.., one_of(('a'..='z', 'A'..='Z', '0'..='9', '_'))).parse_next(i)?; Ok(()) } -fn word(i: &mut Located<&str>) -> PResult { +fn word(i: &mut Input<'_>) -> PResult { let (value, range) = inner_word.take().with_span().parse_next(i)?; - Ok(Token::from_range(range, TokenType::Word, value.to_string())) + Ok(Token::from_range( + range, + i.state.module_id, + TokenType::Word, + value.to_string(), + )) } -fn operator(i: &mut Located<&str>) -> PResult { +fn operator(i: &mut Input<'_>) -> PResult { let (value, range) = alt(( ">=", "<=", "==", "=>", "!=", "|>", "*", "+", "-", "/", "%", "=", "<", ">", r"\", "|", "^", )) .with_span() .parse_next(i)?; - Ok(Token::from_range(range, TokenType::Operator, value.to_string())) + Ok(Token::from_range( + range, + i.state.module_id, + TokenType::Operator, + value.to_string(), + )) } -fn brace_start(i: &mut Located<&str>) -> PResult { +fn brace_start(i: &mut Input<'_>) -> PResult { let (value, range) = alt(('{', '(', '[')).with_span().parse_next(i)?; - Ok(Token::from_range(range, TokenType::Brace, value.to_string())) + Ok(Token::from_range( + range, + i.state.module_id, + TokenType::Brace, + value.to_string(), + )) } -fn brace_end(i: &mut Located<&str>) -> PResult { +fn brace_end(i: &mut Input<'_>) -> PResult { let (value, range) = alt(('}', ')', ']')).with_span().parse_next(i)?; - Ok(Token::from_range(range, TokenType::Brace, value.to_string())) + Ok(Token::from_range( + range, + i.state.module_id, + TokenType::Brace, + value.to_string(), + )) } -fn comma(i: &mut Located<&str>) -> PResult { +fn comma(i: &mut Input<'_>) -> PResult { let (value, range) = ','.with_span().parse_next(i)?; - Ok(Token::from_range(range, TokenType::Comma, value.to_string())) + Ok(Token::from_range( + range, + i.state.module_id, + TokenType::Comma, + value.to_string(), + )) } -fn hash(i: &mut Located<&str>) -> PResult { +fn hash(i: &mut Input<'_>) -> PResult { let (value, range) = '#'.with_span().parse_next(i)?; - Ok(Token::from_range(range, TokenType::Hash, value.to_string())) + Ok(Token::from_range( + range, + i.state.module_id, + TokenType::Hash, + value.to_string(), + )) } -fn bang(i: &mut Located<&str>) -> PResult { +fn bang(i: &mut Input<'_>) -> PResult { let (value, range) = '!'.with_span().parse_next(i)?; - Ok(Token::from_range(range, TokenType::Bang, value.to_string())) + Ok(Token::from_range( + range, + i.state.module_id, + TokenType::Bang, + value.to_string(), + )) } -fn dollar(i: &mut Located<&str>) -> PResult { +fn dollar(i: &mut Input<'_>) -> PResult { let (value, range) = '$'.with_span().parse_next(i)?; - Ok(Token::from_range(range, TokenType::Dollar, value.to_string())) + Ok(Token::from_range( + range, + i.state.module_id, + TokenType::Dollar, + value.to_string(), + )) } -fn question_mark(i: &mut Located<&str>) -> PResult { +fn question_mark(i: &mut Input<'_>) -> PResult { let (value, range) = '?'.with_span().parse_next(i)?; - Ok(Token::from_range(range, TokenType::QuestionMark, value.to_string())) + Ok(Token::from_range( + range, + i.state.module_id, + TokenType::QuestionMark, + value.to_string(), + )) } -fn colon(i: &mut Located<&str>) -> PResult { +fn colon(i: &mut Input<'_>) -> PResult { let (value, range) = ':'.with_span().parse_next(i)?; - Ok(Token::from_range(range, TokenType::Colon, value.to_string())) + Ok(Token::from_range( + range, + i.state.module_id, + TokenType::Colon, + value.to_string(), + )) } -fn period(i: &mut Located<&str>) -> PResult { +fn period(i: &mut Input<'_>) -> PResult { let (value, range) = '.'.with_span().parse_next(i)?; - Ok(Token::from_range(range, TokenType::Period, value.to_string())) + Ok(Token::from_range( + range, + i.state.module_id, + TokenType::Period, + value.to_string(), + )) } -fn double_period(i: &mut Located<&str>) -> PResult { +fn double_period(i: &mut Input<'_>) -> PResult { let (value, range) = "..".with_span().parse_next(i)?; - Ok(Token::from_range(range, TokenType::DoublePeriod, value.to_string())) + Ok(Token::from_range( + range, + i.state.module_id, + TokenType::DoublePeriod, + value.to_string(), + )) } /// Zero or more of either: /// 1. Any character except " or \ /// 2. Any character preceded by \ -fn inner_double_quote(i: &mut Located<&str>) -> PResult<()> { +fn inner_double_quote(i: &mut Input<'_>) -> PResult<()> { repeat(0.., alt((none_of(('"', '\\')), preceded('\\', winnow::token::any)))).parse_next(i) } /// Zero or more of either: /// 1. Any character except ' or \ /// 2. Any character preceded by \ -fn inner_single_quote(i: &mut Located<&str>) -> PResult<()> { +fn inner_single_quote(i: &mut Input<'_>) -> PResult<()> { repeat(0.., alt((none_of(('\'', '\\')), preceded('\\', winnow::token::any)))).parse_next(i) } -fn string(i: &mut Located<&str>) -> PResult { +fn string(i: &mut Input<'_>) -> PResult { let single_quoted_string = ('\'', inner_single_quote.take(), '\''); let double_quoted_string = ('"', inner_double_quote.take(), '"'); let either_quoted_string = alt((single_quoted_string.take(), double_quoted_string.take())); let (value, range): (&str, _) = either_quoted_string.with_span().parse_next(i)?; - Ok(Token::from_range(range, TokenType::String, value.to_string())) + Ok(Token::from_range( + range, + i.state.module_id, + TokenType::String, + value.to_string(), + )) } -fn import_keyword(i: &mut Located<&str>) -> PResult { +fn import_keyword(i: &mut Input<'_>) -> PResult { let (value, range) = "import".with_span().parse_next(i)?; let token_type = peek(alt((' '.map(|_| TokenType::Keyword), '('.map(|_| TokenType::Word)))).parse_next(i)?; - Ok(Token::from_range(range, token_type, value.to_owned())) + Ok(Token::from_range( + range, + i.state.module_id, + token_type, + value.to_owned(), + )) } -fn unambiguous_keywords(i: &mut Located<&str>) -> PResult { +fn unambiguous_keywords(i: &mut Input<'_>) -> PResult { // These are the keywords themselves. let keyword_candidates = alt(( "if", "else", "for", "while", "return", "break", "continue", "fn", "let", "mut", "loop", "true", "false", @@ -188,14 +300,19 @@ fn unambiguous_keywords(i: &mut Located<&str>) -> PResult { peek(none_of(('a'..='z', 'A'..='Z', '-', '_', '0'..='9'))), ); let (value, range) = keyword.with_span().parse_next(i)?; - Ok(Token::from_range(range, TokenType::Keyword, value.to_owned())) + Ok(Token::from_range( + range, + i.state.module_id, + TokenType::Keyword, + value.to_owned(), + )) } -fn keyword(i: &mut Located<&str>) -> PResult { +fn keyword(i: &mut Input<'_>) -> PResult { alt((import_keyword, unambiguous_keywords)).parse_next(i) } -fn type_(i: &mut Located<&str>) -> PResult { +fn type_(i: &mut Input<'_>) -> PResult { // These are the types themselves. let type_candidates = alt(("string", "number", "bool", "sketch", "sketch_surface", "solid")); // Look ahead. If any of these characters follow the type, then it's not a type, it's just @@ -205,7 +322,12 @@ fn type_(i: &mut Located<&str>) -> PResult { peek(none_of(('a'..='z', 'A'..='Z', '-', '_', '0'..='9'))), ); let (value, range) = type_.with_span().parse_next(i)?; - Ok(Token::from_range(range, TokenType::Type, value.to_owned())) + Ok(Token::from_range( + range, + i.state.module_id, + TokenType::Type, + value.to_owned(), + )) } #[cfg(test)] @@ -216,21 +338,28 @@ mod tests { fn assert_parse_err<'i, P, O, E>(mut p: P, s: &'i str) where O: std::fmt::Debug, - P: Parser, O, E>, + P: Parser, O, E>, { - assert!( - p.parse_next(&mut Located::new(s)).is_err(), - "parsed {s} but should have failed" - ); + let state = State::new(ModuleId::default()); + let mut input = Input { + input: Located::new(s), + state, + }; + assert!(p.parse_next(&mut input).is_err(), "parsed {s} but should have failed"); } fn assert_parse_ok<'i, P, O, E>(mut p: P, s: &'i str) where E: std::fmt::Debug, O: std::fmt::Debug, - P: Parser, O, E>, + P: Parser, O, E>, { - let res = p.parse_next(&mut Located::new(s)); + let state = State::new(ModuleId::default()); + let mut input = Input { + input: Located::new(s), + state, + }; + let res = p.parse_next(&mut input); assert!(res.is_ok(), "failed to parse {s}, got {}", res.unwrap_err()); } @@ -246,10 +375,13 @@ mod tests { assert_parse_err(number, invalid); } - assert_eq!( - number.parse(Located::new("0.0000000000")).unwrap().value, - "0.0000000000" - ); + let module_id = ModuleId::from_usize(1); + let input = Input { + input: Located::new("0.0000000000"), + state: State::new(module_id), + }; + + assert_eq!(number.parse(input).unwrap().value, "0.0000000000"); } #[test] @@ -318,37 +450,43 @@ mod tests { #[test] fn test_program0() { let program = "const a=5"; - let actual = lexer(program).unwrap(); + let module_id = ModuleId::from_usize(1); + let actual = lexer(program, module_id).unwrap(); let expected = vec![ Token { token_type: TokenType::Keyword, value: "const".to_string(), start: 0, end: 5, + module_id, }, Token { token_type: TokenType::Whitespace, value: " ".to_string(), start: 5, end: 6, + module_id, }, Token { token_type: TokenType::Word, value: "a".to_string(), start: 6, end: 7, + module_id, }, Token { token_type: TokenType::Operator, value: "=".to_string(), start: 7, end: 8, + module_id, }, Token { token_type: TokenType::Number, value: "5".to_string(), start: 8, end: 9, + module_id, }, ]; assert_tokens(expected, actual); @@ -357,61 +495,71 @@ mod tests { #[test] fn test_program1() { let program = "54 + 22500 + 6"; - let actual = lexer(program).unwrap(); + let module_id = ModuleId::from_usize(1); + let actual = lexer(program, module_id).unwrap(); let expected = vec![ Token { token_type: TokenType::Number, value: "54".to_string(), start: 0, end: 2, + module_id, }, Token { token_type: TokenType::Whitespace, value: " ".to_string(), start: 2, end: 3, + module_id, }, Token { token_type: TokenType::Operator, value: "+".to_string(), start: 3, end: 4, + module_id, }, Token { token_type: TokenType::Whitespace, value: " ".to_string(), start: 4, end: 5, + module_id, }, Token { token_type: TokenType::Number, value: "22500".to_string(), start: 5, end: 10, + module_id, }, Token { token_type: TokenType::Whitespace, value: " ".to_string(), start: 10, end: 11, + module_id, }, Token { token_type: TokenType::Operator, value: "+".to_string(), start: 11, end: 12, + module_id, }, Token { token_type: TokenType::Whitespace, value: " ".to_string(), start: 12, end: 13, + module_id, }, Token { token_type: TokenType::Number, value: "6".to_string(), start: 13, end: 14, + module_id, }, ]; assert_tokens(expected, actual); @@ -432,6 +580,7 @@ fn ghi = (part001) => { } show(part001)"#; + let module_id = ModuleId::from_usize(1); use TokenType::*; @@ -440,676 +589,788 @@ show(part001)"#; token_type: Keyword, start: 0, end: 5, + module_id, value: "const".to_owned(), }, Token { token_type: Whitespace, start: 5, end: 6, + module_id, value: " ".to_owned(), }, Token { token_type: Word, start: 6, end: 13, + module_id, value: "part001".to_owned(), }, Token { token_type: Whitespace, start: 13, end: 14, + module_id, value: " ".to_owned(), }, Token { token_type: Operator, start: 14, end: 15, + module_id, value: "=".to_owned(), }, Token { token_type: Whitespace, start: 15, end: 16, + module_id, value: " ".to_owned(), }, Token { token_type: Word, start: 16, end: 29, + module_id, value: "startSketchAt".to_owned(), }, Token { token_type: Brace, start: 29, end: 30, + module_id, value: "(".to_owned(), }, Token { token_type: Brace, start: 30, end: 31, + module_id, value: "[".to_owned(), }, Token { token_type: Number, start: 31, end: 43, + module_id, value: "0.0000000000".to_owned(), }, Token { token_type: Comma, start: 43, end: 44, + module_id, value: ",".to_owned(), }, Token { token_type: Whitespace, start: 44, end: 45, + module_id, value: " ".to_owned(), }, Token { token_type: Number, start: 45, end: 57, + module_id, value: "5.0000000000".to_owned(), }, Token { token_type: Brace, start: 57, end: 58, + module_id, value: "]".to_owned(), }, Token { token_type: Brace, start: 58, end: 59, + module_id, value: ")".to_owned(), }, Token { token_type: Whitespace, start: 59, end: 64, + module_id, value: "\n ".to_owned(), }, Token { token_type: Operator, start: 64, end: 66, + module_id, value: "|>".to_owned(), }, Token { token_type: Whitespace, start: 66, end: 67, + module_id, value: " ".to_owned(), }, Token { token_type: Word, start: 67, end: 71, + module_id, value: "line".to_owned(), }, Token { token_type: Brace, start: 71, end: 72, + module_id, value: "(".to_owned(), }, Token { token_type: Brace, start: 72, end: 73, + module_id, value: "[".to_owned(), }, Token { token_type: Number, start: 73, end: 85, + module_id, value: "0.4900857016".to_owned(), }, Token { token_type: Comma, start: 85, end: 86, + module_id, value: ",".to_owned(), }, Token { token_type: Whitespace, start: 86, end: 87, + module_id, value: " ".to_owned(), }, Token { token_type: Operator, start: 87, end: 88, + module_id, value: "-".to_owned(), }, Token { token_type: Number, start: 88, end: 100, + module_id, value: "0.0240763666".to_owned(), }, Token { token_type: Brace, start: 100, end: 101, + module_id, value: "]".to_owned(), }, Token { token_type: Comma, start: 101, end: 102, + module_id, value: ",".to_owned(), }, Token { token_type: Whitespace, start: 102, end: 103, + module_id, value: " ".to_owned(), }, Token { token_type: Operator, start: 103, end: 104, + module_id, value: "%".to_owned(), }, Token { token_type: Brace, start: 104, end: 105, + module_id, value: ")".to_owned(), }, Token { token_type: Whitespace, start: 105, end: 107, + module_id, value: "\n\n".to_owned(), }, Token { token_type: Keyword, start: 107, end: 112, + module_id, value: "const".to_owned(), }, Token { token_type: Whitespace, start: 112, end: 113, + module_id, value: " ".to_owned(), }, Token { token_type: Word, start: 113, end: 120, + module_id, value: "part002".to_owned(), }, Token { token_type: Whitespace, start: 120, end: 121, + module_id, value: " ".to_owned(), }, Token { token_type: Operator, start: 121, end: 122, + module_id, value: "=".to_owned(), }, Token { token_type: Whitespace, start: 122, end: 123, + module_id, value: " ".to_owned(), }, Token { token_type: String, start: 123, end: 132, + module_id, value: "\"part002\"".to_owned(), }, Token { token_type: Whitespace, start: 132, end: 133, + module_id, value: "\n".to_owned(), }, Token { token_type: Keyword, start: 133, end: 138, + module_id, value: "const".to_owned(), }, Token { token_type: Whitespace, start: 138, end: 139, + module_id, value: " ".to_owned(), }, Token { token_type: Word, start: 139, end: 145, + module_id, value: "things".to_owned(), }, Token { token_type: Whitespace, start: 145, end: 146, + module_id, value: " ".to_owned(), }, Token { token_type: Operator, start: 146, end: 147, + module_id, value: "=".to_owned(), }, Token { token_type: Whitespace, start: 147, end: 148, + module_id, value: " ".to_owned(), }, Token { token_type: Brace, start: 148, end: 149, + module_id, value: "[".to_owned(), }, Token { token_type: Word, start: 149, end: 156, + module_id, value: "part001".to_owned(), }, Token { token_type: Comma, start: 156, end: 157, + module_id, value: ",".to_owned(), }, Token { token_type: Whitespace, start: 157, end: 158, + module_id, value: " ".to_owned(), }, Token { token_type: Number, start: 158, end: 161, + module_id, value: "0.0".to_owned(), }, Token { token_type: Brace, start: 161, end: 162, + module_id, value: "]".to_owned(), }, Token { token_type: Whitespace, start: 162, end: 163, + module_id, value: "\n".to_owned(), }, Token { token_type: Keyword, start: 163, end: 166, + module_id, value: "let".to_owned(), }, Token { token_type: Whitespace, start: 166, end: 167, + module_id, value: " ".to_owned(), }, Token { token_type: Word, start: 167, end: 171, + module_id, value: "blah".to_owned(), }, Token { token_type: Whitespace, start: 171, end: 172, + module_id, value: " ".to_owned(), }, Token { token_type: Operator, start: 172, end: 173, + module_id, value: "=".to_owned(), }, Token { token_type: Whitespace, start: 173, end: 174, + module_id, value: " ".to_owned(), }, Token { token_type: Number, start: 174, end: 175, + module_id, value: "1".to_owned(), }, Token { token_type: Whitespace, start: 175, end: 176, + module_id, value: "\n".to_owned(), }, Token { token_type: Keyword, start: 176, end: 181, + module_id, value: "const".to_owned(), }, Token { token_type: Whitespace, start: 181, end: 182, + module_id, value: " ".to_owned(), }, Token { token_type: Word, start: 182, end: 185, + module_id, value: "foo".to_owned(), }, Token { token_type: Whitespace, start: 185, end: 186, + module_id, value: " ".to_owned(), }, Token { token_type: Operator, start: 186, end: 187, + module_id, value: "=".to_owned(), }, Token { token_type: Whitespace, start: 187, end: 188, + module_id, value: " ".to_owned(), }, Token { token_type: Keyword, start: 188, end: 193, + module_id, value: "false".to_owned(), }, Token { token_type: Whitespace, start: 193, end: 194, + module_id, value: "\n".to_owned(), }, Token { token_type: Keyword, start: 194, end: 197, + module_id, value: "let".to_owned(), }, Token { token_type: Whitespace, start: 197, end: 198, + module_id, value: " ".to_owned(), }, Token { token_type: Word, start: 198, end: 201, + module_id, value: "baz".to_owned(), }, Token { token_type: Whitespace, start: 201, end: 202, + module_id, value: " ".to_owned(), }, Token { token_type: Operator, start: 202, end: 203, + module_id, value: "=".to_owned(), }, Token { token_type: Whitespace, start: 203, end: 204, + module_id, value: " ".to_owned(), }, Token { token_type: Brace, start: 204, end: 205, + module_id, value: "{".to_owned(), }, Token { token_type: Word, start: 205, end: 206, + module_id, value: "a".to_owned(), }, Token { token_type: Colon, start: 206, end: 207, + module_id, value: ":".to_owned(), }, Token { token_type: Whitespace, start: 207, end: 208, + module_id, value: " ".to_owned(), }, Token { token_type: Number, start: 208, end: 209, + module_id, value: "1".to_owned(), }, Token { token_type: Comma, start: 209, end: 210, + module_id, value: ",".to_owned(), }, Token { token_type: Whitespace, start: 210, end: 211, + module_id, value: " ".to_owned(), }, Token { token_type: Word, start: 211, end: 218, + module_id, value: "part001".to_owned(), }, Token { token_type: Colon, start: 218, end: 219, + module_id, value: ":".to_owned(), }, Token { token_type: Whitespace, start: 219, end: 220, + module_id, value: " ".to_owned(), }, Token { token_type: String, start: 220, end: 227, + module_id, value: "\"thing\"".to_owned(), }, Token { token_type: Brace, start: 227, end: 228, + module_id, value: "}".to_owned(), }, Token { token_type: Whitespace, start: 228, end: 230, + module_id, value: "\n\n".to_owned(), }, Token { token_type: Keyword, start: 230, end: 232, + module_id, value: "fn".to_owned(), }, Token { token_type: Whitespace, start: 232, end: 233, + module_id, value: " ".to_owned(), }, Token { token_type: Word, start: 233, end: 236, + module_id, value: "ghi".to_owned(), }, Token { token_type: Whitespace, start: 236, end: 237, + module_id, value: " ".to_owned(), }, Token { token_type: Operator, start: 237, end: 238, + module_id, value: "=".to_owned(), }, Token { token_type: Whitespace, start: 238, end: 239, + module_id, value: " ".to_owned(), }, Token { token_type: Brace, start: 239, end: 240, + module_id, value: "(".to_owned(), }, Token { token_type: Word, start: 240, end: 247, + module_id, value: "part001".to_owned(), }, Token { token_type: Brace, start: 247, end: 248, + module_id, value: ")".to_owned(), }, Token { token_type: Whitespace, start: 248, end: 249, + module_id, value: " ".to_owned(), }, Token { token_type: Operator, start: 249, end: 251, + module_id, value: "=>".to_owned(), }, Token { token_type: Whitespace, start: 251, end: 252, + module_id, value: " ".to_owned(), }, Token { token_type: Brace, start: 252, end: 253, + module_id, value: "{".to_owned(), }, Token { token_type: Whitespace, start: 253, end: 256, + module_id, value: "\n ".to_owned(), }, Token { token_type: Keyword, start: 256, end: 262, + module_id, value: "return".to_owned(), }, Token { token_type: Whitespace, start: 262, end: 263, + module_id, value: " ".to_owned(), }, Token { token_type: Word, start: 263, end: 270, + module_id, value: "part001".to_owned(), }, Token { token_type: Whitespace, start: 270, end: 271, + module_id, value: "\n".to_owned(), }, Token { token_type: Brace, start: 271, end: 272, + module_id, value: "}".to_owned(), }, Token { token_type: Whitespace, start: 272, end: 274, + module_id, value: "\n\n".to_owned(), }, Token { token_type: Word, start: 274, end: 278, + module_id, value: "show".to_owned(), }, Token { token_type: Brace, start: 278, end: 279, + module_id, value: "(".to_owned(), }, Token { token_type: Word, start: 279, end: 286, + module_id, value: "part001".to_owned(), }, Token { token_type: Brace, start: 286, end: 287, + module_id, value: ")".to_owned(), }, ]; - let actual = lexer(program).unwrap(); + let actual = lexer(program, module_id).unwrap(); assert_tokens(expected, actual); } @@ -1123,301 +1384,351 @@ const key = 'c' const things = "things" // this is also a comment"#; - let actual = lexer(program).unwrap(); + let module_id = ModuleId::from_usize(1); + let actual = lexer(program, module_id).unwrap(); use TokenType::*; let expected = vec![ Token { token_type: Whitespace, start: 0, end: 1, + module_id, value: "\n".to_owned(), }, Token { token_type: LineComment, start: 1, end: 21, + module_id, value: "// this is a comment".to_owned(), }, Token { token_type: Whitespace, start: 21, end: 22, + module_id, value: "\n".to_owned(), }, Token { token_type: Keyword, start: 22, end: 27, + module_id, value: "const".to_owned(), }, Token { token_type: Whitespace, start: 27, end: 28, + module_id, value: " ".to_owned(), }, Token { token_type: Word, start: 28, end: 30, + module_id, value: "yo".to_owned(), }, Token { token_type: Whitespace, start: 30, end: 31, + module_id, value: " ".to_owned(), }, Token { token_type: Operator, start: 31, end: 32, + module_id, value: "=".to_owned(), }, Token { token_type: Whitespace, start: 32, end: 33, + module_id, value: " ".to_owned(), }, Token { token_type: Brace, start: 33, end: 34, + module_id, value: "{".to_owned(), }, Token { token_type: Whitespace, start: 34, end: 35, + module_id, value: " ".to_owned(), }, Token { token_type: Word, start: 35, end: 36, + module_id, value: "a".to_owned(), }, Token { token_type: Colon, start: 36, end: 37, + module_id, value: ":".to_owned(), }, Token { token_type: Whitespace, start: 37, end: 38, + module_id, value: " ".to_owned(), }, Token { token_type: Brace, start: 38, end: 39, + module_id, value: "{".to_owned(), }, Token { token_type: Whitespace, start: 39, end: 40, + module_id, value: " ".to_owned(), }, Token { token_type: Word, start: 40, end: 41, + module_id, value: "b".to_owned(), }, Token { token_type: Colon, start: 41, end: 42, + module_id, value: ":".to_owned(), }, Token { token_type: Whitespace, start: 42, end: 43, + module_id, value: " ".to_owned(), }, Token { token_type: Brace, start: 43, end: 44, + module_id, value: "{".to_owned(), }, Token { token_type: Whitespace, start: 44, end: 45, + module_id, value: " ".to_owned(), }, Token { token_type: Word, start: 45, end: 46, + module_id, value: "c".to_owned(), }, Token { token_type: Colon, start: 46, end: 47, + module_id, value: ":".to_owned(), }, Token { token_type: Whitespace, start: 47, end: 48, + module_id, value: " ".to_owned(), }, Token { token_type: String, start: 48, end: 53, + module_id, value: "'123'".to_owned(), }, Token { token_type: Whitespace, start: 53, end: 54, + module_id, value: " ".to_owned(), }, Token { token_type: Brace, start: 54, end: 55, + module_id, value: "}".to_owned(), }, Token { token_type: Whitespace, start: 55, end: 56, + module_id, value: " ".to_owned(), }, Token { token_type: Brace, start: 56, end: 57, + module_id, value: "}".to_owned(), }, Token { token_type: Whitespace, start: 57, end: 58, + module_id, value: " ".to_owned(), }, Token { token_type: Brace, start: 58, end: 59, + module_id, value: "}".to_owned(), }, Token { token_type: Whitespace, start: 59, end: 61, + module_id, value: "\n\n".to_owned(), }, Token { token_type: Keyword, start: 61, end: 66, + module_id, value: "const".to_owned(), }, Token { token_type: Whitespace, start: 66, end: 67, + module_id, value: " ".to_owned(), }, Token { token_type: Word, start: 67, end: 70, + module_id, value: "key".to_owned(), }, Token { token_type: Whitespace, start: 70, end: 71, + module_id, value: " ".to_owned(), }, Token { token_type: Operator, start: 71, end: 72, + module_id, value: "=".to_owned(), }, Token { token_type: Whitespace, start: 72, end: 73, + module_id, value: " ".to_owned(), }, Token { token_type: String, start: 73, end: 76, + module_id, value: "'c'".to_owned(), }, Token { token_type: Whitespace, start: 76, end: 77, + module_id, value: "\n".to_owned(), }, Token { token_type: Keyword, start: 77, end: 82, + module_id, value: "const".to_owned(), }, Token { token_type: Whitespace, start: 82, end: 83, + module_id, value: " ".to_owned(), }, Token { token_type: Word, start: 83, end: 89, + module_id, value: "things".to_owned(), }, Token { token_type: Whitespace, start: 89, end: 90, + module_id, value: " ".to_owned(), }, Token { token_type: Operator, start: 90, end: 91, + module_id, value: "=".to_owned(), }, Token { token_type: Whitespace, start: 91, end: 92, + module_id, value: " ".to_owned(), }, Token { token_type: String, start: 92, end: 100, + module_id, value: "\"things\"".to_owned(), }, Token { token_type: Whitespace, start: 100, end: 102, + module_id, value: "\n\n".to_owned(), }, Token { token_type: LineComment, start: 102, end: 127, + module_id, value: "// this is also a comment".to_owned(), }, ]; @@ -1427,106 +1738,121 @@ const things = "things" #[test] fn test_program4() { let program = "const myArray = [0..10]"; + let module_id = ModuleId::from_usize(1); use TokenType::*; let expected = vec![ Token { token_type: Keyword, start: 0, end: 5, + module_id, value: "const".to_owned(), }, Token { token_type: Whitespace, start: 5, end: 6, + module_id, value: " ".to_owned(), }, Token { token_type: Word, start: 6, end: 13, + module_id, value: "myArray".to_owned(), }, Token { token_type: Whitespace, start: 13, end: 14, + module_id, value: " ".to_owned(), }, Token { token_type: Operator, start: 14, end: 15, + module_id, value: "=".to_owned(), }, Token { token_type: Whitespace, start: 15, end: 16, + module_id, value: " ".to_owned(), }, Token { token_type: Brace, start: 16, end: 17, + module_id, value: "[".to_owned(), }, Token { token_type: Number, start: 17, end: 18, + module_id, value: "0".to_owned(), }, Token { token_type: DoublePeriod, start: 18, end: 20, + module_id, value: "..".to_owned(), }, Token { token_type: Number, start: 20, end: 22, + module_id, value: "10".to_owned(), }, Token { token_type: Brace, start: 22, end: 23, + module_id, value: "]".to_owned(), }, ]; - let actual = lexer(program).unwrap(); + let actual = lexer(program, module_id).unwrap(); assert_tokens(expected, actual); } #[test] fn test_kitt() { let program = include_str!("../../../tests/executor/inputs/kittycad_svg.kcl"); - let actual = lexer(program).unwrap(); + let actual = lexer(program, ModuleId::default()).unwrap(); assert_eq!(actual.len(), 5103); } #[test] fn test_pipes_on_pipes() { let program = include_str!("../../../tests/executor/inputs/pipes_on_pipes.kcl"); - let actual = lexer(program).unwrap(); + let actual = lexer(program, ModuleId::default()).unwrap(); assert_eq!(actual.len(), 17841); } #[test] fn test_lexer_negative_word() { - let actual = lexer("-legX").unwrap(); + let module_id = ModuleId::from_usize(1); + let actual = lexer("-legX", module_id).unwrap(); let expected = vec![ Token { token_type: TokenType::Operator, value: "-".to_string(), start: 0, end: 1, + module_id, }, Token { token_type: TokenType::Word, value: "legX".to_string(), start: 1, end: 5, + module_id, }, ]; assert_tokens(expected, actual); @@ -1534,49 +1860,57 @@ const things = "things" #[test] fn not_eq() { - let actual = lexer("!=").unwrap(); + let module_id = ModuleId::from_usize(1); + let actual = lexer("!=", module_id).unwrap(); let expected = vec![Token { token_type: TokenType::Operator, value: "!=".to_owned(), start: 0, end: 2, + module_id, }]; assert_eq!(actual, expected); } #[test] fn test_unrecognized_token() { - let actual = lexer("12 ; 8").unwrap(); + let module_id = ModuleId::from_usize(1); + let actual = lexer("12 ; 8", module_id).unwrap(); let expected = vec![ Token { token_type: TokenType::Number, value: "12".to_string(), start: 0, end: 2, + module_id, }, Token { token_type: TokenType::Whitespace, value: " ".to_string(), start: 2, end: 3, + module_id, }, Token { token_type: TokenType::Unknown, value: ";".to_string(), start: 3, end: 4, + module_id, }, Token { token_type: TokenType::Whitespace, value: " ".to_string(), start: 4, end: 5, + module_id, }, Token { token_type: TokenType::Number, value: "8".to_string(), start: 5, end: 6, + module_id, }, ]; @@ -1585,24 +1919,28 @@ const things = "things" #[test] fn import_keyword() { - let actual = lexer("import foo").unwrap(); + let module_id = ModuleId::from_usize(1); + let actual = lexer("import foo", module_id).unwrap(); let expected = Token { token_type: TokenType::Keyword, value: "import".to_owned(), start: 0, end: 6, + module_id, }; assert_eq!(actual[0], expected); } #[test] fn import_function() { - let actual = lexer("import(3)").unwrap(); + let module_id = ModuleId::from_usize(1); + let actual = lexer("import(3)", module_id).unwrap(); let expected = Token { token_type: TokenType::Word, value: "import".to_owned(), start: 0, end: 6, + module_id, }; assert_eq!(actual[0], expected); } diff --git a/src/wasm-lib/kcl/src/unparser.rs b/src/wasm-lib/kcl/src/unparser.rs index 4ada1b8c3..839626247 100644 --- a/src/wasm-lib/kcl/src/unparser.rs +++ b/src/wasm-lib/kcl/src/unparser.rs @@ -573,7 +573,7 @@ impl FunctionExpression { mod tests { use pretty_assertions::assert_eq; - use crate::ast::types::FormatOptions; + use crate::ast::types::{FormatOptions, ModuleId}; #[test] fn test_recast_if_else_if_same() { @@ -585,9 +585,7 @@ mod tests { 5 } "#; - let tokens = crate::token::lexer(input).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(input).unwrap(); let output = program.recast(&Default::default(), 0); assert_eq!(output, input); } @@ -600,9 +598,7 @@ mod tests { 5 } "#; - let tokens = crate::token::lexer(input).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(input).unwrap(); let output = program.recast(&Default::default(), 0); assert_eq!(output, input); } @@ -616,9 +612,7 @@ import a as aaa, b from "a.kcl" import a, b as bbb from "a.kcl" import a as aaa, b as bbb from "a.kcl" "#; - let tokens = crate::token::lexer(input).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(input).unwrap(); let output = program.recast(&Default::default(), 0); assert_eq!(output, input); } @@ -627,7 +621,7 @@ import a as aaa, b as bbb from "a.kcl" fn test_recast_import_as_same_name() { let input = r#"import a as a from "a.kcl" "#; - let program = crate::parser::parse(input).unwrap(); + let program = crate::parser::top_level_parse(input).unwrap(); let output = program.recast(&Default::default(), 0); let expected = r#"import a from "a.kcl" "#; @@ -640,9 +634,7 @@ import a as aaa, b as bbb from "a.kcl" return 0 } "#; - let tokens = crate::token::lexer(input).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(input).unwrap(); let output = program.recast(&Default::default(), 0); assert_eq!(output, input); } @@ -765,9 +757,7 @@ fn zoo = (x0, y0) => { zoo(zoo_x, zoo_y) "#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(some_program_string).unwrap(); let recasted = program.recast(&Default::default(), 0); assert_eq!(recasted, some_program_string); @@ -836,9 +826,7 @@ outsideRevolve = startSketchOn('XZ') |> line([overHangLength - thickness, 0], %) |> close(%) |> revolve({ axis: 'y' }, %)"#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(some_program_string).unwrap(); let recasted = program.recast(&Default::default(), 0); assert_eq!( @@ -914,9 +902,7 @@ outsideRevolve = startSketchOn('XZ') let some_program_string = r#"bing = { yo: 55 } myNestedVar = [{ prop: callExp(bing.yo) }] "#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(some_program_string).unwrap(); let recasted = program.recast(&Default::default(), 0); assert_eq!(recasted, some_program_string); @@ -927,9 +913,7 @@ myNestedVar = [{ prop: callExp(bing.yo) }] let some_program_string = r#"bing = { yo: 55 } myNestedVar = [callExp(bing.yo)] "#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(some_program_string).unwrap(); let recasted = program.recast(&Default::default(), 0); assert_eq!(recasted, some_program_string); @@ -941,9 +925,7 @@ myNestedVar = [callExp(bing.yo)] ten = 10 bar = [0 + 1 .. ten] "#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(some_program_string).unwrap(); let recasted = program.recast(&Default::default(), 0); assert_eq!(recasted, some_program_string); @@ -957,9 +939,7 @@ bar = [0 + 1 .. ten] thing ( 1 ) "#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(some_program_string).unwrap(); let recasted = program.recast(&Default::default(), 0); assert_eq!( @@ -982,9 +962,7 @@ myNestedVar = [ } ] "#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(some_program_string).unwrap(); let recasted = program.recast(&Default::default(), 0); assert_eq!( @@ -1000,9 +978,7 @@ myNestedVar = [ #[test] fn test_recast_empty_file() { let some_program_string = r#""#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(some_program_string).unwrap(); let recasted = program.recast(&Default::default(), 0); // Its VERY important this comes back with zero new lines. @@ -1013,9 +989,7 @@ myNestedVar = [ fn test_recast_empty_file_new_line() { let some_program_string = r#" "#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(some_program_string).unwrap(); let recasted = program.recast(&Default::default(), 0); // Its VERY important this comes back with zero new lines. @@ -1026,14 +1000,12 @@ myNestedVar = [ fn test_recast_shebang_only() { let some_program_string = r#"#!/usr/local/env zoo kcl"#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let result = parser.ast(); + let result = crate::parser::top_level_parse(some_program_string); assert!(result.is_err()); assert_eq!( result.unwrap_err().to_string(), - r#"syntax: KclErrorDetails { source_ranges: [SourceRange([21, 24])], message: "Unexpected end of file. The compiler expected a function body items (functions are made up of variable declarations, expressions, and return statements, each of those is a possible body item" }"# + r#"syntax: KclErrorDetails { source_ranges: [SourceRange([21, 24, 0])], message: "Unexpected end of file. The compiler expected a function body items (functions are made up of variable declarations, expressions, and return statements, each of those is a possible body item" }"# ); } @@ -1048,9 +1020,7 @@ part001 = startSketchOn('XY') |> close(%) "#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(some_program_string).unwrap(); let recasted = program.recast(&Default::default(), 0); assert_eq!( @@ -1081,9 +1051,7 @@ part001 = startSketchOn('XY') |> close(%) "#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(some_program_string).unwrap(); let recasted = program.recast(&Default::default(), 0); assert_eq!( @@ -1113,9 +1081,7 @@ part001 = startSketchOn('XY') |> close(%) "#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(some_program_string).unwrap(); let recasted = program.recast(&Default::default(), 0); assert_eq!( @@ -1253,9 +1219,7 @@ tabs_l = startSketchOn({ distance: length - 10 }, %) "#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(some_program_string).unwrap(); let recasted = program.recast(&Default::default(), 0); // Its VERY important this comes back with zero new lines. @@ -1393,9 +1357,7 @@ tabs_l = startSketchOn({ |> close(%) |> extrude(scale, %) }"#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(some_program_string).unwrap(); let recasted = program.recast(&Default::default(), 0); assert_eq!( @@ -1419,9 +1381,7 @@ tabs_l = startSketchOn({ |> startProfileAt([0.0, 5.0], %) |> line([0.4900857016, -0.0240763666], %) |> line([0.6804562304, 0.9087880491], %)"#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(some_program_string).unwrap(); let recasted = program.recast(&Default::default(), 0); assert_eq!( @@ -1440,9 +1400,7 @@ tabs_l = startSketchOn({ |> startProfileAt([0.0, 5.0], %) |> line([0.4900857016, -0.0240763666], %) // hello world |> line([0.6804562304, 0.9087880491], %)"#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(some_program_string).unwrap(); let recasted = program.recast(&Default::default(), 0); assert_eq!( @@ -1461,9 +1419,7 @@ tabs_l = startSketchOn({ |> line([0.4900857016, -0.0240763666], %) // hello world |> line([0.6804562304, 0.9087880491], %)"#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(some_program_string).unwrap(); let recasted = program.recast(&Default::default(), 0); assert_eq!( @@ -1488,9 +1444,7 @@ tabs_l = startSketchOn({ // this is also a comment return things }"#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(some_program_string).unwrap(); let recasted = program.recast(&Default::default(), 0); assert_eq!( @@ -1514,9 +1468,7 @@ tabs_l = startSketchOn({ // this is also a comment thing = 'foo' "#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(some_program_string).unwrap(); let recasted = program.recast(&Default::default(), 0); assert_eq!( @@ -1537,9 +1489,7 @@ key = 'c' // hello thing = 'foo' "#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(some_program_string).unwrap(); let recasted = program.recast(&Default::default(), 0); assert_eq!( @@ -1567,9 +1517,7 @@ thing = 'c' foo = 'bar' // "#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(some_program_string).unwrap(); let recasted = program.recast(&Default::default(), 0); assert_eq!( @@ -1595,9 +1543,7 @@ foo = 'bar' // // hello thing = 'foo' "#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(some_program_string).unwrap(); let recasted = program.recast(&Default::default(), 0); assert_eq!( @@ -1616,9 +1562,7 @@ thing = 'foo' /* comment at start */ mySk1 = startSketchAt([0, 0])"#; - let tokens = crate::token::lexer(test_program).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(test_program).unwrap(); let recasted = program.recast(&Default::default(), 0); assert_eq!( @@ -1648,9 +1592,7 @@ mySk1 = startSketchOn('XY') |> ry(45, %) |> rx(45, %) // one more for good measure"#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(some_program_string).unwrap(); let recasted = program.recast(&Default::default(), 0); assert_eq!( @@ -1686,9 +1628,7 @@ mySk1 = startSketchOn('XY') intersectTag: seg01 }, %) |> line([-0.42, -1.72], %)"#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(some_program_string).unwrap(); let recasted = program.recast(&Default::default(), 0); assert_eq!(recasted.trim(), some_program_string); @@ -1712,9 +1652,7 @@ yo = [ " hey oooooo really long long long" ] "#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(some_program_string).unwrap(); let recasted = program.recast(&Default::default(), 0); assert_eq!(recasted, some_program_string); @@ -1730,9 +1668,7 @@ key = 'c' things = "things" // this is also a comment"#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(some_program_string).unwrap(); let recasted = program.recast(&Default::default(), 0); let expected = some_program_string.trim(); @@ -1751,9 +1687,7 @@ things = "things" // a comment " }"#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(some_program_string).unwrap(); let recasted = program.recast(&Default::default(), 0); assert_eq!(recasted.trim(), some_program_string.trim()); @@ -1777,9 +1711,7 @@ part001 = startSketchOn('XY') -angleToMatchLengthY(seg01, myVar, %), myVar ], %) // ln-lineTo-yAbsolute should use angleToMatchLengthY helper"#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(some_program_string).unwrap(); let recasted = program.recast(&Default::default(), 0); assert_eq!(recasted.trim(), some_program_string); @@ -1804,9 +1736,7 @@ part001 = startSketchOn('XY') myVar ], %) // ln-lineTo-yAbsolute should use angleToMatchLengthY helper "#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(some_program_string).unwrap(); let recasted = program.recast( &FormatOptions { @@ -1835,9 +1765,7 @@ fn ghi = (part001) => { return part001 } "#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let mut program = parser.ast().unwrap(); + let mut program = crate::parser::top_level_parse(some_program_string).unwrap(); program.rename_symbol("mySuperCoolPart", 6); let recasted = program.recast(&Default::default(), 0); @@ -1865,9 +1793,7 @@ fn ghi = (part001) => { let some_program_string = r#"fn ghi = (x, y, z) => { return x }"#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let mut program = parser.ast().unwrap(); + let mut program = crate::parser::top_level_parse(some_program_string).unwrap(); program.rename_symbol("newName", 10); let recasted = program.recast(&Default::default(), 0); @@ -1889,9 +1815,7 @@ fn ghi = (part001) => { angle_start: 0, angle_end: 180, }, %)"#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(some_program_string).unwrap(); let recasted = program.recast(&Default::default(), 0); assert_eq!( @@ -1921,9 +1845,7 @@ firstExtrude = startSketchOn('XY') |> close(%) |> extrude(h, %) "#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(some_program_string).unwrap(); let recasted = program.recast(&Default::default(), 0); assert_eq!( @@ -1960,9 +1882,7 @@ firstExtrude = startSketchOn('XY') |> close(%) |> extrude(h, %) "#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(some_program_string).unwrap(); let recasted = program.recast(&Default::default(), 0); assert_eq!( @@ -1988,9 +1908,7 @@ firstExtrude = startSketchOn('XY') #[tokio::test(flavor = "multi_thread")] async fn test_recast_math_start_negative() { let some_program_string = r#"myVar = -5 + 6"#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(some_program_string).unwrap(); let recasted = program.recast(&Default::default(), 0); assert_eq!(recasted.trim(), some_program_string); @@ -2007,9 +1925,7 @@ startSketchOn('XY') |> line([0, -(5 - thickness)], %) |> line([0, -(5 - 1)], %) |> line([0, -(-5 - 1)], %)"#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(some_program_string).unwrap(); let recasted = program.recast(&Default::default(), 0); assert_eq!(recasted.trim(), some_program_string); @@ -2023,9 +1939,7 @@ FOS = 2 sigmaAllow = 8 width = 20 thickness = sqrt(distance * p * FOS * 6 / (sigmaAllow * width))"#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(some_program_string).unwrap(); let recasted = program.recast(&Default::default(), 0); assert_eq!(recasted.trim(), some_program_string); @@ -2034,9 +1948,7 @@ thickness = sqrt(distance * p * FOS * 6 / (sigmaAllow * width))"#; #[tokio::test(flavor = "multi_thread")] async fn no_vardec_keyword() { let some_program_string = r#"distance = 5"#; - let tokens = crate::token::lexer(some_program_string).unwrap(); - let parser = crate::parser::Parser::new(tokens); - let program = parser.ast().unwrap(); + let program = crate::parser::top_level_parse(some_program_string).unwrap(); let recasted = program.recast(&Default::default(), 0); assert_eq!(recasted.trim(), some_program_string); @@ -2066,7 +1978,7 @@ thickness = sqrt(distance * p * FOS * 6 / (sigmaAllow * width))"#; .into_iter() .enumerate() { - let tokens = crate::token::lexer(raw).unwrap(); + let tokens = crate::token::lexer(raw, ModuleId::default()).unwrap(); let literal = crate::parser::parser_impl::unsigned_number_literal .parse(&tokens) .unwrap(); @@ -2099,9 +2011,7 @@ sketch002 = startSketchOn({ } }) "#; - let tokens = crate::token::lexer(input).unwrap(); - let p = crate::parser::Parser::new(tokens); - let ast = p.ast().unwrap(); + let ast = crate::parser::top_level_parse(input).unwrap(); let actual = ast.recast(&FormatOptions::new(), 0); assert_eq!(actual, expected); } @@ -2127,7 +2037,7 @@ sketch002 = startSketchOn({ .into_iter() .enumerate() { - let tokens = crate::token::lexer(input).unwrap(); + let tokens = crate::token::lexer(input, ModuleId::default()).unwrap(); crate::parser::parser_impl::print_tokens(&tokens); let expr = crate::parser::parser_impl::object.parse(&tokens).unwrap(); assert_eq!( @@ -2225,7 +2135,7 @@ sketch002 = startSketchOn({ .into_iter() .enumerate() { - let tokens = crate::token::lexer(input).unwrap(); + let tokens = crate::token::lexer(input, ModuleId::default()).unwrap(); let expr = crate::parser::parser_impl::array_elem_by_elem.parse(&tokens).unwrap(); assert_eq!( expr.recast(&FormatOptions::new(), 0, false), diff --git a/src/wasm-lib/kcl/src/walk/ast_node.rs b/src/wasm-lib/kcl/src/walk/ast_node.rs index 4b76f2686..293e7c42f 100644 --- a/src/wasm-lib/kcl/src/walk/ast_node.rs +++ b/src/wasm-lib/kcl/src/walk/ast_node.rs @@ -42,30 +42,30 @@ pub enum Node<'a> { impl From<&Node<'_>> for SourceRange { fn from(node: &Node) -> Self { match node { - Node::Program(p) => SourceRange([p.start, p.end]), - Node::ImportStatement(e) => SourceRange([e.start, e.end]), - Node::ExpressionStatement(e) => SourceRange([e.start, e.end]), - Node::VariableDeclaration(v) => SourceRange([v.start, v.end]), - Node::ReturnStatement(r) => SourceRange([r.start, r.end]), - Node::VariableDeclarator(v) => SourceRange([v.start, v.end]), - Node::Literal(l) => SourceRange([l.start, l.end]), - Node::TagDeclarator(t) => SourceRange([t.start, t.end]), - Node::Identifier(i) => SourceRange([i.start, i.end]), - Node::BinaryExpression(b) => SourceRange([b.start, b.end]), - Node::FunctionExpression(f) => SourceRange([f.start, f.end]), - Node::CallExpression(c) => SourceRange([c.start, c.end]), - Node::PipeExpression(p) => SourceRange([p.start, p.end]), - Node::PipeSubstitution(p) => SourceRange([p.start, p.end]), - Node::ArrayExpression(a) => SourceRange([a.start, a.end]), - Node::ArrayRangeExpression(a) => SourceRange([a.start, a.end]), - Node::ObjectExpression(o) => SourceRange([o.start, o.end]), - Node::MemberExpression(m) => SourceRange([m.start, m.end]), - Node::UnaryExpression(u) => SourceRange([u.start, u.end]), - Node::Parameter(p) => SourceRange([p.identifier.start, p.identifier.end]), - Node::ObjectProperty(o) => SourceRange([o.start, o.end]), - Node::MemberObject(m) => SourceRange([m.start(), m.end()]), - Node::IfExpression(m) => SourceRange([m.start, m.end]), - Node::LiteralIdentifier(l) => SourceRange([l.start(), l.end()]), + Node::Program(n) => SourceRange::from(*n), + Node::ImportStatement(n) => SourceRange::from(*n), + Node::ExpressionStatement(n) => SourceRange::from(*n), + Node::VariableDeclaration(n) => SourceRange::from(*n), + Node::ReturnStatement(n) => SourceRange::from(*n), + Node::VariableDeclarator(n) => SourceRange::from(*n), + Node::Literal(n) => SourceRange::from(*n), + Node::TagDeclarator(n) => SourceRange::from(*n), + Node::Identifier(n) => SourceRange::from(*n), + Node::BinaryExpression(n) => SourceRange::from(*n), + Node::FunctionExpression(n) => SourceRange::from(*n), + Node::CallExpression(n) => SourceRange::from(*n), + Node::PipeExpression(n) => SourceRange::from(*n), + Node::PipeSubstitution(n) => SourceRange::from(*n), + Node::ArrayExpression(n) => SourceRange::from(*n), + Node::ArrayRangeExpression(n) => SourceRange::from(*n), + Node::ObjectExpression(n) => SourceRange::from(*n), + Node::MemberExpression(n) => SourceRange::from(*n), + Node::UnaryExpression(n) => SourceRange::from(*n), + Node::Parameter(p) => SourceRange::from(&p.identifier), + Node::ObjectProperty(n) => SourceRange::from(*n), + Node::MemberObject(m) => SourceRange([m.start(), m.end(), m.module_id().as_usize()]), + Node::IfExpression(n) => SourceRange::from(*n), + Node::LiteralIdentifier(l) => SourceRange([l.start(), l.end(), l.module_id().as_usize()]), } } } diff --git a/src/wasm-lib/kcl/src/walk/ast_walk.rs b/src/wasm-lib/kcl/src/walk/ast_walk.rs index f5836b94e..7e467b63b 100644 --- a/src/wasm-lib/kcl/src/walk/ast_walk.rs +++ b/src/wasm-lib/kcl/src/walk/ast_walk.rs @@ -315,9 +315,7 @@ mod tests { macro_rules! kcl { ( $kcl:expr ) => {{ - let tokens = $crate::token::lexer($kcl).unwrap(); - let parser = $crate::parser::Parser::new(tokens); - parser.ast().unwrap() + $crate::parser::top_level_parse($kcl).unwrap() }}; } diff --git a/src/wasm-lib/kcl/tests/add_lots/program_memory.snap b/src/wasm-lib/kcl/tests/add_lots/program_memory.snap index 24acbd15d..d15686eac 100644 --- a/src/wasm-lib/kcl/tests/add_lots/program_memory.snap +++ b/src/wasm-lib/kcl/tests/add_lots/program_memory.snap @@ -123,7 +123,8 @@ snapshot_kind: text { "sourceRange": [ 7, - 32 + 32, + 0 ] } ] @@ -136,7 +137,8 @@ snapshot_kind: text { "sourceRange": [ 38, - 834 + 834, + 0 ] } ] diff --git a/src/wasm-lib/kcl/tests/array_elem_push/program_memory.snap b/src/wasm-lib/kcl/tests/array_elem_push/program_memory.snap index 9ecdf9a84..7eb65a5fc 100644 --- a/src/wasm-lib/kcl/tests/array_elem_push/program_memory.snap +++ b/src/wasm-lib/kcl/tests/array_elem_push/program_memory.snap @@ -43,7 +43,8 @@ snapshot_kind: text { "sourceRange": [ 6, - 15 + 15, + 0 ] } ] @@ -61,7 +62,8 @@ snapshot_kind: text { "sourceRange": [ 27, - 39 + 39, + 0 ] } ] @@ -80,7 +82,8 @@ snapshot_kind: text { "sourceRange": [ 51, - 68 + 68, + 0 ] } ] diff --git a/src/wasm-lib/kcl/tests/array_range_expr/program_memory.snap b/src/wasm-lib/kcl/tests/array_range_expr/program_memory.snap index 815317548..90b6736b8 100644 --- a/src/wasm-lib/kcl/tests/array_range_expr/program_memory.snap +++ b/src/wasm-lib/kcl/tests/array_range_expr/program_memory.snap @@ -39,7 +39,8 @@ snapshot_kind: text { "sourceRange": [ 175, - 188 + 188, + 0 ] } ] @@ -52,7 +53,8 @@ snapshot_kind: text { "sourceRange": [ 79, - 80 + 80, + 0 ] } ] @@ -71,7 +73,8 @@ snapshot_kind: text { "sourceRange": [ 5, - 11 + 11, + 0 ] } ] @@ -90,7 +93,8 @@ snapshot_kind: text { "sourceRange": [ 95, - 107 + 107, + 0 ] } ] @@ -110,7 +114,8 @@ snapshot_kind: text { "sourceRange": [ 194, - 206 + 206, + 0 ] } ] @@ -128,7 +133,8 @@ snapshot_kind: text { "sourceRange": [ 341, - 373 + 373, + 0 ] } ] @@ -141,7 +147,8 @@ snapshot_kind: text { "sourceRange": [ 88, - 89 + 89, + 0 ] } ] diff --git a/src/wasm-lib/kcl/tests/array_range_negative_expr/program_memory.snap b/src/wasm-lib/kcl/tests/array_range_negative_expr/program_memory.snap index bcc0be744..b62ff7e49 100644 --- a/src/wasm-lib/kcl/tests/array_range_negative_expr/program_memory.snap +++ b/src/wasm-lib/kcl/tests/array_range_negative_expr/program_memory.snap @@ -51,7 +51,8 @@ snapshot_kind: text { "sourceRange": [ 5, - 19 + 19, + 0 ] } ] diff --git a/src/wasm-lib/kcl/tests/cube/program_memory.snap b/src/wasm-lib/kcl/tests/cube/program_memory.snap index 6e8b18ae9..5f76caf6b 100644 --- a/src/wasm-lib/kcl/tests/cube/program_memory.snap +++ b/src/wasm-lib/kcl/tests/cube/program_memory.snap @@ -751,7 +751,8 @@ snapshot_kind: text { "sourceRange": [ 10, - 316 + 316, + 0 ] } ] @@ -766,7 +767,8 @@ snapshot_kind: text "id": "[uuid]", "sourceRange": [ 206, - 219 + 219, + 0 ], "tag": null, "type": "extrudePlane" @@ -776,7 +778,8 @@ snapshot_kind: text "id": "[uuid]", "sourceRange": [ 225, - 238 + 238, + 0 ], "tag": null, "type": "extrudePlane" @@ -786,7 +789,8 @@ snapshot_kind: text "id": "[uuid]", "sourceRange": [ 244, - 257 + 257, + 0 ], "tag": null, "type": "extrudePlane" @@ -796,7 +800,8 @@ snapshot_kind: text "id": "[uuid]", "sourceRange": [ 263, - 276 + 276, + 0 ], "tag": null, "type": "extrudePlane" @@ -811,7 +816,8 @@ snapshot_kind: text "id": "[uuid]", "sourceRange": [ 206, - 219 + 219, + 0 ] }, "from": [ @@ -830,7 +836,8 @@ snapshot_kind: text "id": "[uuid]", "sourceRange": [ 225, - 238 + 238, + 0 ] }, "from": [ @@ -849,7 +856,8 @@ snapshot_kind: text "id": "[uuid]", "sourceRange": [ 244, - 257 + 257, + 0 ] }, "from": [ @@ -868,7 +876,8 @@ snapshot_kind: text "id": "[uuid]", "sourceRange": [ 263, - 276 + 276, + 0 ] }, "from": [ @@ -887,7 +896,8 @@ snapshot_kind: text "id": "[uuid]", "sourceRange": [ 282, - 290 + 290, + 0 ] }, "from": [ @@ -942,7 +952,8 @@ snapshot_kind: text "id": "[uuid]", "sourceRange": [ 183, - 200 + 200, + 0 ] } }, @@ -950,7 +961,8 @@ snapshot_kind: text { "sourceRange": [ 183, - 200 + 200, + 0 ] } ] @@ -962,7 +974,8 @@ snapshot_kind: text { "sourceRange": [ 183, - 200 + 200, + 0 ] } ] diff --git a/src/wasm-lib/kcl/tests/cube/tokens.snap b/src/wasm-lib/kcl/tests/cube/tokens.snap index 6136c0486..ed630c9a7 100644 --- a/src/wasm-lib/kcl/tests/cube/tokens.snap +++ b/src/wasm-lib/kcl/tests/cube/tokens.snap @@ -1,5 +1,5 @@ --- -source: kcl/src/tests.rs +source: kcl/src/simulation_tests.rs description: Result of tokenizing cube.kcl snapshot_kind: text --- diff --git a/src/wasm-lib/kcl/tests/double_map_fn/program_memory.snap b/src/wasm-lib/kcl/tests/double_map_fn/program_memory.snap index 3e588ea41..6ac6c1eaf 100644 --- a/src/wasm-lib/kcl/tests/double_map_fn/program_memory.snap +++ b/src/wasm-lib/kcl/tests/double_map_fn/program_memory.snap @@ -123,7 +123,8 @@ snapshot_kind: text { "sourceRange": [ 15, - 40 + 40, + 0 ] } ] @@ -140,7 +141,8 @@ snapshot_kind: text { "sourceRange": [ 47, - 53 + 53, + 0 ] } ] @@ -157,7 +159,8 @@ snapshot_kind: text { "sourceRange": [ 90, - 107 + 107, + 0 ] } ] diff --git a/src/wasm-lib/kcl/tests/helix_ccw/program_memory.snap b/src/wasm-lib/kcl/tests/helix_ccw/program_memory.snap index 3d55ba52a..6824b22cb 100644 --- a/src/wasm-lib/kcl/tests/helix_ccw/program_memory.snap +++ b/src/wasm-lib/kcl/tests/helix_ccw/program_memory.snap @@ -41,7 +41,8 @@ snapshot_kind: text "id": "[uuid]", "sourceRange": [ 35, - 76 + 76, + 0 ], "tag": null, "type": "extrudeArc" @@ -56,7 +57,8 @@ snapshot_kind: text "id": "[uuid]", "sourceRange": [ 35, - 76 + 76, + 0 ] }, "ccw": true, @@ -117,7 +119,8 @@ snapshot_kind: text "id": "[uuid]", "sourceRange": [ 35, - 76 + 76, + 0 ] } }, @@ -125,7 +128,8 @@ snapshot_kind: text { "sourceRange": [ 35, - 76 + 76, + 0 ] } ] @@ -137,7 +141,8 @@ snapshot_kind: text { "sourceRange": [ 35, - 76 + 76, + 0 ] } ] diff --git a/src/wasm-lib/kcl/tests/if_else/program_memory.snap b/src/wasm-lib/kcl/tests/if_else/program_memory.snap index c396ab169..1a6a881f8 100644 --- a/src/wasm-lib/kcl/tests/if_else/program_memory.snap +++ b/src/wasm-lib/kcl/tests/if_else/program_memory.snap @@ -39,7 +39,8 @@ snapshot_kind: text { "sourceRange": [ 64, - 65 + 65, + 0 ] } ] @@ -52,7 +53,8 @@ snapshot_kind: text { "sourceRange": [ 199, - 200 + 200, + 0 ] } ] @@ -65,7 +67,8 @@ snapshot_kind: text { "sourceRange": [ 332, - 333 + 333, + 0 ] } ] diff --git a/src/wasm-lib/kcl/tests/index_of_array/program_memory.snap b/src/wasm-lib/kcl/tests/index_of_array/program_memory.snap index b679079fc..06c3f7278 100644 --- a/src/wasm-lib/kcl/tests/index_of_array/program_memory.snap +++ b/src/wasm-lib/kcl/tests/index_of_array/program_memory.snap @@ -43,7 +43,8 @@ snapshot_kind: text { "sourceRange": [ 43, - 55 + 55, + 0 ] } ] @@ -56,7 +57,8 @@ snapshot_kind: text { "sourceRange": [ 256, - 266 + 266, + 0 ] } ] @@ -69,7 +71,8 @@ snapshot_kind: text { "sourceRange": [ 93, - 101 + 101, + 0 ] } ] @@ -82,7 +85,8 @@ snapshot_kind: text { "sourceRange": [ 277, - 285 + 285, + 0 ] } ] diff --git a/src/wasm-lib/kcl/tests/property_of_object/program_memory.snap b/src/wasm-lib/kcl/tests/property_of_object/program_memory.snap index 10acfd7ae..35a8f4ef1 100644 --- a/src/wasm-lib/kcl/tests/property_of_object/program_memory.snap +++ b/src/wasm-lib/kcl/tests/property_of_object/program_memory.snap @@ -42,7 +42,8 @@ snapshot_kind: text { "sourceRange": [ 56, - 74 + 74, + 0 ] } ] @@ -60,7 +61,8 @@ snapshot_kind: text { "sourceRange": [ 529, - 543 + 543, + 0 ] } ] @@ -73,7 +75,8 @@ snapshot_kind: text { "sourceRange": [ 122, - 132 + 132, + 0 ] } ] @@ -86,7 +89,8 @@ snapshot_kind: text { "sourceRange": [ 356, - 362 + 362, + 0 ] } ] @@ -99,7 +103,8 @@ snapshot_kind: text { "sourceRange": [ 553, - 570 + 570, + 0 ] } ] @@ -112,7 +117,8 @@ snapshot_kind: text { "sourceRange": [ 757, - 770 + 770, + 0 ] } ] @@ -125,7 +131,8 @@ snapshot_kind: text { "sourceRange": [ 342, - 347 + 347, + 0 ] } ] diff --git a/src/wasm-lib/kcl/tests/sketch_in_object/program_memory.snap b/src/wasm-lib/kcl/tests/sketch_in_object/program_memory.snap index 93d2a364a..7ae586274 100644 --- a/src/wasm-lib/kcl/tests/sketch_in_object/program_memory.snap +++ b/src/wasm-lib/kcl/tests/sketch_in_object/program_memory.snap @@ -329,7 +329,8 @@ snapshot_kind: text { "sourceRange": [ 10, - 157 + 157, + 0 ] } ] @@ -957,7 +958,8 @@ snapshot_kind: text { "sourceRange": [ 10, - 157 + 157, + 0 ] } ] @@ -973,7 +975,8 @@ snapshot_kind: text { "sourceRange": [ 170, - 369 + 369, + 0 ] } ] @@ -986,7 +989,8 @@ snapshot_kind: text { "sourceRange": [ 52, - 77 + 77, + 0 ] } ], @@ -1023,7 +1027,8 @@ snapshot_kind: text "id": "[uuid]", "sourceRange": [ 83, - 98 + 98, + 0 ] }, "from": [ @@ -1042,7 +1047,8 @@ snapshot_kind: text "id": "[uuid]", "sourceRange": [ 104, - 119 + 119, + 0 ] }, "from": [ @@ -1061,7 +1067,8 @@ snapshot_kind: text "id": "[uuid]", "sourceRange": [ 125, - 141 + 141, + 0 ] }, "from": [ @@ -1080,7 +1087,8 @@ snapshot_kind: text "id": "[uuid]", "sourceRange": [ 147, - 155 + 155, + 0 ] }, "from": [ @@ -1100,7 +1108,8 @@ snapshot_kind: text "id": "[uuid]", "sourceRange": [ 52, - 77 + 77, + 0 ] }, "from": [ @@ -1119,7 +1128,8 @@ snapshot_kind: text { "sourceRange": [ 52, - 77 + 77, + 0 ] } ] @@ -1134,7 +1144,8 @@ snapshot_kind: text { "sourceRange": [ 242, - 267 + 267, + 0 ] } ], @@ -1171,7 +1182,8 @@ snapshot_kind: text "id": "[uuid]", "sourceRange": [ 277, - 292 + 292, + 0 ] }, "from": [ @@ -1190,7 +1202,8 @@ snapshot_kind: text "id": "[uuid]", "sourceRange": [ 302, - 317 + 317, + 0 ] }, "from": [ @@ -1209,7 +1222,8 @@ snapshot_kind: text "id": "[uuid]", "sourceRange": [ 327, - 343 + 343, + 0 ] }, "from": [ @@ -1228,7 +1242,8 @@ snapshot_kind: text "id": "[uuid]", "sourceRange": [ 353, - 361 + 361, + 0 ] }, "from": [ @@ -1248,7 +1263,8 @@ snapshot_kind: text "id": "[uuid]", "sourceRange": [ 242, - 267 + 267, + 0 ] }, "from": [ @@ -1269,7 +1285,8 @@ snapshot_kind: text { "sourceRange": [ 187, - 367 + 367, + 0 ] } ] diff --git a/src/wasm-lib/src/wasm.rs b/src/wasm-lib/src/wasm.rs index 5d5393182..741c9a86b 100644 --- a/src/wasm-lib/src/wasm.rs +++ b/src/wasm-lib/src/wasm.rs @@ -8,7 +8,7 @@ use std::{ use futures::stream::TryStreamExt; use gloo_utils::format::JsValueSerdeExt; use kcl_lib::{ - ast::types::{Node, Program}, + ast::types::{ModuleId, Node, Program}, coredump::CoreDump, engine::EngineManager, executor::ExecutorSettings, @@ -153,9 +153,11 @@ pub async fn modify_ast_for_sketch_wasm( .map_err(|e| format!("{:?}", e))?, )); + let module_id = ModuleId::default(); let _ = kcl_lib::ast::modify::modify_ast_for_sketch( &engine, &mut program, + module_id, sketch_name, plane, uuid::Uuid::parse_str(sketch_id).map_err(|e| e.to_string())?, @@ -193,7 +195,8 @@ pub fn deserialize_files(data: &[u8]) -> Result { pub fn lexer_wasm(js: &str) -> Result { console_error_panic_hook::set_once(); - let tokens = kcl_lib::token::lexer(js).map_err(JsError::from)?; + let module_id = ModuleId::default(); + let tokens = kcl_lib::token::lexer(js, module_id).map_err(JsError::from)?; Ok(JsValue::from_serde(&tokens)?) } @@ -201,7 +204,8 @@ pub fn lexer_wasm(js: &str) -> Result { pub fn parse_wasm(js: &str) -> Result { console_error_panic_hook::set_once(); - let tokens = kcl_lib::token::lexer(js).map_err(String::from)?; + let module_id = ModuleId::default(); + let tokens = kcl_lib::token::lexer(js, module_id).map_err(String::from)?; let parser = kcl_lib::parser::Parser::new(tokens); let program = parser.ast().map_err(String::from)?; // The serde-wasm-bindgen does not work here because of weird HashMap issues so we use the diff --git a/src/wasm-lib/tests/executor/main.rs b/src/wasm-lib/tests/executor/main.rs index 5e46ce084..034adb81d 100644 --- a/src/wasm-lib/tests/executor/main.rs +++ b/src/wasm-lib/tests/executor/main.rs @@ -28,7 +28,7 @@ async fn kcl_test_fillet_duplicate_tags() { assert!(result.is_err()); assert_eq!( result.err().unwrap().to_string(), - r#"type: KclErrorDetails { source_ranges: [SourceRange([203, 249])], message: "Duplicate tags are not allowed." }"#, + r#"type: KclErrorDetails { source_ranges: [SourceRange([203, 249, 0])], message: "Duplicate tags are not allowed." }"#, ); } @@ -83,7 +83,7 @@ async fn kcl_test_execute_engine_error_return() { assert!(result.is_err()); assert_eq!( result.err().unwrap().to_string(), - r#"engine: KclErrorDetails { source_ranges: [SourceRange([216, 229])], message: "Modeling command failed: [ApiError { error_code: BadRequest, message: \"The path is not closed. Solid2D construction requires a closed path!\" }]" }"#, + r#"engine: KclErrorDetails { source_ranges: [SourceRange([216, 229, 0])], message: "Modeling command failed: [ApiError { error_code: BadRequest, message: \"The path is not closed. Solid2D construction requires a closed path!\" }]" }"#, ); } @@ -515,7 +515,7 @@ async fn kcl_test_import_file_doesnt_exist() { assert!(result.is_err()); assert_eq!( result.err().unwrap().to_string(), - r#"semantic: KclErrorDetails { source_ranges: [SourceRange([8, 27])], message: "File `thing.obj` does not exist." }"# + r#"semantic: KclErrorDetails { source_ranges: [SourceRange([8, 27, 0])], message: "File `thing.obj` does not exist." }"# ); } @@ -583,7 +583,7 @@ async fn kcl_test_import_ext_doesnt_match() { assert!(result.is_err()); assert_eq!( result.err().unwrap().to_string(), - r#"semantic: KclErrorDetails { source_ranges: [SourceRange([8, 76])], message: "The given format does not match the file extension. Expected: `gltf`, Given: `obj`" }"# + r#"semantic: KclErrorDetails { source_ranges: [SourceRange([8, 76, 0])], message: "The given format does not match the file extension. Expected: `gltf`, Given: `obj`" }"# ); } @@ -742,7 +742,7 @@ part002 = startSketchOn(part001, part001.sketch.tags.here) assert!(result.is_err()); assert_eq!( result.err().unwrap().to_string(), - r#"semantic: KclErrorDetails { source_ranges: [SourceRange([88, 133]), SourceRange([210, 226])], message: "could not sketch tangential arc, because its center would be infinitely far away in the X direction" }"# + r#"semantic: KclErrorDetails { source_ranges: [SourceRange([88, 133, 0]), SourceRange([210, 226, 0])], message: "could not sketch tangential arc, because its center would be infinitely far away in the X direction" }"# ); } @@ -799,7 +799,7 @@ async fn kcl_test_stdlib_kcl_error_right_code_path() { assert!(result.is_err()); assert_eq!( result.err().unwrap().to_string(), - r#"semantic: KclErrorDetails { source_ranges: [SourceRange([151, 189])], message: "Expected an argument at index 1" }"#, + r#"semantic: KclErrorDetails { source_ranges: [SourceRange([151, 189, 0])], message: "Expected an argument at index 1" }"#, ); } @@ -869,7 +869,7 @@ part = rectShape([0, 0], 20, 20) assert!(result.is_err()); assert_eq!( result.err().unwrap().to_string(), - r#"semantic: KclErrorDetails { source_ranges: [SourceRange([863, 912])], message: "Argument at index 0 was supposed to be type kcl_lib::std::shapes::CircleData but found string (text)" }"#, + r#"semantic: KclErrorDetails { source_ranges: [SourceRange([863, 912, 0])], message: "Argument at index 0 was supposed to be type kcl_lib::std::shapes::CircleData but found string (text)" }"#, ); } @@ -954,7 +954,7 @@ async fn kcl_test_revolve_bad_angle_low() { assert!(result.is_err()); assert_eq!( result.err().unwrap().to_string(), - r#"semantic: KclErrorDetails { source_ranges: [SourceRange([272, 308])], message: "Expected angle to be between -360 and 360 and not 0, found `-455`" }"# + r#"semantic: KclErrorDetails { source_ranges: [SourceRange([272, 308, 0])], message: "Expected angle to be between -360 and 360 and not 0, found `-455`" }"# ); } @@ -979,7 +979,7 @@ async fn kcl_test_revolve_bad_angle_high() { assert!(result.is_err()); assert_eq!( result.err().unwrap().to_string(), - r#"semantic: KclErrorDetails { source_ranges: [SourceRange([272, 307])], message: "Expected angle to be between -360 and 360 and not 0, found `455`" }"# + r#"semantic: KclErrorDetails { source_ranges: [SourceRange([272, 307, 0])], message: "Expected angle to be between -360 and 360 and not 0, found `455`" }"# ); } @@ -1073,7 +1073,7 @@ sketch001 = startSketchOn(box, revolveAxis) //this fails right now, but slightly differently, lets just say its enough for it to fail - mike //assert_eq!( // result.err().unwrap().to_string(), - // r#"engine: KclErrorDetails { source_ranges: [SourceRange([346, 390])], message: "Modeling command failed: [ApiError { error_code: InternalEngine, message: \"Solid3D revolve failed: sketch profile must lie entirely on one side of the revolution axis\" }]" }"# + // r#"engine: KclErrorDetails { source_ranges: [SourceRange([346, 390, 0])], message: "Modeling command failed: [ApiError { error_code: InternalEngine, message: \"Solid3D revolve failed: sketch profile must lie entirely on one side of the revolution axis\" }]" }"# //); } @@ -1354,7 +1354,7 @@ secondSketch = startSketchOn(part001, '') assert!(result.is_err()); assert_eq!( result.err().unwrap().to_string(), - r#"semantic: KclErrorDetails { source_ranges: [SourceRange([260, 286])], message: "Argument at index 1 was supposed to be type kcl_lib::std::sketch::FaceTag but found string (text)" }"# + r#"semantic: KclErrorDetails { source_ranges: [SourceRange([260, 286, 0])], message: "Argument at index 1 was supposed to be type kcl_lib::std::sketch::FaceTag but found string (text)" }"# ); } @@ -1385,7 +1385,7 @@ extrusion = startSketchOn('XY') assert!(result.is_err()); assert_eq!( result.err().unwrap().to_string(), - r#"semantic: KclErrorDetails { source_ranges: [SourceRange([68, 334]), SourceRange([428, 461])], message: "Expected 2 arguments, got 3" }"# + r#"semantic: KclErrorDetails { source_ranges: [SourceRange([68, 334, 0]), SourceRange([428, 461, 0])], message: "Expected 2 arguments, got 3" }"# ); } @@ -1681,7 +1681,7 @@ part001 = cube([0,0], 20) assert!(result.is_err()); assert_eq!( result.err().unwrap().to_string(), - r#"type: KclErrorDetails { source_ranges: [SourceRange([259, 345])], message: "You can only tag one edge at a time with a tagged chamfer. Either delete the tag for the chamfer fn if you don't need it OR separate into individual chamfer functions for each tag." }"# + r#"type: KclErrorDetails { source_ranges: [SourceRange([259, 345, 0])], message: "You can only tag one edge at a time with a tagged chamfer. Either delete the tag for the chamfer fn if you don't need it OR separate into individual chamfer functions for each tag." }"# ); } @@ -1708,7 +1708,7 @@ let p = triangle(200) assert!(result.is_err()); assert_eq!( result.err().unwrap().to_string(), - r#"value already defined: KclErrorDetails { source_ranges: [SourceRange([311, 313]), SourceRange([326, 339])], message: "Cannot redefine `a`" }"# + r#"value already defined: KclErrorDetails { source_ranges: [SourceRange([311, 313, 0]), SourceRange([326, 339, 0])], message: "Cannot redefine `a`" }"# ); } @@ -1783,7 +1783,7 @@ async fn kcl_test_arc_error_same_start_end() { assert!(result.is_err()); assert_eq!( result.err().unwrap().to_string(), - r#"type: KclErrorDetails { source_ranges: [SourceRange([57, 140])], message: "Arc start and end angles must be different" }"# + r#"type: KclErrorDetails { source_ranges: [SourceRange([57, 140, 0])], message: "Arc start and end angles must be different" }"# ); } @@ -1803,7 +1803,7 @@ example = extrude(10, exampleSketch) assert!(result.is_err()); assert_eq!( result.err().unwrap().to_string(), - r#"type: KclErrorDetails { source_ranges: [SourceRange([72, 111])], message: "Cannot have an x constrained angle of 90 degrees" }"# + r#"type: KclErrorDetails { source_ranges: [SourceRange([72, 111, 0])], message: "Cannot have an x constrained angle of 90 degrees" }"# ); } @@ -1823,7 +1823,7 @@ example = extrude(10, exampleSketch) assert!(result.is_err()); assert_eq!( result.err().unwrap().to_string(), - r#"type: KclErrorDetails { source_ranges: [SourceRange([72, 112])], message: "Cannot have an x constrained angle of 270 degrees" }"# + r#"type: KclErrorDetails { source_ranges: [SourceRange([72, 112, 0])], message: "Cannot have an x constrained angle of 270 degrees" }"# ); } @@ -1843,7 +1843,7 @@ example = extrude(10, exampleSketch) assert!(result.is_err()); assert_eq!( result.err().unwrap().to_string(), - r#"type: KclErrorDetails { source_ranges: [SourceRange([72, 110])], message: "Cannot have a y constrained angle of 0 degrees" }"# + r#"type: KclErrorDetails { source_ranges: [SourceRange([72, 110, 0])], message: "Cannot have a y constrained angle of 0 degrees" }"# ); } @@ -1863,7 +1863,7 @@ example = extrude(10, exampleSketch) assert!(result.is_err()); assert_eq!( result.err().unwrap().to_string(), - r#"type: KclErrorDetails { source_ranges: [SourceRange([72, 112])], message: "Cannot have a y constrained angle of 180 degrees" }"# + r#"type: KclErrorDetails { source_ranges: [SourceRange([72, 112, 0])], message: "Cannot have a y constrained angle of 180 degrees" }"# ); } @@ -1883,7 +1883,7 @@ extrusion = extrude(10, sketch001) assert!(result.is_err()); assert_eq!( result.err().unwrap().to_string(), - r#"type: KclErrorDetails { source_ranges: [SourceRange([68, 125])], message: "Cannot have an x constrained angle of 90 degrees" }"# + r#"type: KclErrorDetails { source_ranges: [SourceRange([68, 125, 0])], message: "Cannot have an x constrained angle of 90 degrees" }"# ); } @@ -1903,7 +1903,7 @@ extrusion = extrude(10, sketch001) assert!(result.is_err()); assert_eq!( result.err().unwrap().to_string(), - r#"type: KclErrorDetails { source_ranges: [SourceRange([68, 125])], message: "Cannot have an x constrained angle of 90 degrees" }"# + r#"type: KclErrorDetails { source_ranges: [SourceRange([68, 125, 0])], message: "Cannot have an x constrained angle of 90 degrees" }"# ); } @@ -1925,7 +1925,7 @@ example = extrude(10, exampleSketch) assert!(result.is_err()); assert_eq!( result.err().unwrap().to_string(), - r#"type: KclErrorDetails { source_ranges: [SourceRange([94, 142])], message: "Cannot have a y constrained angle of 0 degrees" }"# + r#"type: KclErrorDetails { source_ranges: [SourceRange([94, 142, 0])], message: "Cannot have a y constrained angle of 0 degrees" }"# ); } @@ -1947,7 +1947,7 @@ example = extrude(10, exampleSketch) assert!(result.is_err()); assert_eq!( result.err().unwrap().to_string(), - r#"type: KclErrorDetails { source_ranges: [SourceRange([94, 144])], message: "Cannot have a y constrained angle of 180 degrees" }"# + r#"type: KclErrorDetails { source_ranges: [SourceRange([94, 144, 0])], message: "Cannot have a y constrained angle of 180 degrees" }"# ); } @@ -1969,7 +1969,7 @@ example = extrude(10, exampleSketch) assert!(result.is_err()); assert_eq!( result.err().unwrap().to_string(), - r#"type: KclErrorDetails { source_ranges: [SourceRange([94, 145])], message: "Cannot have a y constrained angle of 180 degrees" }"# + r#"type: KclErrorDetails { source_ranges: [SourceRange([94, 145, 0])], message: "Cannot have a y constrained angle of 180 degrees" }"# ); } @@ -1986,7 +1986,7 @@ someFunction('INVALID') assert!(result.is_err()); assert_eq!( result.err().unwrap().to_string(), - r#"semantic: KclErrorDetails { source_ranges: [SourceRange([37, 61]), SourceRange([65, 88])], message: "Argument at index 0 was supposed to be type kcl_lib::std::sketch::SketchData but found string (text)" }"# + r#"semantic: KclErrorDetails { source_ranges: [SourceRange([37, 61, 0]), SourceRange([65, 88, 0])], message: "Argument at index 0 was supposed to be type kcl_lib::std::sketch::SketchData but found string (text)" }"# ); } @@ -2007,7 +2007,7 @@ someFunction('INVALID') assert!(result.is_err()); assert_eq!( result.err().unwrap().to_string(), - r#"semantic: KclErrorDetails { source_ranges: [SourceRange([89, 114]), SourceRange([126, 155]), SourceRange([159, 182])], message: "Argument at index 0 was supposed to be type kcl_lib::std::sketch::SketchData but found string (text)" }"# + r#"semantic: KclErrorDetails { source_ranges: [SourceRange([89, 114, 0]), SourceRange([126, 155, 0]), SourceRange([159, 182, 0])], message: "Argument at index 0 was supposed to be type kcl_lib::std::sketch::SketchData but found string (text)" }"# ); } diff --git a/src/wasm-lib/tests/executor/no_visuals.rs b/src/wasm-lib/tests/executor/no_visuals.rs index dd374a135..8f573fbfc 100644 --- a/src/wasm-lib/tests/executor/no_visuals.rs +++ b/src/wasm-lib/tests/executor/no_visuals.rs @@ -1,5 +1,5 @@ use kcl_lib::{ - ast::types::{Node, Program}, + ast::types::{ModuleId, Node, Program}, errors::KclError, executor::{ExecutorContext, IdGenerator}, parser, @@ -28,7 +28,8 @@ macro_rules! gen_test_parse_fail { } async fn setup(program: &str) -> (ExecutorContext, Node, IdGenerator) { - let tokens = kcl_lib::token::lexer(program).unwrap(); + let module_id = ModuleId::default(); + let tokens = kcl_lib::token::lexer(program, module_id).unwrap(); let parser = kcl_lib::parser::Parser::new(tokens); let program = parser.ast().unwrap(); let ctx = kcl_lib::executor::ExecutorContext { @@ -60,7 +61,7 @@ async fn run_fail(code: &str) -> KclError { } async fn run_parse_fail(code: &str) -> KclError { - let Err(e) = parser::parse(code) else { + let Err(e) = parser::top_level_parse(code) else { panic!("Expected this KCL program to fail to parse, but it (incorrectly) never threw an error."); }; e diff --git a/src/wasm-lib/tests/modify/main.rs b/src/wasm-lib/tests/modify/main.rs index bf8f20bab..bf18aa734 100644 --- a/src/wasm-lib/tests/modify/main.rs +++ b/src/wasm-lib/tests/modify/main.rs @@ -2,7 +2,7 @@ use anyhow::Result; use kcl_lib::{ ast::{ modify::modify_ast_for_sketch, - types::{Node, Program}, + types::{ModuleId, Node, Program}, }, executor::{ExecutorContext, IdGenerator, KclValue, PlaneType, Sketch, SourceRange}, }; @@ -10,10 +10,9 @@ use kittycad_modeling_cmds::{each_cmd as mcmd, length_unit::LengthUnit, shared:: use pretty_assertions::assert_eq; /// Setup the engine and parse code for an ast. -async fn setup(code: &str, name: &str) -> Result<(ExecutorContext, Node, uuid::Uuid)> { - let tokens = kcl_lib::token::lexer(code)?; - let parser = kcl_lib::parser::Parser::new(tokens); - let program = parser.ast()?; +async fn setup(code: &str, name: &str) -> Result<(ExecutorContext, Node, ModuleId, uuid::Uuid)> { + let module_id = ModuleId::default(); + let program = kcl_lib::parser::parse(code, module_id)?; let ctx = kcl_lib::executor::ExecutorContext::new_with_default_client(Default::default()).await?; let exec_state = ctx.run(&program, None, IdGenerator::default(), None).await?; @@ -60,7 +59,7 @@ async fn setup(code: &str, name: &str) -> Result<(ExecutorContext, Node ) .await?; - Ok((ctx, program, sketch_id)) + Ok((ctx, program, module_id, sketch_id)) } #[tokio::test(flavor = "multi_thread")] @@ -76,9 +75,9 @@ async fn kcl_test_modify_sketch_part001() { name ); - let (ctx, program, sketch_id) = setup(&code, name).await.unwrap(); + let (ctx, program, module_id, sketch_id) = setup(&code, name).await.unwrap(); let mut new_program = program.clone(); - let new_code = modify_ast_for_sketch(&ctx.engine, &mut new_program, name, PlaneType::XY, sketch_id) + let new_code = modify_ast_for_sketch(&ctx.engine, &mut new_program, module_id, name, PlaneType::XY, sketch_id) .await .unwrap(); @@ -101,9 +100,9 @@ async fn kcl_test_modify_sketch_part002() { name ); - let (ctx, program, sketch_id) = setup(&code, name).await.unwrap(); + let (ctx, program, module_id, sketch_id) = setup(&code, name).await.unwrap(); let mut new_program = program.clone(); - let new_code = modify_ast_for_sketch(&ctx.engine, &mut new_program, name, PlaneType::XY, sketch_id) + let new_code = modify_ast_for_sketch(&ctx.engine, &mut new_program, module_id, name, PlaneType::XY, sketch_id) .await .unwrap(); @@ -128,9 +127,9 @@ async fn kcl_test_modify_close_sketch() { name ); - let (ctx, program, sketch_id) = setup(&code, name).await.unwrap(); + let (ctx, program, module_id, sketch_id) = setup(&code, name).await.unwrap(); let mut new_program = program.clone(); - let new_code = modify_ast_for_sketch(&ctx.engine, &mut new_program, name, PlaneType::XY, sketch_id) + let new_code = modify_ast_for_sketch(&ctx.engine, &mut new_program, module_id, name, PlaneType::XY, sketch_id) .await .unwrap(); @@ -154,9 +153,9 @@ async fn kcl_test_modify_line_to_close_sketch() { name ); - let (ctx, program, sketch_id) = setup(&code, name).await.unwrap(); + let (ctx, program, module_id, sketch_id) = setup(&code, name).await.unwrap(); let mut new_program = program.clone(); - let new_code = modify_ast_for_sketch(&ctx.engine, &mut new_program, name, PlaneType::XY, sketch_id) + let new_code = modify_ast_for_sketch(&ctx.engine, &mut new_program, module_id, name, PlaneType::XY, sketch_id) .await .unwrap(); @@ -191,14 +190,14 @@ const {} = startSketchOn("XY") name ); - let (ctx, program, sketch_id) = setup(&code, name).await.unwrap(); + let (ctx, program, module_id, sketch_id) = setup(&code, name).await.unwrap(); let mut new_program = program.clone(); - let result = modify_ast_for_sketch(&ctx.engine, &mut new_program, name, PlaneType::XY, sketch_id).await; + let result = modify_ast_for_sketch(&ctx.engine, &mut new_program, module_id, name, PlaneType::XY, sketch_id).await; assert!(result.is_err()); assert_eq!( result.unwrap_err().to_string(), - r#"engine: KclErrorDetails { source_ranges: [SourceRange([188, 193])], message: "Sketch part002 is constrained `partial` and cannot be modified" }"# + r#"engine: KclErrorDetails { source_ranges: [SourceRange([188, 193, 0])], message: "Sketch part002 is constrained `partial` and cannot be modified" }"# ); } @@ -216,9 +215,9 @@ async fn kcl_test_modify_line_should_close_sketch() { name ); - let (ctx, program, sketch_id) = setup(&code, name).await.unwrap(); + let (ctx, program, module_id, sketch_id) = setup(&code, name).await.unwrap(); let mut new_program = program.clone(); - let new_code = modify_ast_for_sketch(&ctx.engine, &mut new_program, name, PlaneType::XY, sketch_id) + let new_code = modify_ast_for_sketch(&ctx.engine, &mut new_program, module_id, name, PlaneType::XY, sketch_id) .await .unwrap();