From ed04874a8adefd75aab985e8d9f821159df8d809 Mon Sep 17 00:00:00 2001 From: Vyacheslav Matyukhin Date: Fri, 15 Nov 2024 15:21:55 -0300 Subject: [PATCH 1/3] truncate long VArray.toString --- .../__tests__/SqValue/SqLambda_test.ts | 2 +- .../__tests__/library/danger_test.ts | 12 +-- .../__tests__/library/dict_test.ts | 6 +- .../__tests__/library/dist_test.ts | 6 +- .../__tests__/library/list_test.ts | 86 +++++++++---------- .../__tests__/library/number_test.ts | 8 +- .../__tests__/library/pointset_test.ts | 2 +- .../__tests__/library/sampleSet_test.ts | 8 +- .../__tests__/reducer/array_test.ts | 4 +- .../__tests__/reducer/various_test.ts | 10 +-- .../__tests__/value/value_test.ts | 22 +++-- packages/squiggle-lang/src/value/BaseValue.ts | 3 + packages/squiggle-lang/src/value/VArray.ts | 29 ++++++- 13 files changed, 120 insertions(+), 78 deletions(-) diff --git a/packages/squiggle-lang/__tests__/SqValue/SqLambda_test.ts b/packages/squiggle-lang/__tests__/SqValue/SqLambda_test.ts index ef2fa03af6..e34254ae95 100644 --- a/packages/squiggle-lang/__tests__/SqValue/SqLambda_test.ts +++ b/packages/squiggle-lang/__tests__/SqValue/SqLambda_test.ts @@ -13,7 +13,7 @@ describe("SqLambda", () => { ); expect(result.ok).toBe(true); - expect(result.value.toString()).toBe("[1,2,3,4,5]"); + expect(result.value.toString()).toBe("[1, 2, 3, 4, 5]"); }); test("createFromStdlibName for squiggle definition", () => { diff --git a/packages/squiggle-lang/__tests__/library/danger_test.ts b/packages/squiggle-lang/__tests__/library/danger_test.ts index e401db7e94..184b19b615 100644 --- a/packages/squiggle-lang/__tests__/library/danger_test.ts +++ b/packages/squiggle-lang/__tests__/library/danger_test.ts @@ -3,9 +3,9 @@ import { testEvalToBe } from "../helpers/reducerHelpers.js"; describe("Danger functions", () => { describe("combinations", () => { testEvalToBe("Danger.combinations([3,5,8],0)", "[[]]"); - testEvalToBe("Danger.combinations([3,5,8],1)", "[[3],[5],[8]]"); - testEvalToBe("Danger.combinations([3,5,8],2)", "[[3,5],[3,8],[5,8]]"); - testEvalToBe("Danger.combinations([3,5,8],3)", "[[3,5,8]]"); + testEvalToBe("Danger.combinations([3,5,8],1)", "[[3], [5], [8]]"); + testEvalToBe("Danger.combinations([3,5,8],2)", "[[3, 5], [3, 8], [5, 8]]"); + testEvalToBe("Danger.combinations([3,5,8],3)", "[[3, 5, 8]]"); testEvalToBe( "Danger.combinations([3,5,8],4)", "Error(Argument Error: Combinations of length 4 were requested, but full list is only 3 long.)" @@ -14,11 +14,11 @@ describe("Danger functions", () => { describe("allCombinations", () => { testEvalToBe( "Danger.allCombinations([3,5,8])", - "[[3],[5],[8],[3,5],[3,8],[5,8],[3,5,8]]" + "[[3], [5], [8], [3, 5], [3, 8], [5, 8], [3, 5, 8]]" ); testEvalToBe( "Danger.allCombinations([3,5,8])", - "[[3],[5],[8],[3,5],[3,8],[5,8],[3,5,8]]" + "[[3], [5], [8], [3, 5], [3, 8], [5, 8], [3, 5, 8]]" ); testEvalToBe("Danger.allCombinations([3])", "[[3]]"); testEvalToBe("Danger.allCombinations([])", "[]"); @@ -29,7 +29,7 @@ describe("Danger functions", () => { }); describe("json", () => { testEvalToBe("Danger.json(1)", "1"); - testEvalToBe("Danger.json([1,2,3])", "[1,2,3]"); + testEvalToBe("Danger.json([1,2,3])", "[1, 2, 3]"); testEvalToBe( "Danger.json({foo: 'bar'})", '{vtype: "Dict", value: {foo: "bar"}}' diff --git a/packages/squiggle-lang/__tests__/library/dict_test.ts b/packages/squiggle-lang/__tests__/library/dict_test.ts index 59d9301180..69dd1ab23f 100644 --- a/packages/squiggle-lang/__tests__/library/dict_test.ts +++ b/packages/squiggle-lang/__tests__/library/dict_test.ts @@ -11,9 +11,9 @@ describe("Dict", () => { "Dict.mergeMany([{a: 1, b: 2}, {c: 3, d: 4}, {c: 5, e: 6}])", "{a: 1, b: 2, c: 5, d: 4, e: 6}" ); - testEvalToBe("Dict.keys({a: 1, b: 2})", '["a","b"]'); - testEvalToBe("Dict.values({a: 1, b: 2})", "[1,2]"); - testEvalToBe("Dict.toList({a: 1, b: 2})", '[["a",1],["b",2]]'); + testEvalToBe("Dict.keys({a: 1, b: 2})", '["a", "b"]'); + testEvalToBe("Dict.values({a: 1, b: 2})", "[1, 2]"); + testEvalToBe("Dict.toList({a: 1, b: 2})", '[["a", 1], ["b", 2]]'); testEvalToBe("Dict.fromList([['a', 1], ['b', 2]])", "{a: 1, b: 2}"); testEvalToBe("Dict.map({a: 1, b: 2}, {|x| x * 2})", "{a: 2, b: 4}"); testEvalToBe( diff --git a/packages/squiggle-lang/__tests__/library/dist_test.ts b/packages/squiggle-lang/__tests__/library/dist_test.ts index 38bf2505ff..59427d26ba 100644 --- a/packages/squiggle-lang/__tests__/library/dist_test.ts +++ b/packages/squiggle-lang/__tests__/library/dist_test.ts @@ -11,15 +11,15 @@ describe("Dist", () => { ); testEvalToBe( "cumsum([Dist.make(1), Dist.make(5), Dist.make(3)])", - "[PointMass(1),PointMass(6),PointMass(9)]" + "[PointMass(1), PointMass(6), PointMass(9)]" ); testEvalToBe( "cumprod([Dist.make(1),Dist.make(5),Dist.make(3)])", - "[PointMass(1),PointMass(5),PointMass(15)]" + "[PointMass(1), PointMass(5), PointMass(15)]" ); testEvalToBe( "diff([Dist.make(1),Dist.make(5),Dist.make(3)])", - "[PointMass(4),PointMass(-2)]" + "[PointMass(4), PointMass(-2)]" ); testEvalToBe( "Dist.logScore({estimate: mx(Sym.normal(5,2), Sym.uniform(-1000, 1000), [.5, .5]), answer: Sym.normal(5.2,2.2)})", diff --git a/packages/squiggle-lang/__tests__/library/list_test.ts b/packages/squiggle-lang/__tests__/library/list_test.ts index ea62d5392b..d639b7ce76 100644 --- a/packages/squiggle-lang/__tests__/library/list_test.ts +++ b/packages/squiggle-lang/__tests__/library/list_test.ts @@ -14,11 +14,11 @@ describe("List functions", () => { }); describe("make", () => { - testEvalToBe("List(2, 5)", "[5,5]"); - testEvalToBe("List.make(3, 'HI')", '["HI","HI","HI"]'); - testEvalToBe("List.make(3, {|e| e})", "[0,1,2]"); - testEvalToBe("List.make(3, {|| 1})", "[1,1,1]"); - testEvalToBe("List.make(3, {|index| 1 + index})", "[1,2,3]"); + testEvalToBe("List(2, 5)", "[5, 5]"); + testEvalToBe("List.make(3, 'HI')", '["HI", "HI", "HI"]'); + testEvalToBe("List.make(3, {|e| e})", "[0, 1, 2]"); + testEvalToBe("List.make(3, {|| 1})", "[1, 1, 1]"); + testEvalToBe("List.make(3, {|index| 1 + index})", "[1, 2, 3]"); testEvalToBe( "List.make(3.5, 'HI')", "Error(Argument Error: Number must be an integer)" @@ -31,7 +31,7 @@ describe("List functions", () => { }); describe("upTo", () => { - testEvalToBe("List.upTo(1,3)", "[1,2,3]"); + testEvalToBe("List.upTo(1,3)", "[1, 2, 3]"); testEvalToBe( "List.upTo(1.5,3)", "Error(Argument Error: Low and high values must both be integers)" @@ -56,19 +56,19 @@ describe("List functions", () => { }); describe("concat", () => { - testEvalToBe("List.concat([1, 2, 3], [4, 5, 6])", "[1,2,3,4,5,6]"); - testEvalToBe("List.concat([], [1, 2, 3])", "[1,2,3]"); - testEvalToBe("List.concat(['cake'], [1, 2, 3])", '["cake",1,2,3]'); + testEvalToBe("List.concat([1, 2, 3], [4, 5, 6])", "[1, 2, 3, 4, 5, 6]"); + testEvalToBe("List.concat([], [1, 2, 3])", "[1, 2, 3]"); + testEvalToBe("List.concat(['cake'], [1, 2, 3])", '["cake", 1, 2, 3]'); }); describe("sortBy", () => { testEvalToBe( "arr=[5, 2, 3, 1, 4]; List.sortBy(arr, {|n| n})", - "[1,2,3,4,5]" + "[1, 2, 3, 4, 5]" ); testEvalToBe( "arr=[{a: 3}, {a: 1}, {a: 2}]; List.sortBy(arr, {|obj| obj.a})", - "[{a: 1},{a: 2},{a: 3}]" + "[{a: 1}, {a: 2}, {a: 3}]" ); testEvalToBe( "arr=[{a: '3'}, {a: '1'}, {a: '2'}]; List.sortBy(arr, {|obj| obj.a})", @@ -78,8 +78,8 @@ describe("List functions", () => { // Edge cases testEvalToBe("arr=[]; List.sortBy(arr, {|n| n})", "[]"); // Empty array testEvalToBe("arr=[1]; List.sortBy(arr, {|n| n})", "[1]"); // Single-element array - testEvalToBe("arr=[3, 2, 1]; List.sortBy(arr, {|n| -n})", "[3,2,1]"); // Descending order - testEvalToBe("arr=[1, 2, 3]; List.sortBy(arr, {|n| 0})", "[1,2,3]"); // Lambda function always returns the same value + testEvalToBe("arr=[3, 2, 1]; List.sortBy(arr, {|n| -n})", "[3, 2, 1]"); // Descending order + testEvalToBe("arr=[1, 2, 3]; List.sortBy(arr, {|n| 0})", "[1, 2, 3]"); // Lambda function always returns the same value }); describe("minBy", () => { @@ -107,39 +107,39 @@ describe("List functions", () => { }); describe("reverse", () => { - testEvalToBe("List.reverse([3,5,8])", "[8,5,3]"); + testEvalToBe("List.reverse([3, 5, 8])", "[8, 5, 3]"); testEvalToBe("List.reverse([])", "[]"); }); describe("append", () => { - testEvalToBe("List.append([3,5,8], 8)", "[3,5,8,8]"); + testEvalToBe("List.append([3, 5, 8], 8)", "[3, 5, 8, 8]"); testEvalToBe("List.append([], 8)", "[8]"); }); describe("map", () => { - testEvalToBe("arr=[1,2,3]; map(arr, {|x| x*2})", "[2,4,6]"); + testEvalToBe("arr=[1,2,3]; map(arr, {|x| x*2})", "[2, 4, 6]"); testEvalToBe( "double(x)=2*x; arr=[1,2,3]; List.map(arr, double)", - "[2,4,6]" + "[2, 4, 6]" ); - testEvalToBe("double(x)=2*x; arr=[1,2,3]; map(arr, double)", "[2,4,6]"); + testEvalToBe("double(x)=2*x; arr=[1,2,3]; map(arr, double)", "[2, 4, 6]"); // wrong arg types testEvalError("addone(x)=x+1; map(2, addone)"); testEvalError("addone(x)=x+1; map(2, {x: addone})"); // two-arg callback - testEvalToBe("[10,20,30] -> List.map({|x,i|x+i+1})", "[11,22,33]"); + testEvalToBe("[10, 20, 30] -> List.map({|x,i|x+i+1})", "[11, 22, 33]"); testEvalToBe("List.map([[1]], Number.sum)", "[1]"); }); describe("slice", () => { - testEvalToBe("List.slice([1,2,3,4,5,6], 2)", "[3,4,5,6]"); - testEvalToBe("List.slice([1,2,3,4,5,6], 2, 4)", "[3,4]"); - testEvalToBe("List.slice([1,2,3,4,5,6], 8, 3)", "[]"); + testEvalToBe("List.slice([1, 2, 3, 4, 5, 6], 2)", "[3, 4, 5, 6]"); + testEvalToBe("List.slice([1, 2, 3, 4, 5, 6], 2, 4)", "[3, 4]"); + testEvalToBe("List.slice([1, 2, 3, 4, 5, 6], 8, 3)", "[]"); testEvalToBe("List.slice([], 8, 3)", "[]"); - testEvalToBe("List.slice([1,2,3,4,5,6], -4)", "[3,4,5,6]"); - testEvalToBe("List.slice([1,2,3,4,5,6], 2, -1)", "[3,4,5]"); + testEvalToBe("List.slice([1, 2, 3, 4, 5, 6], -4)", "[3, 4, 5, 6]"); + testEvalToBe("List.slice([1, 2, 3, 4, 5, 6], 2, -1)", "[3, 4, 5]"); testEvalToBe( "List.slice([], 3.5, 3)", "Error(Argument Error: Number 3.5 must be an integer)" @@ -147,34 +147,34 @@ describe("List functions", () => { }); describe("uniq", () => { - testEvalToBe("arr=[1,2,3,1,2,3]; List.uniq(arr)", "[1,2,3]"); - testEvalToBe("arr=[1,'1']; List.uniq(arr)", '[1,"1"]'); + testEvalToBe("arr=[1, 2, 3, 1, 2, 3]; List.uniq(arr)", "[1, 2, 3]"); + testEvalToBe("arr=[1, '1']; List.uniq(arr)", '[1, "1"]'); testEvalToBe( "arr=[1,1, 'test', 'test', false, false, true]; List.uniq(arr)", - '[1,"test",false,true]' + '[1, "test", false, true]' ); testEvalToBe( "arr=[1,2,normal(50,1)]; List.uniq(arr)", - "[1,2,Sample Set Distribution]" + "[1, 2, Sample Set Distribution]" ); }); describe("uniqBy", () => { testEvalToBe( "arr=[1.2, 1.6, 2.3, 2.8]; List.uniqBy(arr, floor)", - "[1.2,2.3]" + "[1.2, 2.3]" ); testEvalToBe( "arr=[{a: 1, b: 2}, {a: 1, b: 3}, {a:2, b:5}]; List.uniqBy(arr, {|e| e.a})", - "[{a: 1, b: 2},{a: 2, b: 5}]" + "[{a: 1, b: 2}, {a: 2, b: 5}]" ); testEvalToBe( "arr=[{a: normal(5,2), b: 2}, {a: 1, b: 3}, {a:2, b:5}]; List.uniqBy(arr, {|e| e.a})", - "[{a: Sample Set Distribution, b: 2},{a: 1, b: 3},{a: 2, b: 5}]" + "[{a: Sample Set Distribution, b: 2}, {a: 1, b: 3}, {a: 2, b: 5}]" ); testEvalToBe( "arr=[{a: normal(5,2), b: 2}, {a: 1, b: 3}, {a:2, b:3}]; List.uniqBy(arr, {|e| e.b})", - "[{a: Sample Set Distribution, b: 2},{a: 1, b: 3}]" + "[{a: Sample Set Distribution, b: 2}, {a: 1, b: 3}]" ); }); @@ -243,11 +243,11 @@ describe("List functions", () => { }); describe("reverse", () => { - testEvalToBe("arr=[1,2,3]; List.reverse(arr)", "[3,2,1]"); + testEvalToBe("arr=[1,2,3]; List.reverse(arr)", "[3, 2, 1]"); }); describe("filter", () => { - testEvalToBe("arr=[1,2,3]; List.filter(arr,{|e| e > 1})", "[2,3]"); + testEvalToBe("arr=[1,2,3]; List.filter(arr,{|e| e > 1})", "[2, 3]"); testEvalToBe("arr=[1,2,3]; List.filter(arr,{|e| e > 5})", "[]"); }); @@ -283,19 +283,19 @@ describe("List functions", () => { }); describe("flatten", () => { - testEvalToBe("List.flatten([[1,2], [3,4]])", "[1,2,3,4]"); - testEvalToBe("List.flatten([[1,2], [3,[4,5]]])", "[1,2,3,[4,5]]"); + testEvalToBe("List.flatten([[1, 2], [3, 4]])", "[1, 2, 3, 4]"); + testEvalToBe("List.flatten([[1, 2], [3, [4, 5]]])", "[1, 2, 3, [4, 5]]"); testEvalToBe("List.flatten([])", "[]"); - testEvalToBe("List.flatten([[],[],[]])", "[]"); + testEvalToBe("List.flatten([[], [], []])", "[]"); }); describe("zip", () => { - testEvalToBe("List.zip([1,2], [3,4])", "[[1,3],[2,4]]"); + testEvalToBe("List.zip([1, 2], [3, 4])", "[[1, 3], [2, 4]]"); testEvalToBe( - "List.zip([1,2,4], [3,4])", + "List.zip([1, 2, 4], [3, 4])", "Error(Argument Error: List lengths must be equal)" ); - testEvalToBe("List.zip([1,2], [3,[4,5]])", "[[1,3],[2,[4,5]]]"); + testEvalToBe("List.zip([1, 2], [3, [4, 5]])", "[[1, 3], [2, [4, 5]]]"); testEvalToBe( "List.zip([1,2], [3,[4,5], [5]])", "Error(Argument Error: List lengths must be equal)" @@ -304,9 +304,9 @@ describe("List functions", () => { }); describe("unzip", () => { - testEvalToBe("List.unzip([[1,3],[2,4]])", "[[1,2],[3,4]]"); - testEvalToBe("List.unzip([[1,3],[2,4],[5,6]])", "[[1,2,5],[3,4,6]]"); - testEvalToBe("List.unzip([])", "[[],[]]"); + testEvalToBe("List.unzip([[1,3],[2,4]])", "[[1, 2], [3, 4]]"); + testEvalToBe("List.unzip([[1,3],[2,4],[5,6]])", "[[1, 2, 5], [3, 4, 6]]"); + testEvalToBe("List.unzip([])", "[[], []]"); }); describe("sample", () => { diff --git a/packages/squiggle-lang/__tests__/library/number_test.ts b/packages/squiggle-lang/__tests__/library/number_test.ts index 2277d9d947..ddaf60f855 100644 --- a/packages/squiggle-lang/__tests__/library/number_test.ts +++ b/packages/squiggle-lang/__tests__/library/number_test.ts @@ -27,13 +27,13 @@ describe("Numbers", () => { testEvalToBe("Number.quantile([0,5,10,15,20], 0.25)", "5"); testEvalToBe("Number.median([0,5,10,15,20])", "10"); testEvalToBe("Number.sort([])", "[]"); - testEvalToBe("Number.sort([10,0,15,5])", "[0,5,10,15]"); + testEvalToBe("Number.sort([10,0,15,5])", "[0, 5, 10, 15]"); testEvalToBe("Number.cumsum([])", "[]"); - testEvalToBe("Number.cumsum([1,5,3])", "[1,6,9]"); + testEvalToBe("Number.cumsum([1,5,3])", "[1, 6, 9]"); testEvalToBe("Number.cumprod([])", "[]"); - testEvalToBe("Number.cumprod([1,5,3])", "[1,5,15]"); + testEvalToBe("Number.cumprod([1,5,3])", "[1, 5, 15]"); testEvalToBe("Number.diff([1])", "[]"); - testEvalToBe("Number.diff([1,5,3])", "[4,-2]"); + testEvalToBe("Number.diff([1,5,3])", "[4, -2]"); testEvalToBe("Number.mod(10, 3)", "1"); testEvalToBe("mod(10, 3)", "1"); testEvalToBe("Number.mod(15, 4)", "3"); diff --git a/packages/squiggle-lang/__tests__/library/pointset_test.ts b/packages/squiggle-lang/__tests__/library/pointset_test.ts index c841dc8f24..8ae83577ee 100644 --- a/packages/squiggle-lang/__tests__/library/pointset_test.ts +++ b/packages/squiggle-lang/__tests__/library/pointset_test.ts @@ -77,6 +77,6 @@ describe("Discrete", () => { describe("MixedSet", () => { testEvalToBe( "MixedSet.difference(PointSet.support(mx(Sym.uniform(1, 5), Sym.uniform(10, 30))), PointSet.support(mx(Sym.uniform(3, 8), Sym.uniform(20, 22))))", - "{points: [], segments: [[0.9999999996,2.9999999995],[9.999999998,19.9999999998],[22.0000000002,30.000000002]]}" + "{points: [], segments: [[0.9999999996, 2.9999999995], [9.999999998, 19.9999999998], [22.0000000002, 30.000000002]]}" ); }); diff --git a/packages/squiggle-lang/__tests__/library/sampleSet_test.ts b/packages/squiggle-lang/__tests__/library/sampleSet_test.ts index 4616641619..332e4c1a97 100644 --- a/packages/squiggle-lang/__tests__/library/sampleSet_test.ts +++ b/packages/squiggle-lang/__tests__/library/sampleSet_test.ts @@ -28,7 +28,7 @@ describe("Various SampleSet functions", () => { ); testEvalToBe( "addOne(t)=t+1; SampleSet.toList(SampleSet.map(SampleSet.fromList([1,2,3,4,5,6]), addOne))", - "[2,3,4,5,6,7]" + "[2, 3, 4, 5, 6, 7]" ); testEvalToBe( "SampleSet.fromList([1, 2, 3])", @@ -46,17 +46,17 @@ describe("mapN", () => { // equal length testEvalToBe( sq`SampleSet.mapN([SampleSet.fromList([1,2,3,4,5,6]), SampleSet.fromList([6,5,4,3,2,1])], {|x| x[0] > x[1] ? x[0] : x[1]}) -> SampleSet.toList`, - "[6,5,4,4,5,6]" + "[6, 5, 4, 4, 5, 6]" ); // unequal length testEvalToBe( sq`SampleSet.mapN([SampleSet.fromList([1,2,3,4,5,6]), SampleSet.fromList([6,5,4,3,2,1,1,1])], {|x| x[0] > x[1] ? x[0] : x[1]}) -> SampleSet.toList`, - "[6,5,4,4,5,6]" + "[6, 5, 4, 4, 5, 6]" ); testEvalToBe( sq`SampleSet.mapN([SampleSet.fromList([1,2,3,4,5,6,1,1]), SampleSet.fromList([6,5,4,3,2,1])], {|x| x[0] > x[1] ? x[0] : x[1]}) -> SampleSet.toList`, - "[6,5,4,4,5,6]" + "[6, 5, 4, 4, 5, 6]" ); }); diff --git a/packages/squiggle-lang/__tests__/reducer/array_test.ts b/packages/squiggle-lang/__tests__/reducer/array_test.ts index c495e2d0e6..75f968a786 100644 --- a/packages/squiggle-lang/__tests__/reducer/array_test.ts +++ b/packages/squiggle-lang/__tests__/reducer/array_test.ts @@ -1,10 +1,10 @@ import { testEvalToBe } from "../helpers/reducerHelpers.js"; describe("Array", () => { - testEvalToBe("[1, 2]", "[1,2]"); + testEvalToBe("[1, 2]", "[1, 2]"); testEvalToBe("[]", "[]"); testEvalToBe( "f(x)=x; g(x)=x; [f, g]", - "[(x) => internal code,(x) => internal code]" + "[(x) => internal code, (x) => internal code]" ); }); diff --git a/packages/squiggle-lang/__tests__/reducer/various_test.ts b/packages/squiggle-lang/__tests__/reducer/various_test.ts index 2f673eac3f..157dbcad44 100644 --- a/packages/squiggle-lang/__tests__/reducer/various_test.ts +++ b/packages/squiggle-lang/__tests__/reducer/various_test.ts @@ -20,7 +20,7 @@ describe("eval", () => { testEvalToBe("!true", "false"); testEvalToBe("1 + -1", "0"); testEvalToBe('"hello"', '"hello"'); - testEvalToBe("concat([3,4], [5,6,7])", "[3,4,5,6,7]"); + testEvalToBe("concat([3,4], [5,6,7])", "[3, 4, 5, 6, 7]"); testEvalToBe("log(10)", "2.302585092994046"); testEvalToBe("Math.cos(10)", "-0.8390715290764524"); // most library tests are in __tests__/library/ @@ -39,8 +39,8 @@ describe("eval", () => { test("empty array", async () => { await expectEvalToBe("[]", "[]"); }); - testEvalToBe("[1, 2, 3]", "[1,2,3]"); - testEvalToBe("['hello', 'world']", '["hello","world"]'); + testEvalToBe("[1, 2, 3]", "[1, 2, 3]"); + testEvalToBe("['hello', 'world']", '["hello", "world"]'); testEvalToBe("([0,1,2])[1]", "1"); testDescriptionEvalToBe( "index not found", @@ -48,13 +48,13 @@ describe("eval", () => { "Error(Array index not found: 10)" ); test("trailing comma", async () => { - await expectEvalToBe(`[3,4,]`, "[3,4]"); + await expectEvalToBe(`[3,4,]`, "[3, 4]"); await expectEvalToBe( `[ 3, 4, ]`, - "[3,4]" + "[3, 4]" ); }); }); diff --git a/packages/squiggle-lang/__tests__/value/value_test.ts b/packages/squiggle-lang/__tests__/value/value_test.ts index a9ef4d6981..9830033364 100644 --- a/packages/squiggle-lang/__tests__/value/value_test.ts +++ b/packages/squiggle-lang/__tests__/value/value_test.ts @@ -1,9 +1,21 @@ -import { vNumber, vString } from "../../src/value/index.js"; +import { vArray, vNumber, vString } from "../../src/value/index.js"; -describe("Value", () => { - test("toString", () => { - expect(vNumber(1).toString()).toEqual("1"); - expect(vString("a").toString()).toEqual('"a"'); +test("toString", () => { + expect(vNumber(1).toString()).toEqual("1"); + expect(vString("a").toString()).toEqual('"a"'); +}); + +describe("VArray", () => { + test("VArray.toString", () => { + expect(vArray([vNumber(1), vString("a")]).toString()).toEqual('[1, "a"]'); + }); + + test("long VArray.toString", () => { + expect( + vArray(Array.from({ length: 100 }, (_, i) => vNumber(i))).toString() + ).toEqual( + "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ... 78 more ..., 99]" + ); }); }); diff --git a/packages/squiggle-lang/src/value/BaseValue.ts b/packages/squiggle-lang/src/value/BaseValue.ts index b4696c13a9..9ba014e931 100644 --- a/packages/squiggle-lang/src/value/BaseValue.ts +++ b/packages/squiggle-lang/src/value/BaseValue.ts @@ -42,6 +42,9 @@ export abstract class BaseValue< return this.copyWithTags(this.tags?.merge(args) ?? new ValueTags(args)); } + // This method should be implemented by specific value classes. + // It's allowed to lose some information in the process, e.g. when converting a number to a string, + // or when the array is too long. protected abstract valueToString(): string; toString() { diff --git a/packages/squiggle-lang/src/value/VArray.ts b/packages/squiggle-lang/src/value/VArray.ts index 2eefe86798..b91adc325f 100644 --- a/packages/squiggle-lang/src/value/VArray.ts +++ b/packages/squiggle-lang/src/value/VArray.ts @@ -27,7 +27,34 @@ export class VArray } valueToString() { - return "[" + this.value.map((v) => v.toString()).join(",") + "]"; + // For reference, for long types TypeScript returns the following error: + + // Argument of type '[number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, ... 38 more ..., number]' is not assignable to parameter of type '[number]'. + + // These are not precise, since we can't know the length of the stringified representation of the last element. + // But it's good enough for our purposes. + const maxTotalLength = 150; + const maxBits = 20; + + const bits: string[] = []; + let bitsTotalLength = 0; + for (let i = 0; i < this.value.length; i++) { + const bit = this.value[i].toString(); + bits.push(bit); + bitsTotalLength += bit.length; + if ( + (bitsTotalLength > maxTotalLength || bits.length > maxBits) && + i < this.value.length - 2 // at least one bit to skip + ) { + // skip all items except the last one + // example: for [0,1,2,3,4] we will get [0,1,2,"... 1 more ...",4] + // so we skip 5 - (2 + 2) = 1 element + bits.push(`... ${this.value.length - (i + 2)} more ...`); + bits.push(this.value[this.value.length - 1].toString()); + break; + } + } + return "[" + bits.join(", ") + "]"; } get(key: Value) { From 8e390ef346ab6264acae42e0b620eeb80e9ba1d3 Mon Sep 17 00:00:00 2001 From: Vyacheslav Matyukhin Date: Fri, 15 Nov 2024 15:28:17 -0300 Subject: [PATCH 2/3] changeset --- .changeset/nervous-worms-grow.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/nervous-worms-grow.md diff --git a/.changeset/nervous-worms-grow.md b/.changeset/nervous-worms-grow.md new file mode 100644 index 0000000000..93f91c2ad8 --- /dev/null +++ b/.changeset/nervous-worms-grow.md @@ -0,0 +1,5 @@ +--- +"@quri/squiggle-lang": patch +--- + +Truncate long lists when converting to strings From 2bcdf7151c98e21ff0dc63cd110440af90f63407 Mon Sep 17 00:00:00 2001 From: Vyacheslav Matyukhin Date: Fri, 15 Nov 2024 16:01:39 -0300 Subject: [PATCH 3/3] improve var names and counting --- .../__tests__/value/value_test.ts | 2 +- packages/squiggle-lang/src/value/VArray.ts | 32 +++++++++++-------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/packages/squiggle-lang/__tests__/value/value_test.ts b/packages/squiggle-lang/__tests__/value/value_test.ts index 9830033364..ae8be83b83 100644 --- a/packages/squiggle-lang/__tests__/value/value_test.ts +++ b/packages/squiggle-lang/__tests__/value/value_test.ts @@ -14,7 +14,7 @@ describe("VArray", () => { expect( vArray(Array.from({ length: 100 }, (_, i) => vNumber(i))).toString() ).toEqual( - "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ... 78 more ..., 99]" + "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, ... 79 more ..., 99]" ); }); }); diff --git a/packages/squiggle-lang/src/value/VArray.ts b/packages/squiggle-lang/src/value/VArray.ts index b91adc325f..02ca30c9aa 100644 --- a/packages/squiggle-lang/src/value/VArray.ts +++ b/packages/squiggle-lang/src/value/VArray.ts @@ -31,30 +31,34 @@ export class VArray // Argument of type '[number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, ... 38 more ..., number]' is not assignable to parameter of type '[number]'. - // These are not precise, since we can't know the length of the stringified representation of the last element. - // But it's good enough for our purposes. - const maxTotalLength = 150; - const maxBits = 20; + // We want to limit both the string length and the number of items. + // (To see why max items is not enough, consider a list that consists of long strings, or complex dicts) + const maxTotalLengthBeforeTruncation = 150; + const maxItemsBeforeTruncation = 20; + const separator = ", "; - const bits: string[] = []; - let bitsTotalLength = 0; + const strings: string[] = []; + let totalLength = 0; for (let i = 0; i < this.value.length; i++) { - const bit = this.value[i].toString(); - bits.push(bit); - bitsTotalLength += bit.length; + const str = this.value[i].toString(); + strings.push(str); + totalLength += str.length; if ( - (bitsTotalLength > maxTotalLength || bits.length > maxBits) && - i < this.value.length - 2 // at least one bit to skip + (totalLength >= + maxTotalLengthBeforeTruncation + strings.length * separator.length || + strings.length >= maxItemsBeforeTruncation) && + i < this.value.length - 2 // at least one item to skip ) { // skip all items except the last one // example: for [0,1,2,3,4] we will get [0,1,2,"... 1 more ...",4] // so we skip 5 - (2 + 2) = 1 element - bits.push(`... ${this.value.length - (i + 2)} more ...`); - bits.push(this.value[this.value.length - 1].toString()); + strings.push(`... ${this.value.length - (i + 2)} more ...`); + // always add the last item + strings.push(this.value[this.value.length - 1].toString()); break; } } - return "[" + bits.join(", ") + "]"; + return "[" + strings.join(separator) + "]"; } get(key: Value) {