diff --git a/example.ar b/example.ar index 7575f47..a6994d7 100644 --- a/example.ar +++ b/example.ar @@ -1,8 +1,5 @@ -i = 0 -output = [] +x = map() +x.poo = 10 -while (i < 1000) do - output = append(output, i) - i = i + 1 - -file.write('output.json').json(output) \ No newline at end of file +y = [x,x,x] +term.log(y) \ No newline at end of file diff --git a/output.json b/output.json deleted file mode 100644 index 6e5d3ea..0000000 --- a/output.json +++ /dev/null @@ -1,68 +0,0 @@ -[ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, - 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, - 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, - 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, - 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, - 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, - 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, - 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, - 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, - 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, - 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, - 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, - 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, - 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, - 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, - 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, - 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, - 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, - 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, - 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, - 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, - 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, - 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, - 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, - 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, - 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, - 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, - 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, - 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, - 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, - 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, 499, 500, 501, 502, - 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, - 518, 519, 520, 521, 522, 523, 524, 525, 526, 527, 528, 529, 530, 531, 532, - 533, 534, 535, 536, 537, 538, 539, 540, 541, 542, 543, 544, 545, 546, 547, - 548, 549, 550, 551, 552, 553, 554, 555, 556, 557, 558, 559, 560, 561, 562, - 563, 564, 565, 566, 567, 568, 569, 570, 571, 572, 573, 574, 575, 576, 577, - 578, 579, 580, 581, 582, 583, 584, 585, 586, 587, 588, 589, 590, 591, 592, - 593, 594, 595, 596, 597, 598, 599, 600, 601, 602, 603, 604, 605, 606, 607, - 608, 609, 610, 611, 612, 613, 614, 615, 616, 617, 618, 619, 620, 621, 622, - 623, 624, 625, 626, 627, 628, 629, 630, 631, 632, 633, 634, 635, 636, 637, - 638, 639, 640, 641, 642, 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, - 653, 654, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, - 668, 669, 670, 671, 672, 673, 674, 675, 676, 677, 678, 679, 680, 681, 682, - 683, 684, 685, 686, 687, 688, 689, 690, 691, 692, 693, 694, 695, 696, 697, - 698, 699, 700, 701, 702, 703, 704, 705, 706, 707, 708, 709, 710, 711, 712, - 713, 714, 715, 716, 717, 718, 719, 720, 721, 722, 723, 724, 725, 726, 727, - 728, 729, 730, 731, 732, 733, 734, 735, 736, 737, 738, 739, 740, 741, 742, - 743, 744, 745, 746, 747, 748, 749, 750, 751, 752, 753, 754, 755, 756, 757, - 758, 759, 760, 761, 762, 763, 764, 765, 766, 767, 768, 769, 770, 771, 772, - 773, 774, 775, 776, 777, 778, 779, 780, 781, 782, 783, 784, 785, 786, 787, - 788, 789, 790, 791, 792, 793, 794, 795, 796, 797, 798, 799, 800, 801, 802, - 803, 804, 805, 806, 807, 808, 809, 810, 811, 812, 813, 814, 815, 816, 817, - 818, 819, 820, 821, 822, 823, 824, 825, 826, 827, 828, 829, 830, 831, 832, - 833, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843, 844, 845, 846, 847, - 848, 849, 850, 851, 852, 853, 854, 855, 856, 857, 858, 859, 860, 861, 862, - 863, 864, 865, 866, 867, 868, 869, 870, 871, 872, 873, 874, 875, 876, 877, - 878, 879, 880, 881, 882, 883, 884, 885, 886, 887, 888, 889, 890, 891, 892, - 893, 894, 895, 896, 897, 898, 899, 900, 901, 902, 903, 904, 905, 906, 907, - 908, 909, 910, 911, 912, 913, 914, 915, 916, 917, 918, 919, 920, 921, 922, - 923, 924, 925, 926, 927, 928, 929, 930, 931, 932, 933, 934, 935, 936, 937, - 938, 939, 940, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, - 953, 954, 955, 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966, 967, - 968, 969, 970, 971, 972, 973, 974, 975, 976, 977, 978, 979, 980, 981, 982, - 983, 984, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, - 998, 999 -] diff --git a/src/array.go b/src/array.go index de9971d..b33a964 100644 --- a/src/array.go +++ b/src/array.go @@ -5,7 +5,7 @@ import "strings" var arrayCompile = makeRegex(`( *)\[(.|\n)*\]( *)`) type CreateArray struct { - value ArArray + value []any line int code string path string @@ -15,6 +15,233 @@ func isArray(code UNPARSEcode) bool { return arrayCompile.MatchString(code.code) } +func ArArray(arr []any) ArObject { + val := ArObject{ + "array", + anymap{ + "__value__": arr, + "length": len(arr), + }, + } + val.obj["remove"] = builtinFunc{ + "remove", + func(args ...any) (any, ArErr) { + if len(args) != 1 { + return nil, ArErr{ + TYPE: "TypeError", + message: "missing argument", + EXISTS: true, + } + } + if typeof(args[0]) != "number" { + return nil, ArErr{ + TYPE: "TypeError", + message: "argument must be a number", + EXISTS: true, + } + } + if !args[0].(number).IsInt() { + return nil, ArErr{ + TYPE: "TypeError", + message: "argument must be an integer", + EXISTS: true, + } + } + num := int(args[0].(number).Num().Int64()) + if num < 0 || num >= len(arr) { + return nil, ArErr{ + TYPE: "IndexError", + message: "index out of range", + EXISTS: true, + } + } + arr = append(arr[:num], arr[num+1:]...) + val.obj["length"] = len(arr) + val.obj["__value__"] = arr + return nil, ArErr{} + }} + val.obj["append"] = builtinFunc{ + "append", + func(args ...any) (any, ArErr) { + if len(args) == 0 { + return nil, ArErr{ + TYPE: "TypeError", + message: "missing argument", + EXISTS: true, + } + } + arr = append(arr, args...) + val.obj["length"] = len(arr) + val.obj["__value__"] = arr + return nil, ArErr{} + }, + } + val.obj["insert"] = builtinFunc{ + "insert", + func(args ...any) (any, ArErr) { + if len(args) < 2 { + return nil, ArErr{ + TYPE: "TypeError", + message: "missing argument", + EXISTS: true, + } + } + if typeof(args[0]) != "number" { + return nil, ArErr{ + TYPE: "TypeError", + message: "argument must be a number", + EXISTS: true, + } + } + if !args[0].(number).IsInt() { + return nil, ArErr{ + TYPE: "TypeError", + message: "argument must be an integer", + EXISTS: true, + } + } + num := int(args[0].(number).Num().Int64()) + if num < 0 || num > len(arr) { + return nil, ArErr{ + TYPE: "IndexError", + message: "index out of range", + EXISTS: true, + } + } + arr = append(arr[:num], append(args[1:], arr[num:]...)...) + val.obj["length"] = len(arr) + val.obj["__value__"] = arr + return nil, ArErr{} + }, + } + val.obj["pop"] = builtinFunc{ + "pop", + func(args ...any) (any, ArErr) { + if len(args) > 1 { + return nil, ArErr{ + TYPE: "TypeError", + message: "too many arguments", + EXISTS: true, + } + } + if len(args) == 1 { + if typeof(args[0]) != "number" { + return nil, ArErr{ + TYPE: "TypeError", + message: "argument must be a number", + EXISTS: true, + } + } + if !args[0].(number).IsInt() { + return nil, ArErr{ + TYPE: "TypeError", + message: "argument must be an integer", + EXISTS: true, + } + } + num := int(args[0].(number).Num().Int64()) + if num < 0 || num >= len(arr) { + return nil, ArErr{ + TYPE: "IndexError", + message: "index out of range", + EXISTS: true, + } + } + v := arr[num] + arr = append(arr[:num], arr[num+1:]...) + val.obj["length"] = len(arr) + val.obj["__value__"] = arr + return v, ArErr{} + } + v := arr[len(arr)-1] + arr = arr[:len(arr)-1] + val.obj["length"] = len(arr) + val.obj["__value__"] = arr + return v, ArErr{} + }, + } + val.obj["clear"] = builtinFunc{ + "clear", + func(args ...any) (any, ArErr) { + if len(args) != 0 { + return nil, ArErr{ + TYPE: "TypeError", + message: "too many arguments", + EXISTS: true, + } + } + arr = []any{} + val.obj["length"] = len(arr) + val.obj["__value__"] = arr + return nil, ArErr{} + }, + } + val.obj["extend"] = builtinFunc{ + "extend", + func(args ...any) (any, ArErr) { + if len(args) != 1 { + return nil, ArErr{ + TYPE: "TypeError", + message: "missing argument", + EXISTS: true, + } + } + if typeof(args[0]) != "array" { + return nil, ArErr{ + TYPE: "TypeError", + message: "argument must be an array", + EXISTS: true, + } + } + arr = append(arr, args[0].(ArObject).obj["__value__"].([]any)...) + val.obj["length"] = len(arr) + val.obj["__value__"] = arr + return nil, ArErr{} + }, + } + val.obj["map"] = builtinFunc{ + "map", + func(args ...any) (any, ArErr) { + if len(args) != 1 { + return nil, ArErr{ + TYPE: "TypeError", + message: "missing argument", + EXISTS: true, + } + } + if typeof(args[0]) != "function" { + return nil, ArErr{ + TYPE: "TypeError", + message: "argument must be a function", + EXISTS: true, + } + } + newarr := []any{} + for _, v := range arr { + vv, err := runCall(call{ + args[0], + []any{v}, "", 0, "", + }, stack{vars, newscope()}, 0) + if err.EXISTS { + return nil, err + } + newarr = append(newarr, vv) + } + return ArArray(newarr), ArErr{} + }, + } + return val +} + +func potentialAnyArrayToArArray(arr any) any { + switch arr := arr.(type) { + case []any: + return ArArray(arr) + default: + return arr + } +} + func parseArray(code UNPARSEcode, index int, codelines []UNPARSEcode) (any, bool, ArErr, int) { trimmed := strings.TrimSpace(code.code) trimmed = trimmed[1 : len(trimmed)-1] @@ -27,14 +254,14 @@ func parseArray(code UNPARSEcode, index int, codelines []UNPARSEcode) (any, bool }, worked, err, 1 } -func runArray(a CreateArray, stack stack, stacklevel int) ([]any, ArErr) { - var array ArArray +func runArray(a CreateArray, stack stack, stacklevel int) (ArObject, ArErr) { + var array []any for _, val := range a.value { val, err := runVal(val, stack, stacklevel+1) if err.EXISTS { - return nil, err + return ArObject{}, err } array = append(array, val) } - return array, ArErr{} + return ArArray(array), ArErr{} } diff --git a/src/boolean.go b/src/boolean.go index 2b77152..1e20239 100644 --- a/src/boolean.go +++ b/src/boolean.go @@ -12,8 +12,11 @@ func anyToBool(x any) bool { return x case nil: return false - case ArMap: - return len(x) != 0 + case ArObject: + if x.TYPE == "array" { + return len(x.obj["__value__"].([]any)) != 0 + } + return len(x.obj) != 0 case builtinFunc: return true case Callable: diff --git a/src/built-ins.go b/src/built-ins.go index a2eadaa..89b306d 100644 --- a/src/built-ins.go +++ b/src/built-ins.go @@ -1,97 +1,97 @@ package main -import ( - "fmt" - "strings" -) - -var vars = scope{} +var vars = Map(anymap{}) func init() { - vars["global"] = vars - vars["term"] = ArTerm - vars["input"] = builtinFunc{"input", ArgonInput} - vars["number"] = builtinFunc{"number", ArgonNumber} - vars["string"] = builtinFunc{"string", ArgonString} - vars["infinity"] = infinity - vars["length"] = builtinFunc{"length", func(a ...any) (any, ArErr) { + vars.obj["global"] = vars + vars.obj["term"] = ArTerm + vars.obj["input"] = builtinFunc{"input", ArgonInput} + vars.obj["number"] = builtinFunc{"number", ArgonNumber} + vars.obj["string"] = builtinFunc{"string", ArgonString} + vars.obj["infinity"] = infinity + vars.obj["length"] = builtinFunc{"length", func(a ...any) (any, ArErr) { switch x := a[0].(type) { case string: return len(x), ArErr{} - case ArMap: - return len(x), ArErr{} + case ArObject: + if x.TYPE == "array" { + return len(x.obj["__value__"].([]any)), ArErr{} + } + return len(x.obj), ArErr{} } return nil, ArErr{TYPE: "TypeError", message: "Cannot get length of " + typeof(a[0]), EXISTS: true} }} - vars["map"] = builtinFunc{"map", func(a ...any) (any, ArErr) { + vars.obj["map"] = builtinFunc{"map", func(a ...any) (any, ArErr) { if len(a) == 0 { - return ArMap{}, ArErr{} + return Map(anymap{}), ArErr{} } switch x := a[0].(type) { - case ArMap: + case ArObject: + if x.TYPE == "array" { + newmap := anymap{} + for i, v := range x.obj["__value__"].([]any) { + switch y := v.(type) { + case []any: + if len(y) == 2 { + if isUnhashable(y[0]) { + return nil, ArErr{TYPE: "TypeError", message: "Cannot use unhashable value as key: " + typeof(y[0]), EXISTS: true} + } + newmap[y[0]] = y[1] + continue + } + } + newmap[i] = v + } + return Map(newmap), ArErr{} + } return x, ArErr{} case string: - newmap := ArMap{} + newmap := anymap{} for i, v := range x { newmap[i] = string(v) } - return newmap, ArErr{} - case ArArray: - newmap := ArMap{} - for i, v := range x { - switch y := v.(type) { - case ArArray: - if len(y) == 2 { - if isUnhashable(y[0]) { - return nil, ArErr{TYPE: "TypeError", message: "Cannot use unhashable value as key: " + typeof(y[0]), EXISTS: true} - } - newmap[y[0]] = y[1] - continue - } - } - newmap[i] = v - } - return newmap, ArErr{} + return Map(newmap), ArErr{} } return nil, ArErr{TYPE: "TypeError", message: "Cannot create map from '" + typeof(a[0]) + "'", EXISTS: true} }} - vars["array"] = builtinFunc{"array", func(a ...any) (any, ArErr) { + vars.obj["array"] = builtinFunc{"array", func(a ...any) (any, ArErr) { if len(a) == 0 { - return ArArray{}, ArErr{} + return ArArray([]any{}), ArErr{} } switch x := a[0].(type) { - case ArArray: - return x, ArErr{} case string: - newarray := ArArray{} + newarray := []any{} for _, v := range x { newarray = append(newarray, string(v)) } - return newarray, ArErr{} - case ArMap: - newarray := ArArray{} - for key, val := range x { - newarray = append(newarray, ArArray{key, val}) + return ArArray(newarray), ArErr{} + case ArObject: + if x.TYPE == "array" { + return x, ArErr{} } - return newarray, ArErr{} + newarray := []any{} + for key, val := range x.obj { + newarray = append(newarray, []any{key, val}) + } + return ArArray(newarray), ArErr{} } return nil, ArErr{TYPE: "TypeError", message: "Cannot create array from '" + typeof(a[0]) + "'", EXISTS: true} }} - vars["boolean"] = builtinFunc{"boolean", func(a ...any) (any, ArErr) { + vars.obj["boolean"] = builtinFunc{"boolean", func(a ...any) (any, ArErr) { if len(a) == 0 { return false, ArErr{} } return anyToBool(a[0]), ArErr{} }} - vars["time"] = ArTime - vars["PI"] = PI - vars["π"] = PI - vars["e"] = e - vars["ln"] = builtinFunc{"ln", ArgonLn} - vars["log"] = builtinFunc{"log", ArgonLog} - vars["logN"] = builtinFunc{"logN", ArgonLogN} - vars["thread"] = builtinFunc{"thread", ArThread} - vars["round"] = builtinFunc{"round", func(a ...any) (any, ArErr) { + vars.obj["time"] = ArTime + vars.obj["PI"] = PI + vars.obj["π"] = PI + vars.obj["e"] = e + vars.obj["ln"] = builtinFunc{"ln", ArgonLn} + vars.obj["log"] = builtinFunc{"log", ArgonLog} + vars.obj["logN"] = builtinFunc{"logN", ArgonLogN} + vars.obj["thread"] = builtinFunc{"thread", ArThread} + vars.obj["round"] = builtinFunc{"round", func(a ...any) (any, ArErr) { if len(a) == 0 { return nil, ArErr{TYPE: "round", message: "round takes 1 argument", EXISTS: true} @@ -115,7 +115,7 @@ func init() { } return nil, ArErr{TYPE: "TypeError", message: "Cannot round '" + typeof(a[0]) + "'", EXISTS: true} }} - vars["floor"] = builtinFunc{"floor", func(a ...any) (any, ArErr) { + vars.obj["floor"] = builtinFunc{"floor", func(a ...any) (any, ArErr) { if len(a) == 0 { return nil, ArErr{TYPE: "floor", message: "floor takes 1 argument", EXISTS: true} @@ -126,7 +126,7 @@ func init() { } return nil, ArErr{TYPE: "TypeError", message: "Cannot floor '" + typeof(a[0]) + "'", EXISTS: true} }} - vars["ceil"] = builtinFunc{"ceil", func(a ...any) (any, ArErr) { + vars.obj["ceil"] = builtinFunc{"ceil", func(a ...any) (any, ArErr) { if len(a) == 0 { return nil, ArErr{TYPE: "ceil", message: "ceil takes 1 argument", EXISTS: true} @@ -138,36 +138,10 @@ func init() { } return nil, ArErr{TYPE: "TypeError", message: "Cannot ceil '" + typeof(a[0]) + "'", EXISTS: true} }} - vars["append"] = builtinFunc{"append", func(a ...any) (any, ArErr) { - if len(a) != 2 { - return nil, ArErr{TYPE: "append", message: "append takes 2 arguments, got " + fmt.Sprint(len(a)), - EXISTS: true} - } - switch x := a[0].(type) { - case ArArray: - return append(x, a[1]), ArErr{} - case string: - if typeof(a[1]) != "string" { - return nil, ArErr{TYPE: "TypeError", message: "Cannot append '" + typeof(a[1]) + "' to string", EXISTS: true} - } - return strings.Join([]string{x, a[1].(string)}, ""), ArErr{} - case ArMap: - if typeof(a[1]) != "array" { - return nil, ArErr{TYPE: "TypeError", message: "Cannot append '" + typeof(a[1]) + "' to map", EXISTS: true} - } - y := a[1].(ArArray) - if len(y) != 2 { - return nil, ArErr{TYPE: "TypeError", message: "Cannot append '" + typeof(a[1]) + "' to map", EXISTS: true} - } - x[y[0]] = y[1] - return x, ArErr{} - } - return nil, ArErr{TYPE: "TypeError", message: "Cannot append to '" + typeof(a[0]) + "'", EXISTS: true} - }} - vars["sqrt"] = builtinFunc{"sqrt", ArgonSqrt} - vars["file"] = ArFile - vars["random"] = ArRandom - vars["json"] = ArJSON - vars["sin"] = ArSin - vars["arcsin"] = ArArcsin + vars.obj["sqrt"] = builtinFunc{"sqrt", ArgonSqrt} + vars.obj["file"] = ArFile + vars.obj["random"] = ArRandom + vars.obj["json"] = ArJSON + vars.obj["sin"] = ArSin + vars.obj["arcsin"] = ArArcsin } diff --git a/src/call.go b/src/call.go index 1f8a715..b0609e4 100644 --- a/src/call.go +++ b/src/call.go @@ -15,6 +15,14 @@ type call struct { path string } +type Callable struct { + params []string + run any + code string + stack stack + line int +} + func isCall(code UNPARSEcode) bool { return callCompile.MatchString(code.code) } @@ -30,7 +38,7 @@ func parseCall(code UNPARSEcode, index int, codelines []UNPARSEcode) (any, bool, for i := 1; i < len(splitby); i++ { name := strings.Join(splitby[0:i], "(") argstr := strings.Join(splitby[i:], "(") - args, success, argserr := getValuesFromLetter(argstr, ",", index, codelines, true) + args, success, argserr := getValuesFromLetter(argstr, ",", index, codelines, false) arguments = args if !success { if i == len(splitby)-1 { @@ -68,13 +76,13 @@ func runCall(c call, stack stack, stacklevel int) (any, ArErr) { return nil, err } switch x := callable_.(type) { - case ArMap: - callable_ = x["__call__"] + case ArObject: + callable_ = x.obj["__call__"] } callable = callable_ } args := []any{} - level := append(stack, scope{}) + level := append(stack, newscope()) for _, arg := range c.args { resp, err := runVal(arg, level, stacklevel+1) if err.EXISTS { @@ -101,9 +109,9 @@ func runCall(c call, stack stack, stacklevel int) (any, ArErr) { if len(x.params) != len(args) { return nil, ArErr{"Runtime Error", "expected " + fmt.Sprint(len(x.params)) + " arguments, got " + fmt.Sprint(len(args)), c.line, c.path, c.code, true} } - level := scope{} + level := newscope() for i, param := range x.params { - level[param] = args[i] + level.obj[param] = args[i] } resp, err := runVal(x.run, append(x.stack, level), stacklevel+1) return ThrowOnNonLoop(openReturn(resp), err) diff --git a/src/callable.go b/src/callable.go deleted file mode 100644 index 7076abe..0000000 --- a/src/callable.go +++ /dev/null @@ -1,9 +0,0 @@ -package main - -type Callable struct { - params []string - run any - code string - stack stack - line int -} diff --git a/src/dowraps.go b/src/dowraps.go index 5ca9edf..a0c4d3e 100644 --- a/src/dowraps.go +++ b/src/dowraps.go @@ -49,7 +49,7 @@ func parseDoWrap(code UNPARSEcode, index int, codelines []UNPARSEcode) (any, boo } func runDoWrap(d dowrap, stack stack, stacklevel int) (any, ArErr) { - newstack := append(stack, scope{}) + newstack := append(stack, newscope()) for _, v := range d.run { val, err := runVal(v, newstack, stacklevel+1) if err.EXISTS { diff --git a/src/file.go b/src/file.go index 8085992..b69ef2f 100644 --- a/src/file.go +++ b/src/file.go @@ -7,10 +7,10 @@ import ( "os" ) -var ArFile = ArMap{ +var ArFile = Map(anymap{ "read": builtinFunc{"read", ArRead}, "write": builtinFunc{"write", ArWrite}, -} +}) func readtext(file *os.File) (string, error) { var buf bytes.Buffer @@ -23,68 +23,68 @@ func readtext(file *os.File) (string, error) { func ArRead(args ...any) (any, ArErr) { if len(args) != 1 { - return ArMap{}, ArErr{TYPE: "Runtime Error", message: "read takes 1 argument, got " + fmt.Sprint(len(args)), EXISTS: true} + return ArObject{}, ArErr{TYPE: "Runtime Error", message: "read takes 1 argument, got " + fmt.Sprint(len(args)), EXISTS: true} } if typeof(args[0]) != "string" { - return ArMap{}, ArErr{TYPE: "Runtime Error", message: "read takes a string not type '" + typeof(args[0]) + "'", EXISTS: true} + return ArObject{}, ArErr{TYPE: "Runtime Error", message: "read takes a string not type '" + typeof(args[0]) + "'", EXISTS: true} } filename := args[0].(string) file, err := os.Open(filename) if err != nil { - return ArMap{}, ArErr{TYPE: "Runtime Error", message: err.Error(), EXISTS: true} + return ArObject{}, ArErr{TYPE: "Runtime Error", message: err.Error(), EXISTS: true} } - return ArMap{ + return Map(anymap{ "text": builtinFunc{"text", func(...any) (any, ArErr) { text, err := readtext(file) if err != nil { - return ArMap{}, ArErr{TYPE: "Runtime Error", message: err.Error(), EXISTS: true} + return ArObject{}, ArErr{TYPE: "Runtime Error", message: err.Error(), EXISTS: true} } return text, ArErr{} }}, "json": builtinFunc{"json", func(...any) (any, ArErr) { text, err := readtext(file) if err != nil { - return ArMap{}, ArErr{TYPE: "Runtime Error", message: err.Error(), EXISTS: true} + return ArObject{}, ArErr{TYPE: "Runtime Error", message: err.Error(), EXISTS: true} } return jsonparse(text), ArErr{} }}, - }, ArErr{} + }), ArErr{} } func ArWrite(args ...any) (any, ArErr) { if len(args) != 1 { - return ArMap{}, ArErr{TYPE: "Runtime Error", message: "write takes 1 argument, got " + fmt.Sprint(len(args)), EXISTS: true} + return ArObject{}, ArErr{TYPE: "Runtime Error", message: "write takes 1 argument, got " + fmt.Sprint(len(args)), EXISTS: true} } if typeof(args[0]) != "string" { - return ArMap{}, ArErr{TYPE: "Runtime Error", message: "write takes a string not type '" + typeof(args[0]) + "'", EXISTS: true} + return ArObject{}, ArErr{TYPE: "Runtime Error", message: "write takes a string not type '" + typeof(args[0]) + "'", EXISTS: true} } filename := args[0].(string) file, err := os.Create(filename) if err != nil { - return ArMap{}, ArErr{TYPE: "Runtime Error", message: err.Error(), EXISTS: true} + return ArObject{}, ArErr{TYPE: "Runtime Error", message: err.Error(), EXISTS: true} } - return ArMap{ + return Map(anymap{ "text": builtinFunc{"text", func(args ...any) (any, ArErr) { if len(args) != 1 { - return ArMap{}, ArErr{TYPE: "Runtime Error", message: "text takes 1 argument, got " + fmt.Sprint(len(args)), EXISTS: true} + return ArObject{}, ArErr{TYPE: "Runtime Error", message: "text takes 1 argument, got " + fmt.Sprint(len(args)), EXISTS: true} } if typeof(args[0]) != "string" { - return ArMap{}, ArErr{TYPE: "Runtime Error", message: "text takes a string not type '" + typeof(args[0]) + "'", EXISTS: true} + return ArObject{}, ArErr{TYPE: "Runtime Error", message: "text takes a string not type '" + typeof(args[0]) + "'", EXISTS: true} } file.Write([]byte(args[0].(string))) return nil, ArErr{} }}, "json": builtinFunc{"json", func(args ...any) (any, ArErr) { if len(args) != 1 { - return ArMap{}, ArErr{TYPE: "Runtime Error", message: "json takes 1 argument, got " + fmt.Sprint(len(args)), EXISTS: true} + return ArObject{}, ArErr{TYPE: "Runtime Error", message: "json takes 1 argument, got " + fmt.Sprint(len(args)), EXISTS: true} } jsonstr, err := jsonstringify(args[0], 0) if err != nil { - return ArMap{}, ArErr{TYPE: "Runtime Error", message: err.Error(), EXISTS: true} + return ArObject{}, ArErr{TYPE: "Runtime Error", message: err.Error(), EXISTS: true} } file.Write([]byte(jsonstr)) return nil, ArErr{} }}, - }, ArErr{} + }), ArErr{} } diff --git a/src/getIndex.go b/src/getIndex.go index 63a12d5..df9f3bf 100644 --- a/src/getIndex.go +++ b/src/getIndex.go @@ -5,15 +5,19 @@ import ( "strings" ) -type ArMap = map[any]any -type ArArray = []any +type ArObject struct { + TYPE string + obj anymap +} + +type anymap map[any]any var mapGetCompile = makeRegex(`(.|\n)+\.([a-zA-Z_]|(\p{L}\p{M}*))([a-zA-Z0-9_]|(\p{L}\p{M}*))*( *)`) var indexGetCompile = makeRegex(`(.|\n)+\[(.|\n)+\]( *)`) type ArMapGet struct { VAL any - args ArArray + args []any index bool line int code string @@ -26,7 +30,14 @@ func mapGet(r ArMapGet, stack stack, stacklevel int) (any, ArErr) { return nil, err } switch m := resp.(type) { - case ArMap: + case ArObject: + switch m.TYPE { + case "array": + resp, err := getFromArArray(m, r, stack, stacklevel+1) + if !err.EXISTS { + return resp, err + } + } if len(r.args) > 1 { return nil, ArErr{ "IndexError", @@ -51,7 +62,7 @@ func mapGet(r ArMapGet, stack stack, stacklevel int) (any, ArErr) { true, } } - if _, ok := m[key]; !ok { + if _, ok := m.obj[key]; !ok { return nil, ArErr{ "KeyError", "key '" + fmt.Sprint(key) + "' not found", @@ -61,12 +72,15 @@ func mapGet(r ArMapGet, stack stack, stacklevel int) (any, ArErr) { true, } } - return m[key], ArErr{} - - case ArArray: - return getFromArArray(m, r, stack, stacklevel) + return potentialAnyArrayToArArray(m.obj[key]), ArErr{} case string: - return getFromString(m, r, stack, stacklevel) + if val, ok := r.args[0].(string); !r.index && ok { + switch val { + case "length": + return len(m), ArErr{} + } + } + return getFromString(m, r, stack, stacklevel+1) } key, err := runVal(r.args[0], stack, stacklevel+1) @@ -84,9 +98,9 @@ func mapGet(r ArMapGet, stack stack, stacklevel int) (any, ArErr) { } func classVal(r any) any { - if _, ok := r.(ArMap); ok { - if _, ok := r.(ArMap)["__value__"]; ok { - return r.(ArMap)["__value__"] + if j, ok := r.(ArObject); ok { + if _, ok := j.obj["__value__"]; ok { + return j.obj["__value__"] } } return r @@ -105,7 +119,7 @@ func mapGetParse(code UNPARSEcode, index int, codelines []UNPARSEcode) (ArMapGet if !worked { return ArMapGet{}, false, err, i } - return ArMapGet{resp, ArArray{key}, false, code.line, code.realcode, code.path}, true, ArErr{}, 1 + return ArMapGet{resp, []any{key}, false, code.line, code.realcode, code.path}, true, ArErr{}, 1 } func isIndexGet(code UNPARSEcode) bool { @@ -126,7 +140,6 @@ func indexGetParse(code UNPARSEcode, index int, codelines []UNPARSEcode) (ArMapG } continue } - fmt.Println(args) if len(args) > 3 { return ArMapGet{}, false, ArErr{ "SyntaxError", @@ -161,7 +174,7 @@ func isUnhashable(val any) bool { return keytype == "array" || keytype == "map" } -func getFromArArray(m []any, r ArMapGet, stack stack, stacklevel int) (ArArray, ArErr) { +func getFromArArray(m ArObject, r ArMapGet, stack stack, stacklevel int) (ArObject, ArErr) { var ( start int = 0 end any = nil @@ -170,12 +183,12 @@ func getFromArArray(m []any, r ArMapGet, stack stack, stacklevel int) (ArArray, { startval, err := runVal(r.args[0], stack, stacklevel+1) if err.EXISTS { - return nil, err + return ArObject{}, err } if startval == nil { start = 0 - } else if typeof(startval) != "number" && !startval.(number).IsInt() { - return nil, ArErr{ + } else if typeof(startval) != "number" || !startval.(number).IsInt() { + return ArObject{}, ArErr{ "TypeError", "slice index must be an integer", r.line, @@ -190,12 +203,12 @@ func getFromArArray(m []any, r ArMapGet, stack stack, stacklevel int) (ArArray, if len(r.args) > 1 { endval, err := runVal(r.args[1], stack, stacklevel+1) if err.EXISTS { - return nil, err + return ArObject{}, err } if endval == nil { - end = len(m) + end = m.obj["length"] } else if typeof(endval) != "number" && !endval.(number).IsInt() { - return nil, ArErr{ + return ArObject{}, ArErr{ "TypeError", "slice ending index must be an integer", r.line, @@ -210,12 +223,12 @@ func getFromArArray(m []any, r ArMapGet, stack stack, stacklevel int) (ArArray, if len(r.args) > 2 { stepval, err := runVal(r.args[2], stack, stacklevel+1) if err.EXISTS { - return nil, err + return ArObject{}, err } if stepval == nil { step = 1 } else if typeof(stepval) != "number" && !stepval.(number).IsInt() { - return nil, ArErr{ + return ArObject{}, ArErr{ "TypeError", "slice step must be an integer", r.line, @@ -228,29 +241,27 @@ func getFromArArray(m []any, r ArMapGet, stack stack, stacklevel int) (ArArray, } } if start < 0 { - start = len(m) + start + start = m.obj["length"].(int) + start } if _, ok := end.(int); ok && end.(int) < 0 { - end = len(m) + end.(int) + end = m.obj["length"].(int) + end.(int) } - - fmt.Println(start, end, step) if end == nil { - return ArArray{m[start]}, ArErr{} + return ArArray([]any{m.obj["__value__"].([]any)[start]}), ArErr{} } else if step == 1 { - return m[start:end.(int)], ArErr{} + return ArArray([]any{m.obj["__value__"].([]any)[start:end.(int)]}), ArErr{} } else { - output := ArArray{} + output := []any{} if step > 0 { for i := start; i < end.(int); i += step { - output = append(output, m[i]) + output = append(output, m.obj["__value__"].([]any)[i]) } } else { for i := end.(int) - 1; i >= start; i += step { - output = append(output, m[i]) + output = append(output, m.obj["__value__"].([]any)[i]) } } - return (output), ArErr{} + return ArArray(output), ArErr{} } } @@ -267,7 +278,7 @@ func getFromString(m string, r ArMapGet, stack stack, stacklevel int) (string, A } if startval == nil { start = 0 - } else if typeof(startval) != "number" && !startval.(number).IsInt() { + } else if typeof(startval) != "number" || !startval.(number).IsInt() { return "", ArErr{ "TypeError", "slice index must be an integer", @@ -326,8 +337,6 @@ func getFromString(m string, r ArMapGet, stack stack, stacklevel int) (string, A if _, ok := end.(int); ok && end.(int) < 0 { end = len(m) + end.(int) } - - fmt.Println(start, end, step) if end == nil { return string(m[start]), ArErr{} } else if step == 1 { diff --git a/src/ifstatement.go b/src/ifstatement.go index 0f2ae9e..308077f 100644 --- a/src/ifstatement.go +++ b/src/ifstatement.go @@ -118,7 +118,7 @@ func parseIfStatement(code UNPARSEcode, index int, codeline []UNPARSEcode) (ifst func runIfStatement(code ifstatement, stack stack, stacklevel int) (any, ArErr) { for _, condition := range code.conditions { - newstack := append(stack, scope{}) + newstack := append(stack, newscope()) resp, err := runVal(condition.condition, newstack, stacklevel+1) if err.EXISTS { return nil, err @@ -128,7 +128,7 @@ func runIfStatement(code ifstatement, stack stack, stacklevel int) (any, ArErr) } } if code.ELSE != nil { - return runVal(code.ELSE, append(stack, scope{}), stacklevel+1) + return runVal(code.ELSE, append(stack, newscope()), stacklevel+1) } return nil, ArErr{} } diff --git a/src/import.go b/src/import.go index 09a163e..803fbbc 100644 --- a/src/import.go +++ b/src/import.go @@ -8,6 +8,9 @@ import ( "path/filepath" ) +var imported = make(map[string]ArObject) +var importing = make(map[string]bool) + func FileExists(filename string) bool { if _, err := os.Stat(filename); err == nil { return true @@ -44,7 +47,7 @@ func readFile(path string) []UNPARSEcode { return output } -func importMod(realpath string, origin string) (scope, ArErr) { +func importMod(realpath string, origin string, main bool) (ArObject, ArErr) { extention := filepath.Ext(realpath) path := realpath if extention == "" { @@ -52,13 +55,13 @@ func importMod(realpath string, origin string) (scope, ArErr) { } ex, err := os.Getwd() if err != nil { - return nil, ArErr{"Import Error", err.Error(), 0, realpath, "", true} + return ArObject{}, ArErr{TYPE: "Import Error", message: "Could not get working directory", EXISTS: true} } - executable, err := os.Executable() + exc, err := os.Executable() if err != nil { - return nil, ArErr{"Import Error", err.Error(), 0, realpath, "", true} + return ArObject{}, ArErr{TYPE: "Import Error", message: "Could not get executable", EXISTS: true} } - executable = filepath.Dir(executable) + executable := filepath.Dir(exc) isABS := filepath.IsAbs(path) var pathsToTest []string if isABS { @@ -90,18 +93,52 @@ func importMod(realpath string, origin string) (scope, ArErr) { } if !found { - return nil, ArErr{"Import Error", "File does not exist: " + realpath, 0, realpath, "", true} + return ArObject{}, ArErr{TYPE: "Import Error", message: "File does not exist: " + realpath, EXISTS: true} + } else if importing[p] { + return ArObject{}, ArErr{TYPE: "Import Error", message: "Circular import: " + realpath, EXISTS: true} + } else if _, ok := imported[p]; ok { + return imported[p], ArErr{} } + importing[p] = true codelines := readFile(p) translated, translationerr := translate(codelines) if translationerr.EXISTS { - return nil, translationerr + return ArObject{}, translationerr } - global := scope{} - _, runimeErr, _ := run(translated, stack{vars, global}) + ArgsArArray := []any{} + for _, arg := range Args[1:] { + ArgsArArray = append(ArgsArArray, arg) + } + global := newscope() + localvars := Map(anymap{ + "program": Map(anymap{ + "args": ArArray(ArgsArArray), + "origin": origin, + "import": builtinFunc{"import", func(args ...any) (any, ArErr) { + if len(args) != 1 { + return nil, ArErr{"Import Error", "Invalid number of arguments", 0, realpath, "", true} + } + if _, ok := args[0].(string); !ok { + return nil, ArErr{"Import Error", "Invalid argument type", 0, realpath, "", true} + } + return importMod(args[0].(string), filepath.Dir(p), false) + }}, + "cwd": ex, + "exc": exc, + "file": Map(anymap{ + "name": filepath.Base(p), + "path": p, + }), + "main": main, + "scope": global, + }), + }) + _, runimeErr, _ := run(translated, stack{vars, localvars, global}) + importing[p] = false if runimeErr.EXISTS { - return nil, runimeErr + return ArObject{}, runimeErr } + imported[p] = global return global, ArErr{} } diff --git a/src/json.go b/src/json.go index 6c85664..d60c6c2 100644 --- a/src/json.go +++ b/src/json.go @@ -11,17 +11,13 @@ import ( func convertToArgon(obj any) any { switch x := obj.(type) { case map[string]interface{}: - newmap := ArMap{} + newmap := Map(anymap{}) for key, value := range x { - newmap[key] = convertToArgon(value) + newmap.obj[key] = convertToArgon(value) } return newmap - case ArArray: - newarray := ArArray{} - for _, value := range x { - newarray = append(newarray, convertToArgon(value)) - } - return newarray + case []any: + return ArArray(x) case string: return x case float64: @@ -47,8 +43,18 @@ func jsonstringify(obj any, level int) (string, error) { output := []string{} obj = classVal(obj) switch x := obj.(type) { - case ArMap: - for key, value := range x { + case ArObject: + if x.TYPE == "array" { + for _, value := range x.obj["__value__"].([]any) { + str, err := jsonstringify(value, level+1) + if err != nil { + return "", err + } + output = append(output, str) + } + return "[" + strings.Join(output, ", ") + "]", nil + } + for key, value := range x.obj { str, err := jsonstringify(value, level+1) if err != nil { return "", err @@ -56,7 +62,7 @@ func jsonstringify(obj any, level int) (string, error) { output = append(output, ""+strconv.Quote(anyToArgon(key, false, true, 3, 0, false, 0))+": "+str) } return "{" + strings.Join(output, ", ") + "}", nil - case ArArray: + case []any: for _, value := range x { str, err := jsonstringify(value, level+1) if err != nil { @@ -82,24 +88,24 @@ func jsonstringify(obj any, level int) (string, error) { return "", err } -var ArJSON = ArMap{ +var ArJSON = Map(anymap{ "parse": builtinFunc{"parse", func(args ...any) (any, ArErr) { if len(args) == 0 { - return ArMap{}, ArErr{TYPE: "Runtime Error", message: "parse takes 1 argument", EXISTS: true} + return nil, ArErr{TYPE: "Runtime Error", message: "parse takes 1 argument", EXISTS: true} } if typeof(args[0]) != "string" { - return ArMap{}, ArErr{TYPE: "Runtime Error", message: "parse takes a string not a '" + typeof(args[0]) + "'", EXISTS: true} + return nil, ArErr{TYPE: "Runtime Error", message: "parse takes a string not a '" + typeof(args[0]) + "'", EXISTS: true} } return jsonparse(args[0].(string)), ArErr{} }}, "stringify": builtinFunc{"stringify", func(args ...any) (any, ArErr) { if len(args) == 0 { - return ArMap{}, ArErr{TYPE: "Runtime Error", message: "stringify takes 1 argument", EXISTS: true} + return nil, ArErr{TYPE: "Runtime Error", message: "stringify takes 1 argument", EXISTS: true} } str, err := jsonstringify(args[0], 0) if err != nil { - return ArMap{}, ArErr{TYPE: "Runtime Error", message: err.Error(), EXISTS: true} + return nil, ArErr{TYPE: "Runtime Error", message: err.Error(), EXISTS: true} } return str, ArErr{} }}, -} +}) diff --git a/src/main.go b/src/main.go index 3778238..1084ea9 100644 --- a/src/main.go +++ b/src/main.go @@ -7,8 +7,14 @@ import ( // args without the program path var Args = os.Args[1:] -type scope = ArMap -type stack = []scope +type stack = []ArObject + +func newscope() ArObject { + return ArObject{ + TYPE: "map", + obj: make(anymap), + } +} func main() { ex, e := os.Getwd() @@ -19,7 +25,7 @@ func main() { shell() os.Exit(0) } - _, err := importMod(Args[0], ex) + _, err := importMod(Args[0], ex, true) if err.EXISTS { panicErr(err) os.Exit(1) diff --git a/src/map.go b/src/map.go new file mode 100644 index 0000000..1992345 --- /dev/null +++ b/src/map.go @@ -0,0 +1,8 @@ +package main + +func Map(val anymap) ArObject { + return ArObject{ + TYPE: "map", + obj: val, + } +} diff --git a/src/parseImport.go b/src/parseImport.go index ec05c07..0b6f26a 100644 --- a/src/parseImport.go +++ b/src/parseImport.go @@ -56,21 +56,30 @@ func runImport(importOBJ ArImport, stack stack, stacklevel int) (any, ArErr) { if e != nil { return nil, ArErr{"File Error", "could not get current working directory", importOBJ.line, importOBJ.path, importOBJ.code, true} } - stackMap, err := importMod(path, ex) + stackMap, err := importMod(path, ex, false) if err.EXISTS { + if err.line == 0 { + err.line = importOBJ.line + } + if err.path == "" { + err.path = importOBJ.path + } + if err.code == "" { + err.code = importOBJ.code + } return nil, err } switch x := importOBJ.values.(type) { case []string: for _, v := range x { - val, ok := stackMap[v] + val, ok := stackMap.obj[v] if !ok { return nil, ArErr{"Import Error", "could not find value " + anyToArgon(v, true, false, 3, 0, false, 0) + " in module " + anyToArgon(path, true, false, 3, 0, false, 0), importOBJ.line, importOBJ.path, importOBJ.code, true} } - stack[len(stack)-1][v] = val + stack[len(stack)-1].obj[v] = val } case string: - stack[len(stack)-1][x] = stackMap + stack[len(stack)-1].obj[x] = stackMap } return nil, ArErr{} } diff --git a/src/random.go b/src/random.go index 05b27ec..54ba68a 100644 --- a/src/random.go +++ b/src/random.go @@ -49,7 +49,7 @@ func randomRange(args ...any) (any, ArErr) { return rand, ArErr{} } -var ArRandom = ArMap{ +var ArRandom = Map(anymap{ "__call__": builtinFunc{"random", func(args ...any) (any, ArErr) { if len(args) != 0 { return nil, ArErr{ @@ -68,7 +68,7 @@ var ArRandom = ArMap{ return round(resp.(number), 0), ArErr{} }}, "range": builtinFunc{"range", randomRange}, -} +}) func init() { rand.Seed( diff --git a/src/shell.go b/src/shell.go index 024dfb0..dac69b8 100644 --- a/src/shell.go +++ b/src/shell.go @@ -7,7 +7,7 @@ import ( ) func shell() { - global := stack{vars, scope{}} + global := stack{vars, newscope()} c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt) go func() { diff --git a/src/term-class.go b/src/term-class.go index e36a9c5..f2c02bb 100644 --- a/src/term-class.go +++ b/src/term-class.go @@ -5,9 +5,9 @@ import ( "time" ) -var timing = ArMap{} +var timing = anymap{} -var plain = ArMap{ +var plain = Map(anymap{ "log": builtinFunc{"log", func(args ...any) (any, ArErr) { output := []any{} for i := 0; i < len(args); i++ { @@ -32,9 +32,9 @@ var plain = ArMap{ fmt.Println(output...) return nil, ArErr{} }}, -} +}) -var ArTerm = ArMap{ +var ArTerm = Map(anymap{ "log": builtinFunc{"log", func(args ...any) (any, ArErr) { output := []any{} for i := 0; i < len(args); i++ { @@ -103,4 +103,4 @@ var ArTerm = ArMap{ fmt.Printf("\x1b[%dm%s\x1b[0m", 34, fmt.Sprint(anyToArgon(id, false, true, 3, 0, false, 0), ": ", timesince)+"\n") return nil, ArErr{} }}, -} +}) diff --git a/src/thread.go b/src/thread.go index 3681772..df13d6b 100644 --- a/src/thread.go +++ b/src/thread.go @@ -17,11 +17,11 @@ func ArThread(args ...any) (any, ArErr) { } var resp any var err ArErr - currentscope := stack{vars, scope{}} + currentscope := stack{vars, newscope()} hasrun := false joined := false var wg sync.WaitGroup - threaMap := ArMap{ + threadMap := Map(anymap{ "start": builtinFunc{"start", func(args ...any) (any, ArErr) { if hasrun { return nil, ArErr{TYPE: "Runtime Error", message: "Cannot start a thread twice", EXISTS: true} @@ -44,6 +44,6 @@ func ArThread(args ...any) (any, ArErr) { wg.Wait() return resp, err }}, - } - return threaMap, ArErr{} + }) + return threadMap, ArErr{} } diff --git a/src/time.go b/src/time.go index 09048e0..2699bd0 100644 --- a/src/time.go +++ b/src/time.go @@ -6,8 +6,8 @@ import ( var MicroSeconds = newNumber().SetInt64(1000000) -func ArTimeClass(N time.Time) ArMap { - return ArMap{ +func ArTimeClass(N time.Time) ArObject { + return Map(anymap{ "__value__": newNumber().Quo(newNumber().SetInt64(N.UnixMicro()), MicroSeconds), "year": builtinFunc{ "year", @@ -96,10 +96,10 @@ func ArTimeClass(N time.Time) ArMap { return N.Format(a[0].(string)), ArErr{} }, }, - } + }) } -var ArTime = map[any]any{ +var ArTime = Map(anymap{ "snooze": builtinFunc{"snooze", func(a ...any) (any, ArErr) { if len(a) > 0 { float, _ := a[0].(number).Float64() @@ -212,4 +212,4 @@ var ArTime = map[any]any{ } }, }, -} +}) diff --git a/src/to-argon.go b/src/to-argon.go index e7492eb..0feb80c 100644 --- a/src/to-argon.go +++ b/src/to-argon.go @@ -72,17 +72,17 @@ func anyToArgon(x any, quote bool, simplify bool, depth int, indent int, colored if colored { output = append(output, "\x1b[0m") } - case ArMap: - if _, ok := x["__value__"]; ok { - return anyToArgon(x["__value__"], quote, simplify, depth, indent, colored, plain) + case ArObject: + if x.TYPE == "array" { + return anyToArgon(x.obj["__value__"], quote, simplify, depth, indent, colored, plain) } - if len(x) == 0 { + if len(x.obj) == 0 { return "{}" } - keys := make([]any, len(x)) + keys := make([]any, len(x.obj)) i := 0 - for k := range x { + for k := range x.obj { keys[i] = k i++ } @@ -103,13 +103,11 @@ func anyToArgon(x any, quote bool, simplify bool, depth int, indent int, colored } keyval = strings.Join(outputkeyval, "") } - output = append(output, keyval+": "+anyToArgon(x[key], true, true, depth-1, indent+1, colored, plain)) + output = append(output, keyval+": "+anyToArgon(x.obj[key], true, true, depth-1, indent+1, colored, plain)) } return "{" + maybenewline + (strings.Repeat(" ", (indent+1)*plain)) + strings.Join(output, ","+maybenewline+(strings.Repeat(" ", (indent+1)*plain))) + maybenewline + (strings.Repeat(" ", indent*plain)) + "}" - case ArArray: - if len(x) == 0 { - return "[]" - } + case []any: + singleline := len(x) <= 3 output := []string{} if simplify && len(x) >= 100 { for i := 0; i < 10; i++ { @@ -128,9 +126,17 @@ func anyToArgon(x any, quote bool, simplify bool, depth int, indent int, colored } else { for i := 0; i < len(x); i++ { item := x[i] - output = append(output, anyToArgon(item, true, true, depth-1, indent+1, colored, plain)) + converted := anyToArgon(item, true, true, depth-1, indent+1, colored, plain) + if singleline && strings.Contains(converted, "\n") { + singleline = false + } + output = append(output, converted) } } + + if singleline { + return "[" + strings.Join(output, ", ") + "]" + } return "[" + maybenewline + (strings.Repeat(" ", (indent+1)*plain)) + strings.Join(output, ","+maybenewline+(strings.Repeat(" ", (indent+1)*plain))) + maybenewline + (strings.Repeat(" ", indent*plain)) + "]" case builtinFunc: if colored { diff --git a/src/typeof.go b/src/typeof.go index c53ae45..ea5be47 100644 --- a/src/typeof.go +++ b/src/typeof.go @@ -1,7 +1,7 @@ package main func typeof(val any) string { - switch val.(type) { + switch x := val.(type) { case number: return "number" case string: @@ -14,10 +14,11 @@ func typeof(val any) string { return "function" case builtinFunc: return "function" - case ArMap: + case ArObject: + if x.TYPE == "array" { + return "array" + } return "map" - case ArArray: - return "array" case accessVariable: return "variable" } diff --git a/src/variable.go b/src/variable.go index 73235ed..45d61f8 100644 --- a/src/variable.go +++ b/src/variable.go @@ -78,7 +78,7 @@ func parseVariable(code UNPARSEcode) (accessVariable, bool, ArErr, int) { func readVariable(v accessVariable, stack stack) (any, ArErr) { for i := len(stack) - 1; i >= 0; i-- { varMutex.RLock() - val, ok := stack[i][v.name] + val, ok := stack[i].obj[v.name] varMutex.RUnlock() if ok { return val, ArErr{} @@ -208,30 +208,30 @@ func setVariableValue(v setVariable, stack stack, stacklevel int) (any, ArErr) { if v.TYPE == "let" { varMutex.RLock() - _, ok := stack[len(stack)-1][v.toset.(accessVariable).name] + _, ok := stack[len(stack)-1].obj[v.toset.(accessVariable).name] varMutex.RUnlock() if ok { return nil, ArErr{"Runtime Error", "variable \"" + v.toset.(accessVariable).name + "\" already exists", v.line, v.path, v.code, true} } varMutex.Lock() - stack[len(stack)-1][v.toset.(accessVariable).name] = resp + stack[len(stack)-1].obj[v.toset.(accessVariable).name] = resp varMutex.Unlock() } else { switch x := v.toset.(type) { case accessVariable: for i := len(stack) - 1; i >= 0; i-- { varMutex.RLock() - _, ok := stack[i][x.name] + _, ok := stack[i].obj[x.name] varMutex.RUnlock() if ok { varMutex.Lock() - stack[i][x.name] = resp + stack[i].obj[x.name] = resp varMutex.Unlock() return ThrowOnNonLoop(resp, ArErr{}) } } varMutex.Lock() - stack[len(stack)-1][x.name] = resp + stack[len(stack)-1].obj[x.name] = resp varMutex.Unlock() case ArMapGet: respp, err := runVal(x.VAL, stack, stacklevel+1) @@ -246,12 +246,12 @@ func setVariableValue(v setVariable, stack stack, stacklevel int) (any, ArErr) { return nil, err } switch y := respp.(type) { - case ArMap: + case ArObject: if isUnhashable(key) { return nil, ArErr{"Runtime Error", "can't use unhashable type as map key: " + typeof(key), v.line, v.path, v.code, true} } varMutex.Lock() - y[key] = resp + y.obj[key] = resp varMutex.Unlock() default: return nil, ArErr{"Runtime Error", "can't set for non map", v.line, v.path, v.code, true} @@ -285,8 +285,8 @@ func runDelete(d ArDelete, stack stack, stacklevel int) (any, ArErr) { switch x := d.value.(type) { case accessVariable: for i := len(stack) - 1; i >= 0; i-- { - if _, ok := stack[i][x.name]; ok { - delete(stack[i], x.name) + if _, ok := stack[i].obj[x.name]; ok { + delete(stack[i].obj, x.name) return nil, ArErr{} } } @@ -304,8 +304,14 @@ func runDelete(d ArDelete, stack stack, stacklevel int) (any, ArErr) { return nil, err } switch y := respp.(type) { - case ArMap: - delete(y, key) + case ArObject: + if y.TYPE == "array" { + return nil, ArErr{"Runtime Error", "can't delete from array", d.line, d.path, d.code, true} + } + if isUnhashable(key) { + return nil, ArErr{"Runtime Error", "can't use unhashable type as map key: " + typeof(key), d.line, d.path, d.code, true} + } + delete(y.obj, key) default: return nil, ArErr{"Runtime Error", "can't delete for non map", d.line, d.path, d.code, true} }