diff --git a/.github/labeler.yml b/.github/labeler.yml index 7f3c20a50a..476153901a 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -1,6 +1,9 @@ 3rd-party: - src/extras/* +ast: + - src/vm/ast.nim + bytecode: - src/vm/bytecode.nim - src/vm/opcodes.nim diff --git a/.github/workflows/web.yml b/.github/workflows/web.yml index 98d9dd510f..984ea9779b 100644 --- a/.github/workflows/web.yml +++ b/.github/workflows/web.yml @@ -62,7 +62,7 @@ jobs: - name: Setup nim uses: jiro4989/setup-nim-action@v1 with: - nim-version: '1.6.6' + nim-version: 'stable' - name: Verify dependecies run: | diff --git a/build.nims b/build.nims index f3c6b6e0e9..d9707c29b3 100755 --- a/build.nims +++ b/build.nims @@ -55,7 +55,7 @@ let "arm" : "--cpu:arm", "arm64" : "--cpu:arm64 --gcc.path:/usr/bin --gcc.exe:aarch64-linux-gnu-gcc --gcc.linkerexe:aarch64-linux-gnu-gcc", "debug" : "-d:DEBUG --debugger:on --debuginfo --linedir:on", - "dev" : "--embedsrc:on -d:DEV --listCmd --verbosity:1 --hints:on --hint:ProcessingStmt:off --hint:XCannotRaiseY:off --warning:GcUnsafe:off --warning:ProveInit:off --warning:ProveField:off --warning:Uninit:off", + "dev" : "--embedsrc:on -d:DEV --listCmd", "docgen" : "-d:DOCGEN", "dontcompress" : "", "dontinstall" : "", @@ -98,7 +98,8 @@ var IS_DEV = false MODE = "" - FLAGS* = "--skipUserCfg:on --colors:off -d:danger " & + FLAGS* = "--verbosity:1 --hints:on --hint:ProcessingStmt:off --hint:XCannotRaiseY:off --warning:GcUnsafe:off --warning:ProveInit:off --warning:ProveField:off --warning:Uninit:off " & + "--skipUserCfg:on --colors:off -d:danger " & "--panics:off --mm:orc -d:useMalloc --checks:off " & "-d:ssl --cincludes:extras --opt:speed --nimcache:.cache " & "--path:src " diff --git a/examples/rosetta/abundant odd numbers.res b/examples/rosetta/abundant odd numbers.res new file mode 100644 index 0000000000..c3dc2d43f0 --- /dev/null +++ b/examples/rosetta/abundant odd numbers.res @@ -0,0 +1,28 @@ +the first 25 abundant odd numbers: +945 => sum: 1920 +1575 => sum: 3224 +2205 => sum: 4446 +2835 => sum: 5808 +3465 => sum: 7488 +4095 => sum: 8736 +4725 => sum: 9920 +5355 => sum: 11232 +5775 => sum: 11904 +5985 => sum: 12480 +6435 => sum: 13104 +6615 => sum: 13680 +6825 => sum: 13888 +7245 => sum: 14976 +7425 => sum: 14880 +7875 => sum: 16224 +8085 => sum: 16416 +8415 => sum: 16848 +8505 => sum: 17472 +8925 => sum: 17856 +9135 => sum: 18720 +9555 => sum: 19152 +9765 => sum: 19968 +10395 => sum: 23040 +11025 => sum: 22971 +the 1000th abundant odd number: 492975 => sum: 1012336 +the first abundant odd number greater than one billion (10^9): 1000000575 => sum: 2083561584 \ No newline at end of file diff --git a/examples/rosetta/alternade words.res b/examples/rosetta/alternade words.res new file mode 100644 index 0000000000..0f36c15828 --- /dev/null +++ b/examples/rosetta/alternade words.res @@ -0,0 +1,58 @@ +accost => cot acs +accuse => cue acs +afield => fed ail +agleam => gem ala +alcott => lot act +allele => lee all +allied => lid ale +alpert => let apr +ambient => min abet +annette => net ante +apport => pot apr +ariadne => ran aide +assist => sit ass +battle => ate btl +blaine => lie ban +brenda => rna bed +calliope => aloe clip +choose => hoe cos +choosy => hoy cos +claire => lie car +collude => old clue +effete => fee eft +fabric => arc fbi +fealty => ely fat +fluent => let fun +forwent => own fret +friend => red fin +george => ere gog +inroad => nod ira +israel => sal ire +jaunty => any jut +joanne => one jan +lounge => one lug +oriole => roe oil +oswald => sad owl +parrot => art pro +peoria => era poi +pierre => ire per +poodle => ode pol +pounce => one puc +racial => ail rca +realty => ely rat +sordid => odd sri +spatial => pta sail +sprain => pan sri +strain => tan sri +strait => tat sri +sturdy => try sud +sweaty => way set +tattle => ate ttl +theorem => hoe term +though => huh tog +throaty => hot tray +triode => roe tid +triune => rue tin +troupe => rue top +truant => rat tun +twirly => wry til \ No newline at end of file diff --git a/examples/rosetta/ascending primes.art b/examples/rosetta/ascending primes.art index 16113c9dc2..e4a7eacf1d 100644 --- a/examples/rosetta/ascending primes.art +++ b/examples/rosetta/ascending primes.art @@ -3,7 +3,7 @@ ascending?: function [x][ and? [equal? sort initial initial][equal? size initial size unique initial] ] -candidates: select (1..1456789) ++ [ +candidates: select (@1..1456789) ++ [ 12345678, 12345679, 12345689, 12345789, 12346789, 12356789, 12456789, 13456789, 23456789, 123456789 ] => prime? diff --git a/examples/rosetta/ascending primes.res b/examples/rosetta/ascending primes.res new file mode 100644 index 0000000000..78441f23e7 --- /dev/null +++ b/examples/rosetta/ascending primes.res @@ -0,0 +1,10 @@ + 2 3 5 7 13 17 19 23 29 37 + 47 59 67 79 89 127 137 139 149 157 + 167 179 239 257 269 347 349 359 367 379 + 389 457 467 479 569 1237 1249 1259 1279 1289 + 1367 1459 1489 1567 1579 1789 2347 2357 2389 2459 + 2467 2579 2689 2789 3457 3467 3469 4567 4679 4789 + 5689 12347 12379 12457 12479 12569 12589 12689 13457 13469 + 13567 13679 13789 15679 23459 23567 23689 23789 25679 34589 + 34679 123457 123479 124567 124679 125789 134789 145679 234589 235679 + 235789 245789 345679 345689 1234789 1235789 1245689 1456789 12356789 23456789 \ No newline at end of file diff --git a/examples/rosetta/brazilian numbers.res b/examples/rosetta/brazilian numbers.res new file mode 100644 index 0000000000..5e3383b85d --- /dev/null +++ b/examples/rosetta/brazilian numbers.res @@ -0,0 +1,8 @@ +First 20 brazilian numbers: +7 8 10 12 13 14 15 16 18 20 21 22 24 26 27 28 30 31 32 33 + +First 20 odd brazilian numbers: +7 13 15 21 27 31 33 35 39 43 45 51 55 57 63 65 69 73 75 77 + +First 20 prime brazilian numbers: +7 13 31 43 73 127 157 211 241 307 421 463 601 757 1093 1123 1483 1723 2551 2801 \ No newline at end of file diff --git a/examples/rosetta/change e letters to i in words.res b/examples/rosetta/change e letters to i in words.res new file mode 100644 index 0000000000..798cdd6d90 --- /dev/null +++ b/examples/rosetta/change e letters to i in words.res @@ -0,0 +1,26 @@ +analyses => analysis +atlantes => atlantis +bellow => billow +breton => briton +clench => clinch +convect => convict +crises => crisis +diagnoses => diagnosis +enfant => infant +enquiry => inquiry +frances => francis +galatea => galatia +harden => hardin +heckman => hickman +inequity => iniquity +inflect => inflict +jacobean => jacobian +marten => martin +module => moduli +pegging => pigging +psychoses => psychosis +rabbet => rabbit +sterling => stirling +synopses => synopsis +vector => victor +welles => willis \ No newline at end of file diff --git a/examples/rosetta/changeable words.res b/examples/rosetta/changeable words.res new file mode 100644 index 0000000000..e87c455755 --- /dev/null +++ b/examples/rosetta/changeable words.res @@ -0,0 +1,26 @@ +aristotelean - aristotelian +claustrophobia - claustrophobic +committeeman - committeemen +committeewoman - committeewomen +complementary - complimentary +confirmation - conformation +congresswoman - congresswomen +councilwoman - councilwomen +craftsperson - draftsperson +eavesdropped - eavesdropper +frontiersman - frontiersmen +handicraftsman - handicraftsmen +incommutable - incomputable +installation - instillation +kaleidescope - kaleidoscope +neuroanatomy - neuroanotomy +newspaperman - newspapermen +nonagenarian - nonogenarian +onomatopoeia - onomatopoeic +philanthrope - philanthropy +prescription - proscription +schizophrenia - schizophrenic +shakespearean - shakespearian +spectroscope - spectroscopy +underclassman - underclassmen +upperclassman - upperclassmen \ No newline at end of file diff --git a/examples/rosetta/circular primes.res b/examples/rosetta/circular primes.res new file mode 100644 index 0000000000..ca23cc6223 --- /dev/null +++ b/examples/rosetta/circular primes.res @@ -0,0 +1,19 @@ +2 +3 +5 +7 +11 +13 +17 +37 +79 +113 +197 +199 +337 +1193 +3779 +11939 +19937 +193939 +199933 \ No newline at end of file diff --git a/examples/rosetta/composite numbers k with no single digit factors whose factors are all substrings of k.art b/examples/rosetta/composite numbers k with no single digit factors whose factors are all substrings of k.art index ac20179b3b..29bc814f33 100644 --- a/examples/rosetta/composite numbers k with no single digit factors whose factors are all substrings of k.art +++ b/examples/rosetta/composite numbers k with no single digit factors whose factors are all substrings of k.art @@ -1,6 +1,5 @@ valid?: function [n][ - pf: factors.prime n - every? pf 'f -> + every? factors.prime n 'f -> and? [contains? to :string n to :string f] [1 <> size digits f] ] diff --git a/examples/rosetta/count the coins.art b/examples/rosetta/count the coins.art index a441acb51d..a14f63901a 100644 --- a/examples/rosetta/count the coins.art +++ b/examples/rosetta/count the coins.art @@ -4,7 +4,7 @@ changes: function [amount coins][ loop coins 'coin [ loop coin..amount 'j -> - set ways j (get ways j) + get ways j-coin + ways\[j]: ways\[j] + ways\[j-coin] ] ways\[amount] diff --git a/examples/rosetta/count the coins.res b/examples/rosetta/count the coins.res new file mode 100644 index 0000000000..58d4a97c1c --- /dev/null +++ b/examples/rosetta/count the coins.res @@ -0,0 +1,2 @@ +242 +13398445413854501 \ No newline at end of file diff --git a/examples/rosetta/descending primes.res b/examples/rosetta/descending primes.res new file mode 100644 index 0000000000..856e57ff79 --- /dev/null +++ b/examples/rosetta/descending primes.res @@ -0,0 +1,9 @@ + 2 3 5 7 31 41 43 53 61 71 + 73 83 97 421 431 521 541 631 641 643 + 653 743 751 761 821 853 863 941 953 971 + 983 5431 6421 6521 7321 7541 7621 7643 8431 8521 + 8543 8641 8731 8741 8753 8761 9421 9431 9521 9631 + 9643 9721 9743 9851 9871 75431 76421 76541 76543 86531 + 87421 87541 87631 87641 87643 94321 96431 97651 98321 98543 + 98621 98641 98731 764321 865321 876431 975421 986543 987541 987631 + 8764321 8765321 9754321 9875321 97654321 98764321 98765431 \ No newline at end of file diff --git a/examples/rosetta/detect division by zero.art b/examples/rosetta/detect division by zero.art index bf92c58eaa..fa8d46f9db 100644 --- a/examples/rosetta/detect division by zero.art +++ b/examples/rosetta/detect division by zero.art @@ -1,2 +1,3 @@ -try? -> 3/0 +a: 0 +try? -> 3 / a else -> print "division by zero" \ No newline at end of file diff --git a/examples/rosetta/emirp primes.res b/examples/rosetta/emirp primes.res new file mode 100644 index 0000000000..99768a010c --- /dev/null +++ b/examples/rosetta/emirp primes.res @@ -0,0 +1,8 @@ +The first 20 emirps: +13 17 31 37 71 73 79 97 107 113 149 157 167 179 199 311 337 347 359 389 + +Emirps between 7700 and 8000: +7717 7757 7817 7841 7867 7879 7901 7927 7949 7951 7963 + +The 10000th emirp: +948349 \ No newline at end of file diff --git a/examples/rosetta/factorions.res b/examples/rosetta/factorions.res new file mode 100644 index 0000000000..5670a7d95b --- /dev/null +++ b/examples/rosetta/factorions.res @@ -0,0 +1,4 @@ +Base 9 factorions: [1 2 41282] +Base 10 factorions: [1 2 145 40585] +Base 11 factorions: [1 2 26 48 40472] +Base 12 factorions: [1 2] \ No newline at end of file diff --git a/examples/rosetta/fibonacci sequence - recursive.res b/examples/rosetta/fibonacci sequence - recursive.res new file mode 100644 index 0000000000..217a83c3ae --- /dev/null +++ b/examples/rosetta/fibonacci sequence - recursive.res @@ -0,0 +1,25 @@ +Fibonacci of 1 = 1 +Fibonacci of 2 = 2 +Fibonacci of 3 = 3 +Fibonacci of 4 = 5 +Fibonacci of 5 = 8 +Fibonacci of 6 = 13 +Fibonacci of 7 = 21 +Fibonacci of 8 = 34 +Fibonacci of 9 = 55 +Fibonacci of 10 = 89 +Fibonacci of 11 = 144 +Fibonacci of 12 = 233 +Fibonacci of 13 = 377 +Fibonacci of 14 = 610 +Fibonacci of 15 = 987 +Fibonacci of 16 = 1597 +Fibonacci of 17 = 2584 +Fibonacci of 18 = 4181 +Fibonacci of 19 = 6765 +Fibonacci of 20 = 10946 +Fibonacci of 21 = 17711 +Fibonacci of 22 = 28657 +Fibonacci of 23 = 46368 +Fibonacci of 24 = 75025 +Fibonacci of 25 = 121393 \ No newline at end of file diff --git a/examples/rosetta/hailstone sequence.res b/examples/rosetta/hailstone sequence.res new file mode 100644 index 0000000000..96eb8abac6 --- /dev/null +++ b/examples/rosetta/hailstone sequence.res @@ -0,0 +1,3 @@ +Hailstone sequence for 27: +27 82 41 124 62 31 94 47 142 71 214 107 322 161 484 242 121 364 182 91 274 137 412 206 103 310 155 466 233 700 350 175 526 263 790 395 1186 593 1780 890 445 1336 668 334 167 502 251 754 377 1132 566 283 850 425 1276 638 319 958 479 1438 719 2158 1079 3238 1619 4858 2429 7288 3644 1822 911 2734 1367 4102 2051 6154 3077 9232 4616 2308 1154 577 1732 866 433 1300 650 325 976 488 244 122 61 184 92 46 23 70 35 106 53 160 80 40 20 10 5 16 8 4 2 1 +max hailstone sequence found (<100000): of length 179 for 871 \ No newline at end of file diff --git a/examples/rosetta/hamming numbers_res b/examples/rosetta/hamming numbers_res new file mode 100644 index 0000000000..719ae7a5aa --- /dev/null +++ b/examples/rosetta/hamming numbers_res @@ -0,0 +1,3 @@ +1 2 3 4 5 6 8 9 10 12 15 16 18 20 24 25 27 30 32 36 +2125764000 +519312780448388736089589843750000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/examples/rosetta/largest int from concatenated ints.art b/examples/rosetta/largest int from concatenated ints.art index de01a8afd1..533b3aa963 100644 --- a/examples/rosetta/largest int from concatenated ints.art +++ b/examples/rosetta/largest int from concatenated ints.art @@ -1,6 +1,6 @@ largestConcInt: function [arr]-> max map permutate arr 's [ - to :integer join map s => [to :string] + to :integer join map s => [to :string &] ] loop [[1 34 3 98 9 76 45 4] [54 546 548 60]] 'a -> diff --git a/examples/rosetta/largest int from concatenated ints.res b/examples/rosetta/largest int from concatenated ints.res new file mode 100644 index 0000000000..757f435d27 --- /dev/null +++ b/examples/rosetta/largest int from concatenated ints.res @@ -0,0 +1,2 @@ +998764543431 +6054854654 \ No newline at end of file diff --git a/examples/rosetta/largest number divisible by its digits.res b/examples/rosetta/largest number divisible by its digits.res new file mode 100644 index 0000000000..f722cd6356 --- /dev/null +++ b/examples/rosetta/largest number divisible by its digits.res @@ -0,0 +1 @@ +9867312 \ No newline at end of file diff --git a/examples/rosetta/ludic numbers.art b/examples/rosetta/ludic numbers.art index 139688e796..6001c6f132 100644 --- a/examples/rosetta/ludic numbers.art +++ b/examples/rosetta/ludic numbers.art @@ -1,7 +1,7 @@ ludicGen: function [nmax][ result: [1] - lst: 2..nmax+1 + lst: @2..nmax+1 i: 0 worked: false while [and? [not? empty? lst] [i < size lst]][ diff --git a/examples/rosetta/ludic numbers.res b/examples/rosetta/ludic numbers.res new file mode 100644 index 0000000000..62711f53f6 --- /dev/null +++ b/examples/rosetta/ludic numbers.res @@ -0,0 +1,9 @@ +The first 25 ludic numbers: +1 2 3 5 7 11 13 17 23 25 29 37 41 43 47 53 61 67 71 77 83 89 91 97 107 + +There are 142 ludic numbers less than/or equal to 1000 + +The ludic numbers from 2000th to 2005th are: [21475 21481 21487 21493 21503 21511] + +The triplets of ludic numbers less than 250 are: +[1 3 7] [5 7 11] [11 13 17] [23 25 29] [41 43 47] [173 175 179] [221 223 227] [233 235 239] \ No newline at end of file diff --git a/examples/rosetta/magic constant_res b/examples/rosetta/magic constant_res new file mode 100644 index 0000000000..eab3668878 --- /dev/null +++ b/examples/rosetta/magic constant_res @@ -0,0 +1,25 @@ +The first 20 magic constants are: +15 34 65 111 175 260 369 505 671 870 1105 1379 1695 2056 2465 2925 3439 4010 4641 + +The 1,000th magic constant is: +503006505 + +10 ^ 1 => 3 +10 ^ 2 => 6 +10 ^ 3 => 13 +10 ^ 4 => 28 +10 ^ 5 => 59 +10 ^ 6 => 126 +10 ^ 7 => 272 +10 ^ 8 => 585 +10 ^ 9 => 1260 +10 ^ 10 => 2715 +10 ^ 11 => 5849 +10 ^ 12 => 12600 +10 ^ 13 => 27145 +10 ^ 14 => 58481 +10 ^ 15 => 125993 +10 ^ 16 => 271442 +10 ^ 17 => 584804 +10 ^ 18 => 1259922 +10 ^ 19 => 2714418 \ No newline at end of file diff --git a/examples/rosetta/odd words.res b/examples/rosetta/odd words.res new file mode 100644 index 0000000000..023a261775 --- /dev/null +++ b/examples/rosetta/odd words.res @@ -0,0 +1,14 @@ +barbarian => brain +childbear => cider +corrigenda => cried +gargantuan => grata +headdress => hades +palladian => plain +propionate => point +salvation => slain +siltation => slain +slingshot => sight +statuette => saute +supersede => spree +supervene => spree +terminable => trial \ No newline at end of file diff --git a/examples/rosetta/ordered words_res b/examples/rosetta/ordered words.res similarity index 100% rename from examples/rosetta/ordered words_res rename to examples/rosetta/ordered words.res diff --git a/examples/rosetta/pell's equation_res b/examples/rosetta/pell's equation_res new file mode 100644 index 0000000000..40a14b1ba4 --- /dev/null +++ b/examples/rosetta/pell's equation_res @@ -0,0 +1,4 @@ +x² - 61 * y² = 1 for (x,y) = 1766319049 , 226153980 +x² - 109 * y² = 1 for (x,y) = 158070671986249 , 15140424455100 +x² - 181 * y² = 1 for (x,y) = 2469645423824185801 , 183567298683461940 +x² - 277 * y² = 1 for (x,y) = 159150073798980475849 , 9562401173878027020 \ No newline at end of file diff --git a/examples/rosetta/perfect numbers.art b/examples/rosetta/perfect numbers.art index 5d90733bb8..ac15ae5821 100644 --- a/examples/rosetta/perfect numbers.art +++ b/examples/rosetta/perfect numbers.art @@ -1,6 +1,6 @@ divisors: $[n][ select 1..(n/2)+1 'i -> 0 = n % i ] perfect?: $[n][ n = sum divisors n ] -loop 2..1000 'i [ +loop 2..10000 'i [ if perfect? i -> print i ] \ No newline at end of file diff --git a/examples/rosetta/perfect numbers.res b/examples/rosetta/perfect numbers.res new file mode 100644 index 0000000000..965906a195 --- /dev/null +++ b/examples/rosetta/perfect numbers.res @@ -0,0 +1,4 @@ +6 +28 +496 +8128 \ No newline at end of file diff --git a/examples/rosetta/perlin noise.res b/examples/rosetta/perlin noise.res new file mode 100644 index 0000000000..a5370afa97 --- /dev/null +++ b/examples/rosetta/perlin noise.res @@ -0,0 +1 @@ +0.1369199587840001 \ No newline at end of file diff --git a/examples/rosetta/permutations - derangements.art b/examples/rosetta/permutations - derangements.art index 0e3b28cbaf..45875198e2 100644 --- a/examples/rosetta/permutations - derangements.art +++ b/examples/rosetta/permutations - derangements.art @@ -6,7 +6,7 @@ isClean?: function [s,o][ ] derangements: function [n][ - original: 1..n + original: @1..n select permutate original 'x -> isClean? x original ] diff --git a/examples/rosetta/permutations - derangements.res b/examples/rosetta/permutations - derangements.res new file mode 100644 index 0000000000..01e6f4b0f1 --- /dev/null +++ b/examples/rosetta/permutations - derangements.res @@ -0,0 +1,26 @@ +Derangements of 1 2 3 4: +2 1 4 3 +2 3 4 1 +2 4 1 3 +3 1 4 2 +3 4 1 2 +3 4 2 1 +4 1 2 3 +4 3 1 2 +4 3 2 1 + +Number of derangements: + n counted calculated +--------------------------------------- + 0 1 1 + 1 0 0 + 2 1 1 + 3 2 2 + 4 9 9 + 5 44 44 + 6 265 265 + 7 1854 1854 + 8 14833 14833 + 9 133496 133496 + +!20 = 895014631192902121 \ No newline at end of file diff --git a/examples/rosetta/prime numbers which contain 123.res b/examples/rosetta/prime numbers which contain 123.res new file mode 100644 index 0000000000..d125e599c4 --- /dev/null +++ b/examples/rosetta/prime numbers which contain 123.res @@ -0,0 +1,7 @@ + 1123 1231 1237 8123 11239 12301 12323 12329 12343 12347 +12373 12377 12379 12391 17123 20123 22123 28123 29123 31123 +31231 31237 34123 37123 40123 41231 41233 44123 47123 49123 +50123 51239 56123 59123 61231 64123 65123 70123 71233 71237 +76123 81233 81239 89123 91237 98123 + +'123' Numbers < 1000000: 451 \ No newline at end of file diff --git a/examples/rosetta/pythagorean triples.art b/examples/rosetta/pythagorean triples.art index 7c540734a3..83fd5568c8 100644 --- a/examples/rosetta/pythagorean triples.art +++ b/examples/rosetta/pythagorean triples.art @@ -14,7 +14,7 @@ unique 'triples print ["Found" size triples "pythagorean triples with a perimeter no larger than 100:"] print triples -primitive: select triples => [1 = gcd] +primitive: select triples => [1 = gcd &] print "" print [size primitive "of them are primitive:"] diff --git a/examples/rosetta/pythagorean triples.res b/examples/rosetta/pythagorean triples.res new file mode 100644 index 0000000000..5e71bc2823 --- /dev/null +++ b/examples/rosetta/pythagorean triples.res @@ -0,0 +1,5 @@ +Found 17 pythagorean triples with a perimeter no larger than 100: +[3 4 5] [5 12 13] [6 8 10] [7 24 25] [8 15 17] [9 12 15] [9 40 41] [10 24 26] [12 16 20] [12 35 37] [15 20 25] [15 36 39] [16 30 34] [18 24 30] [20 21 29] [21 28 35] [24 32 40] + +7 of them are primitive: +[3 4 5] [5 12 13] [7 24 25] [8 15 17] [9 40 41] [12 35 37] [20 21 29] \ No newline at end of file diff --git a/examples/rosetta/search a list.art b/examples/rosetta/search a list.art index a2a230e27a..78478bdc6d 100644 --- a/examples/rosetta/search a list.art +++ b/examples/rosetta/search a list.art @@ -3,6 +3,6 @@ haystack: [Zig Zag Wally Ronald Bush Krusty Charlie Bush Bozo] loop [Bush Washington] 'needle [ i: index haystack needle - if? empty? i -> panic ~"|needle| is not in haystack" + if? null? i -> panic ~"|needle| is not in haystack" else -> print [i needle] ] diff --git a/examples/rosetta/search a list.res b/examples/rosetta/search a list.res new file mode 100644 index 0000000000..542b6b33ab --- /dev/null +++ b/examples/rosetta/search a list.res @@ -0,0 +1,5 @@ +4 Bush +>> Program | File: search a list.art + error | Line: 6 + | + | Washington is not in haystack \ No newline at end of file diff --git a/examples/rosetta/semordnilap.art b/examples/rosetta/semordnilap.art index 382b2bd8ba..f004c885e0 100644 --- a/examples/rosetta/semordnilap.art +++ b/examples/rosetta/semordnilap.art @@ -1,4 +1,4 @@ -words: read.lines "http://wiki.puzzlers.org/pub/wordlists/unixdict.txt" +words: read.lines relative "unixdict.txt" pairs: [] loop words 'wrd [ if and? contains? words reverse wrd diff --git a/examples/rosetta/sequence of non-squares.res b/examples/rosetta/sequence of non-squares.res new file mode 100644 index 0000000000..1429b018b6 --- /dev/null +++ b/examples/rosetta/sequence of non-squares.res @@ -0,0 +1,23 @@ +1 -> 2 +2 -> 3 +3 -> 5 +4 -> 6 +5 -> 7 +6 -> 8 +7 -> 10 +8 -> 11 +9 -> 12 +10 -> 13 +11 -> 14 +12 -> 15 +13 -> 17 +14 -> 18 +15 -> 19 +16 -> 20 +17 -> 21 +18 -> 22 +19 -> 23 +20 -> 24 +21 -> 26 +22 -> 27 +Didn't find any squares! \ No newline at end of file diff --git a/examples/rosetta/sorting algorithms - permutation sort.res b/examples/rosetta/sorting algorithms - permutation sort.res new file mode 100644 index 0000000000..1358f1b649 --- /dev/null +++ b/examples/rosetta/sorting algorithms - permutation sort.res @@ -0,0 +1 @@ +1 2 3 4 5 6 7 8 9 \ No newline at end of file diff --git a/examples/rosetta/square root by hand_res b/examples/rosetta/square root by hand_res new file mode 100644 index 0000000000..c53f3a1b5c --- /dev/null +++ b/examples/rosetta/square root by hand_res @@ -0,0 +1 @@ +14142135623730950488016887242096980785696718753769480731766797379907324784621070388503875343276415727350138462309122970249248360558507372126441214970999358314132226659275055927557999505011527820605714701095599716059702745345968620147285174186408891986095523292304843087143214508397626036279952514079896872533965463318088296406206152583523950547457502877599617298355752203375318570113543746034084988471603868999706990048150305440277903164542478230684929369186215805784631115966687130130156185689872372 \ No newline at end of file diff --git a/examples/rosetta/successive prime differences.res b/examples/rosetta/successive prime differences.res new file mode 100644 index 0000000000..3b6e469e25 --- /dev/null +++ b/examples/rosetta/successive prime differences.res @@ -0,0 +1,24 @@ +Differences of 2 + First: 3 5 + Last: 999959 999961 + Count: 8169 +Differences of 1 + First: 2 3 + Last: 2 3 + Count: 1 +Differences of 2, 2 + First: 3 5 7 + Last: 3 5 7 + Count: 1 +Differences of 2, 4 + First: 5 7 11 + Last: 999431 999433 999437 + Count: 1393 +Differences of 4, 2 + First: 7 11 13 + Last: 997807 997811 997813 + Count: 1444 +Differences of 6, 4, 2 + First: 31 37 41 43 + Last: 997141 997147 997151 997153 + Count: 306 \ No newline at end of file diff --git a/examples/rosetta/sylvester's sequence_res b/examples/rosetta/sylvester's sequence_res new file mode 100644 index 0000000000..706ccabf90 --- /dev/null +++ b/examples/rosetta/sylvester's sequence_res @@ -0,0 +1,5 @@ +First 10 terms of the Sylvester sequence: +2 3 7 43 1807 3263443 10650056950807 113423713055421844361000443 12864938683278671740537145998360961546653259485195807 165506647324519964198468195444439180017513152706377497841851388766535868639572406808911988131737645185443 + +Sum of the reciprocals of the first 10 items: +1.0 \ No newline at end of file diff --git a/examples/rosetta/teacup rim text.res b/examples/rosetta/teacup rim text.res new file mode 100644 index 0000000000..3060371093 --- /dev/null +++ b/examples/rosetta/teacup rim text.res @@ -0,0 +1,3 @@ +tea -> eat -> ate +rca -> car -> arc +pta -> tap -> apt \ No newline at end of file diff --git a/examples/rosetta/truncatable primes_res b/examples/rosetta/truncatable primes.res similarity index 100% rename from examples/rosetta/truncatable primes_res rename to examples/rosetta/truncatable primes.res diff --git a/examples/rosetta/twin primes.res b/examples/rosetta/twin primes.res new file mode 100644 index 0000000000..f1cbae6930 --- /dev/null +++ b/examples/rosetta/twin primes.res @@ -0,0 +1,6 @@ +From 2 to 10 : there are 3 pairs of twin primes +From 2 to 100 : there are 9 pairs of twin primes +From 2 to 1000 : there are 35 pairs of twin primes +From 2 to 10000 : there are 205 pairs of twin primes +From 2 to 100000 : there are 1224 pairs of twin primes +From 2 to 1000000 : there are 8169 pairs of twin primes \ No newline at end of file diff --git a/examples/rosetta/words containing the substring.art b/examples/rosetta/words containing the substring.art index c8c66be25b..70384752b6 100644 --- a/examples/rosetta/words containing the substring.art +++ b/examples/rosetta/words containing the substring.art @@ -1,3 +1,4 @@ -select read.lines "unixdict.txt" 'l -> - and? contains? l "the" - 11 < size l \ No newline at end of file +print.lines + select read.lines relative "unixdict.txt" 'l -> + and? [11 < size l] + [contains? l "the"] \ No newline at end of file diff --git a/examples/rosetta/words containing the substring.res b/examples/rosetta/words containing the substring.res new file mode 100644 index 0000000000..5bcc9ff22d --- /dev/null +++ b/examples/rosetta/words containing the substring.res @@ -0,0 +1,32 @@ +authenticate +chemotherapy +chrysanthemum +clothesbrush +clotheshorse +eratosthenes +featherbedding +featherbrain +featherweight +gaithersburg +hydrothermal +lighthearted +mathematician +neurasthenic +nevertheless +northeastern +northernmost +otherworldly +parasympathetic +physiotherapist +physiotherapy +psychotherapeutic +psychotherapist +psychotherapy +radiotherapy +southeastern +southernmost +theoretician +weatherbeaten +weatherproof +weatherstrip +weatherstripping \ No newline at end of file diff --git a/examples/rosetta/words from neighbour ones.res b/examples/rosetta/words from neighbour ones.res new file mode 100644 index 0000000000..524a254e27 --- /dev/null +++ b/examples/rosetta/words from neighbour ones.res @@ -0,0 +1,24 @@ + 1: applicate + 2: architect + 3: astronomy + 4: christine + 5: christoph + 6: committee + 7: composite + 8: constrict + 9: construct + 10: different + 11: extensive + 12: greenwood + 13: implement + 14: improvise + 15: intercept + 16: interpret + 17: interrupt + 18: philosoph + 19: prescript + 20: receptive + 21: telephone + 22: transcend + 23: transport + 24: transpose \ No newline at end of file diff --git a/src/helpers/arrays.nim b/src/helpers/arrays.nim index 853c728468..a10e22e892 100644 --- a/src/helpers/arrays.nim +++ b/src/helpers/arrays.nim @@ -16,7 +16,6 @@ import helpers/sets import vm/values/comparison import vm/values/value -import vm/values/clean #======================================= # Methods @@ -171,77 +170,12 @@ proc deduplicated*[T](s: openArray[T], isSorted: bool = false): seq[T] = for itm in items(s): if not result.contains(itm): result.add(itm) -# Backward compatibility -proc cleanAppend*(s: ValueArray, t: ValueArray): ValueArray {.inline,enforceNoRaises.} = - ## Appends `t` to `s`, cleaning the blocks and returning a `ValueArray` - ## - ## Note: - ## - Use if `x.kind != Literal` in `builtin` functions - ## - `s` and `t` must be a `ValueArray`, not a `Value` - ## - ## To understand more about cleaning blocks, read `vm/values/clean.nim` - - result = newSeq[Value](len(s) + len(t)) - var cnt = 0 - for i in s: - when not defined(NOERRORLINES): - if i.kind != Newline: - result[cnt] = i - cnt += 1 - else: - result[cnt] = i - cnt += 1 - - for i in t: - when not defined(NOERRORLINES): - if i.kind != Newline: - result[cnt] = i - cnt += 1 - else: - result[cnt] = i - cnt += 1 - - setLen(result, cnt) - -func cleanAppend*(s: Value, t: Value, singleValue: static bool = false): ValueArray {.inline,enforceNoRaises.} = - ## Appends `t` to `s`, cleaning the blocks and returning a `ValueArray` - ## - ## Note: - ## - Use if `x.kind != Literal` in `builtin` functions - ## - `s` and `t` must be a Block value - ## - ## To understand more about cleaning blocks, read `vm/values/clean.nim` - - let L1 = len(s.a) - when not singleValue: - let L2 = len(t.a) - result = newSeq[Value](L1 + L2) - else: - result = newSeq[Value](L1 + 1) - - var cnt = 0 - for i in cleanedBlockValues(s, L1): - result[cnt] = i - inc cnt - - when not singleValue: - for i in cleanedBlockValues(t, L2): - result[cnt] = i - inc cnt - else: - result[cnt] = t - inc cnt - - setLen(result, cnt) - -func cleanPrepend*(s: Value, t: Value, singleValue: static bool = false): ValueArray {.inline,enforceNoRaises.} = - ## Prepends `t` to `s`, cleaning the blocks and returning a `ValueArray` +func prepend*(s: Value, t: Value, singleValue: static bool = false): ValueArray {.inline,enforceNoRaises.} = + ## Prepends `t` to `s`, and returning a `ValueArray` ## ## Note: ## - Use if `x.kind != Literal` in `builtin` functions ## - `s` and `t` must be a Block value - ## - ## To understand more about cleaning blocks, read `vm/values/clean.nim` let L1 = len(s.a) when not singleValue: @@ -252,60 +186,28 @@ func cleanPrepend*(s: Value, t: Value, singleValue: static bool = false): ValueA var cnt = 0 when not singleValue: - for i in cleanedBlockValues(t, L2): + for i in t.a: result[cnt] = i inc cnt else: result[cnt] = t inc cnt - for i in cleanedBlockValues(s, L1): + for i in s.a: result[cnt] = i inc cnt setLen(result, cnt) -proc cleanAppendInPlace*(s: var Value, t: Value) {.inline,enforceNoRaises.} = - ## Appends `t` to `s`, cleaning the blocks and changing `s` in-place +proc prependInPlace*(s: var Value, t: Value) {.inline,enforceNoRaises.} = + ## Prepends `t` to `s`, and changing `s` in-place ## ## Note: ## - Use if `x.kind == Literal`, in `builtin` functions - ## - Else, use `cleanAppend` ## - `s` and `t` values must be a Block value, ## - It doesn't return a new value, it modifies `s` - ## - ## To understand more about cleaning blocks, read `vm/values/clean.nim` - - cleanBlock(s) - - let L1 = len(s.a) - let L2 = len(t.a) - - s.a.setLen(L1 + L2) - - var cnt = L1 - for i in cleanedBlockValues(t, L2): - s.a[cnt] = i - cnt += 1 - - setLen(s.a, cnt) - -proc cleanPrependInPlace*(s: var Value, t: Value) {.inline,enforceNoRaises.} = - ## Prepends `t` to `s`, cleaning the blocks and changing `s` in-place - ## - ## Note: - ## - Use if `x.kind == Literal`, in `builtin` functions - ## - Else, use `cleanAppend` - ## - `s` and `t` values must be a Block value, - ## - It doesn't return a new value, it modifies `s` - ## - ## To understand more about cleaning blocks, read `vm/values/clean.nim` - - cleanBlock(s) - - let L2 = len(t.a) var cnt = 0 - for i in cleanedBlockValues(t, L2): + for i in t.a: s.a.insert(i, cnt) cnt += 1 diff --git a/src/helpers/jsonobject.nim b/src/helpers/jsonobject.nim index 70aca6d4a1..8bb1f60c0a 100644 --- a/src/helpers/jsonobject.nim +++ b/src/helpers/jsonobject.nim @@ -88,7 +88,6 @@ proc generateJsonNode*(n: Value): JsonNode = Database, Socket, Bytecode, - Newline, Nothing, Any : discard @@ -172,7 +171,6 @@ when defined(WEB): Socket, Bytecode, Nothing, - Newline, Any : discard func isArray(x: JsObject): bool {.importcpp: "(Array.isArray(#))".} diff --git a/src/library/Arithmetic.nim b/src/library/Arithmetic.nim index 79e6f0ac78..eda5864603 100644 --- a/src/library/Arithmetic.nim +++ b/src/library/Arithmetic.nim @@ -29,6 +29,7 @@ proc defineSymbols*() = builtin "add", alias = plus, + op = opAdd, rule = InfixPrecedence, description = "add given values and return result", args = { @@ -50,6 +51,7 @@ proc defineSymbols*() = builtin "dec", alias = unaliased, + op = opDec, rule = PrefixPrecedence, description = "decrease given value by 1", args = { @@ -69,6 +71,7 @@ proc defineSymbols*() = builtin "div", alias = slash, + op = opDiv, rule = InfixPrecedence, description = "perform integer division between given values and return result", args = { @@ -90,6 +93,7 @@ proc defineSymbols*() = builtin "divmod", alias = slashpercent, + op = opNop, rule = InfixPrecedence, description = "perform integer division between given values and return tuple with quotient and remainder", args = { @@ -113,6 +117,7 @@ proc defineSymbols*() = builtin "fdiv", alias = doubleslash, + op = opFDiv, rule = InfixPrecedence, description = "divide given values and return result", args = { @@ -133,6 +138,7 @@ proc defineSymbols*() = builtin "inc", alias = unaliased, + op = opInc, rule = PrefixPrecedence, description = "increase given value by 1", args = { @@ -152,6 +158,7 @@ proc defineSymbols*() = builtin "mod", alias = percent, + op = opMod, rule = InfixPrecedence, description = "calculate the modulo of given values and return result", args = { @@ -173,6 +180,7 @@ proc defineSymbols*() = builtin "mul", alias = asterisk, + op = opMul, rule = InfixPrecedence, description = "calculate the product of given values and return result", args = { @@ -194,6 +202,7 @@ proc defineSymbols*() = builtin "neg", alias = unaliased, + op = opNeg, rule = PrefixPrecedence, description = "reverse sign of given value and return it", args = { @@ -213,6 +222,7 @@ proc defineSymbols*() = builtin "pow", alias = caret, + op = opPow, rule = InfixPrecedence, description = "calculate the power of given values and return result", args = { @@ -237,6 +247,7 @@ proc defineSymbols*() = builtin "sub", alias = minus, + op = opSub, rule = InfixPrecedence, description = "subtract given values and return result", args = { diff --git a/src/library/Bitwise.nim b/src/library/Bitwise.nim index 35528e3fe4..e5c78be882 100644 --- a/src/library/Bitwise.nim +++ b/src/library/Bitwise.nim @@ -35,6 +35,7 @@ proc defineSymbols*() = builtin "and", alias = unaliased, + op = opBAnd, rule = InfixPrecedence, description = "calculate the binary AND for the given values", args = { @@ -56,6 +57,7 @@ proc defineSymbols*() = builtin "nand", alias = unaliased, + op = opNop, rule = InfixPrecedence, description = "calculate the binary NAND for the given values", args = { @@ -76,6 +78,7 @@ proc defineSymbols*() = builtin "nor", alias = unaliased, + op = opNop, rule = InfixPrecedence, description = "calculate the binary NOR for the given values", args = { @@ -96,6 +99,7 @@ proc defineSymbols*() = builtin "not", alias = unaliased, + op = opBNot, rule = PrefixPrecedence, description = "calculate the binary complement the given value", args = { @@ -115,6 +119,7 @@ proc defineSymbols*() = builtin "or", alias = unaliased, + op = opBOr, rule = InfixPrecedence, description = "calculate the binary OR for the given values", args = { @@ -135,6 +140,7 @@ proc defineSymbols*() = builtin "shl", alias = unaliased, + op = opShl, rule = InfixPrecedence, description = "shift-left first value bits by second value", args = { @@ -167,6 +173,7 @@ proc defineSymbols*() = builtin "shr", alias = unaliased, + op = opShr, rule = InfixPrecedence, description = "shift-right first value bits by second value", args = { @@ -187,6 +194,7 @@ proc defineSymbols*() = builtin "xnor", alias = unaliased, + op = opNop, rule = InfixPrecedence, description = "calculate the binary XNOR for the given values", args = { @@ -207,6 +215,7 @@ proc defineSymbols*() = builtin "xor", alias = unaliased, + op = opNop, rule = InfixPrecedence, description = "calculate the binary XOR for the given values", args = { diff --git a/src/library/Collections.nim b/src/library/Collections.nim index 1ae9845fb1..2ecb0072be 100644 --- a/src/library/Collections.nim +++ b/src/library/Collections.nim @@ -50,6 +50,7 @@ proc defineSymbols*() = builtin "append", alias = doubleplus, + op = opAppend, rule = InfixPrecedence, description = "append value to given collection", args = { @@ -94,9 +95,7 @@ proc defineSymbols*() = InPlaced.n &= numberToBinary(y.i) else: if y.kind == Block: - # TODO(Collections\append) In-place appending should actually work in-place - # labels: enhancement, library - InPlaced.cleanAppendInPlace(y) + InPlaced.a.add(y.a) else: InPlaced.a.add(y) else: @@ -117,13 +116,14 @@ proc defineSymbols*() = push(newBinary(x.n & numberToBinary(y.i))) else: if y.kind==Block: - push newBlock(cleanAppend(x, y)) + push newBlock(x.a & y.a) else: - push newBlock(cleanAppend(x, y, singleValue=true)) + push newBlock(x.a & y) builtin "chop", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "remove last item from given collection", args = { @@ -161,15 +161,15 @@ proc defineSymbols*() = if x.kind == String: push(newString(x.s[0..^(times + 1)])) elif x.kind == Block: - ensureCleaned(x) - if cleanX.len == 0: push(newBlock()) - else: push(newBlock(cleanX[0..^(times + 1)])) + if x.a.len == 0: push(newBlock()) + else: push(newBlock(x.a[0..^(times + 1)])) # TODO(Collections/combine) should also work with in-place Literals? # labels: library, enhancement, open discussion builtin "combine", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "get all possible combinations of the elements in given collection", args = { @@ -203,17 +203,15 @@ proc defineSymbols*() = #======================================================= let doRepeat = hadAttr("repeated") - ensureCleaned(x) - - var sz = cleanX.len + var sz = x.a.len if checkAttr("by"): if aBy.i > 0 and aBy.i < sz: sz = aBy.i if hadAttr("count"): - push(countCombinations(cleanX, sz, doRepeat)) + push(countCombinations(x.a, sz, doRepeat)) else: - push(newBlock(getCombinations(cleanX, sz, doRepeat).map(( + push(newBlock(getCombinations(x.a, sz, doRepeat).map(( z)=>newBlock(z)))) # TODO(Collections/contains?) add new `.deep` option? @@ -227,6 +225,7 @@ proc defineSymbols*() = # labels: library, enhancement, open discussion builtin "contains?", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "check if collection contains given value", args = { @@ -277,8 +276,7 @@ proc defineSymbols*() = else: push(newLogical(x.s.continuesWith(y.s, at))) of Block: - ensureCleaned(x) - push(newLogical(cleanX[at] == y)) + push(newLogical(x.a[at] == y)) of Range: push(newLogical(x.rng[at] == y)) of Dictionary: @@ -296,8 +294,7 @@ proc defineSymbols*() = else: push(newLogical(y.s in x.s)) of Block: - ensureCleaned(x) - push(newLogical(y in cleanX)) + push(newLogical(y in x.a)) of Range: push(newLogical(y in x.rng)) of Dictionary: @@ -310,6 +307,7 @@ proc defineSymbols*() = # labels: library, enhancement builtin "couple", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "get combination of elements in given collections as array of tuples", args = { @@ -323,12 +321,11 @@ proc defineSymbols*() = ; => [[1 "one"] [2 "two"] [3 "three"]] """: #======================================================= - ensureCleaned(x) - ensureCleaned(y) - push(newBlock(zip(cleanX, cleanY).map((z)=>newBlock(@[z[0], z[1]])))) + push(newBlock(zip(x.a, y.a).map((z)=>newBlock(@[z[0], z[1]])))) builtin "decouple", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "get tuple of collections from a coupled collection of tuples", args = { @@ -349,12 +346,12 @@ proc defineSymbols*() = let res = unzip(InPlaced.a.map((w)=>(w.a[0], w.a[1]))) InPlaced.a = @[newBlock(res[0]), newBlock(res[1])] else: - ensureCleaned(x) - let res = unzip(cleanX.map((z)=>(z.a[0], z.a[1]))) + let res = unzip(x.a.map((z)=>(z.a[0], z.a[1]))) push(newBlock(@[newBlock(res[0]), newBlock(res[1])])) builtin "drop", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "drop first *number* of elements from given collection and return the remaining ones", args = { @@ -382,12 +379,12 @@ proc defineSymbols*() = if x.kind == String: push(newString(x.s[y.i..^1])) elif x.kind == Block: - ensureCleaned(x) - if cleanX.len == 0: push(newBlock()) - else: push(newBlock(cleanX[y.i..^1])) + if x.a.len == 0: push(newBlock()) + else: push(newBlock(x.a[y.i..^1])) builtin "empty", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "empty given collection", args = { @@ -412,6 +409,7 @@ proc defineSymbols*() = builtin "empty?", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "check if given collection is empty", args = { @@ -431,13 +429,13 @@ proc defineSymbols*() = of Null: push(VTRUE) of String: push(newLogical(x.s == "")) of Block: - ensureCleaned(x) - push(newLogical(cleanX.len == 0)) + push(newLogical(x.a.len == 0)) of Dictionary: push(newLogical(x.d.len == 0)) else: discard builtin "extend", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "get new dictionary by merging given ones", args = { @@ -466,6 +464,7 @@ proc defineSymbols*() = builtin "first", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "return the first item of the given collection", args = { @@ -495,9 +494,8 @@ proc defineSymbols*() = if i == aN.i: break push(newBlock(res)) else: - ensureCleaned(x) - if cleanX.len == 0: push(newBlock()) - else: push(newBlock(cleanX[0..aN.i-1])) + if x.a.len == 0: push(newBlock()) + else: push(newBlock(x.a[0..aN.i-1])) else: if x.kind == String: if x.s.len == 0: push(VNULL) @@ -505,12 +503,12 @@ proc defineSymbols*() = elif x.kind == Range: push(x.rng[0]) else: - ensureCleaned(x) - if cleanX.len == 0: push(VNULL) - else: push(cleanX[0]) + if x.a.len == 0: push(VNULL) + else: push(x.a[0]) builtin "flatten", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "flatten given collection by eliminating nested blocks", args = { @@ -540,10 +538,11 @@ proc defineSymbols*() = ensureInPlace() SetInPlace(InPlaced.flattened(once = hadAttr("once"))) else: - push(newBlock(cleanedBlock(x.a)).flattened(once = hadAttr("once"))) + push(newBlock(x.a).flattened(once = hadAttr("once"))) builtin "get", alias = unaliased, + op = opGet, rule = PrefixPrecedence, description = "get collection's item by given index", args = { @@ -583,8 +582,7 @@ proc defineSymbols*() = case x.kind: of Block: if likely(y.kind==Integer): - ensureCleaned(x) - push(GetArrayIndex(cleanX, y.i)) + push(GetArrayIndex(x.a, y.i)) else: let rLen = y.rng.len var res: ValueArray = newSeq[Value](rLen) @@ -648,6 +646,7 @@ proc defineSymbols*() = # labels: library, enhancement, open discussion builtin "in?", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "check if value exists in given collection", args = { @@ -698,8 +697,7 @@ proc defineSymbols*() = else: push(newLogical(y.s.continuesWith(x.s, at))) of Block: - ensureCleaned(y) - push(newLogical(cleanY[at] == x)) + push(newLogical(y.a[at] == x)) of Range: push(newLogical(y.rng[at] == x)) of Dictionary: @@ -728,6 +726,7 @@ proc defineSymbols*() = builtin "index", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "return first index of value in given collection", args = { @@ -752,8 +751,7 @@ proc defineSymbols*() = if indx != -1: push(newInteger(indx)) else: push(VNULL) of Block: - ensureCleaned(x) - let indx = cleanX.find(y) + let indx = x.a.find(y) if indx != -1: push(newInteger(indx)) else: push(VNULL) of Range: @@ -779,6 +777,7 @@ proc defineSymbols*() = # labels: library, enhancement, open discussion builtin "insert", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "insert value in collection at given index", args = { @@ -825,7 +824,7 @@ proc defineSymbols*() = copied.insert($(z.c), y.i) push(newString(copied)) of Block: - var copied = cleanedBlock(x.a) + var copied = x.a copied.insert(z, y.i) push(newBlock(copied)) of Dictionary: @@ -836,6 +835,7 @@ proc defineSymbols*() = builtin "key?", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "check if collection contains given key", args = { @@ -868,6 +868,7 @@ proc defineSymbols*() = builtin "keys", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "get list of keys for given collection", args = { @@ -895,6 +896,7 @@ proc defineSymbols*() = builtin "last", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "return the last item of the given collection", args = { @@ -919,9 +921,8 @@ proc defineSymbols*() = let items = toSeq(x.rng.items) push(newBlock(items[x.rng.len-aN.i..^1])) else: - ensureCleaned(x) - if cleanX.len == 0: push(newBlock()) - else: push(newBlock(cleanX[cleanX.len-aN.i..^1])) + if x.a.len == 0: push(newBlock()) + else: push(newBlock(x.a[x.a.len-aN.i..^1])) else: if x.kind == String: if x.s.len == 0: push(VNULL) @@ -930,12 +931,12 @@ proc defineSymbols*() = let items = toSeq(x.rng.items) push(items[x.rng.len-1]) else: - ensureCleaned(x) - if cleanX.len == 0: push(VNULL) - else: push(cleanX[cleanX.len-1]) + if x.a.len == 0: push(VNULL) + else: push(x.a[x.a.len-1]) builtin "max", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "get maximum element in given collection", args = { @@ -956,31 +957,31 @@ proc defineSymbols*() = if withIndex: push(newInteger(maxIndex)) else: push(maxElement) else: - ensureCleaned(x) - if cleanX.len == 0: push(VNULL) + if x.a.len == 0: push(VNULL) else: - var maxElement = cleanX[0] + var maxElement = x.a[0] if withIndex: var maxIndex = 0 var i = 1 - while i < cleanX.len: - if (cleanX[i] > maxElement): - maxElement = cleanX[i] + while i < x.a.len: + if (x.a[i] > maxElement): + maxElement = x.a[i] maxIndex = i inc(i) push(newInteger(maxIndex)) else: var i = 1 - while i < cleanX.len: - if (cleanX[i] > maxElement): - maxElement = cleanX[i] + while i < x.a.len: + if (x.a[i] > maxElement): + maxElement = x.a[i] inc(i) push(maxElement) builtin "min", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "get minimum element in given collection", args = { @@ -1001,31 +1002,31 @@ proc defineSymbols*() = if withIndex: push(newInteger(minIndex)) else: push(minElement) else: - ensureCleaned(x) - if cleanX.len == 0: push(VNULL) + if x.a.len == 0: push(VNULL) else: - var minElement = cleanX[0] + var minElement = x.a[0] var minIndex = 0 if withIndex: var i = 1 - while i < cleanX.len: - if (cleanX[i] < minElement): - minElement = cleanX[i] + while i < x.a.len: + if (x.a[i] < minElement): + minElement = x.a[i] minIndex = i inc(i) push(newInteger(minIndex)) else: var i = 1 - while i < cleanX.len: - if (cleanX[i] < minElement): - minElement = cleanX[i] + while i < x.a.len: + if (x.a[i] < minElement): + minElement = x.a[i] inc(i) push(minElement) builtin "one?", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "check if given number or collection size is one", args = { @@ -1063,8 +1064,7 @@ proc defineSymbols*() = of String: push(newLogical(runeLen(x.s) == 1)) of Block: - ensureCleaned(x) - push(newLogical(cleanX.len == 1)) + push(newLogical(x.a.len == 1)) of Range: push(newLogical(x.rng.len == 1)) of Dictionary: @@ -1076,6 +1076,7 @@ proc defineSymbols*() = builtin "permutate", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "get all possible permutations of the elements in given collection", args = { @@ -1109,21 +1110,20 @@ proc defineSymbols*() = #======================================================= let doRepeat = hadAttr("repeated") - ensureCleaned(x) - - var sz = cleanX.len + var sz = x.a.len if checkAttr("by"): if aBy.i > 0 and aBy.i < sz: sz = aBy.i if hadAttr("count"): - push(countPermutations(cleanX, sz, doRepeat)) + push(countPermutations(x.a, sz, doRepeat)) else: - push(newBlock(getPermutations(cleanX, sz, doRepeat).map(( + push(newBlock(getPermutations(x.a, sz, doRepeat).map(( z)=>newBlock(z)))) builtin "prepend", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "prepend value to given collection", args = { @@ -1154,7 +1154,7 @@ proc defineSymbols*() = InPlaced.n.insert(numberToBinary(y.i), 0) else: if y.kind == Block: - InPlaced.cleanPrependInPlace(y) + InPlaced.prependInPlace(y) else: InPlaced.a.insert(y, 0) else: @@ -1175,15 +1175,16 @@ proc defineSymbols*() = push(newBinary(numberToBinary(y.i) & x.n)) else: if y.kind==Block: - push newBlock(cleanPrepend(x, y)) + push newBlock(prepend(x, y)) else: - push newBlock(cleanPrepend(x, y, singleValue=true)) + push newBlock(prepend(x, y, singleValue=true)) # TODO(Collections/remove) is `.index` broken? # Example: `remove.index 3 'a, debug a` # labels: library, bug builtin "remove", alias = doubleminus, + op = opNop, rule = InfixPrecedence, description = "remove value from given collection", args = { @@ -1275,18 +1276,17 @@ proc defineSymbols*() = else: push(newString(x.s.removeAll(y))) elif x.kind == Block: - ensureCleaned(x) if y.kind == Block and hadAttr("instance"): if hadAttr("once"): - push(newBlock(cleanX.removeFirstInstance(y))) + push(newBlock(x.a.removeFirstInstance(y))) else: - push(newBlock(cleanX.removeAllInstances(y))) + push(newBlock(x.a.removeAllInstances(y))) elif (hadAttr("once")): - push(newBlock(cleanX.removeFirst(y))) + push(newBlock(x.a.removeFirst(y))) elif (hadAttr("index")): - push(newBlock(cleanX.removeByIndex(y.i))) + push(newBlock(x.a.removeByIndex(y.i))) else: - push(newBlock(cleanX.removeAll(y))) + push(newBlock(x.a.removeAll(y))) elif x.kind == Dictionary: let key = (hadAttr("key")) if (hadAttr("once")): @@ -1296,6 +1296,7 @@ proc defineSymbols*() = builtin "repeat", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "repeat value the given number of times and return new one", args = { @@ -1330,13 +1331,13 @@ proc defineSymbols*() = if x.kind == String: push(newString(x.s.repeat(y.i))) elif x.kind == Block: - ensureCleaned(x) - push(newBlock(safeCycle(cleanX, y.i))) + push(newBlock(safeCycle(x.a, y.i))) else: push(newBlock(safeRepeat(x, y.i))) builtin "reverse", alias = unaliased, + op = opReverse, rule = PrefixPrecedence, description = "reverse given collection", args = { @@ -1376,8 +1377,7 @@ proc defineSymbols*() = InPlaced.a.reverse() else: if x.kind == Block: - ensureCleaned(x) - push(newBlock(cleanX.reversed)) + push(newBlock(x.a.reversed)) elif x.kind == Range: push(newRange(x.rng.reversed(safe=exact))) else: @@ -1385,6 +1385,7 @@ proc defineSymbols*() = builtin "rotate", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "right-rotate collection by given distance", args = { @@ -1416,11 +1417,11 @@ proc defineSymbols*() = push(newString(toSeq(runes(x.s)).map((w) => $(w)) .rotatedLeft(distance).join(""))) elif x.kind == Block: - ensureCleaned(x) - push(newBlock(cleanX.rotatedLeft(distance))) + push(newBlock(x.a.rotatedLeft(distance))) builtin "sample", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "get a random element from given collection", args = { @@ -1438,9 +1439,8 @@ proc defineSymbols*() = let rnd = rand(0..int(x.rng.len-1)) push(x.rng[rnd]) else: - ensureCleaned(x) - if cleanX.len == 0: push(VNULL) - else: push(sample(cleanX)) + if x.a.len == 0: push(VNULL) + else: push(sample(x.a)) # TODO(Collections/set) not working with Bytecode values # example: @@ -1451,6 +1451,7 @@ proc defineSymbols*() = # labels: library, bug builtin "set", alias = unaliased, + op = opSet, rule = PrefixPrecedence, description = "set collection's item at index to given value", args = { @@ -1484,7 +1485,6 @@ proc defineSymbols*() = #======================================================= case x.kind: of Block: - cleanBlock(x) SetArrayIndex(x.a, y.i, z) of Binary: let bn = numberToBinary(z.i) @@ -1538,6 +1538,7 @@ proc defineSymbols*() = builtin "shuffle", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "get given collection shuffled", args = { @@ -1557,11 +1558,11 @@ proc defineSymbols*() = ensureInPlace() InPlaced.a.shuffle() else: - ensureCleaned(x) - push(newBlock(cleanX.dup(shuffle))) + push(newBlock(x.a.dup(shuffle))) builtin "size", alias = unaliased, + op = opSize, rule = PrefixPrecedence, description = "get size/length of given collection", args = { @@ -1593,13 +1594,13 @@ proc defineSymbols*() = if sz == InfiniteRange: push(newFloating(Inf)) else: push(newInteger(sz)) elif x.kind == Block: - ensureCleaned(x) - push(newInteger(cleanX.len)) + push(newInteger(x.a.len)) else: # Null push(newInteger(0)) builtin "slice", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "get a slice of collection between given indices", args = { @@ -1634,14 +1635,14 @@ proc defineSymbols*() = else: push(newString("")) else: - ensureCleaned(x) - if y.i >= 0 and z.i <= cleanX.len-1: - push(newBlock(cleanX[y.i..z.i])) + if y.i >= 0 and z.i <= x.a.len-1: + push(newBlock(x.a[y.i..z.i])) else: push(newBlock()) builtin "sort", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "sort given block in ascending order", args = { @@ -1673,20 +1674,19 @@ proc defineSymbols*() = sortOrdering = SortOrder.Descending if x.kind == Block: - ensureCleaned(x) - if cleanX.len == 0: push(newBlock()) + if x.a.len == 0: push(newBlock()) else: if checkAttr("by"): - if cleanX.len > 0: + if x.a.len > 0: var sorted: ValueArray - if cleanX[0].kind == Dictionary: - sorted = cleanX.sorted( + if x.a[0].kind == Dictionary: + sorted = x.a.sorted( proc (v1, v2: Value): int = cmp(v1.d[aBy.s], v2.d[aBy.s]), order = sortOrdering) else: - sorted = cleanX.sorted( + sorted = x.a.sorted( proc (v1, v2: Value): int = cmp(v1.o[aBy.s], v2.o[aBy.s]), order = sortOrdering) @@ -1698,21 +1698,21 @@ proc defineSymbols*() = var sortAscii = (hadAttr("ascii")) if checkAttr("as"): - push(newBlock(cleanX.unisorted(aAs.s, + push(newBlock(x.a.unisorted(aAs.s, sensitive = hadAttr("sensitive"), order = sortOrdering, ascii = sortAscii))) else: if (hadAttr("sensitive")): - push(newBlock(cleanX.unisorted("en", + push(newBlock(x.a.unisorted("en", sensitive = true, order = sortOrdering, ascii = sortAscii))) else: - if cleanX[0].kind == String: - push(newBlock(cleanX.unisorted("en", + if x.a[0].kind == String: + push(newBlock(x.a.unisorted("en", order = sortOrdering, ascii = sortAscii))) else: - push(newBlock(cleanX.sorted( + push(newBlock(x.a.sorted( order = sortOrdering))) elif x.kind == Dictionary: @@ -1826,6 +1826,7 @@ proc defineSymbols*() = # labels: library, enhancement builtin "sorted?", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "check if given collection is already sorted", args = { @@ -1865,6 +1866,7 @@ proc defineSymbols*() = # labels: library, bug builtin "split", alias = unaliased, + op = opSplit, rule = PrefixPrecedence, description = "split collection to components", args = { @@ -2000,20 +2002,19 @@ proc defineSymbols*() = else: push(newStringBlock(toSeq(runes(x.s)).map((x) => $(x)))) else: - ensureCleaned(x) if checkAttr("at"): - push(newBlock(@[newBlock(cleanX[0..aAt.i-1]), newBlock( - cleanX[aAt.i..^1])])) + push(newBlock(@[newBlock(x.a[0..aAt.i-1]), newBlock( + x.a[aAt.i..^1])])) elif checkAttr("every"): var ret: ValueArray - var length = cleanX.len + var length = x.a.len var i = 0 while i < length: if i+aEvery.i > length: - ret.add(newBlock(cleanX[i..^1])) + ret.add(newBlock(x.a[i..^1])) else: - ret.add(newBlock(cleanX[i..i+aEvery.i-1])) + ret.add(newBlock(x.a[i..i+aEvery.i-1])) i += aEvery.i @@ -2022,6 +2023,7 @@ proc defineSymbols*() = builtin "squeeze", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "reduce adjacent elements in given collection", args = { @@ -2074,16 +2076,16 @@ proc defineSymbols*() = elif x.kind == Block: var i = 0 var ret: ValueArray - ensureCleaned(x) - while i < cleanX.len: - ret.add(cleanX[i]) - while (i+1 < cleanX.len and cleanX[i+1] == cleanX[i]): + while i < x.a.len: + ret.add(x.a[i]) + while (i+1 < x.a.len and x.a[i+1] == x.a[i]): i += 1 i += 1 push(newBlock(ret)) builtin "take", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "keep first of elements from given collection and return the remaining ones", args = { @@ -2129,12 +2131,11 @@ proc defineSymbols*() = upperLimit = x.s.len-1 push(newString(x.s[0..upperLimit])) elif x.kind == Block: - ensureCleaned(x) - if cleanX.len == 0: push(newBlock()) + if x.a.len == 0: push(newBlock()) else: - if upperLimit > cleanX.len - 1: - upperLimit = cleanX.len-1 - push(newBlock(cleanX[0..upperLimit])) + if upperLimit > x.a.len - 1: + upperLimit = x.a.len-1 + push(newBlock(x.a[0..upperLimit])) elif x.kind == Range: var res: ValueArray = newSeq[Value](upperLimit+1) var i = 0 @@ -2146,6 +2147,7 @@ proc defineSymbols*() = builtin "tally", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "find number of occurences of each value within given block and return as dictionary", args = { @@ -2182,6 +2184,7 @@ proc defineSymbols*() = builtin "unique", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "get given collection without duplicates", args = { @@ -2207,8 +2210,7 @@ proc defineSymbols*() = push newString(x.s & $(genOid())) else: if x.kind == Block: - ensureCleaned(x) - push(newBlock(cleanX.deduplicated())) + push(newBlock(x.a.deduplicated())) elif x.kind == String: push newString(toSeq(runes(x.s)).deduplicate.map((w) => $(w)).join("")) else: @@ -2220,6 +2222,7 @@ proc defineSymbols*() = builtin "values", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "get list of values for given collection", args = { @@ -2251,6 +2254,7 @@ proc defineSymbols*() = builtin "zero?", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "check if given number or collection size is zero", args = { @@ -2288,8 +2292,7 @@ proc defineSymbols*() = of String: push(newLogical(runeLen(x.s) == 0)) of Block: - ensureCleaned(x) - push(newLogical(cleanX.len == 0)) + push(newLogical(x.a.len == 0)) of Range: push(newLogical(x.rng.len == 0)) of Dictionary: diff --git a/src/library/Colors.nim b/src/library/Colors.nim index 298f186a37..cf8c7c8ec6 100644 --- a/src/library/Colors.nim +++ b/src/library/Colors.nim @@ -34,6 +34,7 @@ proc defineSymbols*() = builtin "blend", alias = at, + op = opNop, rule = PrefixPrecedence, description = "blend given colors and get result", args = { @@ -63,6 +64,7 @@ proc defineSymbols*() = builtin "darken", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "darken color by given percentage (0.0-1.0)", args = { @@ -86,6 +88,7 @@ proc defineSymbols*() = builtin "desaturate", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "desaturate color by given percentage (0.0-1.0)", args = { @@ -109,6 +112,7 @@ proc defineSymbols*() = builtin "grayscale", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "convert color to grayscale", args = { @@ -131,6 +135,7 @@ proc defineSymbols*() = builtin "invert", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "get complement for given color", args = { @@ -152,6 +157,7 @@ proc defineSymbols*() = builtin "lighten", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "lighten color by given percentage (0.0-1.0)", args = { @@ -177,6 +183,7 @@ proc defineSymbols*() = builtin "palette", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "create palette using given base color", args = { @@ -241,6 +248,7 @@ proc defineSymbols*() = builtin "saturate", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "saturate color by given percentage (0.0-1.0)", args = { @@ -266,6 +274,7 @@ proc defineSymbols*() = builtin "spin", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "spin color around the hue wheel by given amount", args = { diff --git a/src/library/Comparison.nim b/src/library/Comparison.nim index 9c378b224f..8fcb803feb 100644 --- a/src/library/Comparison.nim +++ b/src/library/Comparison.nim @@ -33,6 +33,7 @@ proc defineSymbols*() = # labels: library, enhancement, open discussion builtin "between?", alias = thickarrowboth, + op = opNop, rule = InfixPrecedence, description = "check if given value is between the given values (inclusive)", args = { @@ -63,6 +64,7 @@ proc defineSymbols*() = builtin "compare", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "compare given values and return -1, 0, or 1 based on the result", args = { @@ -86,6 +88,7 @@ proc defineSymbols*() = builtin "equal?", alias = equal, + op = opEq, rule = InfixPrecedence, description = "check if valueA = valueB (equality)", args = { @@ -105,6 +108,7 @@ proc defineSymbols*() = builtin "greater?", alias = greaterthan, + op = opGt, rule = InfixPrecedence, description = "check if valueA > valueB (greater than)", args = { @@ -124,6 +128,7 @@ proc defineSymbols*() = builtin "greaterOrEqual?", alias = greaterequal, + op = opGe, rule = InfixPrecedence, description = "check if valueA >= valueB (greater than or equal)", args = { @@ -143,6 +148,7 @@ proc defineSymbols*() = builtin "less?", alias = lessthan, + op = opLt, rule = InfixPrecedence, description = "check if valueA < valueB (less than)", args = { @@ -162,6 +168,7 @@ proc defineSymbols*() = builtin "lessOrEqual?", alias = equalless, + op = opLe, rule = InfixPrecedence, description = "check if valueA =< valueB (less than or equal)", args = { @@ -181,6 +188,7 @@ proc defineSymbols*() = builtin "notEqual?", alias = lessgreater, + op = opNe, rule = InfixPrecedence, description = "check if valueA <> valueB (not equal)", args = { @@ -200,6 +208,7 @@ proc defineSymbols*() = builtin "same?", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "check if given values are exactly the same (identity)", args = { diff --git a/src/library/Converters.nim b/src/library/Converters.nim index 67a480272a..242fc25926 100644 --- a/src/library/Converters.nim +++ b/src/library/Converters.nim @@ -315,22 +315,18 @@ proc convertedValueToType(x, y: Value, tp: ValueKind, aFormat:Value = nil): Valu of Inline: case tp: of Block: - ensureCleaned(y) - return newBlock(cleanY) + return newBlock(y.a) else: throwCannotConvert() of Block: case tp: of Complex: - ensureCleaned(y) - return newComplex(cleanY[0], cleanY[1]) + return newComplex(y.a[0], y.a[1]) of Rational: - ensureCleaned(y) - return newRational(cleanY[0], cleanY[1]) + return newRational(y.a[0], y.a[1]) of Inline: - ensureCleaned(y) - return newInline(cleanY) + return newInline(y.a) of Dictionary: let stop = SP execUnscoped(y) @@ -360,34 +356,31 @@ proc convertedValueToType(x, y: Value, tp: ValueKind, aFormat:Value = nil): Valu throwCannotConvert() of Quantity: - ensureCleaned(y) - return newQuantity(cleanY[0], parseQuantitySpec(cleanY[1].s)) + return newQuantity(y.a[0], parseQuantitySpec(y.a[1].s)) of Color: - ensureCleaned(y) - if cleanY.len < 3 or cleanY.len > 4: + if y.a.len < 3 or y.a.len > 4: echo "wrong number of attributes" else: if (hadAttr("hsl")): - if cleanY.len==3: - return newColor(HSLtoRGB((cleanY[0].i, cleanY[1].f, cleanY[2].f, 1.0))) - elif cleanY.len==4: - return newColor(HSLtoRGB((cleanY[0].i, cleanY[1].f, cleanY[2].f, cleanY[3].f))) + if y.a.len==3: + return newColor(HSLtoRGB((y.a[0].i, y.a[1].f, y.a[2].f, 1.0))) + elif y.a.len==4: + return newColor(HSLtoRGB((y.a[0].i, y.a[1].f, y.a[2].f, y.a[3].f))) elif (hadAttr("hsv")): - if cleanY.len==3: - return newColor(HSVtoRGB((cleanY[0].i, cleanY[1].f, cleanY[2].f, 1.0))) - elif cleanY.len==4: - return newColor(HSVtoRGB((cleanY[0].i, cleanY[1].f, cleanY[2].f, cleanY[3].f))) + if y.a.len==3: + return newColor(HSVtoRGB((y.a[0].i, y.a[1].f, y.a[2].f, 1.0))) + elif y.a.len==4: + return newColor(HSVtoRGB((y.a[0].i, y.a[1].f, y.a[2].f, y.a[3].f))) else: - if cleanY.len==3: - return newColor((cleanY[0].i, cleanY[1].i, cleanY[2].i, 255)) - elif cleanY.len==4: - return newColor((cleanY[0].i, cleanY[1].i, cleanY[2].i, cleanY[3].i)) + if y.a.len==3: + return newColor((y.a[0].i, y.a[1].i, y.a[2].i, 255)) + elif y.a.len==4: + return newColor((y.a[0].i, y.a[1].i, y.a[2].i, y.a[3].i)) of Binary: var res: VBinary - ensureCleaned(y) - for item in cleanY: + for item in y.a: if item.kind==Integer: res &= numberToBinary(item.i) else: @@ -396,9 +389,7 @@ proc convertedValueToType(x, y: Value, tp: ValueKind, aFormat:Value = nil): Valu return newBinary(res) of Bytecode: - var evaled = doEval(y) - if (hadAttr("optimized")): - evaled = Translation(constants: evaled.constants, instructions: optimizeBytecode(evaled)) + var evaled = doEval(y, omitNewlines=hadAttr("intrepid")) return newBytecode(evaled) @@ -420,8 +411,6 @@ proc convertedValueToType(x, y: Value, tp: ValueKind, aFormat:Value = nil): Valu throwCannotConvert() of Bytecode: var evaled = Translation(constants: y.d["data"].a, instructions: y.d["code"].a.map(proc (x:Value):byte = byte(x.i))) - if (hadAttr("optimized")): - evaled.instructions = optimizeBytecode(evaled) return newBytecode(evaled) else: @@ -530,7 +519,6 @@ proc convertedValueToType(x, y: Value, tp: ValueKind, aFormat:Value = nil): Valu of Function, Database, Socket, - Newline, Nothing, Any, Path, @@ -550,6 +538,7 @@ proc defineSymbols*() = builtin "array", alias = at, + op = opArray, rule = PrefixPrecedence, description = "create array from given block, by reducing/calculating all internal values", args = { @@ -630,8 +619,11 @@ proc defineSymbols*() = else: push(newBlock(@[x])) + # TODO(Converters/as) is `.unwrapped` working as expected? + # labels: library, bug builtin "as", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "format given value as implied type", args = { @@ -679,8 +671,7 @@ proc defineSymbols*() = elif (hadAttr("octal")): push(newString(fmt"{x.i:o}")) elif (hadAttr("agnostic")): - ensureCleaned(x) - let res = cleanX.map(proc(v:Value):Value = + let res = x.a.map(proc(v:Value):Value = if v.kind == Word and not SymExists(v.s): newLiteral(v.s) else: v ) @@ -706,6 +697,7 @@ proc defineSymbols*() = # labels: library, enhancement builtin "define", alias = dollar, + op = opNop, rule = PrefixPrecedence, description = "define new type with given prototype", args = { @@ -769,7 +761,7 @@ proc defineSymbols*() = ; NAME: Jane, SURNAME: Doe, AGE: 33 """: #======================================================= - x.ts.fields = cleanedBlock(y.a) + x.ts.fields = y.a if checkAttr("as"): x.ts.inherits = aAs.ts @@ -818,6 +810,7 @@ proc defineSymbols*() = builtin "dictionary", alias = sharp, + op = opDict, rule = PrefixPrecedence, description = "create dictionary from given block or file, by getting all internal symbols", args = { @@ -861,9 +854,8 @@ proc defineSymbols*() = if (hadAttr("raw")): dict = initOrderedTable[string,Value]() var idx = 0 - ensureCleaned(x) - while idx < cleanX.len: - dict[cleanX[idx].s] = cleanX[idx+1] + while idx < x.a.len: + dict[x.a[idx].s] = x.a[idx+1] idx += 2 else: dict = execDictionary(x) @@ -896,6 +888,7 @@ proc defineSymbols*() = # labels: library, enhancement, open discussion builtin "from", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "get value from string, using given representation", args = { @@ -939,10 +932,11 @@ proc defineSymbols*() = builtin "function", alias = dollar, + op = opFunc, rule = PrefixPrecedence, description = "create function with given arguments and body", args = { - "arguments" : {Block}, + "arguments" : {Literal, Block}, "body" : {Block} }, attrs = { @@ -1047,33 +1041,35 @@ proc defineSymbols*() = var memoize = (hadAttr("memoize")) var inline = (hadAttr("inline")) + let argBlock {.cursor.} = + if x.kind == Block: x.a + else: @[x] + # TODO(Converters\function) Verify safety of implicit `.inline`s # labels: library, benchmark, open discussion if not inline: if canBeInlined(y): inline = true - - cleanBlock(x) var ret: Value var argTypes = initOrderedTable[string,ValueSpec]() - if x.a.countIt(it.kind == Type) > 0: + if argBlock.countIt(it.kind == Type) > 0: var args: seq[string] var body: ValueArray var i = 0 - while i < x.a.len: - let varName = x.a[i] - args.add(x.a[i].s) - argTypes[x.a[i].s] = {} - if i+1 < x.a.len and x.a[i+1].kind == Type: + while i < argBlock.len: + let varName = argBlock[i] + args.add(argBlock[i].s) + argTypes[argBlock[i].s] = {} + if i+1 < argBlock.len and argBlock[i+1].kind == Type: var typeArr: ValueArray - while i+1 < x.a.len and x.a[i+1].kind == Type: + while i+1 < argBlock.len and argBlock[i+1].kind == Type: typeArr.add(newWord("is?")) - typeArr.add(x.a[i+1]) - argTypes[varName.s].incl(x.a[i+1].t) + typeArr.add(argBlock[i+1]) + argTypes[varName.s].incl(argBlock[i+1].t) typeArr.add(varName) i += 1 @@ -1095,12 +1091,12 @@ proc defineSymbols*() = ret = newFunction(args,newBlock(mainBody),imports,exports,memoize,inline) else: - if x.a.len > 0: - for arg in x.a: + if argBlock.len > 0: + for arg in argBlock: argTypes[arg.s] = {Any} else: argTypes[""] = {Nothing} - ret = newFunction(x.a.map((w)=>w.s),y,imports,exports,memoize,inline) + ret = newFunction(argBlock.map((w)=>w.s),y,imports,exports,memoize,inline) ret.info = ValueInfo(kind: Function) @@ -1150,6 +1146,7 @@ proc defineSymbols*() = builtin "in", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "convert quantity to given unit", args = { @@ -1174,6 +1171,7 @@ proc defineSymbols*() = builtin "range", alias = ellipsis, + op = opRange, rule = InfixPrecedence, description = "get list of values in given range (inclusive)", args = { @@ -1227,6 +1225,7 @@ proc defineSymbols*() = when not defined(WEB): builtin "store", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "create or load a persistent store on disk", args = { @@ -1324,6 +1323,7 @@ proc defineSymbols*() = builtin "to", alias = unaliased, + op = opTo, rule = PrefixPrecedence, description = "convert value to given type", args = { @@ -1333,7 +1333,7 @@ proc defineSymbols*() = attrs = { "format" : ({String},"use given format (for dates or floating-point numbers)"), "unit" : ({String,Literal},"use given unit (for quantities)"), - "optimized" : ({Logical},"convert to optimized bytecode"), + "intrepid" : ({Logical},"convert to bytecode without error-line tracking"), "hsl" : ({Logical},"convert HSL block to color"), "hsv" : ({Logical},"convert HSV block to color") }, @@ -1401,26 +1401,25 @@ proc defineSymbols*() = push convertedValueToType(x, y, tp, popAttr("format")) else: var ret: ValueArray - ensureCleaned(x) - let tp = cleanX[0].t + let tp = x.a[0].t if y.kind==String: ret = toSeq(runes(y.s)).map((c) => newChar(c)) else: let aFormat = popAttr("format") if y.kind == Block: - ensureCleaned(y) - for item in cleanY: - ret.add(convertedValueToType(cleanX[0], item, tp, aFormat)) + for item in y.a: + ret.add(convertedValueToType(x.a[0], item, tp, aFormat)) else: for item in items(y.rng): - ret.add(convertedValueToType(cleanX[0], item, tp, aFormat)) + ret.add(convertedValueToType(x.a[0], item, tp, aFormat)) push newBlock(ret) builtin "with", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "create closure-style block by embedding given words", args = { @@ -1442,13 +1441,12 @@ proc defineSymbols*() = ; the multiple of 10 is 20 """: #======================================================= - var blk: ValueArray = cleanedBlock(y.a) + var blk: ValueArray = y.a if x.kind == Literal: blk.insert(FetchSym(x.s)) blk.insert(newLabel(x.s)) else: - ensureCleaned(x) - for item in cleanX: + for item in x.a: blk.insert(FetchSym(item.s)) blk.insert(newLabel(item.s)) diff --git a/src/library/Core.nim b/src/library/Core.nim index 7fcf010df6..3612995a68 100644 --- a/src/library/Core.nim +++ b/src/library/Core.nim @@ -39,6 +39,7 @@ proc defineSymbols*() = builtin "alias", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "assign symbol to given function", args = { @@ -84,6 +85,7 @@ proc defineSymbols*() = builtin "break", alias = unaliased, + op = opBreak, rule = PrefixPrecedence, description = "break out of current block or loop", args = NoArgs, @@ -109,6 +111,7 @@ proc defineSymbols*() = builtin "call", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "call function with given list of parameters", args = { @@ -193,7 +196,8 @@ proc defineSymbols*() = fun.action()() builtin "case", - alias = unaliased, + alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "initiate a case block to check for different cases", args = { @@ -217,6 +221,7 @@ proc defineSymbols*() = builtin "coalesce", alias = doublequestion, + op = opNop, rule = InfixPrecedence, description = "if first value is null or false, return second value; otherwise return the first one", args = { @@ -236,6 +241,7 @@ proc defineSymbols*() = builtin "continue", alias = unaliased, + op = opContinue, rule = PrefixPrecedence, description = "immediately continue with next iteration", args = NoArgs, @@ -267,6 +273,7 @@ proc defineSymbols*() = # labels: bug, critical, library, values builtin "do", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "evaluate and execute given code", args = { @@ -351,6 +358,7 @@ proc defineSymbols*() = builtin "dup", alias = thickarrowleft, + op = opNop, rule = PrefixPrecedence, description = "duplicate the top of the stack and convert non-returning call to a do-return call", args = { @@ -376,6 +384,7 @@ proc defineSymbols*() = builtin "else", alias = unaliased, + op = opElse, rule = PrefixPrecedence, description = "perform action, if last condition was not true", args = { @@ -401,6 +410,7 @@ proc defineSymbols*() = builtin "ensure", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "assert given condition is true, or exit", args = { @@ -422,6 +432,7 @@ proc defineSymbols*() = builtin "if", alias = unaliased, + op = opIf, rule = PrefixPrecedence, description = "perform action, if given condition is not false or null", args = { @@ -443,6 +454,7 @@ proc defineSymbols*() = builtin "if?", alias = unaliased, + op = opIfE, rule = PrefixPrecedence, description = "perform action, if given condition is not false or null and return condition result", args = { @@ -479,6 +491,7 @@ proc defineSymbols*() = builtin "let", alias = colon, + op = opNop, rule = InfixPrecedence, description = "set symbol to given value", args = { @@ -516,19 +529,18 @@ proc defineSymbols*() = """: #======================================================= if x.kind==Block: - ensureCleaned(x) if y.kind==Block: - ensureCleaned(y) - for i,w in pairs(cleanX): - SetSym(w.s, cleanY[i], safe=true) + for i,w in pairs(x.a): + SetSym(w.s, y.a[i], safe=true) else: - for w in items(cleanX): + for w in items(x.a): SetSym(w.s, y, safe=true) else: SetInPlace(y, safe=true) builtin "new", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "create new value by cloning given one", args = { @@ -557,6 +569,7 @@ proc defineSymbols*() = builtin "pop", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "pop top values from stack", args = { @@ -598,6 +611,7 @@ proc defineSymbols*() = builtin "return", alias = unaliased, + op = opReturn, rule = PrefixPrecedence, description = "return given value from current function", args = { @@ -622,6 +636,7 @@ proc defineSymbols*() = builtin "switch", alias = question, + op = opSwitch, rule = InfixPrecedence, description = "if condition is not false or null perform given action, otherwise perform alternative action", args = { @@ -647,6 +662,7 @@ proc defineSymbols*() = builtin "try", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "perform action and catch possible errors", args = { @@ -675,6 +691,7 @@ proc defineSymbols*() = builtin "try?", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "perform action, catch possible errors and return status", args = { @@ -709,6 +726,7 @@ proc defineSymbols*() = builtin "unless", alias = unaliased, + op = opUnless, rule = PrefixPrecedence, description = "perform action, if given condition is false or null", args = { @@ -730,6 +748,7 @@ proc defineSymbols*() = builtin "unless?", alias = unaliased, + op = opUnlessE, rule = PrefixPrecedence, description = "perform action, if given condition is false or null and return condition result", args = { @@ -766,6 +785,7 @@ proc defineSymbols*() = builtin "until", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "execute action until the given condition is not false or null", args = { @@ -810,6 +830,7 @@ proc defineSymbols*() = builtin "var", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "get symbol value by given name", args = { @@ -832,6 +853,7 @@ proc defineSymbols*() = builtin "when?", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "check if a specific condition is fulfilled and, if so, execute given action", args = { @@ -850,14 +872,13 @@ proc defineSymbols*() = #======================================================= let z = pop() if isFalse(z): - ensureCleaned(x) let top = sTop() var newb: Value = newBlock() for old in top.a: newb.a.add(old) - for cond in cleanX: + for cond in x.a: newb.a.add(cond) execUnscoped(newb) @@ -872,6 +893,7 @@ proc defineSymbols*() = builtin "while", alias = unaliased, + op = opWhile, rule = PrefixPrecedence, description = "execute action while the given condition is is not false or null", args = { diff --git a/src/library/Crypto.nim b/src/library/Crypto.nim index cec166377e..6449039b12 100644 --- a/src/library/Crypto.nim +++ b/src/library/Crypto.nim @@ -45,6 +45,7 @@ proc defineSymbols*() = builtin "crc", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "calculate the CRC32 polynomial of given string", args = { @@ -65,6 +66,7 @@ proc defineSymbols*() = builtin "decode", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "encode given value (default: base-64)", args = { @@ -100,6 +102,7 @@ proc defineSymbols*() = # labels: library, open discussion builtin "encode", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "decode given value (default: base-64)", args = { @@ -173,6 +176,7 @@ proc defineSymbols*() = # labels: library,enhancement,open discussion,web builtin "digest", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "get digest for given value (default: MD5)", args = { @@ -205,6 +209,7 @@ proc defineSymbols*() = builtin "hash", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "get hash for given value", args = { diff --git a/src/library/Databases.nim b/src/library/Databases.nim index 62f4516e16..8bd88108e7 100644 --- a/src/library/Databases.nim +++ b/src/library/Databases.nim @@ -51,6 +51,7 @@ proc defineSymbols*() = builtin "close", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "close given database", args = { @@ -73,6 +74,7 @@ proc defineSymbols*() = builtin "open", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "opens a new database connection and returns database", args = { @@ -101,6 +103,7 @@ proc defineSymbols*() = builtin "query", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "execute command or block of commands in given database and get returned rows", args = { @@ -135,7 +138,7 @@ proc defineSymbols*() = if (let got = execSqliteDb(x.sqlitedb, y.s, with); got[0]==ValidQueryResult): push(newBlock(got[1])) else: - if (let got = execManySqliteDb(x.sqlitedb, cleanedBlock(y.a).map(proc (v:Value):string = v.s), with); got[0]==ValidQueryResult): + if (let got = execManySqliteDb(x.sqlitedb, y.a.map(proc (v:Value):string = v.s), with); got[0]==ValidQueryResult): push(newBlock(got[1])) if (hadAttr("id")): diff --git a/src/library/Dates.nim b/src/library/Dates.nim index a7e3776b3f..b2d7ce12e1 100644 --- a/src/library/Dates.nim +++ b/src/library/Dates.nim @@ -35,6 +35,7 @@ proc defineSymbols*() = builtin "after", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "get date after given one using interval", args = { @@ -94,6 +95,7 @@ proc defineSymbols*() = builtin "before", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "get date before given one using interval", args = { @@ -156,6 +158,7 @@ proc defineSymbols*() = builtin "friday?", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "check if given date is a Friday", args = { @@ -171,6 +174,7 @@ proc defineSymbols*() = builtin "future?", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "check if given date is in the future", args = { @@ -189,6 +193,7 @@ proc defineSymbols*() = builtin "leap?", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "check if given year is a leap year", args = { @@ -210,6 +215,7 @@ proc defineSymbols*() = builtin "monday?", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "check if given date is a Monday", args = { @@ -225,6 +231,7 @@ proc defineSymbols*() = builtin "now", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "get date/time now", args = NoArgs, @@ -257,6 +264,7 @@ proc defineSymbols*() = builtin "past?", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "check if given date is in the past", args = { @@ -278,6 +286,7 @@ proc defineSymbols*() = builtin "saturday?", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "check if given date is a Saturday", args = { @@ -292,7 +301,8 @@ proc defineSymbols*() = push(newLogical(x.eobj.weekday == dSat)) builtin "sunday?", - alias = unaliased, + alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "check if given date is a Sunday", args = { @@ -308,6 +318,7 @@ proc defineSymbols*() = builtin "thursday?", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "check if given date is a Thursday", args = { @@ -322,7 +333,8 @@ proc defineSymbols*() = push(newLogical(x.eobj.weekday == dThu)) builtin "today?", - alias = unaliased, + alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "check if given date is today", args = { @@ -343,6 +355,7 @@ proc defineSymbols*() = builtin "tuesday?", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "check if given date is a Tuesday", args = { @@ -358,6 +371,7 @@ proc defineSymbols*() = builtin "wednesday?", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "check if given date is a Wednesday", args = { diff --git a/src/library/Files.nim b/src/library/Files.nim index 04392e6039..2e804ad114 100644 --- a/src/library/Files.nim +++ b/src/library/Files.nim @@ -54,6 +54,7 @@ proc defineSymbols*() = builtin "copy", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "copy file at path to given destination", args = { @@ -88,6 +89,7 @@ proc defineSymbols*() = builtin "delete", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "delete file at given path", args = { @@ -114,6 +116,7 @@ proc defineSymbols*() = builtin "exists?", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "check if given file exists", args = { @@ -138,6 +141,7 @@ proc defineSymbols*() = builtin "hidden?", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "check if file/folder at given path is hidden", args = { @@ -156,6 +160,7 @@ proc defineSymbols*() = builtin "move", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "move file at path to given destination", args = { @@ -190,6 +195,7 @@ proc defineSymbols*() = builtin "permissions", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "check permissions of given file", args = { @@ -270,6 +276,7 @@ proc defineSymbols*() = builtin "read", alias = doublearrowleft, + op = opNop, rule = PrefixPrecedence, description = "read file from given path", args = { @@ -360,6 +367,7 @@ proc defineSymbols*() = builtin "rename", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "rename file at path using given new path name", args = { @@ -391,7 +399,8 @@ proc defineSymbols*() = discard builtin "symlink", - alias = unaliased, + alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "create symbolic link of file to given destination", args = { @@ -428,6 +437,7 @@ proc defineSymbols*() = builtin "timestamp", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "get file timestamps", args = { @@ -454,6 +464,7 @@ proc defineSymbols*() = builtin "unzip", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "unzip given archive to destination", args = { @@ -472,6 +483,7 @@ proc defineSymbols*() = builtin "volume", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "get file size for given path", args = { @@ -491,6 +503,7 @@ proc defineSymbols*() = builtin "write", alias = doublearrowright, + op = opNop, rule = PrefixPrecedence, description = "write content to file at given path", args = { @@ -540,6 +553,7 @@ proc defineSymbols*() = builtin "zip", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "zip given files to file at destination", args = { @@ -554,9 +568,7 @@ proc defineSymbols*() = #======================================================= when defined(SAFE): RuntimeError_OperationNotPermitted("zip") - ensureCleaned(y) - - let files: seq[string] = cleanY.map((z)=>z.s) + let files: seq[string] = y.a.map((z)=>z.s) miniz.zip(files, x.s) #======================================= diff --git a/src/library/Io.nim b/src/library/Io.nim index 1a7dc5b8a8..f933cea9de 100644 --- a/src/library/Io.nim +++ b/src/library/Io.nim @@ -51,6 +51,7 @@ proc defineSymbols*() = builtin "clear", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "clear terminal", args = NoArgs, @@ -64,6 +65,7 @@ proc defineSymbols*() = builtin "color", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "get colored version of given string", args = { @@ -128,6 +130,7 @@ proc defineSymbols*() = when not defined(WEB): builtin "cursor", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "turn cursor visibility on/off", args = { @@ -147,6 +150,7 @@ proc defineSymbols*() = builtin "goto", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "move cursor to given coordinates", args = { @@ -176,6 +180,7 @@ proc defineSymbols*() = builtin "input", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "print prompt and get user input", args = { @@ -245,6 +250,7 @@ proc defineSymbols*() = builtin "print", alias = unaliased, + op = opPrint, rule = PrefixPrecedence, description = "print given value to screen with newline", args = { @@ -283,7 +289,8 @@ proc defineSymbols*() = echo $(x) builtin "prints", - alias = unaliased, + alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "print given value to screen", args = { @@ -323,6 +330,7 @@ proc defineSymbols*() = when not defined(WEB): builtin "terminal", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "get info about terminal", args = NoArgs, diff --git a/src/library/Iterators.nim b/src/library/Iterators.nim index 7b286bb5a6..a7ebd510e5 100644 --- a/src/library/Iterators.nim +++ b/src/library/Iterators.nim @@ -267,8 +267,7 @@ template fetchParamsBlock() {.dirty.} = if hasIndex: params.add(withIndex.s) if y.kind != Null: for item in mitems(y.a): - if item.kind != Newline: - params.add(item.s) + params.add(item.s) template prepareIteration(doesAcceptLiterals=true) {.dirty.} = let preevaled = evalOrGet(z) @@ -289,7 +288,8 @@ template fetchIterableItems(doesAcceptLiterals=true, defaultReturn: untyped) {.d var blo = case iterable.kind: of Block,Inline: - cleanedBlockValuesCopy(iterable) + #cleanedBlockValuesCopy(iterable) + iterable.a of Dictionary: iterable.d.flattenedDictionary() of Object: @@ -409,6 +409,7 @@ proc defineSymbols*() = builtin "arrange", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "sort items in collection using given action, in ascending order", args = { @@ -466,6 +467,7 @@ proc defineSymbols*() = builtin "chunk", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "chunk together consecutive items in collection that abide by given predicate", args = { @@ -519,6 +521,7 @@ proc defineSymbols*() = builtin "cluster", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "group together items in collection that abide by given predicate", args = { @@ -577,6 +580,7 @@ proc defineSymbols*() = builtin "collect", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "collect items from given collection condition while is true", args = { @@ -654,6 +658,7 @@ proc defineSymbols*() = builtin "enumerate", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "calculate the number of given collection's items that satisfy condition", args = { @@ -683,6 +688,7 @@ proc defineSymbols*() = builtin "every?", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "check if every item in collection satisfies given condition", args = { @@ -723,6 +729,7 @@ proc defineSymbols*() = builtin "filter", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "get collection's items by filtering those that do not fulfil given condition", args = { @@ -855,6 +862,7 @@ proc defineSymbols*() = builtin "fold", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "left-fold given collection returning accumulator", args = { @@ -970,6 +978,7 @@ proc defineSymbols*() = builtin "gather", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "group items in collection by block result and return as dictionary", args = { @@ -1008,6 +1017,7 @@ proc defineSymbols*() = builtin "loop", alias = unaliased, + op = opLoop, rule = PrefixPrecedence, description = "loop through collection, using given iterator and block", args = { @@ -1074,6 +1084,7 @@ proc defineSymbols*() = builtin "map", alias = unaliased, + op = opMap, rule = PrefixPrecedence, description = "map collection's items by applying given action", args = { @@ -1137,6 +1148,7 @@ proc defineSymbols*() = builtin "maximum", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "get maximum item from collection based on given predicate", args = { @@ -1190,6 +1202,7 @@ proc defineSymbols*() = builtin "minimum", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "get minimum item from collection based on given predicate", args = { @@ -1243,6 +1256,7 @@ proc defineSymbols*() = builtin "select", alias = unaliased, + op = opSelect, rule = PrefixPrecedence, description = "get collection's items that fulfil given condition", args = { @@ -1390,6 +1404,7 @@ proc defineSymbols*() = builtin "some?", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "check if any of collection's items satisfy given condition", args = { diff --git a/src/library/Logic.nim b/src/library/Logic.nim index 60eedfd327..e33da1189f 100644 --- a/src/library/Logic.nim +++ b/src/library/Logic.nim @@ -30,6 +30,7 @@ proc defineSymbols*() = builtin "all?", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "check if all values in given block are true", args = { @@ -46,15 +47,14 @@ proc defineSymbols*() = ; false """: #======================================================= - ensureCleaned(x) # check if empty - if cleanX.len==0: + if x.a.len==0: push(newLogical(false)) return var allOK = true - for item in cleanX: + for item in x.a: var val {.cursor.}: Value if item.kind == Block: execUnscoped(item) @@ -72,6 +72,7 @@ proc defineSymbols*() = builtin "and?", alias = logicaland, + op = opAnd, rule = InfixPrecedence, description = "return the logical AND for the given values", args = { @@ -119,6 +120,7 @@ proc defineSymbols*() = builtin "any?", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "check if any of the values in given block is true", args = { @@ -135,14 +137,13 @@ proc defineSymbols*() = ; false """: #======================================================= - ensureCleaned(x) # check if empty - if cleanX.len==0: + if x.a.len==0: push(newLogical(false)) return var anyOK = false - for item in cleanX: + for item in x.a: var val: Value if item.kind == Block: execUnscoped(item) @@ -165,6 +166,7 @@ proc defineSymbols*() = builtin "false?", alias = unaliased, + op = opNop, rule = InfixPrecedence, description = "returns true if given value is false; otherwise, it returns false", args = { @@ -190,6 +192,7 @@ proc defineSymbols*() = builtin "nand?", alias = logicalnand, + op = opNop, rule = InfixPrecedence, description = "return the logical NAND for the given values", args = { @@ -240,6 +243,7 @@ proc defineSymbols*() = builtin "nor?", alias = unaliased, + op = opNop, rule = InfixPrecedence, description = "return the logical NOR for the given values", args = { @@ -290,6 +294,7 @@ proc defineSymbols*() = builtin "not?", alias = logicalnot, + op = opNot, rule = PrefixPrecedence, description = "return the logical complement of the given value", args = { @@ -314,6 +319,7 @@ proc defineSymbols*() = builtin "or?", alias = logicalor, + op = opOr, rule = InfixPrecedence, description = "return the logical OR for the given values", args = { @@ -366,6 +372,7 @@ proc defineSymbols*() = builtin "true?", alias = unaliased, + op = opNop, rule = InfixPrecedence, description = "returns true if given value is true; otherwise, it returns false", args = { @@ -386,6 +393,7 @@ proc defineSymbols*() = builtin "xnor?", alias = unaliased, + op = opNop, rule = InfixPrecedence, description = "return the logical XNOR for the given values", args = { @@ -426,6 +434,7 @@ proc defineSymbols*() = builtin "xor?", alias = logicalxor, + op = opNop, rule = InfixPrecedence, description = "return the logical XOR for the given values", args = { diff --git a/src/library/Net.nim b/src/library/Net.nim index 3e10cdd2fa..7278da2bf4 100644 --- a/src/library/Net.nim +++ b/src/library/Net.nim @@ -47,6 +47,7 @@ proc defineSymbols*() = builtin "browse", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "open given URL with default browser", args = { @@ -64,6 +65,7 @@ proc defineSymbols*() = builtin "download", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "download file from url to disk", args = { @@ -98,6 +100,7 @@ proc defineSymbols*() = builtin "mail", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "send mail using given title and message to selected recipient", args = { @@ -140,6 +143,7 @@ proc defineSymbols*() = # labels: library,enhancement,open discussion,web builtin "request", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "perform HTTP request to url with given data and get response", args = { @@ -315,6 +319,7 @@ proc defineSymbols*() = builtin "serve", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "start web server using given routes", args = { diff --git a/src/library/Numbers.nim b/src/library/Numbers.nim index 6873ffde74..403bef8a72 100644 --- a/src/library/Numbers.nim +++ b/src/library/Numbers.nim @@ -52,6 +52,7 @@ proc defineSymbols*() = builtin "abs", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "get the absolute value for given integer", args = { @@ -82,6 +83,7 @@ proc defineSymbols*() = builtin "acos", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "calculate the inverse cosine of given angle", args = { @@ -102,6 +104,7 @@ proc defineSymbols*() = builtin "acosh", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "calculate the inverse hyperbolic cosine of given angle", args = { @@ -122,6 +125,7 @@ proc defineSymbols*() = builtin "acsec", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "calculate the inverse cosecant of given angle", args = { @@ -142,6 +146,7 @@ proc defineSymbols*() = builtin "acsech", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "calculate the inverse hyperbolic cosecant of given angle", args = { @@ -162,6 +167,7 @@ proc defineSymbols*() = builtin "actan", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "calculate the inverse cotangent of given angle", args = { @@ -182,6 +188,7 @@ proc defineSymbols*() = builtin "actanh", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "calculate the inverse hyperbolic cotangent of given angle", args = { @@ -202,6 +209,7 @@ proc defineSymbols*() = builtin "angle", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "calculate the phase angle of given number", args = { @@ -218,6 +226,7 @@ proc defineSymbols*() = builtin "asec", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "calculate the inverse secant of given angle", args = { @@ -237,7 +246,8 @@ proc defineSymbols*() = processTrigonometric(arcsec) builtin "asech", - alias = unaliased, + alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "calculate the inverse hyperbolic secant of given angle", args = { @@ -258,6 +268,7 @@ proc defineSymbols*() = builtin "asin", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "calculate the inverse sine of given angle", args = { @@ -278,6 +289,7 @@ proc defineSymbols*() = builtin "asinh", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "calculate the inverse hyperbolic sine of given angle", args = { @@ -298,6 +310,7 @@ proc defineSymbols*() = builtin "atan", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "calculate the inverse tangent of given angle", args = { @@ -318,6 +331,7 @@ proc defineSymbols*() = builtin "atan2", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "calculate the inverse tangent of y / x", args = { @@ -335,6 +349,7 @@ proc defineSymbols*() = builtin "atanh", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "calculate the inverse hyperbolic tangent of given angle", args = { @@ -355,6 +370,7 @@ proc defineSymbols*() = builtin "ceil", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "calculate the smallest integer not smaller than given value", args = { @@ -373,6 +389,7 @@ proc defineSymbols*() = builtin "clamp", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "force value within given range", args = { @@ -397,6 +414,7 @@ proc defineSymbols*() = builtin "conj", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "calculate the complex conjugate of given number", args = { @@ -413,6 +431,7 @@ proc defineSymbols*() = builtin "cos", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "calculate the cosine of given angle", args = { @@ -433,6 +452,7 @@ proc defineSymbols*() = builtin "cosh", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "calculate the hyperbolic cosine of given angle", args = { @@ -453,6 +473,7 @@ proc defineSymbols*() = builtin "csec", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "calculate the cosecant of given angle", args = { @@ -473,6 +494,7 @@ proc defineSymbols*() = builtin "csech", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "calculate the hyperbolic cosecant of given angle", args = { @@ -493,6 +515,7 @@ proc defineSymbols*() = builtin "ctan", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "calculate the cotangent of given angle", args = { @@ -512,7 +535,8 @@ proc defineSymbols*() = processTrigonometric(cot) builtin "ctanh", - alias = unaliased, + alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "calculate the hyperbolic cotangent of given angle", args = { @@ -533,6 +557,7 @@ proc defineSymbols*() = builtin "denominator", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "get the denominator of given number", args = { @@ -562,6 +587,7 @@ proc defineSymbols*() = builtin "digits", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "get array of digits of given number", args = { @@ -602,6 +628,7 @@ proc defineSymbols*() = builtin "even?", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "check if given number is even", args = { @@ -620,6 +647,7 @@ proc defineSymbols*() = builtin "exp", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "calculate the exponential function for given value", args = { @@ -641,6 +669,7 @@ proc defineSymbols*() = builtin "factorial", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "calculate the factorial of given value", args = { @@ -658,6 +687,7 @@ proc defineSymbols*() = builtin "factors", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "get list of factors for given integer", args = { @@ -699,6 +729,7 @@ proc defineSymbols*() = builtin "floor", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "calculate the largest integer not greater than given value", args = { @@ -718,6 +749,7 @@ proc defineSymbols*() = when not defined(WEB): builtin "gamma", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "calculate the gamma function for given value", args = { @@ -735,6 +767,7 @@ proc defineSymbols*() = builtin "gcd", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "calculate greatest common divisor for given collection of integers", args = { @@ -746,31 +779,31 @@ proc defineSymbols*() = print gcd [48 60 120] ; 12 """: #======================================================= - ensureCleaned(x) - var current = cleanX[0] + var current = x.a[0] var i = 1 # TODO(Numbers\gcd) not working for Web builds # labels: web,enhancement - while iz.a) + let blk = x.a.map((z)=>z.a) push(newBlock(cartesianProduct(blk).map((z) => newBlock(z)))) else: var product = I1.copyValue @@ -1084,18 +1126,18 @@ proc defineSymbols*() = product *= item push(product) else: - ensureCleaned(x) - if cleanX.len==0: push(I0.copyValue) + if x.a.len==0: push(I0.copyValue) else: var i = 0 - while i true """: #======================================================= - push(newLogical(disjoint(toOrderedSet(cleanedBlock(x.a)), toOrderedSet(cleanedBlock(y.a))))) + push(newLogical(disjoint(toOrderedSet(x.a), toOrderedSet(y.a)))) builtin "intersect?", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "check if given sets intersect (they have at least one common element)", args = { @@ -134,7 +137,7 @@ proc defineSymbols*() = ; => false """: #======================================================= - let res = intersection(toOrderedSet(cleanedBlock(x.a)), toOrderedSet(cleanedBlock(y.a))) + let res = intersection(toOrderedSet(x.a), toOrderedSet(y.a)) if len(res) >= 0: push(VTRUE) else: @@ -142,6 +145,7 @@ proc defineSymbols*() = builtin "intersection", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "return the intersection of given sets", args = { @@ -162,12 +166,13 @@ proc defineSymbols*() = #======================================================= if x.kind==Literal: ensureInPlace() - SetInPlace(newBlock(toSeq(intersection(toOrderedSet(cleanedBlock(InPlaced.a)), toOrderedSet(cleanedBlock(y.a)))))) + SetInPlace(newBlock(toSeq(intersection(toOrderedSet(InPlaced.a), toOrderedSet(y.a))))) else: - push(newBlock(toSeq(intersection(toOrderedSet(cleanedBlock(x.a)), toOrderedSet(cleanedBlock(y.a)))))) + push(newBlock(toSeq(intersection(toOrderedSet(x.a), toOrderedSet(y.a))))) builtin "powerset", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "return the powerset of given set", args = { @@ -182,12 +187,13 @@ proc defineSymbols*() = #======================================================= if x.kind==Literal: ensureInPlace() - SetInPlace(newBlock(toSeq(powerset(toOrderedSet(cleanedBlock(InPlaced.a)))).map((hs) => newBlock(toSeq(hs))))) + SetInPlace(newBlock(toSeq(powerset(toOrderedSet(InPlaced.a))).map((hs) => newBlock(toSeq(hs))))) else: - push(newBlock(toSeq(powerset(toOrderedSet(cleanedBlock(x.a))).map((hs) => newBlock(toSeq(hs)))))) + push(newBlock(toSeq(powerset(toOrderedSet(x.a)).map((hs) => newBlock(toSeq(hs)))))) builtin "subset?", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "check if given set is a subset of second set", args = { @@ -220,8 +226,8 @@ proc defineSymbols*() = push(newLogical(false)) else: var contains = true - let xblk = cleanedBlock(x.a) - let yblk = cleanedBlock(y.a) + let xblk = x.a + let yblk = y.a for item in xblk: if item notin yblk: contains = false @@ -233,8 +239,8 @@ proc defineSymbols*() = push(newLogical(true)) else: var contains = true - let xblk = cleanedBlock(x.a) - let yblk = cleanedBlock(y.a) + let xblk = x.a + let yblk = y.a for item in xblk: if item notin yblk: contains = false @@ -244,6 +250,7 @@ proc defineSymbols*() = builtin "superset?", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "check if given set is a superset of second set", args = { @@ -276,8 +283,8 @@ proc defineSymbols*() = push(newLogical(false)) else: var contains = true - let xblk = cleanedBlock(x.a) - let yblk = cleanedBlock(y.a) + let xblk = x.a + let yblk = y.a for item in yblk: if item notin xblk: contains = false @@ -289,8 +296,8 @@ proc defineSymbols*() = push(newLogical(true)) else: var contains = true - let xblk = cleanedBlock(x.a) - let yblk = cleanedBlock(y.a) + let xblk = x.a + let yblk = y.a for item in yblk: if item notin xblk: contains = false @@ -300,6 +307,7 @@ proc defineSymbols*() = builtin "union", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "return the union of given sets", args = { @@ -320,9 +328,9 @@ proc defineSymbols*() = #======================================================= if x.kind==Literal: ensureInPlace() - SetInPlace(newBlock(toSeq(union(toOrderedSet(cleanedBlock(InPlaced.a)), toOrderedSet(cleanedBlock(y.a)))))) + SetInPlace(newBlock(toSeq(union(toOrderedSet(InPlaced.a), toOrderedSet(y.a))))) else: - push(newBlock(toSeq(union(toOrderedSet(cleanedBlock(x.a)), toOrderedSet(cleanedBlock(y.a)))))) + push(newBlock(toSeq(union(toOrderedSet(x.a), toOrderedSet(y.a))))) #======================================= # Add Library diff --git a/src/library/Sockets.nim b/src/library/Sockets.nim index ea2794e01b..56f6ab603d 100644 --- a/src/library/Sockets.nim +++ b/src/library/Sockets.nim @@ -45,7 +45,8 @@ proc defineSymbols*() = when not defined(WEB): builtin "accept", - alias = unaliased, + alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "accept incoming connection and return corresponding socket", args = { @@ -73,6 +74,7 @@ proc defineSymbols*() = builtin "connect", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "create new socket connection to given server port", args = { @@ -120,6 +122,7 @@ proc defineSymbols*() = builtin "listen", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "start listening on given port and return new socket", args = { @@ -157,6 +160,7 @@ proc defineSymbols*() = builtin "receive", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "receive line of data from selected socket", args = { @@ -202,6 +206,7 @@ proc defineSymbols*() = builtin "send", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "send given message to selected socket", args = { @@ -232,6 +237,7 @@ proc defineSymbols*() = builtin "send?", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "send given message to selected socket and return true if successful", args = { @@ -257,6 +263,7 @@ proc defineSymbols*() = builtin "unplug", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "close given socket", args = { diff --git a/src/library/Statistics.nim b/src/library/Statistics.nim index a1fdf27360..5aab802675 100644 --- a/src/library/Statistics.nim +++ b/src/library/Statistics.nim @@ -38,6 +38,7 @@ proc defineSymbols*() = builtin "average", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "get average from given collection of numbers", args = { @@ -52,11 +53,10 @@ proc defineSymbols*() = #======================================================= var res = F0.copyValue if x.kind == Block: - ensureCleaned(x) - for num in cleanX: + for num in x.a: res += num - res //= newFloating(cleanX.len) + res //= newFloating(x.a.len) else: for item in items(x.rng): res += item @@ -67,6 +67,7 @@ proc defineSymbols*() = builtin "deviation", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "get population standard deviation of given collection of numbers", args = { @@ -87,14 +88,14 @@ proc defineSymbols*() = deviation.sample arr2 ; => 45.65847597731914 """: #======================================================= - ensureCleaned(x) if (hadAttr("sample")): - push newFloating(standardDeviationS(cleanX.map((z)=>asFloat(z)))) + push newFloating(standardDeviationS(x.a.map((z)=>asFloat(z)))) else: - push newFloating(standardDeviation(cleanX.map((z)=>asFloat(z)))) + push newFloating(standardDeviation(x.a.map((z)=>asFloat(z)))) builtin "kurtosis", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "get population kurtosis of given collection of numbers", args = { @@ -115,14 +116,14 @@ proc defineSymbols*() = kurtosis.sample arr2 ; => 0.5886192422439724 """: #======================================================= - ensureCleaned(x) if (hadAttr("sample")): - push newFloating(kurtosisS(cleanX.map((z)=>asFloat(z)))) + push newFloating(kurtosisS(x.a.map((z)=>asFloat(z)))) else: - push newFloating(kurtosis(cleanX.map((z)=>asFloat(z)))) + push newFloating(kurtosis(x.a.map((z)=>asFloat(z)))) builtin "median", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "get median from given collection of numbers", args = { @@ -138,20 +139,20 @@ proc defineSymbols*() = ; 3.5 """: #======================================================= - ensureCleaned(x) - if cleanX.len==0: + if x.a.len==0: push(VNULL) else: - let first = cleanX[(cleanX.len-1) div 2] - let second = cleanX[((cleanX.len-1) div 2)+1] + let first = x.a[(x.a.len-1) div 2] + let second = x.a[((x.a.len-1) div 2)+1] - if cleanX.len mod 2 == 1: + if x.a.len mod 2 == 1: push(first) else: push((first + second)//I2) builtin "skewness", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "get population skewness of given collection of numbers", args = { @@ -172,14 +173,14 @@ proc defineSymbols*() = skewness.sample arr2 ; => 1.40680083744453 """: #======================================================= - ensureCleaned(x) if (hadAttr("sample")): - push newFloating(skewnessS(cleanX.map((z)=>asFloat(z)))) + push newFloating(skewnessS(x.a.map((z)=>asFloat(z)))) else: - push newFloating(skewness(cleanX.map((z)=>asFloat(z)))) + push newFloating(skewness(x.a.map((z)=>asFloat(z)))) builtin "variance", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "get population variance of given collection of numbers", args = { @@ -200,11 +201,10 @@ proc defineSymbols*() = variance.sample arr2 ; => 2084.696428571428 """: #======================================================= - ensureCleaned(x) if (hadAttr("sample")): - push newFloating(varianceS(cleanX.map((z)=>asFloat(z)))) + push newFloating(varianceS(x.a.map((z)=>asFloat(z)))) else: - push newFloating(variance(cleanX.map((z)=>asFloat(z)))) + push newFloating(variance(x.a.map((z)=>asFloat(z)))) #======================================= # Add Library diff --git a/src/library/Strings.nim b/src/library/Strings.nim index 8c492d811b..fa251c91b6 100644 --- a/src/library/Strings.nim +++ b/src/library/Strings.nim @@ -67,6 +67,7 @@ proc defineSymbols*() = # labels: library, enhancement, cleanup, open discussion builtin "alphabet", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "get dictionary-index charset for given locale", args = { @@ -110,6 +111,7 @@ proc defineSymbols*() = builtin "ascii?", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "check if given character/string is in ASCII", args = { @@ -142,6 +144,7 @@ proc defineSymbols*() = builtin "capitalize", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "convert given string to capitalized", args = { @@ -167,6 +170,7 @@ proc defineSymbols*() = builtin "escape", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "escape given string", args = { @@ -226,6 +230,7 @@ proc defineSymbols*() = builtin "indent", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "indent each line of given text", args = { @@ -267,6 +272,7 @@ proc defineSymbols*() = builtin "jaro", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "calculate Jaro distance/similarity between given strings", args = { @@ -288,6 +294,7 @@ proc defineSymbols*() = builtin "join", alias = unaliased, + op = opJoin, rule = PrefixPrecedence, description = "join collection of values into string", args = { @@ -321,8 +328,7 @@ proc defineSymbols*() = ensureInPlace() SetInPlace(newString(joinPath(InPlaced.a.map(proc (v:Value):string = $(v))))) else: - ensureCleaned(x) - push(newString(joinPath(cleanX.map(proc (v:Value):string = $(v))))) + push(newString(joinPath(x.a.map(proc (v:Value):string = $(v))))) else: var sep: string if checkAttr("with"): @@ -332,11 +338,11 @@ proc defineSymbols*() = ensureInPlace() SetInPlace(newString(InPlaced.a.map(proc (v:Value):string = $(v)).join(sep))) else: - ensureCleaned(x) - push(newString(cleanX.map(proc (v:Value):string = $(v)).join(sep))) + push(newString(x.a.map(proc (v:Value):string = $(v)).join(sep))) builtin "levenshtein", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "calculate Levenshtein distance/similarity between given strings", args = { @@ -367,6 +373,7 @@ proc defineSymbols*() = builtin "lower", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "convert given string to lowercase", args = { @@ -396,6 +403,7 @@ proc defineSymbols*() = builtin "lower?", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "check if given string is lowercase", args = { @@ -428,6 +436,7 @@ proc defineSymbols*() = when not defined(WEB): builtin "match", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "get matches within string, using given regular expression", args = { @@ -558,6 +567,7 @@ proc defineSymbols*() = # labels: library, web, bug builtin "match?", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "check if string matches given regular expression", args = { @@ -603,6 +613,7 @@ proc defineSymbols*() = builtin "numeric?", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "check if given string is numeric", args = { @@ -628,6 +639,7 @@ proc defineSymbols*() = builtin "outdent", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "outdent each line of given text, by using minimum shared indentation", args = { @@ -682,6 +694,7 @@ proc defineSymbols*() = builtin "pad", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "align string by adding given padding", args = { @@ -728,6 +741,7 @@ proc defineSymbols*() = builtin "prefix?", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "check if string starts with given prefix", args = { @@ -752,6 +766,7 @@ proc defineSymbols*() = # labels: enhancement,library,web builtin "render", alias = tilde, + op = opNop, rule = PrefixPrecedence, description = "render template with |string| interpolation", args = { @@ -841,6 +856,7 @@ proc defineSymbols*() = builtin "replace", alias = unaliased, + op = opReplace, rule = PrefixPrecedence, description = "replace every matched substring/s by given replacement string and return result", args = { @@ -896,6 +912,7 @@ proc defineSymbols*() = builtin "strip", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "strip whitespace from given string", args = { @@ -932,6 +949,7 @@ proc defineSymbols*() = builtin "suffix?", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "check if string ends with given suffix", args = { @@ -952,6 +970,7 @@ proc defineSymbols*() = builtin "translate", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "takes a dictionary of translations and replaces each instance sequentially", args = { @@ -980,6 +999,7 @@ proc defineSymbols*() = builtin "truncate", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "truncate string at given length", args = { @@ -1024,6 +1044,7 @@ proc defineSymbols*() = builtin "upper", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "convert given string to uppercase", args = { @@ -1053,6 +1074,7 @@ proc defineSymbols*() = builtin "upper?", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "check if given string is uppercase", args = { @@ -1082,6 +1104,7 @@ proc defineSymbols*() = builtin "wordwrap", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "word wrap a given string", args = { @@ -1122,6 +1145,7 @@ proc defineSymbols*() = builtin "whitespace?", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "check if given string consists only of whitespace", args = { diff --git a/src/library/System.nim b/src/library/System.nim index b2fb97f30d..73cf465ea5 100644 --- a/src/library/System.nim +++ b/src/library/System.nim @@ -70,6 +70,7 @@ proc defineSymbols*() = # labels: library,enhancement,open discussion,web builtin "env", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "get environment variables", args = NoArgs, @@ -100,6 +101,7 @@ proc defineSymbols*() = # labels: library,enhancement,web builtin "execute", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "execute given shell command", args = { @@ -170,6 +172,7 @@ proc defineSymbols*() = builtin "exit", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "exit program", args = NoArgs, @@ -194,6 +197,7 @@ proc defineSymbols*() = builtin "panic", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "exit program with error message", args = { @@ -233,6 +237,7 @@ proc defineSymbols*() = # labels: library,enhancement,web builtin "pause", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "pause program's execution~for the given amount of time", args = { @@ -261,6 +266,7 @@ proc defineSymbols*() = builtin "process", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "get information on current process/program", args = NoArgs, @@ -306,6 +312,7 @@ proc defineSymbols*() = when not defined(WEB): builtin "superuser?", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "check if current user has administrator/root privileges", args = NoArgs, @@ -322,50 +329,52 @@ proc defineSymbols*() = push newLogical(isAdmin()) builtin "sys", - alias = unaliased, - rule = PrefixPrecedence, - description = "get current system information", - args = NoArgs, - attrs = NoAttrs, - returns = {Dictionary}, - example = """ - inspect sys - ;[ :dictionary - ; author : Yanis Zafirópulos :string - ; copyright : (c) 2019-2022 :string - ; version : 0.9.80 :version - ; build : 3246 :integer - ; buildDate : [ :date - ; hour : 11 :integer - ; minute : 27 :integer - ; second : 54 :integer - ; nanosecond : 389131000 :integer - ; day : 7 :integer - ; Day : Wednesday :string - ; days : 340 :integer - ; month : 12 :integer - ; Month : December :string - ; year : 2022 :integer - ; utc : -3600 :integer - ; ] - ; deps : [ :dictionary - ; gmp : 6.2.1 :version - ; mpfr : 4.1.0 :version - ; sqlite : 3.37.0 :version - ; pcre : 8.45.0 :version - ; ] - ; binary : /Users/drkameleon/OpenSource/arturo-lang/arturo/bin/arturo :string - ; cpu : amd64 :string - ; os : macosx :string - ; release : full :literal - ;] - """: - #======================================================= - push newDictionary(getSystemInfo()) + alias = unaliased, + op = opNop, + rule = PrefixPrecedence, + description = "get current system information", + args = NoArgs, + attrs = NoAttrs, + returns = {Dictionary}, + example = """ + inspect sys + ;[ :dictionary + ; author : Yanis Zafirópulos :string + ; copyright : (c) 2019-2022 :string + ; version : 0.9.80 :version + ; build : 3246 :integer + ; buildDate : [ :date + ; hour : 11 :integer + ; minute : 27 :integer + ; second : 54 :integer + ; nanosecond : 389131000 :integer + ; day : 7 :integer + ; Day : Wednesday :string + ; days : 340 :integer + ; month : 12 :integer + ; Month : December :string + ; year : 2022 :integer + ; utc : -3600 :integer + ; ] + ; deps : [ :dictionary + ; gmp : 6.2.1 :version + ; mpfr : 4.1.0 :version + ; sqlite : 3.37.0 :version + ; pcre : 8.45.0 :version + ; ] + ; binary : /Users/drkameleon/OpenSource/arturo-lang/arturo/bin/arturo :string + ; cpu : amd64 :string + ; os : macosx :string + ; release : full :literal + ;] + """: + #======================================================= + push newDictionary(getSystemInfo()) when not defined(WEB): builtin "terminate", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "kill process with given id", args = { diff --git a/src/library/Ui.nim b/src/library/Ui.nim index f9e5f04338..d07650d46b 100644 --- a/src/library/Ui.nim +++ b/src/library/Ui.nim @@ -44,7 +44,8 @@ proc defineSymbols*() = when not defined(NODIALOGS): builtin "alert", - alias = unaliased, + alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "show notification with given title and message", args = { @@ -80,6 +81,7 @@ proc defineSymbols*() = builtin "clip", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "set clipboard content to given text", args = { @@ -97,6 +99,7 @@ proc defineSymbols*() = builtin "dialog", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "show a file selection dialog and return selection", args = { @@ -124,6 +127,7 @@ proc defineSymbols*() = builtin "popup", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "show popup dialog with given title and message and return result", args = { @@ -198,6 +202,7 @@ proc defineSymbols*() = builtin "unclip", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "get clipboard content", args = NoArgs, @@ -218,6 +223,7 @@ proc defineSymbols*() = builtin "webview", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "show webview window with given url or html source", args = { @@ -322,6 +328,7 @@ proc defineSymbols*() = builtin "eval", alias = unaliased, + op = opNop, rule = PrefixPrecedence, description = "Evaluate JavaScript code in active webview", args = { diff --git a/src/vm/ast.nim b/src/vm/ast.nim new file mode 100644 index 0000000000..12c6c1c0cb --- /dev/null +++ b/src/vm/ast.nim @@ -0,0 +1,881 @@ +#======================================================= +# Arturo +# Programming Language + Bytecode VM compiler +# (c) 2019-2023 Yanis Zafirópulos +# +# @file: vm/ast.nim +#======================================================= + +## This module contains the AST implementation for the VM. +## +## In a few words, it: +## - takes a Block of values coming from the parser +## - transforms it into an AST tree with semantics +## ready for the evaluator +## +## The main entry point is ``generateAst``. + +# Pending task for new AST+Eval: +# - [x] make Attribute's work +# - [x] make AttributeLabel's work +# - [x] make Path's work +# - [x] make PathLabel's work +# - [x] make Newline values work +# - [x] create new opCode for append +# - [x] clean up opCode's +# - [x] attach opCode's to built-in function (for faster lookups) +# - [x] optimize appends +# - [x] optimize returns when it's the last statement of a function block +# - [x] make labels store new functions in TmpArities +# - [x] make labels unstore overwritten functions in TmpArities +# - [x] make if/if?/else/while/switch work +# - [x] correctly process to :string/:integer +# - [ ] make sure all this left/right (when checking for optimization) are not Newline's + +#======================================= +# Libraries +#======================================= + +import sequtils, strutils +import sugar, tables, unicode, std/with + +import vm/[globals, values/value, values/comparison, values/types] +import vm/values/printable +import vm/values/custom/[vbinary, vcolor, vcomplex, vlogical, vrational, vsymbol, vversion] + +import vm/profiler + +import vm/bytecode +#======================================= +# Types +#======================================= + +type + # abstract syntax tree definition + NodeKind* = enum + RootNode # Root node of the AST + + NewlineNode # Newline node + + # TerminalNode + ConstantValue # Terminal node of the AST containing a value + VariableLoad # Load a variable + + # CallNode + AttributeNode # Either an Attribute or an AttributeLabel + VariableStore # Store a variable + + OtherCall # Call to a function that is not a builtin + BuiltinCall # Call to a builtin function + SpecialCall # Call to a special function + + NodeArray* = seq[Node] + + Node* = ref object + case kind*: NodeKind: + of RootNode, ConstantValue, VariableLoad: + discard + of NewlineNode: + line*: uint32 + else: + op*: OpCode + arity*: int8 + params*: int8 + + value*: Value + parent*: Node + children*: NodeArray + + NodeObj = typeof(Node()[]) + +# Benchmarking +{.hints: on.} +{.hint: "Node's inner type is currently " & $sizeof(NodeObj) & ".".} +{.hints: off.} + +#======================================= +# Variables +#======================================= + +var + TmpArities : Table[string,int8] + ArrowBlock : seq[ValueArray] + OldChild : Node + OldParent : Node + +#======================================= +# Constants +#======================================= + +const + NoStartingLine = 1896618966'u32 + + TerminalNode* : set[NodeKind] = {ConstantValue, VariableLoad} + CallNode* : set[NodeKind] = {AttributeNode..SpecialCall} + +#======================================= +# Forward declarations +#======================================= + +proc dumpNode*(node: Node, level = 0, single: static bool=false, showNewlines: static bool=false): string + +#======================================= +# Helpers +#======================================= + +#------------------------ +# Tree manipulation +#------------------------ + +func setOnlyChild(node: Node, child: Node) {.enforceNoRaises.} = + child.parent = node + node.children.setLen(1) + node.children[0] = child + +func addChild*(node: Node, child: Node) {.enforceNoRaises.} = + child.parent = node + node.children.add(child) + if node.kind in CallNode and child.kind notin {NewlineNode, AttributeNode}: + node.params += 1 + +func addChildren*(node: Node, children: openArray[Node]) {.enforceNoRaises.} = + for child in children: + node.addChild(child) + +func deleteNode(node: Node) = + if not node.parent.isNil: + node.parent.children.delete(node.parent.children.find(node)) + node.parent = nil + +proc replaceNode(node: Node, newNode: Node) = + newNode.parent = node.parent + node.parent.children[node.parent.children.find(node)] = newNode + +proc addSibling(node: Node, newNode: Node) = + newNode.parent = node.parent + node.parent.children.insert(newNode, node.parent.children.find(node)+1) + +proc isLastChild(node: Node): bool = + var j = node.parent.children.len-1 + while j >= 0 and node.parent.children[j].kind == NewlineNode: + j -= 1 + return node.parent.children[j] == node + +#------------------------ +# Iterators +#------------------------ + +iterator traverse*(node: Node): Node = + # reverse post-order traversal (RLN) + var preStack = @[node] + var postStack: seq[Node] + + while preStack.len > 0: + var subnode = preStack.pop() + postStack.add(subnode) + var j = subnode.children.len-1 + while j >= 0: + preStack.add(subnode.children[j]) + j -= 1 + + while postStack.len > 0: + var subnode = postStack.pop() + yield subnode + +#------------------------ +# Misc +#------------------------ + +template isSymbol(val: Value, sym: VSymbol): bool = + val.kind == Symbol and val.m == sym + +#======================================= +# Constructors +#======================================= + +template newRootNode(): Node = + Node( + kind: RootNode + ) + +template newTerminalNode(kn: NodeKind, va: Value): Node = + Node( + kind: kn, + value: va + ) + +template newConstant(v: Value): Node = + newTerminalNode(ConstantValue, v) + +template newVariable(v: Value): Node = + newTerminalNode(VariableLoad, v) + +template newCallNode(kn: NodeKind, ar: int8, va: Value, oper: OpCode = opNop): Node = + Node( + kind: kn, + arity: ar, + op: oper, + value: va + ) + +func copyNode(node: Node): Node = + result = Node(kind: node.kind) + case node.kind: + of NewlineNode: + result.line = node.line + of ConstantValue, VariableLoad: + result.value = node.value + else: + result.op = node.op + result.arity = node.arity + result.params = node.params + result.value = node.value + result.addChildren(node.children) + +#======================================= +# Methods +#======================================= + +proc processBlock*( + root: Node, + blok: Value, + start = 0, + startingLine: uint32 = NoStartingLine, + asDictionary: bool = false, + asFunction: bool = false, + processingArrow: static bool = false +): int = + var i: int = start + var nLen: int = blok.a.len + + var currentLine: uint32 = + if startingLine == NoStartingLine: + if i < nLen: + blok.a[i].ln + else: + 0 + else: + startingLine + + var current = root + + when processingArrow: + ArrowBlock.add(@[]) + + proc addCall(target: var Node, name: string, arity: int8 = -1, fun: Value = nil) + + #------------------------ + # Optimization + #------------------------ + + proc optimizeAdd(target: var Node) {.enforceNoRaises.} = + var left = target.children[0] + var right = target.children[1] + + if left.kind == ConstantValue and left.value.kind in {Integer, Floating}: + # Constant folding + if right.kind == ConstantValue and right.value.kind in {Integer, Floating}: + hookOptimProfiler("add (CF)") + target.replaceNode(newConstant(left.value + right.value)) + # Convert 1 + X -> inc X + elif right.kind==VariableLoad and left.kind==ConstantValue and left.value == I1: + hookOptimProfiler("add (inc)") + target.op = opInc + target.arity = 1 + target.setOnlyChild(right) + + # Convert X + 1 -> inc X + elif left.kind==VariableLoad and right.kind==ConstantValue and right.value == I1: + hookOptimProfiler("add (inc)") + target.op = opInc + target.arity = 1 + target.setOnlyChild(left) + + # Convert X + X * Y -> X * (1 + Y) and + # X + Y * X -> X * (Y + 1) + elif left.kind == VariableLoad and right.op == opMul: + if right.children[0].kind == VariableLoad and right.children[0].value == left.value: + hookOptimProfiler("add (distributive)") + target.op = opMul + if right.children[1].kind == ConstantValue and right.children[1].value.kind in {Integer, Floating}: + right.replaceNode(newConstant(right.children[1].value + I1)) + else: + right.op = opAdd + right.children[0].kind = ConstantValue + right.children[0].value = newInteger(1) + elif right.children[1].kind == VariableLoad and right.children[1].value == left.value: + hookOptimProfiler("add (distributive)") + target.op = opMul + if right.children[0].kind == ConstantValue and right.children[0].value.kind in {Integer, Floating}: + right.replaceNode(newConstant(right.children[0].value + I1)) + else: + right.op = opAdd + right.children[1].kind = ConstantValue + right.children[1].value = newInteger(1) + + # Convert (X * Y) + X -> (1 + Y) * X and + # (Y * X) + X -> (Y + 1) * X + elif right.kind == VariableLoad and left.op == opMul: + if left.children[0].kind == VariableLoad and left.children[0].value == right.value: + hookOptimProfiler("add (distributive)") + target.op = opMul + if left.children[1].kind == ConstantValue and left.children[1].value.kind in {Integer, Floating}: + left.replaceNode(newConstant(left.children[1].value + I1)) + else: + left.op = opAdd + left.children[0].kind = ConstantValue + left.children[0].value = newInteger(1) + elif left.children[1].kind == VariableLoad and left.children[1].value == right.value: + hookOptimProfiler("add (distributive)") + target.op = opMul + if left.children[0].kind == ConstantValue and left.children[0].value.kind in {Integer, Floating}: + left.replaceNode(newConstant(left.children[0].value + I1)) + else: + left.op = opAdd + left.children[1].kind = ConstantValue + left.children[1].value = newInteger(1) + + proc optimizeSub(target: var Node) {.enforceNoRaises.} = + var left = target.children[0] + var right = target.children[1] + + if left.kind == ConstantValue and left.value.kind in {Integer,Floating} and right.kind == ConstantValue and right.value.kind in {Integer,Floating}: + hookOptimProfiler("sub (CF)") + # Constant folding + target.replaceNode(newConstant(left.value - right.value)) + elif left.kind == VariableLoad and right.kind == ConstantValue and right.value == I1: + hookOptimProfiler("sub (dec)") + # Convert X - 1 -> dec X + target.op = opDec + target.arity = 1 + target.setOnlyChild(left) + + template optimizeArithmeticOp(target: var Node, op: untyped) = + var left = target.children[0] + var right = target.children[1] + + if left.kind == ConstantValue and left.value.kind in {Integer,Floating} and + right.kind == ConstantValue and right.value.kind in {Integer,Floating}: + hookOptimProfiler("other (CF)") + target.replaceNode(newConstant(op(left.value,right.value))) + + proc optimizeAppend(target: var Node) {.enforceNoRaises.} = + var left = target.children[0] + var right = target.children[1] + + if left.kind == ConstantValue and left.value.kind == String: + # Constant folding + if right.kind == ConstantValue and right.value.kind == String: + hookOptimProfiler("append (CF)") + target.replaceNode(newConstant(newString(left.value.s & right.value.s))) + + proc optimizeTo(target: var Node) {.enforceNoRaises.} = + var left = target.children[0] + + if left.kind == ConstantValue and left.value.kind==Type: + if left.value.t == Integer: + hookOptimProfiler("to (:integer)") + # convert `to :integer` -> opToI + target.op = opToI + target.arity = 1 + target.children.delete(0) + elif left.value.t == String: + hookOptimProfiler("To (:string)") + # convert `to :string` -> opToS + target.op = opToS + target.arity = 1 + target.children.delete(0) + + proc optimizeReturn(target: var Node) {.enforceNoRaises.} = + if isLastChild(target): + hookOptimProfiler("return (eliminate last)") + # Replace last return + var left = target.children[0] + target.replaceNode(left) + + proc updateAritiesFromStore(target: var Node) {.enforceNoRaises.} = + var child = target.children[0] + + if child.op == opFunc: + let params {.cursor.} = child.children[0] + + if params.value.kind == Literal: + TmpArities[target.value.s] = 1 + else: + TmpArities[target.value.s] = int8(params.value.a.countIt(it.kind != Type)) + else: + TmpArities.del(target.value.s) + + #------------------------ + # Helper Functions + #------------------------ + + template rewindCallBranches(target: var Node, optimize: bool = false): untyped = + while target.kind in CallNode and target.params == target.arity: + when optimize: + if target.kind == VariableStore: + target.updateAritiesFromStore() + else: + try: + case target.op: + of opAdd : target.optimizeAdd() + of opSub : target.optimizeSub() + of opMul : target.optimizeArithmeticOp(`*`) + of opDiv : target.optimizeArithmeticOp(`/`) + of opFDiv : target.optimizeArithmeticOp(`//`) + of opMod : target.optimizeArithmeticOp(`%`) + of opPow : target.optimizeArithmeticOp(`^`) + of opAppend : target.optimizeAppend() + of opTo : target.optimizeTo() + of opReturn : + if asFunction and i == nLen-1: + target.optimizeReturn() + + else: + discard + except: + discard + + target = target.parent + + template rollThrough(target: var Node): untyped = + target = target.children[^1] + + #------------------------ + # AST Generation + #------------------------ + + template addPotentialInfixCall(target: var Node): untyped = + if i < nLen - 1: + let nextNode {.cursor.} = blok.a[i+1] + if nextNode.kind == Symbol and nextNode.m notin {arrowright, thickarrowright, pipe}: + if (let aliased = Aliases.getOrDefault(nextNode.m, NoAliasBinding); aliased != NoAliasBinding): + var symfunc {.cursor.} = GetSym(aliased.name.s) + + if symfunc.kind==Function and aliased.precedence==InfixPrecedence: + when processingArrow: + ArrowBlock[^1].add(nextNode) + i += 1 + target.addCall(aliased.name.s, fun=symfunc) + + proc getCallNode(name: string, arity: int8 = -1, fun: Value = nil): Node = + var callType: OtherCall..SpecialCall = OtherCall + + var fn {.cursor.}: Value = + if fun.isNil: + Syms.getOrDefault(name, nil) + else: + fun + + var ar: int8 = + if arity == -1 and not fn.isNil: + fn.arity + else: + arity + + var op: OpCode = opNop + + if (not fn.isNil) and fn.fnKind == BuiltinFunction: + if (op = fn.op; op != opNop): + callType = + if op in {opIf, opIfE, opUnless, opUnlessE, opElse, opSwitch, opWhile}: + SpecialCall + else: + BuiltinCall + + var v: Value = + if callType == OtherCall: + newWord(name) + else: + nil + + result = newCallNode(callType, ar, v, op) + + proc addCall(target: var Node, name: string, arity: int8 = -1, fun: Value = nil) = + let newCall = getCallNode(name, arity, fun) + + if newCall.arity != 0: + with target: + addChild(newCall) + rollThrough() + else: + with target: + addPotentialInfixCall() + addChild(newCall) + rewindCallBranches(optimize=true) + + func addStore(target: var Node, val: Value) {.enforceNoRaises.} = + target.addChild(newCallNode(VariableStore, 1, val)) + + target.rollThrough() + + proc addAttribute(target: var Node, val: Value, isLabel: static bool = false) {.enforceNoRaises.} = + let attrNode = newCallNode(AttributeNode, 1, val) + + when not isLabel: + attrNode.addChild(newConstant(VTRUE)) + + target.addChild(attrNode) + + when isLabel: + target.rollThrough() + + proc addNewline(target: var Node) = + target.addChild(Node(kind: NewlineNode, line: currentLine)) + + proc addTerminal(target: var Node, node: Node) = + with target: + rewindCallBranches() + + addPotentialInfixCall() + + addChild(node) + + rewindCallBranches(optimize=true) + + proc addTerminals(target: var Node, nodes: openArray[Node], dontOptimize: bool =false) = + with target: + rewindCallBranches() + addPotentialInfixCall() + addChildren(nodes) + + if dontOptimize: + target.rewindCallBranches(optimize=false) + else: + target.rewindCallBranches(optimize=true) + + proc addPath(target: var Node, val: Value, isLabel: static bool=false) = + var pathCallV: Value = nil + + when not isLabel: + if (let curr = Syms.getOrDefault(val.p[0].s, nil); not curr.isNil): + let next {.cursor.} = val.p[1] + if curr.kind==Dictionary and (next.kind==Literal or next.kind==Word): + if (let item = curr.d.getOrDefault(next.s, nil); not item.isNil): + if item.kind == Function: + pathCallV = item + + if not pathCallV.isNil: + target.addChild(Node(kind: OtherCall, arity: pathCallV.arity, op: opNop, value: pathCallV)) + target.rollThrough() + else: + let basePath {.cursor.} = val.p[0] + + when isLabel: + var baseNode = newVariable(basePath) + else: + var baseNode = + if TmpArities.getOrDefault(basePath.s, -1) == 0: + newCallNode(OtherCall, 0, basePath) + else: + newVariable(basePath) + + var i = 1 + + while i < val.p.len: + when isLabel: + let newNode = + if i == val.p.len - 1: + newCallNode(BuiltinCall, 3, nil, opSet) + else: + newCallNode(BuiltinCall, 2, nil, opGet) + else: + let newNode = newCallNode(BuiltinCall, 2, nil, opGet) + + newNode.addChild(baseNode) + + if val.p[i].kind==Block: + var subNode = newRootNode() + discard subNode.processBlock(val.p[i], startingLine=currentLine, asDictionary=false) + newNode.addChildren(subNode.children) + else: + newNode.addChild(newConstant(val.p[i])) + + baseNode = newNode + i += 1 + + when isLabel: + target.addChild(baseNode) + target.rollThrough() + else: + target.addTerminal(baseNode) + + template addPotentialTrailingPipe(target: var Node): untyped = + var added = false + if i < nLen - 1: + var nextNode {.cursor.} = blok.a[i+1] + if nextNode.kind == Word: + if (let funcArity = TmpArities.getOrDefault(nextNode.s, -1); funcArity != -1): + i += 1 + target.rewindCallBranches() + + var toSpot = target.children.len - 1 + var toWrap: Node + + var lastChild: Node = target.children[toSpot] + while lastChild.kind==NewlineNode: + toSpot -= 1 + lastChild = target.children[toSpot] + + toWrap = lastChild + + if lastChild.kind == VariableStore: + toSpot = 0 + toWrap = copyNode(lastChild.children[0]) + + let newCall = getCallNode(nextNode.s, funcArity) + newCall.addChild(toWrap) + + lastChild.children[0].replaceNode(newCall) + target = lastChild + target.rollThrough() + else: + + target.children.delete(toSpot) + + target.addCall(nextNode.s, funcArity) + target.addChild(toWrap) + + target.rewindCallBranches() + + added = true + + if not added: + target.addTerminal(newConstant(newSymbol(pipe))) + + proc addInline(target: var Node, val: Value) = + var subNode = newRootNode() + discard subNode.processBlock(val, startingLine=currentLine, asDictionary=false) + + target.addTerminals(subNode.children) + + proc addArrowBlock(target: var Node, val: Value) = + var subNode = newRootNode() + i = subNode.processBlock(val, start=i+1, startingLine=currentLine, asDictionary=false, processingArrow=true) + + let poppedArrowBlock = newBlock(ArrowBlock.pop()) + when processingArrow: + ArrowBlock[^1].add(poppedArrowBlock) + + target.addTerminal(newConstant(poppedArrowBlock)) + + proc addThickArrowBlocks(target: var Node) = + # get next node + let subnode {.cursor.} = blok.a[i+1] + + # we'll want to create the two blocks, + # for functions like loop, map, select, filter + # so let's get them ready + var argblock, subblock: ValueArray + + if subnode.kind==Block: + # replace ampersand symbols, + # sequentially, with arguments + var idx = 0 + var fnd: int8 = 0 + while idx\n" + else: + if node.kind == AttributeNode: + result &= "Attribute: " + else: + result &= "Call: " + if node.value.isNil: + var callName = ($node.op).toLowerAscii() + callName.removePrefix("op") + result &= callName & " <" & $node.arity & ">\n" + else: + result &= node.value.s & " <" & $node.arity & ">\n" + + when not single: + for child in node.children: + result &= dumpNode(child, level+1) + + result &= "\n" + +#======================================= +# Main +#======================================= + +proc generateAst*(parsed: Value, asDictionary=false, asFunction=false, reuseArities: static bool=false): Node = + result = newRootNode() + + when not reuseArities: + TmpArities = collect: + for k,v in Syms.pairs: + if v.kind == Function: + {k: v.arity} + + discard result.processBlock(parsed, asDictionary=asDictionary, asFunction=asFunction) + + #echo dumpNode(result) diff --git a/src/vm/bytecode.nim b/src/vm/bytecode.nim index e34e0a31a8..8b1e10d034 100644 --- a/src/vm/bytecode.nim +++ b/src/vm/bytecode.nim @@ -17,115 +17,18 @@ when not defined(WEB): import extras/miniz -import os - import opcodes export opcodes -import vm/values/value - -import vm/values/custom/[vbinary] - #======================================= # Constants #======================================= -const - #opPushAny = opPush0..opPush13 - #opStoreAny = opStore0..opStore13 - opLoadAny = opLoad0..opLoad13 - #opCallAny = opCall0..opCall13 - when not defined(WEB): const BcodeMagic = uint32(0x86BC0DE0) BcodeMagicCompressed = uint32(0x86BC0DE1) -#======================================= -# Helpers -#======================================= - -template Op(sth: untyped): untyped = OpCode(sth) -template By(sth: untyped): untyped = Byte(sth) - -template skip(steps: int): untyped = - i.inc(steps) - -template current(): untyped = a[i] -template next(): untyped = - while Op(a[i+1]) in {opEol,opEolX}: - if Op(a[i+1])==opEol: skip(2) - else: skip(3) - a[i+1] - -template consume(num: int = 1): untyped = - result[p] = a[initialI] - when num > 1: - result[p+1] = a[initialI+1] - when num > 2: - result[p+2] = a[initialI+2] - p.inc(num) - i.inc(num) - -template inject(what: untyped): untyped = - result[p] = what - p.inc() - -template keep(): untyped = - result[p] = current() - p.inc() - -proc optimize(trans: Translation): VBinary = - let a = trans.instructions - var i = 0 - var p = 0 - let aLen = a.len - newSeq(result, aLen) - while i < aLen: - let initial = current - let initialI = i - #echo fmt"I = {i} -> {Op(initial)}" - case Op(initial): - of opStore0..opStore13: - if Op(next) in opLoadAny and By(opLoad)-a[i+1]==By(opStore)-initial: - # (opStore*) + (opLoad*) -> (opStorl*) - inject(): By(opStorl0) + initial - By(opStore0) - skip(2) - else: - consume(1) - of opPush0..opPush13, opLoad0..opLoad13: - if Op(next) == Op(initial): - # (opPush/opLoad*) x N -> (opPush/opLoad*) + (opDup) x N - keep() - while Op(next) == Op(initial): - inject(): By(opDup) - skip(1) - skip(1) - else: - consume(1) - - # of opCall0..opCall29: - # let idx = initial - By(opCall0) - # echo fmt"found opCall* -> {idx}" - # if d[idx].kind==Word and Syms[d[idx].s] == AddF: - # inject(): By(opIAdd) - # skip(1) - # else: - # consume(1) - - of opPush,opStore,opLoad,opCall,opStorl,opAttr: - consume(2) - of opPushX,opStoreX,opLoadX,opCallX,opStorlX: - consume(3) - of opEol: - skip(2) - of opEolX: - skip(3) - else: - consume(1) - - result.setLen(p) - #======================================= # Methods #======================================= @@ -186,7 +89,4 @@ proc readBytecode*(origin: string): (string, seq[byte]) = return (dataSegment, codeSegment) # return the result else: - discard - -proc optimizeBytecode*(t: Translation): seq[byte] = - result = optimize(t) \ No newline at end of file + discard \ No newline at end of file diff --git a/src/vm/eval.nim b/src/vm/eval.nim index 162487b931..db507f495a 100644 --- a/src/vm/eval.nim +++ b/src/vm/eval.nim @@ -10,7 +10,8 @@ ## ## The evaluator: ## - takes a Block of values coming from the parser -## - interpretes it and returns a Translation object +## - passes to the AST generator +## - interpretes the AST and returns a Translation object ## ## The main entry point is ``doEval``. @@ -18,22 +19,19 @@ # Libraries #======================================= -import algorithm, hashes, sequtils -import sugar, tables, unicode +import hashes, sugar, tables -import vm/[bytecode, globals, values/value] +import vm/[ast, bytecode, values/value] +import vm/values/custom/[vbinary, vlogical] import vm/profiler -import vm/values/custom/[vbinary, vsymbol] - #======================================= # Variables #======================================= var - StoredEval : Table[Hash, Translation] - TmpArities : Table[string,int8] + StoredTranslations : Table[Hash, Translation] #======================================= # Helpers @@ -46,1023 +44,505 @@ func indexOfValue(a: ValueArray, item: Value): int {.inline,enforceNoRaises.}= inc(result) result = -1 -#======================================= -# Methods -#======================================= - -when not defined(NOERRORLINES): - template addEol(it: var VBinary, line: untyped):untyped = - if line > 255: - it.add([ - byte(opEolX), - byte(line shr 8), - byte(line) - ]) +template addByte(instructions: var VBinary, b: untyped): untyped = + when b is OpCode: + instructions.add(byte(b)) + else: + instructions.add(b) + +template addOpWithNumber(instructions: var VBinary, oper: OpCode, num: untyped, hasShortcut = true): untyped = + if num > 255: + instructions.addByte([ + byte(oper)+1, + byte(num shr 8), + byte(num) + ]) + else: + when hasShortcut: + if num <= 13: + instructions.addByte((byte(oper)-0x0E) + byte(num)) + else: + instructions.addByte([ + byte(oper), + byte(num) + ]) else: - it.add([ - byte(opEol), - byte(line) + instructions.addByte([ + byte(oper), + byte(num) ]) -func hasBranching(blk: Value, continueW: string = "continue", breakW: string = "break"): bool {.enforceNoRaises.} = - for item in blk.a: - if item.kind==Word and (item.s==continueW or item.s==breakW): - return true - elif item.kind==Block: - if hasBranching(item, continueW, breakW): - return true - return false - -proc evalOne(n: Value, consts: var ValueArray, it: var VBinary, inBlock: bool = false, isDictionary: bool = false) = - var argStack: seq[int] - var currentCommand: VBinary - - let nLen = n.a.len - - var foundIf = false - var foundIfE = false - var foundUnless = false - var foundElse = false - var foundSwitch = false - var foundWhile = false - var foundAdd = false - var foundSub = false - - #------------------------ - # Shortcuts - #------------------------ - - template addConst(v: Value, op: OpCode): untyped = - addConst(currentCommand, consts, v, op) - - template addShortConst(v: Value, op: OpCode): untyped = - addShortConst(currentCommand, consts, v, op) - - template addTrailingConst(v: Value, op: OpCode): untyped = - addTrailingConst(currentCommand, consts, v, op) +template addReplaceOpWithIndex(instructions: var VBinary, oper: OpCode, num: untyped): untyped = + if num > 255: + instructions[^1] = byte(oper)+1 + instructions.addByte([ + byte(num shr 8), + byte(num) + ]) + else: + instructions[^1] = byte(oper) + instructions.addByte(byte(num)) + +proc cleanChildren(node: Node): seq[Node] = + result = collect: + for subnode in node.children: + if subnode.kind != NewlineNode: + subnode + +proc getNextNonNewlineNode(blok: Node, i: var int, nLen: int): Node = + result = nil + if i + 1 >= nlen: return + result = blok.children[i+1] + + var j = i+1 + while result.kind == NewlineNode and j + 1 < nLen: + j += 1 + result = blok.children[j] + + if result.kind == NewlineNode: + result = nil + else: + i = j + +proc addConst(consts: var ValueArray, instructions: var VBinary, v: Value, op: OpCode, hasShortcut: static bool=true) {.inline,enforceNoRaises.} = + var indx = consts.indexOfValue(v) + if indx == -1: + let newv = v + newv.readonly = true + consts.add(newv) + indx = consts.len-1 + + instructions.addOpWithNumber(op, indx, hasShortcut) + +proc addConstAndGetIndex(consts: var ValueArray, instructions: var VBinary, v: Value, op: OpCode, hasShortcut: static bool=true): int {.inline,enforceNoRaises.} = + result = consts.indexOfValue(v) + if result == -1: + let newv = v + newv.readonly = true + consts.add(newv) + result = consts.len-1 + + instructions.addOpWithNumber(op, result, hasShortcut) + +proc addVariableLoad(consts: var ValueArray, instructions: var VBinary, nd: Node, v: Value, previousStore: int, previousStorePos: int) {.inline,enforceNoRaises.} = + var indx = consts.indexOfValue(v) + if indx == -1: + let newv = v + newv.readonly = true + consts.add(newv) + indx = consts.len-1 + + if indx == previousStore and nd.parent.kind != VariableStore: + hookOptimProfiler("opStorl") - #------------------------ - # Helper Functions - #------------------------ - - template addToCommand(b: untyped):untyped {.dirty.} = - when b is OpCode: - currentCommand.add(byte(b)) + if indx <= 13: + instructions[previousStorePos-1] = (byte(opStorl)-0x0E) + byte(indx) + elif indx <= 255: + instructions[previousStorePos-2] = byte(opStorl) else: - currentCommand.add(b) - - template addToCommandHead(b: untyped, at = 0):untyped {.dirty.} = - when b is OpCode: - currentCommand.insert(byte(b), at) + instructions[previousStorePos-3] = byte(opStorlX) + else: + instructions.addOpWithNumber(opLoad, indx, hasShortcut=true) + +proc getOperand*(node: Node, inverted: static bool=false): (OpCode, bool) = + if node.kind notin CallNode: + when inverted: (opJmpIfNot, false) else: (opJmpIf, false) + else: + case node.op: + of opEq: + when inverted: (opJmpIfNe, true) else: (opJmpIfEq, true) + of opNe: + when inverted: (opJmpIfEq, true) else: (opJmpIfNe, true) + of opLt: + when inverted: (opJmpIfGe, true) else: (opJmpIfLt, true) + of opLe: + when inverted: (opJmpIfGt, true) else: (opJmpIfLe, true) + of opGt: + when inverted: (opJmpIfLe, true) else: (opJmpIfGt, true) + of opGe: + when inverted: (opJmpIfLt, true) else: (opJmpIfGe, true) + of opNot: + when inverted: (opJmpIf, true) else: (opJmpIfNot, true) + else: + when inverted: (opJmpIfNot, false) else: (opJmpIf, false) + +# TODO(VM/eval) better `while` optimization? +# what if the user has actually re-defined `continue` or `break`? +# labels: vm, evaluator, enhancement +func doesNotContainBranching(blok: Value): bool {.enforceNoRaises.} = + for subvalue in blok.a: + if subvalue.kind == Word and subvalue.s in ["continue", "break"]: + return false + elif subvalue.kind == Block: + if not doesNotContainBranching(subvalue): + return false + return true + +func doesNotContainBranching(node: Node): bool {.enforceNoRaises.} = + for subnode in node.children: + if subnode.kind == BuiltinCall and subnode.op in {opContinue, opBreak}: + return false + elif subnode.kind == ConstantValue and subnode.value.kind == Block: + if not doesNotContainBranching(subnode.value): + return false else: - currentCommand.insert(b, at) - - proc addConst(currentCommand: var VBinary, consts: var ValueArray, v: Value, op: OpCode) {.inline,enforceNoRaises.} = - var indx = consts.indexOfValue(v) - if indx == -1: - let newv = v - newv.readonly = true - consts.add(newv) - indx = consts.len-1 - - if indx <= 13: - addToCommand((byte(op)-0x0E) + byte(indx)) + if not doesNotContainBranching(subnode): + return false + return true + +#------------------------ +# Optimization +#------------------------ + +# TODO(VM/eval) optimize conditionals combined with `and?` +# Not sure what the benefit-to-effort ratio of this proposal would be, +# but we could definitely optimize this type of scenario. +# +# For example, right now, `if? x = 2 -> print "X is 2" else -> "X is not 2"` +# would lead to pseudo-bytecode looking like this: +# ``` +# push 2 +# load x +# jump_if_not_equal_to else +# push "X is 2" +# print +# else: +# push "X is not 2" +# print +# ``` +# +# Now, what if there is lazy `and?` involved? +# E.g. `if? and? [x = 2][y = 3] -> print "YES" -> print "NO"` +# +# Right now, it produces something like: +# ``` +# push [y=3] +# push [x=2] +# and +# jump_if_not +# push "YES" +# print +# else: +# push "NO" +# print +# ``` +# +# This could/should produce bytecode like this: +# ``` +# push 2 +# load x +# jump_if_not_equal_to else +# push 3 +# load y +# jump_if_not_equal_to else +# push "YES" +# print +# else: +# push "NO" +# print +# ``` +# +# The produced/suggested bytecode is longer but it should be faster, +# given that there'll be no implicit function calls; but just linear, +# more cache-friendly code to just jump through. +# +# labels: vm, evaluator, enhancement, open discussion + +template optimizeConditional( + consts: var ValueArray, + it: var VBinary, + special: untyped, + withLoop=false, + withPotentialElse=false, + isSwitch=false, + withInversion=false +): untyped = + # let's keep some references + # to the children + let cleanedChildren = cleanChildren(special) + let left {.cursor.} = cleanedChildren[0] + let right {.cursor.} = cleanedChildren[1] + + # can we optimize? + var canWeOptimize = false + + when withPotentialElse: + var elseChild: Node + when isSwitch: + elseChild = cleanedChildren[2] + canWeOptimize = right.kind == ConstantValue and right.value.kind == Block and + elseChild.kind == ConstantValue and elseChild.value.kind == Block else: - if indx>255: - addToCommand([ - byte(indx), - byte(indx shr 8), - byte(op)+1 - ]) + let previousI = i + let elseNode = getNextNonNewlineNode(blok, i, nLen) + + if (not elseNode.isNil) and elseNode.kind == SpecialCall and elseNode.op == opElse: + var j = -1 + elseChild = getNextNonNewlineNode(elseNode, j, elseNode.children.len) + if not elseChild.isNil: + canWeOptimize = right.kind == ConstantValue and right.value.kind == Block and + elseChild.kind == ConstantValue and elseChild.value.kind == Block + else: + i = previousI else: - addToCommand([ - byte(indx), - byte(op) - ]) - - proc addShortConst(currentCommand: var VBinary, consts: var ValueArray, v: Value, op: OpCode) {.inline,enforceNoRaises.} = - var indx = consts.indexOfValue(v) - if indx == -1: - let newv = v - newv.readonly = true - consts.add(newv) - indx = consts.len-1 - - if indx>255: - addToCommand([ - byte(indx), - byte(indx shr 8), - byte(op)+1 - ]) + i = previousI + else: + when withLoop: + canWeOptimize = right.kind == ConstantValue and right.value.kind == Block and + left.kind == ConstantValue and left.value.kind == Block else: - addToCommand([ - byte(indx), - byte(op) - ]) + canWeOptimize = right.kind == ConstantValue and right.value.kind == Block - proc addTrailingConst(currentCommand: var VBinary, consts: var ValueArray, v: Value, op: OpCode) {.inline,enforceNoRaises.} = - var atPos = 0 - if currentCommand[0] in opStore0.byte..opStoreX.byte: - atPos = 1 + if canWeOptimize: + let rightNode = generateAst(right.value, reuseArities=true) - var indx = consts.indexOfValue(v) - if indx == -1: - let newv = v - newv.readonly = true - consts.add(newv) - indx = consts.len-1 + when withLoop: + var leftIt: VBinary + let leftNode = generateAst(left.value, reuseArities=true) - if indx <= 13: - addToCommandHead((byte(op)-0x0E) + byte(indx), atPos) - else: - if indx>255: - addToCommandHead([ - byte(op)+1, - byte(indx shr 8), - byte(indx) - ], atPos) + let stillProceed = + when withLoop: + leftNode.children.len > 0 and doesNotContainBranching(rightNode) else: - addToCommandHead([ - byte(op), - byte(indx) - ], atPos) - - proc evalFunctionCall(currentCommand: var VBinary, fun: var Value, toHead: bool, checkAhead: bool, i: var int, funcArity: var int8): bool {.enforceNoRaises.} = - var bt: OpCode = opNop - var doElse = true - - let fn {.cursor.} = fun - - if fn == ArrayF: bt = opArray - elif fn == DictF: bt = opDict - elif fn == FuncF: bt = opFunc - elif fn == AddF: - bt = opAdd - foundAdd = true - elif fn == SubF: - bt = opSub - foundSub = true - elif fn == MulF: bt = opMul - elif fn == DivF: bt = opDiv - elif fn == FdivF: bt = opFdiv - elif fn == ModF: bt = opMod - elif fn == PowF: bt = opPow - elif fn == NegF: bt = opNeg - elif fn == BNotF: bt = opBNot - elif fn == BAndF: bt = opBAnd - elif fn == BOrF: bt = opBOr - elif fn == ShlF: bt = opShl - elif fn == ShrF: bt = opShr - elif fn == NotF: bt = opNot - elif fn == AndF: bt = opAnd - elif fn == OrF: bt = opOr - elif fn == EqF: bt = opEq - elif fn == NeF: bt = opNe - elif fn == GtF: bt = opGt - elif fn == GeF: bt = opGe - elif fn == LtF: bt = opLt - elif fn == LeF: bt = opLe - elif fn == IfF: - foundIf = true - bt = opIf - elif fn == IfEF: - foundIfE = true - bt = opIfE - elif fn == UnlessF: - foundUnless = true - bt = opUnless - elif fn == ElseF: - foundElse = true - bt = opElse - elif fn == SwitchF: - foundSwitch = true - bt = opSwitch - elif fn == WhileF: - foundWhile = true - bt = opWhile - elif fn == ReturnF: bt = opReturn - elif fn == ToF: - bt = opTo - if checkAhead: - let nextNode {.cursor.} = n.a[i+1] - if nextNode.kind==Type: - if nextNode.t==String: - addToCommand(opToS) - bt = opNop - doElse = false - funcArity -= 1 - i += 1 - elif nextNode.t==Integer: - addToCommand(opToI) - bt = opNop - doElse = false - funcArity -= 1 - i += 1 - elif fn == PrintF: bt = opPrint - elif fn == GetF: bt = opGet - elif fn == SetF: bt = opSet - elif fn == RangeF: bt = opRange - elif fn == LoopF: bt = opLoop - elif fn == MapF: bt = opMap - elif fn == SelectF: bt = opSelect - elif fn == SizeF: bt = opSize - elif fn == ReplaceF: bt = opReplace - elif fn == SplitF: bt = opSplit - elif fn == JoinF: bt = opJoin - elif fn == ReverseF: bt = opReverse - elif fn == IncF: bt = opInc - elif fn == DecF: bt = opDec - - if bt != opNop: - if toHead: - addToCommandHead(bt) + true + + if stillProceed: + + when withLoop: + # separately ast+evaluate right child block + evaluateBlock(leftNode, consts, leftIt) + it.add(leftIt) else: - addToCommand(bt) + # inline-evaluate left child + evaluateBlock(Node(kind:RootNode, children: @[left]), consts, it) + + # separately ast+evaluate right child block + var rightIt: VBinary + evaluateBlock(rightNode, consts, rightIt) + + when withPotentialElse: + # separately ast+evaluate else child block + var elseIt: VBinary + evaluateBlock(generateAst(elseChild.value, reuseArities=true), consts, elseIt) + + # get operand & added to the instructions + let (newOp, replaceOp) = + when withLoop: + getOperand(leftNode.children[0], inverted=withInversion) + else: + getOperand(left, inverted=withInversion) - return true - else: - return not doElse - - proc getConstIdWithShift(currentCommand: var VBinary, pos: int): (int,int) {.inline,enforceNoRaises.} = - let currentCommandLast = currentCommand[pos] - if OpCode(currentCommandLast) in {opPush0..opPush13}: - return (int(currentCommandLast) - int(opPush0), 0) - elif OpCode(currentCommandLast) == opPush: - return (int(currentCommand[pos+1]), 1) - elif OpCode(currentCommandLast) == opPushX: - return ((int(currentCommand[pos+1]) shl 8) + int(currentCommand[pos+2]), 2) - - return (-1, -1) - - proc optimizeIf(consts: var ValueArray, it: var VBinary) {.enforceNoRaises.} = - let (cnstId, shift) = getConstIdWithShift(currentCommand, 0) - - if cnstId != -1: - let blk = consts[cnstId] - if blk.kind == Block and OpCode(currentCommand[^1]) in {opIf,opIfE}: - currentCommand.delete(0..shift) - discard currentCommand.pop() - var injectable = opJmpIfNot - case OpCode(currentCommand[^1]): - of opNot: - discard currentCommand.pop() - injectable = opJmpIf - of opEq: - discard currentCommand.pop() - injectable = opJmpIfNe - of opNe: - discard currentCommand.pop() - injectable = opJmpIfEq - of opGt: - discard currentCommand.pop() - injectable = opJmpIfLe - of opGe: - discard currentCommand.pop() - injectable = opJmpIfLt - of opLt: - discard currentCommand.pop() - injectable = opJmpIfGe - of opLe: - discard currentCommand.pop() - injectable = opJmpIfGt + # get jump distance + var jumpDistance = + when withPotentialElse: + if elseIt.len > 255: + rightIt.len + 3 else: - discard - currentCommand.add([byte(injectable), byte(0), byte(0)]) - let injPos = currentCommand.len - 2 - evalOne(blk, consts, currentCommand, inBlock=inBlock, isDictionary=isDictionary) - if foundIfE: - currentCommand.add([byte(opNop), byte(opNop), byte(opNop)]) - let finPos = currentCommand.len - injPos - 2 - currentCommand[injPos] = byte(finPos shr 8) - currentCommand[injPos+1] = byte(finPos) - - foundIf = false - foundIfE = false - - proc optimizeUnless(consts: var ValueArray, it: var VBinary) {.enforceNoRaises.} = - let (cnstId, shift) = getConstIdWithShift(currentCommand, 0) - - if cnstId != -1: - let blk = consts[cnstId] - if blk.kind == Block and OpCode(currentCommand[^1]) == opUnless: - currentCommand.delete(0..shift) - discard currentCommand.pop() - var injectable = opJmpIf - case OpCode(currentCommand[^1]): - of opNot: - discard currentCommand.pop() - injectable = opJmpIfNot - of opEq: - discard currentCommand.pop() - injectable = opJmpIfEq - of opNe: - discard currentCommand.pop() - injectable = opJmpIfNe - of opGt: - discard currentCommand.pop() - injectable = opJmpIfGt - of opGe: - discard currentCommand.pop() - injectable = opJmpIfGe - of opLt: - discard currentCommand.pop() - injectable = opJmpIfLt - of opLe: - discard currentCommand.pop() - injectable = opJmpIfLe + rightIt.len + 2 + elif withLoop: + if (leftIt.len + rightIt.len) > 255: + rightIt.len + 3 else: - discard - currentCommand.add([byte(injectable), byte(0), byte(0)]) - let injPos = currentCommand.len - 2 - evalOne(blk, consts, currentCommand, inBlock=inBlock, isDictionary=isDictionary) - if foundIfE: - currentCommand.add([byte(opNop), byte(opNop), byte(opNop)]) - let finPos = currentCommand.len - injPos - 2 - currentCommand[injPos] = byte(finPos shr 8) - currentCommand[injPos+1] = byte(finPos) - - foundUnless = false - - proc optimizeLess(consts: var ValueArray, it: var VBinary) {.enforceNoRaises.} = - let (cnstId, shift) = getConstIdWithShift(currentCommand, 0) - - if cnstId != -1: - let blk = consts[cnstId] - if blk.kind == Block: - currentCommand.delete(0..shift) - discard currentCommand.pop() - - evalOne(blk, consts, currentCommand, inBlock=inBlock, isDictionary=isDictionary) - let currentPos = currentCommand.len - it[^3] = byte(opGoto) - it[^2] = byte(currentPos shr 8) - it[^1] = byte(currentPos) - - foundElse = false - - proc optimizeSwitch(consts: var ValueArray, it: var VBinary) {.enforceNoRaises.} = - # TODO(eval/optimizeSwitch) `switch` not always processed correctly - # see: https://rosettacode.org/wiki/Perlin_noise#Arturo - # labels: bug, vm, evaluator, critical - let (cnstId, shift) = getConstIdWithShift(currentCommand, 0) - - if cnstId != -1: - let blk = consts[cnstId] - if blk.kind == Block: - let (cnstId2, shift2) = getConstIdWithShift(currentCommand, 1+shift) - - if cnstId2 != -1: - let blk2 = consts[cnstId2] - if blk2.kind == Block: - # for b in currentCommand: - # echo stringify(OpCode(b)) - currentCommand.delete(0..shift+shift2+1) - var toPushBack: VBinary - var jpb = currentCommand.len - 1 - while OpCode(currentCommand[jpb]) != opSwitch: - toPushBack.add(currentCommand.pop()) - jpb -= 1 - reverse(toPushBack) - discard currentCommand.pop() - var injectable = opJmpIfNot - case OpCode(currentCommand[^1]): - of opNot: - discard currentCommand.pop() - injectable = opJmpIf - of opEq: - discard currentCommand.pop() - injectable = opJmpIfNe - of opNe: - discard currentCommand.pop() - injectable = opJmpIfEq - of opGt: - discard currentCommand.pop() - injectable = opJmpIfLe - of opGe: - discard currentCommand.pop() - injectable = opJmpIfLt - of opLt: - discard currentCommand.pop() - injectable = opJmpIfGe - of opLe: - discard currentCommand.pop() - injectable = opJmpIfGt - else: - discard - currentCommand.add([byte(injectable), byte(0), byte(0)]) - let injPos = currentCommand.len - 2 - evalOne(blk2, consts, currentCommand, inBlock=inBlock, isDictionary=isDictionary) - let preInjection = currentCommand.len - currentCommand.add([byte(opNop), byte(opNop), byte(opNop)]) - let finPos = currentCommand.len - injPos - 2 - currentCommand[injPos] = byte(finPos shr 8) - currentCommand[injPos+1] = byte(finPos) - let lastLen = currentCommand.len - evalOne(blk, consts, currentCommand, inBlock=inBlock, isDictionary=isDictionary) - let currentPos = currentCommand.len - lastLen - currentCommand[preInjection] = byte(opGoto) - currentCommand[preInjection+1] = byte(currentPos shr 8) - currentCommand[preInjection+2] = byte(currentPos) - if toPushBack.len > 0: - currentCommand.add(toPushBack) - - foundSwitch = false - - proc optimizeWhile(consts: var ValueArray, it: var VBinary) {.enforceNoRaises.} = - if OpCode(currentCommand[^1]) == opWhile: - - let (cnstId, shift) = getConstIdWithShift(currentCommand, 0) - - if cnstId != -1: - let blk1 = consts[cnstId] - if blk1.kind == Block: - let (cnstId2, shift2) = getConstIdWithShift(currentCommand, 1+shift) - - if cnstId2 != -1: - let blk2 = consts[cnstId2] - if blk2.kind == Block: - if not hasBranching(blk1): - currentCommand.delete(0..shift+shift2+1) - discard currentCommand.pop() - let initialLen = currentCommand.len - evalOne(blk2, consts, currentCommand, inBlock=inBlock, isDictionary=isDictionary) - var injectable = opJmpIfNot - case OpCode(currentCommand[^1]): - of opNot: - discard currentCommand.pop() - injectable = opJmpIf - of opEq: - discard currentCommand.pop() - injectable = opJmpIfNe - of opNe: - discard currentCommand.pop() - injectable = opJmpIfEq - of opGt: - discard currentCommand.pop() - injectable = opJmpIfLe - of opGe: - discard currentCommand.pop() - injectable = opJmpIfLt - of opLt: - discard currentCommand.pop() - injectable = opJmpIfGe - of opLe: - discard currentCommand.pop() - injectable = opJmpIfGt - else: - discard - currentCommand.add([byte(injectable), byte(0), byte(0)]) - let injPos = currentCommand.len - 2 - evalOne(blk1, consts, currentCommand, inBlock=inBlock, isDictionary=isDictionary) - currentCommand.add([byte(opGoup), byte(0), byte(0)]) - let gotoPos = currentCommand.len - 2 - let finPos = currentCommand.len - injPos - 2 - currentCommand[injPos] = byte(finPos shr 8) - currentCommand[injPos+1] = byte(finPos) - let dist = currentCommand.len - initialLen - currentCommand[gotoPos] = byte(dist shr 8) - currentCommand[gotoPos+1] = byte(dist) - - foundWhile = false - - proc optimizeAdd(consts: var ValueArray, it: var VBinary) {.enforceNoRaises.} = - if OpCode(currentCommand[^1]) == opAdd: - if currentCommand.len == 3 and OpCode(currentCommand[0])==opConstI1: - currentCommand = @[currentCommand[1], byte(opInc)] - elif OpCode(currentCommand[^2])==opConstI1: - currentCommand[^2] = byte(opInc) - discard currentCommand.pop() - elif OpCode(currentCommand[0]) == opAdd: - if currentCommand.len == 3 and OpCode(currentCommand[2])==opConstI1: - currentCommand = @[byte(opInc), currentCommand[1]] - elif OpCode(currentCommand[1])==opConstI1: - currentCommand[1] = byte(opInc) - currentCommand.delete(0) - - foundAdd = false - - proc optimizeSub(consts: var ValueArray, it: var VBinary) {.enforceNoRaises.} = - if OpCode(currentCommand[^1]) == opSub: - if currentCommand.len == 3 and OpCode(currentCommand[0])==opConstI1: - currentCommand = @[currentCommand[1], byte(opDec)] - elif OpCode(currentCommand[0])==opSub: - if currentCommand.len == 3 and OpCode(currentCommand[2])==opConstI1: - currentCommand = @[byte(opDec), currentCommand[1]] - - foundSub = false - - template addCurrentCommandToBytecode() {.dirty.} = - if not inBlock: reverse(currentCommand) - - if foundIf or (foundIfE and i+1= nLen - 1: return - let nextNode {.cursor.} = n.a[i+1] - if nextNode.kind == Symbol: - - if (let aliased = Aliases.getOrDefault(nextNode.m, NoAliasBinding); aliased != NoAliasBinding): - var symfunc {.cursor.} = GetSym(aliased.name.s) - - if symfunc.kind==Function and aliased.precedence==InfixPrecedence: - i += 1 - var placeholder: int8 - if (not inBlock) and (not evalFunctionCall(currentCommand, symfunc, toHead=false, checkAhead=false, i, placeholder)): - addConst(aliased.name, opCall) - result = symfunc.arity - - template resetArgStackAndCheckForTrailingPipe(currentArgStack: var seq[int], commandFinished: untyped, withTrailingPipe: untyped) = - if currentArgStack.len != 0: currentArgStack[^1] -= 1 - - while currentArgStack.len != 0 and currentArgStack[^1] == 0: - discard currentArgStack.pop() - currentArgStack[^1] -= 1 - - if not (i+11: - argStack.add(tmpFuncArity-1) - # TODO(VM/eval) to be fixed - # labels: bug, evaluator, vm - var placeholder: int8 - if not evalFunctionCall(currentCommand, nextNode, toHead=true, checkAhead=false, i, placeholder): - addTrailingConst(nextNode, opCall) - else: - addTrailingConst(nextNode, opCall) - if argStack.len==0: - addCurrentCommandToBytecode() - i += 1 - - proc postAddTerminalSubValue(consts: var ValueArray, currentCommand: var VBinary, i: var int, n: Value, subargStack: var seq[int], ret: var ValueArray, ended: var bool) = - # Check if command complete - - resetArgStackAndCheckForTrailingPipe(subargStack): - # The subcommand is finished - ended = true - do: - discard - - template addTerminalValue(inBlock: bool, code: untyped): untyped {.dirty.} = - block: - # Check for potential Infix operator ahead - if (let infixArity = getArityForInfixOperatorAhead(inBlock, consts, currentCommand, i, n); infixArity != -1): - when not inBlock: - argStack.add(infixArity) - else: - subargStack.add(infixArity) - ret.add(n.a[i]) + # add the else block + it.add(elseIt) + elif withLoop: + let upDistance = leftIt.len + jumpDistance + it.addOpWithNumber(opGoup, upDistance, hasShortcut=false) - # Run main code - code + # processing finished + alreadyProcessed = true - # Check if command is complete - when not inBlock: - postAddTerminalValue(consts, currentCommand, i, n, it) - else: - postAddTerminalSubValue(consts, currentCommand, i, n, subargStack, ret, ended) +#======================================= +# Methods +#======================================= - template processArrowRight(): untyped = - i += 1 +proc evaluateBlock*(blok: Node, consts: var ValueArray, it: var VBinary, isDictionary=false, omitNewlines: bool = false) = + let nLen = blok.children.len + var i = 0 - while i < nLen and not ended: - let subnode {.cursor.} = n.a[i] - ret.add(subnode) - - case subnode.kind: - of Null, - Logical: discard - of Integer, - Floating, - Type, - Char, - String, - Literal, - Path, - Inline, - Block: - addTerminalValue(inBlock=true): - discard - of Word: - if (let funcArity = TmpArities.getOrDefault(subnode.s, -1); funcArity != -1): - if funcArity!=0: - subargStack.add(funcArity) - else: - addTerminalValue(inBlock=true): - discard - else: - addTerminalValue(inBlock=true): - discard - - of Symbol: - let symalias = subnode.m - let aliased = Aliases.getOrDefault(symalias, NoAliasBinding) - if likely(aliased != NoAliasBinding): - let symfunc {.cursor.} = GetSym(aliased.name.s) - if symfunc.kind==Function: - if aliased.precedence==PrefixPrecedence: - if symfunc.arity != 0: - subargStack.add(symfunc.arity) - else: - addTerminalValue(inBlock=true): - discard - else: - ret.add(newSymbol(ampersand)) - swap(ret[^1],ret[^2]) - subargStack.add(symfunc.arity-1) - else: - addTerminalValue(inBlock=true): - discard - else: - addTerminalValue(inBlock=true): - discard - - of AttributeLabel: - subargStack[subargStack.len-1] += 1 - - else: discard - - - i += 1 - - i -= 1 - ret - - proc processThickArrowRight(argblock: var ValueArray, subblock: var ValueArray, it: var VBinary, n: Value, i: var int, funcArity: var int8) = - while n.a[i+1].kind == Newline: - when not defined(NOERRORLINES): - addEol(it, n.a[i+1].line) - i += 1 - - # get next node - let subnode {.cursor.} = n.a[i+1] - - # we'll want to create the two blocks, - # for functions like loop, map, select, filter - # so let's get them ready - argblock.setLen(0) - subblock = @[subnode] - - # if it's a word - if subnode.kind==Word: - # check if it's a function - funcArity = TmpArities.getOrDefault(subnode.s, -1) - if funcArity != -1: - # automatically "push" all its required arguments - for i in 0..(funcArity-1): - let arg = newWord("_" & $(i)) - argblock.add(arg) - subblock.add(arg) - - elif subnode.kind==Block: - # replace ampersand symbols, - # sequentially, with arguments - var idx = 0 - var fnd: int8 = 0 - while idx funcArity-1 for ToS/ToI! - else: - addConst(node, opCall) - argStack.add(funcArity) - else: - addTerminalValue(inBlock=false): - addConst(node, opCall) - else: - addTerminalValue(inBlock=false): - if node.s == "true": - addToCommand(opConstBT) - elif node.s == "false": - addToCommand(opConstBF) - else: - addConst(node, opLoad) + template addVariableLoad(nd: Node): untyped = + addVariableLoad(consts, it, nd, nd.value, lastOpStore, lastOpStorePos) + lastOpStore = -1 - of Block: - addTerminalValue(inBlock=false): - if node.a.len==0: - addToCommand(opConstA) - else: - addConst(node, opPush) - - of Integer: - addTerminalValue(inBlock=false): - when defined(WEB) or not defined(NOGMP): - if likely(node.iKind==NormalInteger): - if node.i>=0 and node.i<=15: addToCommand(byte(opConstI0) + byte(node.i)) - else: addConst(node, opPush) - else: - addConst(node, opPush) - else: - if node.i>=0 and node.i<=15: addToCommand(byte(opConstI0) + byte(node.i)) - else: addConst(node, opPush) - - of String: - {.linearScanEnd.} - addTerminalValue(inBlock=false): - if node.s.len==0: - addToCommand(opConstS) - else: - addConst(node, opPush) - - #--------------------------- - - of Label: - - let funcIndx {.cursor.} = node.s - var hasThickArrow = false - var ab: ValueArray - var sb: ValueArray - let nextNode {.cursor.} = n.a[i+1] - if (nextNode.kind == Word and nextNode.s == "function") or - (nextNode.kind == Symbol and nextNode.m == dollar): - let afterNextNode {.cursor.} = n.a[i+2] - if afterNextNode.kind == Symbol and afterNextNode.m == thickarrowright: - i += 2 - var funcArity: int8 = 0 - processThickArrowRight(ab, sb, it, n, i, funcArity) - TmpArities[funcIndx] = funcArity - hasThickArrow = true - i += 1 - else: - TmpArities[funcIndx] = int8(afterNextNode.a.countIt(it.kind != Type)) #n.a[i+2].a.len - else: - if not isDictionary: - TmpArities.del(funcIndx) + template addSingleCommand(op: untyped): untyped = + lastOpStore = -1 + it.addByte(op) - if unlikely(isDictionary): - addShortConst(node, opDStore) - else: - addConst(node, opStore) - - argStack.add(1) - - if hasThickArrow: - addConst(newWord("function"), opCall) - argStack.add(2) - - # add the blocks - addTerminalValue(inBlock=false): - addConst(newBlock(ab), opPush) - addTerminalValue(inBlock=false): - addConst(newBlock(sb), opPush) - - of Range: - addTerminalValue(inBlock=false): - addConst(node, opPush) - - of Null: addToCommand(opConstN) - of Logical: - if isTrue(node): addToCommand(opConstBT) - elif isFalse(node): addToCommand(opConstBF) - else: addToCommand(opConstBM) - - of Floating: - addTerminalValue(inBlock=false): - if node.f==0.0: addToCommand(opConstF0) - elif node.f==1.0: addToCommand(opConstF1) - elif node.f==2.0: addToCommand(opConstF2) - else: addConst(node, opPush) - - of Attribute: - addConst(node, opAttr) - addToCommand(opConstBT) - - of AttributeLabel: - addConst(node, opAttr) - argStack[argStack.len-1] += 1 - - of Path: - var pathCallV: Value = nil - - if (let curr = Syms.getOrDefault(node.p[0].s, nil); not curr.isNil): - let next {.cursor.} = node.p[1] - if curr.kind==Dictionary and (next.kind==Literal or next.kind==Word): - if (let item = curr.d.getOrDefault(next.s, nil); not item.isNil): - if item.kind == Function: - pathCallV = item - - if not pathCallV.isNil: - addConst(pathCallV, opCall) - argStack.add(pathCallV.arity) - else: - addTerminalValue(inBlock=false): - addToCommand(opGet) - - var i=1 - while i= -1 and iv.i <= 15: + addSingleCommand(byte(opConstI0) + byte(iv.i)) + alreadyPut = true + of Floating: + case iv.f: + of -1.0: + addSingleCommand(opConstF1M) + alreadyPut = true + of 0.0: + addSingleCommand(opConstF0) + alreadyPut = true + of 1.0: + addSingleCommand(opConstF1) + alreadyPut = true + of 2.0: + addSingleCommand(opConstF2) + alreadyPut = true + else: + discard + of String: + if iv.s == "": + addSingleCommand(opConstS) + alreadyPut = true + of Block: + if iv.a.len == 0: + addSingleCommand(opConstA) + alreadyPut = true + of Dictionary: + if iv.d.len == 0: + addSingleCommand(opConstD) + alreadyPut = true + else: + discard - else: - # of Complex, Rational, Version, Type, Char, Literal, SymbolLiteral, Quantity, Regex, Color, Object, Function: - addTerminalValue(inBlock=false): - addConst(node, opPush) + if not alreadyPut: + addConst(instruction.value, opPush) + of VariableLoad: + addVariableLoad(instruction) + of AttributeNode: + addConst(instruction.value, opAttr) + of VariableStore: + if unlikely(isDictionary): + addConst(instruction.value, opDStore, hasShortcut=false) + else: + lastOpStore = addConstAndGetIndex(instruction.value, opStore) + lastOpStorePos = it.len + of OtherCall: + addConst(instruction.value, opCall) + of BuiltinCall: + addSingleCommand(instruction.op) + of SpecialCall: + # TODO(VM/eval) nested `switch` calls are not being optimized + # labels: vm, evaluator, performance, enhancement + addSingleCommand(instruction.op) i += 1 - if currentCommand.len > 0: - addCurrentCommandToBytecode() +#======================================= +# Main +#======================================= -proc doEval*(root: Value, isDictionary=false, useStored: static bool = true): Translation {.inline.} = +proc doEval*(root: Value, isDictionary=false, isFunctionBlock=false, omitNewlines=false, useStored: static bool = true): Translation {.inline.} = ## Take a parsed Block of values and return its Translation - ## that is: the constants found + the list of bytecode instructions @@ -1071,29 +551,23 @@ proc doEval*(root: Value, isDictionary=false, useStored: static bool = true): Tr when useStored: if not root.dynamic: vhash = hash(root) - if (let stEv = StoredEval.getOrDefault(vhash, nil); not stEv.isNil): - return stEv - - var cnsts: ValueArray - var newit: VBinary + if (let storedTranslation = StoredTranslations.getOrDefault(vhash, nil); not storedTranslation.isNil): + return storedTranslation - TmpArities = collect: - for k,v in Syms.pairs: - if v.kind == Function: - {k: v.arity} + var consts: ValueArray + var it: VBinary - evalOne(root, cnsts, newit, isDictionary=isDictionary) - newit.add(byte(opEnd)) + evaluateBlock(generateAst(root, asDictionary=isDictionary, asFunction=isFunctionBlock), consts, it, isDictionary=isDictionary, omitNewlines=omitNewlines) + it.add(byte(opEnd)) - # when defined(OPTIMIZED): - # newit = optimizeBytecode(newit) + result = Translation(constants: consts, instructions: it) - result = Translation(constants: cnsts, instructions: newit) + #dump(newBytecode(result)) when useStored: if vhash != -1: - StoredEval[vhash] = result + StoredTranslations[vhash] = result -template evalOrGet*(item: Value): untyped = +template evalOrGet*(item: Value, isFunction=false): untyped = if item.kind==Bytecode: item.trans - else: doEval(item) \ No newline at end of file + else: doEval(item, isFunctionBlock=isFunction) \ No newline at end of file diff --git a/src/vm/exec.nim b/src/vm/exec.nim index da67932858..d8a4a31f96 100644 --- a/src/vm/exec.nim +++ b/src/vm/exec.nim @@ -121,8 +121,14 @@ template callByIndex(idx: int):untyped = template fetchAttributeByIndex(idx: int):untyped = stack.pushAttr(cnst[idx].s, move stack.pop()) -macro performConditionalJump(symb: untyped): untyped = - result = quote do: +template performConditionalJump(symb: untyped, short: static bool=false): untyped = + when short: + let x = move stack.pop() + let y = move stack.pop() + i += 1 + if `symb`(x,y): + i += int(it[i]) + else: let x = move stack.pop() let y = move stack.pop() i += 2 @@ -292,7 +298,7 @@ proc execFunction*(fun: Value, fid: Hash) = SetSym(arg, move stack.pop()) if fun.bcode.isNil: - fun.bcode = newBytecode(doEval(fun.main)) + fun.bcode = newBytecode(doEval(fun.main, isFunctionBlock=true)) try: ExecLoop(fun.bcode().trans.constants, fun.bcode().trans.instructions) @@ -347,7 +353,7 @@ proc execFunctionInline*(fun: Value, fid: Hash) = SetSym(arg, move stack.pop()) if fun.bcode.isNil: - fun.bcode = newBytecode(doEval(fun.main)) + fun.bcode = newBytecode(doEval(fun.main, isFunctionBlock=true)) try: ExecLoop(fun.bcode().trans.constants, fun.bcode().trans.instructions) @@ -387,6 +393,7 @@ proc ExecLoop*(cnst: ValueArray, it: VBinary) = case op: # [0x00-0x1F] # push constants + of opConstI1M : stack.push(I1M) of opConstI0 : stack.push(I0) of opConstI1 : stack.push(I1) of opConstI2 : stack.push(I2) @@ -404,14 +411,11 @@ proc ExecLoop*(cnst: ValueArray, it: VBinary) = of opConstI14 : stack.push(I14) of opConstI15 : stack.push(I15) - of opConstI1M : stack.push(I1M) # unused by evaluator - + of opConstF1M : stack.push(F1M) of opConstF0 : stack.push(F0) of opConstF1 : stack.push(F1) of opConstF2 : stack.push(F2) - of opConstF1M : stack.push(F1M) # unused by evaluator - of opConstBT : stack.push(VTRUE) of opConstBF : stack.push(VFALSE) of opConstBM : stack.push(VMAYBE) @@ -561,102 +565,106 @@ proc ExecLoop*(cnst: ValueArray, it: VBinary) = # [0x80-0x8F] # arithmetic operators - of opAdd : AddF.action()() - of opSub : SubF.action()() - of opMul : MulF.action()() - of opDiv : DivF.action()() - of opFdiv : FdivF.action()() - of opMod : ModF.action()() - of opPow : PowF.action()() + of opAdd : DoAdd() + of opSub : DoSub() + of opMul : DoMul() + of opDiv : DoDiv() + of opFdiv : DoFdiv() + of opMod : DoMod() + of opPow : DoPow() - of opNeg : NegF.action()() + of opNeg : DoNeg() + + # increment/decrement + of opInc : DoInc() + of opDec : DoDec() # binary operators - of opBNot : BNotF.action()() - of opBAnd : BAndF.action()() - of opBOr : BOrF.action()() + of opBNot : DoBNot() + of opBAnd : DoBAnd() + of opBOr : DoBOr() - of opShl : ShlF.action()() - of opShr : ShrF.action()() + of opShl : DoShl() + of opShr : DoShr() - # logical operators - of opNot : NotF.action()() - of opAnd : AndF.action()() - of opOr : OrF.action()() + of RSRV1 : discard # [0x90-0x9F] + # logical operators + of opNot : DoNot() + of opAnd : DoAnd() + of opOr : DoOr() + # comparison operators - of opEq : EqF.action()() - of opNe : NeF.action()() - of opGt : GtF.action()() - of opGe : GeF.action()() - of opLt : LtF.action()() - of opLe : LeF.action()() + of opEq : DoEq() + of opNe : DoNe() + of opGt : DoGt() + of opGe : DoGe() + of opLt : DoLt() + of opLe : DoLe() + # getters/setters + of opGet : DoGet() + of opSet : DoSet() + + of RSRV2 : discard + of RSRV3 : discard + of RSRV4 : discard + of RSRV5 : discard + of RSRV6 : discard + + # [0xA0-0xAF] # branching - of opIf : IfF.action()() - of opIfE : IfEF.action()() - of opUnless : UnlessF.action()() - of opElse : ElseF.action()() - of opSwitch : SwitchF.action()() - of opWhile : WhileF.action()() - of opReturn : ReturnF.action()() + of opIf : DoIf() + of opIfE : DoIfE() + of opUnless : DoUnless() + of opUnlessE : DoUnlessE() + of opElse : DoElse() + of opSwitch : DoSwitch() + of opWhile : DoWhile() + + of opReturn : DoReturn() + of opBreak : DoBreak() + of opContinue : DoContinue() # converters - of opTo : ToF.action()() + of opTo : DoTo() of opToS : stack.push(VSTRINGT) - ToF.action()() + DoTo() of opToI : stack.push(VINTEGERT) - ToF.action()() + DoTo() - # [0xA0-0xAF] - # getters/setters - of opGet : GetF.action()() - of opSet : SetF.action()() + of RSRV7 : discard + of RSRV8 : discard + of RSRV9 : discard + # [0xB0-0xBF] # generators - of opArray : ArrayF.action()() - of opDict : DictF.action()() - of opFunc : FuncF.action()() - - # ranges & iterators - of opRange : RangeF.action()() - of opLoop : LoopF.action()() - of opMap : MapF.action()() - of opSelect : SelectF.action()() + of opArray : DoArray() + of opDict : DoDict() + of opFunc : DoFunc() + of opRange : DoRange() + + # iterators + of opLoop : DoLoop() + of opMap : DoMap() + of opSelect : DoSelect() # collections - of opSize : SizeF.action()() - of opReplace : ReplaceF.action()() - of opSplit : SplitF.action()() - of opJoin : JoinF.action()() - of opReverse : ReverseF.action()() - - # increment/decrement - of opInc : IncF.action()() - of opDec : DecF.action()() + of opSize : DoSize() + of opReplace : DoReplace() + of opSplit : DoSplit() + of opJoin : DoJoin() + of opReverse : DoReverse() + of opAppend : DoAppend() - # [0xB0-0xBF] # i/o operations - of opPrint : PrintF.action()() + of opPrint : DoPrint() - of RSRV1 : discard - of RSRV2 : discard - of RSRV3 : discard - of RSRV4 : discard - of RSRV5 : discard - of RSRV6 : discard - of RSRV7 : discard - of RSRV8 : discard - of RSRV9 : discard of RSRV10 : discard of RSRV11 : discard - of RSRV12 : discard - of RSRV13 : discard - of RSRV14 : discard - of RSRV15 : discard #--------------------------------- # LOW-LEVEL OPERATIONS @@ -674,36 +682,58 @@ proc ExecLoop*(cnst: ValueArray, it: VBinary) = # conditional jumps of opJmpIf : + let x = move stack.pop() + i += 1 + if not (x.kind==Null or isFalse(x)): + i += int(it[i]) + + of opJmpIfX: let x = move stack.pop() i += 2 if not (x.kind==Null or isFalse(x)): i += int(uint16(it[i-1]) shl 8 + byte(it[i])) of opJmpIfNot : + let x = move stack.pop() + i += 1 + if x.kind==Null or isFalse(x): + i += int(it[i]) + + of opJmpIfNotX: let x = move stack.pop() i += 2 if x.kind==Null or isFalse(x): i += int(uint16(it[i-1]) shl 8 + byte(it[i])) - of opJmpIfEq : performConditionalJump(`==`) - of opJmpIfNe : performConditionalJump(`!=`) - of opJmpIfGt : performConditionalJump(`>`) - of opJmpIfGe : performConditionalJump(`>=`) - of opJmpIfLt : performConditionalJump(`<`) - of opJmpIfLe : performConditionalJump(`<=`) - - of RSRV16 : discard - of RSRV17 : discard - of RSRV18 : discard + of opJmpIfEq : performConditionalJump(`==`, short=true) + of opJmpIfEqX : performConditionalJump(`==`) + of opJmpIfNe : performConditionalJump(`!=`, short=true) + of opJmpIfNeX : performConditionalJump(`!=`) + of opJmpIfGt : performConditionalJump(`>`, short=true) + of opJmpIfGtX : performConditionalJump(`>`) + of opJmpIfGe : performConditionalJump(`>=`, short=true) + of opJmpIfGeX : performConditionalJump(`>=`) + of opJmpIfLt : performConditionalJump(`<`, short=true) + of opJmpIfLtX : performConditionalJump(`<`) + of opJmpIfLe : performConditionalJump(`<=`, short=true) + of opJmpIfLeX : performConditionalJump(`<=`) # flow control of opGoto : + i += 1 + i += int(it[i]) + + of opGotoX : i += 2 i += int(uint16(it[i-1]) shl 8 + byte(it[i])) of opGoup : + i += 1 + i -= (int(it[i]) + 2) + + of opGoupX : i += 2 - i -= int(uint16(it[i-1]) shl 8 + byte(it[i])) + i -= (int(uint16(it[i-1]) shl 8 + byte(it[i])) + 3) of opRet : discard diff --git a/src/vm/lib.nim b/src/vm/lib.nim index 5a78704912..10ba61270c 100644 --- a/src/vm/lib.nim +++ b/src/vm/lib.nim @@ -15,8 +15,8 @@ import sequtils, strutils, tables export strutils, tables -import vm/[globals, errors, stack, values/comparison, values/clean, values/printable, values/value] -export clean, comparison, globals, printable, stack, value +import vm/[globals, errors, opcodes, stack, values/comparison, values/printable, values/value] +export comparison, globals, printable, opcodes, stack, value import vm/values/custom/[vcolor, vcomplex, vlogical, vquantity, vrational, vregex, vsymbol] export vcolor, vcomplex, vlogical, vquantity, vrational, vregex, vsymbol @@ -54,7 +54,7 @@ else: # else: # args -template builtin*(n: string, alias: VSymbol, rule: PrecedenceKind, description: string, args: untyped, attrs: untyped, returns: ValueSpec, example: string, act: untyped):untyped = +template builtin*(n: string, alias: VSymbol, op: OpCode, rule: PrecedenceKind, description: string, args: untyped, attrs: untyped, returns: ValueSpec, example: string, act: untyped):untyped = ## add new builtin, function with given name, alias, ## rule, etc - followed by the code block to be ## executed when the function is called @@ -83,6 +83,7 @@ template builtin*(n: string, alias: VSymbol, rule: PrecedenceKind, description: when not defined(WEB): attrs.toOrderedTable else: initOrderedTable[string,(ValueSpec,string)](), returns, cleanExample, + op, proc () = hookProcProfiler("lib/require"): require(n, args) @@ -97,53 +98,57 @@ template builtin*(n: string, alias: VSymbol, rule: PrecedenceKind, description: SetSym(n, b) - when n=="array" : ArrayF = b - elif n=="dictionary" : DictF = b - elif n=="function" : FuncF = b - elif n=="add" : AddF = b - elif n=="sub" : SubF = b - elif n=="mul" : MulF = b - elif n=="div" : DivF = b - elif n=="fdiv" : FdivF = b - elif n=="mod" : ModF = b - elif n=="pow" : PowF = b - elif n=="neg" : NegF = b - elif n=="not" : BNotF = b - elif n=="and" : BAndF = b - elif n=="or" : BOrF = b - elif n=="shl" : ShlF = b - elif n=="shr" : ShrF = b - elif n=="not?" : NotF = b - elif n=="and?" : AndF = b - elif n=="or?" : OrF = b - elif n=="equal?" : EqF = b - elif n=="notEqual?" : NeF = b - elif n=="greater?" : GtF = b - elif n=="greaterOrEqual?" : GeF = b - elif n=="less?" : LtF = b - elif n=="lessOrEqual?" : LeF = b - elif n=="if" : IfF = b - elif n=="if?" : IfEF = b - elif n=="unless" : UnlessF = b - elif n=="else" : ElseF = b - elif n=="switch" : SwitchF = b - elif n=="while" : WhileF = b - elif n=="return" : ReturnF = b - elif n=="to" : ToF = b - elif n=="print" : PrintF = b - elif n=="get" : GetF = b - elif n=="set" : SetF = b - elif n=="range" : RangeF = b - elif n=="loop" : LoopF = b - elif n=="map" : MapF = b - elif n=="select" : SelectF = b - elif n=="size" : SizeF = b - elif n=="replace" : ReplaceF = b - elif n=="split" : SplitF = b - elif n=="join" : JoinF = b - elif n=="reverse" : ReverseF = b - elif n=="inc" : IncF = b - elif n=="dec" : DecF = b + when n=="add" : DoAdd = b.action() + elif n=="sub" : DoSub = b.action() + elif n=="mul" : DoMul = b.action() + elif n=="div" : DoDiv = b.action() + elif n=="fdiv" : DoFdiv = b.action() + elif n=="mod" : DoMod = b.action() + elif n=="pow" : DoPow = b.action() + elif n=="neg" : DoNeg = b.action() + elif n=="inc" : DoInc = b.action() + elif n=="dec" : DoDec = b.action() + elif n=="not" : DoBNot = b.action() + elif n=="and" : DoBAnd = b.action() + elif n=="or" : DoBOr = b.action() + elif n=="shl" : DoShl = b.action() + elif n=="shr" : DoShr = b.action() + elif n=="not?" : DoNot = b.action() + elif n=="and?" : DoAnd = b.action() + elif n=="or?" : DoOr = b.action() + elif n=="equal?" : DoEq = b.action() + elif n=="notEqual?" : DoNe = b.action() + elif n=="greater?" : DoGt = b.action() + elif n=="greaterOrEqual?" : DoGe = b.action() + elif n=="less?" : DoLt = b.action() + elif n=="lessOrEqual?" : DoLe = b.action() + elif n=="get" : DoGet = b.action() + elif n=="set" : DoSet = b.action() + elif n=="if" : DoIf = b.action() + elif n=="if?" : DoIfE = b.action() + elif n=="unless" : DoUnless = b.action() + elif n=="unless?" : DoUnlessE = b.action() + elif n=="else" : DoElse = b.action() + elif n=="switch" : DoSwitch = b.action() + elif n=="while" : DoWhile = b.action() + elif n=="return" : DoReturn = b.action() + elif n=="break" : DoBreak = b.action() + elif n=="continue" : DoContinue = b.action() + elif n=="to" : DoTo = b.action() + elif n=="array" : DoArray = b.action() + elif n=="dictionary" : DoDict = b.action() + elif n=="function" : DoFunc = b.action() + elif n=="range" : DoRange = b.action() + elif n=="loop" : DoLoop = b.action() + elif n=="map" : DoMap = b.action() + elif n=="select" : DoSelect = b.action() + elif n=="size" : DoSize = b.action() + elif n=="replace" : DoReplace = b.action() + elif n=="split" : DoSplit = b.action() + elif n=="join" : DoJoin = b.action() + elif n=="reverse" : DoReverse = b.action() + elif n=="append" : DoAppend = b.action() + elif n=="print" : DoPrint = b.action() when alias != unaliased: Aliases[alias] = AliasBinding( diff --git a/src/vm/opcodes.nim b/src/vm/opcodes.nim index 8f62ac64fe..a44537c81e 100644 --- a/src/vm/opcodes.nim +++ b/src/vm/opcodes.nim @@ -31,30 +31,28 @@ type # [0x00-0x1F] # push constants - opConstI0 = 0x00 # () # # 0 - opConstI1 = 0x01 # () # # 1 - opConstI2 = 0x02 # () # # 2 - opConstI3 = 0x03 # () # # 3 - opConstI4 = 0x04 # () # # 4 - opConstI5 = 0x05 # () # # 5 - opConstI6 = 0x06 # () # # 6 - opConstI7 = 0x07 # () # # 7 - opConstI8 = 0x08 # () # # 8 - opConstI9 = 0x09 # () # # 9 - opConstI10 = 0x0A # () # # 10 - opConstI11 = 0x0B # () # # 11 - opConstI12 = 0x0C # () # # 12 - opConstI13 = 0x0D # () # # 13 - opConstI14 = 0x0E # () # # 14 - opConstI15 = 0x0F # () # # 15 - - opConstI1M = 0x10 # () # # -1 - - opConstF0 = 0x11 # () # # 0.0 - opConstF1 = 0x12 # () # # 1.0 - opConstF2 = 0x13 # () # # 2.0 - - opConstF1M = 0x14 # () # # -1.0 + opConstI1M = 0x00 # () # # -1 + opConstI0 = 0x01 # () # # 0 + opConstI1 = 0x02 # () # # 1 + opConstI2 = 0x03 # () # # 2 + opConstI3 = 0x04 # () # # 3 + opConstI4 = 0x05 # () # # 4 + opConstI5 = 0x06 # () # # 5 + opConstI6 = 0x07 # () # # 6 + opConstI7 = 0x08 # () # # 7 + opConstI8 = 0x09 # () # # 8 + opConstI9 = 0x0A # () # # 9 + opConstI10 = 0x0B # () # # 10 + opConstI11 = 0x0C # () # # 11 + opConstI12 = 0x0D # () # # 12 + opConstI13 = 0x0E # () # # 13 + opConstI14 = 0x0F # () # # 14 + opConstI15 = 0x10 # () # # 15 + + opConstF1M = 0x11 # () # # -1.0 + opConstF0 = 0x12 # () # # 0.0 + opConstF1 = 0x13 # () # # 1.0 + opConstF2 = 0x14 # () # # 2.0 opConstBT = 0x15 # () # # true opConstBF = 0x16 # () # # false @@ -211,94 +209,99 @@ type opNeg = 0x87 # () # x # result + # increment/decrement + opInc = 0x88 # () # value # result + opDec = 0x89 # () # value # result + # binary operators - opBNot = 0x88 # () # x # result - opBAnd = 0x89 # () # x,y # result - opBOr = 0x8A # () # x,y # result + opBNot = 0x8A # () # x # result + opBAnd = 0x8B # () # x,y # result + opBOr = 0x8C # () # x,y # result - opShl = 0x8B # () # x,y # result - opShr = 0x8C # () # x,y # result + opShl = 0x8D # () # x,y # result + opShr = 0x8E # () # x,y # result - # logical operators - opNot = 0x8D # () # x # result - opAnd = 0x8E # () # x,y # result - opOr = 0x8F # () # x,y # result + RSRV1 = 0x8F # # [0x90-0x9F] + # logical operators + opNot = 0x90 # () # x # result + opAnd = 0x91 # () # x,y # result + opOr = 0x92 # () # x,y # result + # comparison operators - opEq = 0x90 # () # x,y # result - opNe = 0x91 # () # x,y # result - opGt = 0x92 # () # x,y # result - opGe = 0x93 # () # x,y # result - opLt = 0x94 # () # x,y # result - opLe = 0x95 # () # x,y # result + opEq = 0x93 # () # x,y # result + opNe = 0x94 # () # x,y # result + opGt = 0x95 # () # x,y # result + opGe = 0x96 # () # x,y # result + opLt = 0x97 # () # x,y # result + opLe = 0x98 # () # x,y # result + # getters/setters + opGet = 0x99 # () # obj,key # result + opSet = 0x9A # () # obj,key,rvalue # + + RSRV2 = 0x9B # + RSRV3 = 0x9C # + RSRV4 = 0x9D # + RSRV5 = 0x9E # + RSRV6 = 0x9F # + + # [0xA0-0xAF] # branching - opIf = 0x96 # () # cond,bl # X - opIfE = 0x97 # () # cond,bl # cond - opUnless = 0x98 # () # cond,bl # X - opElse = 0x99 # () # success # X - opSwitch = 0x9A # () # cond,a,b # X - opWhile = 0x9B # () # cond,bl # X - opReturn = 0x9C # () # value # + opIf = 0xA0 # () # cond,bl # X + opIfE = 0xA1 # () # cond,bl # cond + opUnless = 0xA2 # () # cond,bl # X + opUnlessE = 0xA3 # () # cond,bl # cond + opElse = 0xA4 # () # success # X + opSwitch = 0xA5 # () # cond,a,b # X + opWhile = 0xA6 # () # cond,bl # X + + opReturn = 0xA7 # () # value # + opBreak = 0xA8 # () # # + opContinue = 0xA9 # () # # # converters - opTo = 0x9D # () # tp,value # result - opToS = 0x9E # () # value # result - opToI = 0x9F # () # value # result + opTo = 0xAA # () # tp,value # result + opToS = 0xAB # () # value # result + opToI = 0xAC # () # value # result - # [0xA0-0xAF] - # getters/setters - opGet = 0xA0 # () # obj,key # result - opSet = 0xA1 # () # obj,key,rvalue # + RSRV7 = 0xAD # + RSRV8 = 0xAE # + RSRV9 = 0xAF # + # [0xB0-0xBF] # generators - opArray = 0xA2 # () # blk # result - opDict = 0xA3 # () # blk # result - opFunc = 0xA4 # () # params,blk # result + opArray = 0xB0 # () # blk # result + opDict = 0xB1 # () # blk # result + opFunc = 0xB2 # () # params,blk # result + opRange = 0xB3 # () # start,stop # result # ranges & iterators - opRange = 0xA5 # () # start,stop # result - opLoop = 0xA6 # () # range,param,blk # X - opMap = 0xA7 # () # range,param,blk # result - opSelect = 0xA8 # () # range,param,blk # result + + opLoop = 0xB4 # () # range,param,blk # X + opMap = 0xB5 # () # range,param,blk # result + opSelect = 0xB6 # () # range,param,blk # result # collections - opSize = 0xA9 # () # obj # result - opReplace = 0xAA # () # obj,what,with # result - opSplit = 0xAB # () # obj,what # result - opJoin = 0xAC # () # obj # result - opReverse = 0xAD # () # blk # result + opSize = 0xB7 # () # obj # result + opReplace = 0xB8 # () # obj,what,with # result + opSplit = 0xB9 # () # obj,what # result + opJoin = 0xBA # () # obj # result + opReverse = 0xBB # () # blk # result + opAppend = 0xBC # () # x,y # result - # increment/decrement - opInc = 0xAE # () # value # result - opDec = 0xAF # () # value # result - - # [0xB0-0xBF] # i/o operations - opPrint = 0xB0 # () # value # - - RSRV1 = 0xB1 # - RSRV2 = 0xB2 # - RSRV3 = 0xB3 # - RSRV4 = 0xB4 # - RSRV5 = 0xB5 # - RSRV6 = 0xB6 # - RSRV7 = 0xB7 # - RSRV8 = 0xB8 # - RSRV9 = 0xB9 # - RSRV10 = 0xBA # - RSRV11 = 0xBB # - RSRV12 = 0xBC # - RSRV13 = 0xBD # - RSRV14 = 0xBE # - RSRV15 = 0xBF # + opPrint = 0xBD # () # value # + + RSRV10 = 0xBE # + RSRV11 = 0xBF # #--------------------------------- # LOW-LEVEL OPERATIONS #--------------------------------- - # [0xB0-0xCF] + # [0xC0-0xDF] # no operation opNop = 0xC0 # () # # @@ -309,24 +312,31 @@ type opSwap = 0xC4 # () # X,Y # Y,X # conditional jumps - opJmpIf = 0xC5 # (idx,idxB) # cond # - opJmpIfNot = 0xC6 # (idx,idxB) # cond # - opJmpIfEq = 0xC7 # (idx,idxB) # cond # - opJmpIfNe = 0xC8 # (idx,idxB) # cond # - opJmpIfGt = 0xC9 # (idx,idxB) # cond # - opJmpIfGe = 0xCA # (idx,idxB) # cond # - opJmpIfLt = 0xCB # (idx,idxB) # cond # - opJmpIfLe = 0xCC # (idx,idxB) # cond # - - RSRV16 = 0xCD # - RSRV17 = 0xCE # - RSRV18 = 0xCF # + opJmpIf = 0xC5 # (idx) # cond # + opJmpIfX = 0xC6 # (idx,idxB) # cond # + opJmpIfNot = 0xC7 # (idx) # cond # + opJmpIfNotX = 0xC8 # (idx,idxB) # cond # + opJmpIfEq = 0xC9 # (idx) # cond # + opJmpIfEqX = 0xCA # (idx,idxB) # cond # + opJmpIfNe = 0xCB # (idx) # cond # + opJmpIfNeX = 0xCC # (idx,idxB) # cond # + opJmpIfGt = 0xCD # (idx) # cond # + opJmpIfGtX = 0xCE # (idx,idxB) # cond # + opJmpIfGe = 0xCF # (idx) # cond # + opJmpIfGeX = 0xD0 # (idx,idxB) # cond # + opJmpIfLt = 0xD1 # (idx) # cond # + opJmpIfLtX = 0xD2 # (idx,idxB) # cond # + opJmpIfLe = 0xD3 # (idx) # cond # + opJmpIfLeX = 0xD4 # (idx,idxB) # cond # # flow control - opGoto = 0xD0 # (idx,idxB) # # - opGoup = 0xD1 # (idx,idxB) # # - opRet = 0xD2 # () # # - opEnd = 0xD3 # () # # + opGoto = 0xD5 # (idx) # # + opGotoX = 0xD6 # (idx,idxB) # # + opGoup = 0xD7 # (idx) # # + opGoupX = 0xD8 # (idx,idxB) # # + + opRet = 0xD9 # () # # + opEnd = 0xDA # () # # when false: #======================================= diff --git a/src/vm/parse.nim b/src/vm/parse.nim index 1ea8cc4031..8027ea9dd0 100644 --- a/src/vm/parse.nim +++ b/src/vm/parse.nim @@ -24,7 +24,7 @@ # Libraries #======================================= -import lexbase, os, sequtils, streams +import lexbase, os, streams import strutils, tables, unicode import vm/[errors, profiler, values/value] @@ -94,8 +94,12 @@ proc parseDataBlock*(blk: Value): Value template Empty(s: var string): bool = s.len == 0 +func addAnnotatedTokenToBlock(blok: var Value, token: Value, p: var Parser) {.enforceNoRaises.} = + token.ln = uint32(p.lineNumber) + blok.a.add(token) + template AddToken(token: untyped): untyped = - topBlock.a.add(token) + topBlock.addAnnotatedTokenToBlock(token, p) template LastToken(): untyped = topBlock.a[^1] @@ -103,14 +107,6 @@ template LastToken(): untyped = template ReplaceLastToken(with: untyped): untyped = topBlock.a[^1] = with -template stripTrailingNewlines(): untyped = - if topBlock.a[^1].kind == Newline: - let lastN = topBlock.a.len-1 - var firstN = lastN - while firstN-1 >= 0 and topBlock.a[firstN-1].kind == Newline: - firstN -= 1 - topBlock.a.delete(firstN..lastN) - #======================================= # Helpers #======================================= @@ -151,14 +147,14 @@ template skip(p: var Parser, scriptStr: var string) = break of CR: pos = lexbase.handleCR(p, pos) - when not defined(NOERRORLINES): - AddToken newNewline(p.lineNumber) + # when not defined(NOERRORLINES): + # AddToken newNewline(p.lineNumber) scriptStr &= "\n" break of LF: pos = lexbase.handleLF(p, pos) - when not defined(NOERRORLINES): - AddToken newNewline(p.lineNumber) + # when not defined(NOERRORLINES): + # AddToken newNewline(p.lineNumber) scriptStr &= "\n" break else: @@ -170,13 +166,13 @@ template skip(p: var Parser, scriptStr: var string) = break of CR: pos = lexbase.handleCR(p, pos) - when not defined(NOERRORLINES): - AddToken newNewline(p.lineNumber) + # when not defined(NOERRORLINES): + # AddToken newNewline(p.lineNumber) break of LF: pos = lexbase.handleLF(p, pos) - when not defined(NOERRORLINES): - AddToken newNewline(p.lineNumber) + # when not defined(NOERRORLINES): + # AddToken newNewline(p.lineNumber) break else: inc(pos) @@ -184,12 +180,12 @@ template skip(p: var Parser, scriptStr: var string) = inc(pos) of CR: pos = lexbase.handleCR(p, pos) - when not defined(NOERRORLINES): - AddToken newNewline(p.lineNumber) + # when not defined(NOERRORLINES): + # AddToken newNewline(p.lineNumber) of LF: pos = lexbase.handleLF(p, pos) - when not defined(NOERRORLINES): - AddToken newNewline(p.lineNumber) + # when not defined(NOERRORLINES): + # AddToken newNewline(p.lineNumber) of '#': if p.buf[pos+1]=='!': inc(pos) @@ -575,7 +571,6 @@ template parseAndAddSymbol(p: var Parser, topBlock: var Value) = inc(pos) p.symbol = triangleright else: - stripTrailingNewlines() p.symbol = pipe of '/' : if p.buf[pos+1]=='/': @@ -802,8 +797,8 @@ template parseExponent(p: var Parser) = proc parseBlock(p: var Parser, level: int, isDeferred: bool = true): Value {.inline.} = var topBlock: Value var scriptStr: string - if isDeferred: topBlock = newBlock(dirty=true) - else: topBlock = newInline(dirty=true) + if isDeferred: topBlock = newBlock() + else: topBlock = newInline() let initial = p.bufpos let initialLine = p.lineNumber while true: @@ -1026,7 +1021,7 @@ proc parseAsDictionary(blk: Value, start: int): Value = let lbl = blk.a[i].s i += 1 var values: ValueArray - while i < blk.a.len and blk.a[i].kind!=Newline and blk.a[i].kind!=Label: + while i < blk.a.len and blk.a[i].kind!=Label: case blk.a[i].kind: of Block: values.add(parseDataBlock(blk.a[i])) @@ -1057,15 +1052,15 @@ proc parseAsBlock(blk: Value, start: int): Value = values.add(parseDataBlock(blk.a[i])) of String, Literal, Word, Label: values.add(newString(blk.a[i].s)) - of Newline: - if values.len > 1: - result.a.add(newBlock(values)) - values.setLen(0) - elif values.len == 1: - result.a.add(values[0]) - values.setLen(0) - else: - discard + # of Newline: + # if values.len > 1: + # result.a.add(newBlock(values)) + # values.setLen(0) + # elif values.len == 1: + # result.a.add(values[0]) + # values.setLen(0) + # else: + # discard else: values.add(blk.a[i]) @@ -1088,8 +1083,8 @@ proc parseDataBlock*(blk: Value): Value = return VNULL var i = 0 - while i < blk.a.len and blk.a[i].kind==Newline: - i += 1 + # while i < blk.a.len and blk.a[i].kind==Newline: + # i += 1 if i==blk.a.len: return VNULL diff --git a/src/vm/profiler.nim b/src/vm/profiler.nim index 40bb449976..9f6b578277 100644 --- a/src/vm/profiler.nim +++ b/src/vm/profiler.nim @@ -23,7 +23,7 @@ when defined(PROFILER): # Libraries #======================================= - import algorithm, hashes, std/monotimes + import algorithm, hashes, std/monotimes, std/sets import strformat, strutils, sugar, tables, times import helpers/terminal @@ -270,9 +270,9 @@ template hookProcProfiler*(name: string, actionContent: untyped): untyped = else: actionContent -template hookMiscProfiler*(name: string): untyped = +template hookOptimProfiler*(name: string): untyped = when defined(PROFILER): - var newRow = addMetricIfNotExists(name, "misc") + var newRow = addMetricIfNotExists(name, "optimizations") newRow.runs += 1 #======================================= @@ -285,7 +285,7 @@ proc initProfiler*() = "functions": initOrderedTable[string, ProfilerDataRow](), "ops": initOrderedTable[string, ProfilerDataRow](), "procs": initOrderedTable[string, ProfilerDataRow](), - "misc": initOrderedTable[string, ProfilerDataRow]() + "optimizations": initOrderedTable[string, ProfilerDataRow]() }.toOrderedTable # TODO(VM/profiler) Completely remove or make it work "properly" @@ -302,7 +302,7 @@ proc showProfilerData*() = printProfilerDataTable("functions") printProfilerDataTable("ops") printProfilerDataTable("procs") - printProfilerDataTable("misc") + printProfilerDataTable("optimizations") when false: printProfilerCallTree() else: diff --git a/src/vm/values/clean.nim b/src/vm/values/clean.nim deleted file mode 100644 index 309918f307..0000000000 --- a/src/vm/values/clean.nim +++ /dev/null @@ -1,116 +0,0 @@ -#======================================================= -# Arturo -# Programming Language + Bytecode VM compiler -# (c) 2019-2023 Yanis Zafirópulos -# -# @file: vm/values/clean.nim -#======================================================= - -## This module has helper used to clean blocks -## Blocks may have `Newline` Values, -## so when you want to actually use its contents, we make sure that there will be none - -import macros, sequtils, strutils, sugar - -import vm/values/types - -#======================================= -# Helpers -#======================================= - -template cleanBlock*(v: Value) = - ## remove all Newline values from a Block value, in-place - ## - ## **Hint**: ``v`` must be a Value, not a ValueArray ( - ## This is an optimization, as Block values have a - ## `.dirty` attribute: if `.dirty` is false it'll do - ## nothing.). When done, `.dirty`'ll be set to *false*. - - when not defined(NOERRORLINES): - if v.dirty: - v.a.keepIf((vv) => vv.kind != Newline) - v.dirty = false # Updates `.dirty` value - else: - discard - -func cleanedBlockImpl*(va: ValueArray): ValueArray {.inline,enforceNoRaises.} = - result = collect(newSeqOfCap(va.len)): - for vv in va: - if vv.kind != Newline: - vv - -template cleanedBlock*(va: ValueArray, inplace=false): untyped = - when not defined(NOERRORLINES): - cleanedBlockImpl(va) - else: - va - -template cleanedBlockValuesCopy*(v: Value): untyped = - when not defined(NOERRORLINES): - if v.dirty: - cleanedBlockImpl(v.a) - else: - v.a - else: - v.a - -iterator cleanedBlockValues*(v: Value, L: int): lent Value = - ## yield a Value object per iteration, while - ## ignoring all Newline values - ## - ## **Note:** - ## - This is to be used inside a *for loop*. - ## - ## **Usage:** - ## ```nim - ## for i in cleanedBlockValues(y, y.a.len): - ## x.a[cnt] = i - ## inc cnt - ## ``` - - when not defined(NOERRORLINES): - if v.dirty: - for i in 0..L-1: - if v.a[i].kind != Newline: - yield v.a[i] - else: - for i in 0..L-1: - yield v.a[i] - else: - for i in 0..L-1: - yield v.a[i] - -template cleanedBlockValues*(v: Value): untyped = - ## yield a Value object per iteration, while - ## ignoring all Newline values - ## - ## **Note:** - ## - Detects `v.a`'s length automatically - ## - This is to be used inside a *for loop*. - ## - ## **Usage:** - ## ```nim - ## for i in cleanedBlockValues(y): - ## x.a[cnt] = i - ## inc cnt - ## ``` - - let l = v.a.len - cleanedBlockValues(v, l) - -macro ensureCleaned*(name: untyped): untyped = - let cleanName = ident("clean" & ($name).capitalizeAscii()) - let cleanedBlock = ident("cleanedBlockTmp" & ($name).capitalizeAscii()) - when not defined(NOERRORLINES): - result = quote do: - var `cleanedBlock`: ValueArray - let `cleanName` {.cursor.} = ( - if `name`.dirty: - `cleanedBlock` = cleanedBlockImpl(`name`.a) - `cleanedBlock` - else: - `name`.a - ) - else: - result = quote do: - let `cleanName` {.cursor.} = `name`.a diff --git a/src/vm/values/comparison.nim b/src/vm/values/comparison.nim index 363c1f7fba..206380848b 100644 --- a/src/vm/values/comparison.nim +++ b/src/vm/values/comparison.nim @@ -22,7 +22,6 @@ when not defined(NOGMP): import vm/values/custom/[vcolor, vcomplex, vlogical, vquantity, vrange, vrational, vversion] import vm/values/value -import vm/values/clean #======================================= # Methods @@ -117,13 +116,11 @@ proc `==`*(x: Value, y: Value): bool {.inline, enforceNoRaises.}= of Bytecode: return x.trans == y.trans of Inline, Block: - ensureCleaned(x) - ensureCleaned(y) - if cleanX.len != cleanY.len: return false + if x.a.len != y.a.len: return false - for i,child in cleanX: - if not (child==cleanY[i]): return false + for i,child in x.a: + if not (child==y.a[i]): return false return true @@ -244,9 +241,7 @@ proc `<`*(x: Value, y: Value): bool {.inline.}= of Symbol: return false of Inline, Block: - ensureCleaned(x) - ensureCleaned(y) - return cleanX.len < cleanY.len + return x.a.len < y.a.len of Dictionary: return false of Object: diff --git a/src/vm/values/custom/vbinary.nim b/src/vm/values/custom/vbinary.nim index 96a3afa2d1..044062986e 100644 --- a/src/vm/values/custom/vbinary.nim +++ b/src/vm/values/custom/vbinary.nim @@ -28,10 +28,10 @@ type #======================================= template loopOp(a, b: VBinary, op: untyped) = - if 0 notin [a.len, b.len]: - result = newSeq[byte](min(a.high, b.high)) - for i in 0..result.high: - result[i] = op(a[i], b[i]) + if 0 notin [a.len, b.len]: + result = newSeq[byte](min(a.high, b.high)) + for i in 0..result.high: + result[i] = op(a[i], b[i]) #======================================= # Overloads diff --git a/src/vm/values/flags.nim b/src/vm/values/flags.nim index 31eafb3fa6..5f84466176 100644 --- a/src/vm/values/flags.nim +++ b/src/vm/values/flags.nim @@ -22,7 +22,6 @@ type ValueFlag* = enum IsReadOnly # Value has to be copied on assignment - IsDirty # For blocks: it may contain Newline values IsDynamic # For blocks: it has to be re-evaluated # prior to execution diff --git a/src/vm/values/printable.nim b/src/vm/values/printable.nim index 5f7994a950..4c73d68db4 100644 --- a/src/vm/values/printable.nim +++ b/src/vm/values/printable.nim @@ -26,7 +26,6 @@ import helpers/terminal as TerminalHelper import vm/globals import vm/opcodes import vm/values/value -import vm/values/clean import vm/values/custom/[vbinary, vcolor, vcomplex, vlogical, vquantity, vrange, vrational, vregex, vsocket, vversion] @@ -106,9 +105,7 @@ proc `$`*(v: Value): string {.inline.} = # for i,child in v.a: # result &= $(child) & " " # result &= "]" - - ensureCleaned(v) - result = "[" & cleanV.map((child) => $(child)).join(" ") & "]" + result = "[" & v.a.map((child) => $(child)).join(" ") & "]" of Range : result = $(v.rng) @@ -159,7 +156,6 @@ proc `$`*(v: Value): string {.inline.} = of Bytecode: result = "" & "(" & fmt("{cast[ByteAddress](v):#X}") & ")" - of Newline: discard of Nothing: discard of ANY: discard @@ -303,9 +299,8 @@ proc dump*(v: Value, level: int=0, isLast: bool=false, muted: bool=false, prepen of Inline, Block : dumpBlockStart(v) - ensureCleaned(v) - for i,child in cleanV: - dump(child, level+1, i==(cleanV.len-1), muted=muted) + for i,child in v.a: + dump(child, level+1, i==(v.a.len-1), muted=muted) stdout.write "\n" @@ -397,10 +392,10 @@ proc dump*(v: Value, level: int=0, isLast: bool=false, muted: bool=false, prepen while j < v.trans.instructions.len: let op = OpCode(v.trans.instructions[j]) instrs.add(newWord(stringify((OpCode(op))))) - if op in {opPush, opStore, opCall, opLoad, opStorl, opAttr, opEol}: + if op in {opPush, opStore, opDStore, opCall, opLoad, opStorl, opAttr, opEol, opJmpIf, opJmpIfNot, opJmpIfEq, opJmpIfNe, opJmpIfGt, opJmpIfGe, opJmpIfLt, opJmpIfLe, opGoto, opGoup}: j += 1 instrs.add(newInteger(int(v.trans.instructions[j]))) - elif op in {opPushX, opStoreX, opCallX, opLoadX, opStorlX, opEolX, opJmpIf, opJmpIfNot, opJmpIfEq, opJmpIfNe, opJmpIfGt, opJmpIfGe, opJmpIfLt, opJmpIfLe, opGoto, opGoup}: + elif op in {opPushX, opStoreX, opDStore, opCallX, opLoadX, opStorlX, opEolX, opJmpIfX, opJmpIfNotX, opJmpIfEqX, opJmpIfNeX, opJmpIfGtX, opJmpIfGeX, opJmpIfLtX, opJmpIfLeX, opGotoX, opGoupX}: j += 2 instrs.add(newInteger(int(uint16(v.trans.instructions[j-1]) shl 8 + byte(v.trans.instructions[j])))) @@ -422,19 +417,24 @@ proc dump*(v: Value, level: int=0, isLast: bool=false, muted: bool=false, prepen var i = 0 while i < instrs.len: for i in 0..level: stdout.write " " - stdout.write instrs[i].s + let preop = instrs[i].s + stdout.write preop i += 1 if i < instrs.len and instrs[i].kind==Integer: - stdout.write " " + stdout.write " ".repeat(20 - preop.len) while i < instrs.len and instrs[i].kind==Integer: - if not muted: stdout.write fmt("{resetColor}{fg(grayColor)} #{instrs[i].i}{resetColor}") - else: stdout.write " #" & $(instrs[i].i) + var numstr = $(instrs[i].i) + if preop.contains("jmp") or preop.contains("go"): + numstr = "@" & numstr + else: + numstr = "#" & numstr + if not muted: stdout.write fmt("{resetColor}{fg(grayColor)} {numstr}{resetColor}") + else: stdout.write numstr i += 1 stdout.write "\n" dumpBlockEnd() - of Newline : discard of Nothing : discard of ANY : discard @@ -487,6 +487,11 @@ proc codify*(v: Value, pretty = false, unwrapped = false, level: int=0, isLast: of Label : result &= v.s & ":" of Attribute : result &= "." & v.s of AttributeLabel : result &= "." & v.s & ":" + of Path, + PathLabel : + result = v.p.map((x) => $(x)).join("\\") + if v.kind==PathLabel: + result &= ":" of Symbol : result &= $(v.m) of SymbolLiteral: result &= "'" & $(v.m) of Quantity : result &= $(v.nm) & ":" & toLowerAscii($(v.unit.name)) @@ -502,9 +507,8 @@ proc codify*(v: Value, pretty = false, unwrapped = false, level: int=0, isLast: result &= "\n" var parts: seq[string] - ensureCleaned(v) - for i,child in cleanV: - parts.add(codify(child,pretty,unwrapped,level+1, i==(cleanV.len-1), safeStrings=safeStrings)) + for i,child in v.a: + parts.add(codify(child,pretty,unwrapped,level+1, i==(v.a.len-1), safeStrings=safeStrings)) result &= parts.join(" ") diff --git a/src/vm/values/types.nim b/src/vm/values/types.nim index aa46684573..987bcfda75 100644 --- a/src/vm/values/types.nim +++ b/src/vm/values/types.nim @@ -23,6 +23,7 @@ when defined(WEB): when not defined(NOGMP): import helpers/bignums +import vm/opcodes import vm/values/custom/[vbinary, vcolor, vcomplex, vlogical, vquantity, vrange, vrational, vregex, vsocket, vsymbol, vversion] import vm/values/flags @@ -91,9 +92,8 @@ type Socket = 32 Bytecode = 33 - Newline = 34 - Nothing = 35 - Any = 36 + Nothing = 34 + Any = 35 ValueSpec* = set[ValueKind] @@ -169,6 +169,7 @@ type inline* : bool bcode* : Value of BuiltinFunction: + op* : OpCode action* : BuiltinAction VStore* = ref object @@ -193,6 +194,7 @@ type when not defined(PORTABLE): info* : ValueInfo + ln* : uint32 flags* : ValueFlags case kind*: ValueKind: @@ -272,8 +274,6 @@ type of Bytecode: trans*: Translation - of Newline: - line*: int ValueObj = typeof(Value()[]) FuncObj = typeof(VFunction()[]) @@ -295,9 +295,6 @@ when sizeof(ValueObj) > 72: # At time of writing it was '72', 8 - 64 bit integer template readonly*(val: Value): bool = IsReadOnly in val.flags template `readonly=`*(val: Value, newVal: bool) = val.flags[IsReadOnly] = newVal -template dirty*(val: Value): bool = IsDirty in val.flags -template `dirty=`*(val: Value, newVal: bool) = val.flags[IsDirty] = newVal - template dynamic*(val: Value): bool = IsDynamic in val.flags template `dynamic=`*(val: Value, newVal: bool) = val.flags[IsDynamic] = newVal @@ -340,3 +337,4 @@ makeAccessor(funcType, memoize) makeAccessor(funcType, bcode) makeAccessor(funcType, inline) makeAccessor(funcType, action) +makeAccessor(funcType, op) diff --git a/src/vm/values/value.nim b/src/vm/values/value.nim index 1d55512e51..c9c963630c 100644 --- a/src/vm/values/value.nim +++ b/src/vm/values/value.nim @@ -36,9 +36,10 @@ when not defined(NOGMP): when not defined(WEB): import vm/errors +import vm/opcodes + import vm/values/custom/[vbinary, vcolor, vcomplex, vlogical, vquantity, vrange, vrational, vregex, vsocket, vsymbol, vversion] -import vm/values/clean import vm/values/types import vm/values/flags export types @@ -115,18 +116,20 @@ let var TypeLookup = initOrderedTable[string,Value]() - # global implementation references - AddF*, SubF*, MulF*, DivF*, FdivF*, ModF*, PowF* : Value - NegF*, BNotF*, BAndF*, BOrF*, ShlF*, ShrF* : Value - NotF*, AndF*, OrF* : Value - EqF*, NeF*, GtF*, GeF*, LtF*, LeF* : Value - IfF*, IfEF*, UnlessF*, ElseF*, SwitchF*, WhileF*, ReturnF* : Value - ToF*, PrintF* : Value - GetF*, SetF* : Value - ArrayF*, DictF*, FuncF* : Value - RangeF*, LoopF*, MapF*, SelectF* : Value - SizeF*, ReplaceF*, SplitF*, JoinF*, ReverseF* : Value - IncF*, DecF* : Value + # global action references + DoAdd*, DoSub*, DoMul*, DoDiv*, DoFdiv*, DoMod*, DoPow* : BuiltinAction + DoNeg*, DoInc*, DoDec* : BuiltinAction + DoBNot*, DoBAnd*, DoBOr*, DoShl*, DoShr* : BuiltinAction + DoNot*, DoAnd*, DoOr* : BuiltinAction + DoEq*, DoNe*, DoGt*, DoGe*, DoLt*, DoLe* : BuiltinAction + DoGet*, DoSet* : BuiltinAction + DoIf*, DoIfE*, DoUnless*, DoUnlessE*, DoElse*, DoSwitch*, DoWhile* : BuiltinAction + DoReturn*, DoBreak*, DoContinue* : BuiltinAction + DoTo* : BuiltinAction + DoArray*, DoDict*, DoFunc*, DoRange* : BuiltinAction + DoLoop*, DoMap*, DoSelect* : BuiltinAction + DoSize*, DoReplace*, DoSplit*, DoJoin*, DoReverse*, DoAppend* : BuiltinAction + DoPrint* : BuiltinAction #======================================= # Forward Declarations @@ -542,7 +545,7 @@ func newFunction*(params: seq[string], main: Value, imports: Value = nil, export ) ) -func newBuiltin*(desc: sink string, modl: sink string, line: int, ar: int8, ag: sink OrderedTable[string,ValueSpec], at: sink OrderedTable[string,(ValueSpec,string)], ret: ValueSpec, exa: sink string, act: BuiltinAction): Value {.inline, enforceNoRaises.} = +func newBuiltin*(desc: sink string, modl: sink string, line: int, ar: int8, ag: sink OrderedTable[string,ValueSpec], at: sink OrderedTable[string,(ValueSpec,string)], ret: ValueSpec, exa: sink string, opc: OpCode, act: BuiltinAction): Value {.inline, enforceNoRaises.} = ## create Function (BuiltinFunction) value with given details result = Value( kind: Function, @@ -557,6 +560,7 @@ func newBuiltin*(desc: sink string, modl: sink string, line: int, ar: int8, ag: funcType: VFunction( fnKind: BuiltinFunction, arity: ar, + op: opc, action: act ) ) @@ -581,23 +585,13 @@ func newBytecode*(t: sink Translation): Value {.inline, enforceNoRaises.} = ## create Bytecode value from Translation Value(kind: Bytecode, trans: t) -func newInline*(a: sink ValueArray = @[], dirty = false): Value {.inline, enforceNoRaises.} = +func newInline*(a: sink ValueArray = @[]): Value {.inline, enforceNoRaises.} = ## create Inline value from ValueArray - let flags = - if dirty: - {IsDirty} - else: - {} - Value(kind: Inline, a: a, flags: flags) + Value(kind: Inline, a: a) -func newBlock*(a: sink ValueArray = @[], data: sink Value = nil, dirty = false): Value {.inline, enforceNoRaises.} = +func newBlock*(a: sink ValueArray = @[], data: sink Value = nil): Value {.inline, enforceNoRaises.} = ## create Block value from ValueArray - let flags = - if dirty: - {IsDirty} - else: - {} - Value(kind: Block, a: a, data: data, flags: flags) + Value(kind: Block, a: a, data: data) func newBlock*(a: (Value, Value)): Value {.inline, enforceNoRaises.} = ## create Block value from tuple of two values @@ -634,10 +628,6 @@ proc newRange*(start: int, stop: int, step: int, infinite: bool, numeric: bool, proc newRange*(rng: VRange): Value {.inline, enforceNoRaises.} = Value(kind: Range, rng: rng) -func newNewline*(l: int): Value {.inline, enforceNoRaises.} = - ## create Newline value with given line number - Value(kind: Newline, line: l) - proc newStringDictionary*(a: Table[string, string]): Value = ## create Dictionary value from a string-string Table result = newDictionary() @@ -706,8 +696,7 @@ proc copyValue*(v: Value): Value {.inline.} = of Inline: result = newInline(v.a) of Block: if v.data.isNil: - let newValues = cleanedBlockValuesCopy(v) - result = Value(kind: Block, a: newValues) + result = Value(kind: Block, a: v.a) else: result = newBlock(v.a.map((vv)=>copyValue(vv)), copyValue(v.data)) of Range: @@ -722,9 +711,9 @@ proc copyValue*(v: Value): Value {.inline.} = result = newFunction(v.params, v.main, v.imports, v.exports, v.memoize, v.inline) else: when defined(DOCGEN): - result = newBuiltin(v.info.descr, v.info.module, v.info.line, v.arity, v.info.args, v.info.attrs, v.info.returns, v.info.example, v.action) + result = newBuiltin(v.info.descr, v.info.module, v.info.line, v.arity, v.info.args, v.info.attrs, v.info.returns, v.info.example, v.op, v.action) else: - result = newBuiltin(v.info.descr, v.info.module, 0, v.arity, v.info.args, v.info.attrs, v.info.returns, "", v.action) + result = newBuiltin(v.info.descr, v.info.module, 0, v.arity, v.info.args, v.info.attrs, v.info.returns, "", v.op, v.action) of Database: when not defined(NOSQLITE): @@ -738,7 +727,7 @@ proc copyValue*(v: Value): Value {.inline.} = of Bytecode: result = newBytecode(v.trans) - of Newline, Nothing, Any: + of Nothing, Any: discard #======================================= @@ -2481,7 +2470,6 @@ func hash*(v: Value): Hash {.inline.}= of Bytecode: result = result !& cast[Hash](unsafeAddr v) - of Newline : discard of Nothing : discard of ANY : discard diff --git a/tests/errors/assertion.AssertionFailed.2.res b/tests/errors/assertion.AssertionFailed.2.res index f080e22f52..7850b67042 100644 --- a/tests/errors/assertion.AssertionFailed.2.res +++ b/tests/errors/assertion.AssertionFailed.2.res @@ -1,5 +1,5 @@ doing sth: 12 >> Assertion | File: assertion.AssertionFailed.2.art - error | Line: 2 + error | Line: 7 | | [integer? x] \ No newline at end of file diff --git a/tests/errors/runtime.WrongArgumentType.1.res b/tests/errors/runtime.WrongArgumentType.1.res index 838853956f..89d46ffb1d 100644 --- a/tests/errors/runtime.WrongArgumentType.1.res +++ b/tests/errors/runtime.WrongArgumentType.1.res @@ -1,5 +1,5 @@ >> Runtime | File: runtime.WrongArgumentType.1.art - error | Line: 5 + error | Line: 3 | | cannot perform map -> :range :integer | incorrect argument type for second parameter diff --git a/tests/unittests/arturo.data.res b/tests/unittests/arturo.data_res similarity index 100% rename from tests/unittests/arturo.data.res rename to tests/unittests/arturo.data_res diff --git a/tests/unittests/evaluator.art b/tests/unittests/evaluator.art new file mode 100644 index 0000000000..f78876bbc6 --- /dev/null +++ b/tests/unittests/evaluator.art @@ -0,0 +1,2529 @@ +; Helper functions + +spacer: " " + +supersection: function [title][ + cl: #magenta + print "" + print color cl repeat "*" 50 + print color cl "*" + print color cl "* " ++ title + print color cl "*" + print color cl repeat "*" 50 +] + +topic: function [title][ + print "" + print spacer ++ ">" ++ repeat "-" 50 + print spacer ++ "> " ++ color.bold #white title + print spacer ++ ">" ++ repeat "-" 50 + print "" +] + +byteCode: function [blk][ + btc: to.intrepid :bytecode blk ; this little hack is needed to remove newlines from our block + ; thus, avoiding all opEol/opEolX bytecode being produced + + ;inspect btc ; uncomment this line to see the bytecode + ; in a more readable format + prints spacer + print ["input:" as.code blk] + prints spacer + print ["data:" get btc 'data] + prints spacer + cd: get btc 'code + print ["code:" cd "(" ++ (to :string size cd) ++ " bytes)"] + print "" +] + +; Main unit-tests + +;///////////////////////////////////////////////////////// +supersection "SIMPLE VALUES" +;///////////////////////////////////////////////////////// + + ;***************************************************************** + topic "Boolean" + ;*******************************************å********************** + + byteCode [true] + ; ================================ + ; DATA + ; ================================ + + ; ================================ + ; CODE + ; ================================ + ; constbt + ; end + + byteCode [false] + ; ================================ + ; DATA + ; ================================ + + ; ================================ + ; CODE + ; ================================ + ; constbf + ; end + + byteCode [maybe] + ; ================================ + ; DATA + ; ================================ + ; 0: maybe :word + + ; ================================ + ; CODE + ; ================================ + ; load0 + ; end + + ;***************************************************************** + topic "Integer" + ;***************************************************************** + + byteCode [1] + ; ================================ + ; DATA + ; ================================ + + ; ================================ + ; CODE + ; ================================ + ; consti1 + ; end + + byteCode [10] + ; ================================ + ; DATA + ; ================================ + + ; ================================ + ; CODE + ; ================================ + ; consti10 + ; end + + byteCode [123] + ; ================================ + ; DATA + ; ================================ + ; 0: 123 :integer + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; end + + byteCode [1234567890123] + ; ================================ + ; DATA + ; ================================ + ; 0: 1234567890123 :integer + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; end + + ;***************************************************************** + topic "Floating" + ;***************************************************************** + + byteCode [0.0] + ; ================================ + ; DATA + ; ================================ + + ; ================================ + ; CODE + ; ================================ + ; constf0 + ; end + + byteCode [1.0] + ; ================================ + ; DATA + ; ================================ + + ; ================================ + ; CODE + ; ================================ + ; constf1 + ; end + + byteCode [10.0] + ; ================================ + ; DATA + ; ================================ + ; 0: 10.0 :floating + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; end + + byteCode [12345.1234567] + ; ================================ + ; DATA + ; ================================ + ; 0: 12345.1234567 :floating + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; end + + ;***************************************************************** + topic "Char" + ;***************************************************************** + + byteCode [`a`] + ; ================================ + ; DATA + ; ================================ + ; 0: a :char + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; end + + byteCode [`😀`] + ; ================================ + ; DATA + ; ================================ + ; 0: 😀 :char + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; end + + ;***************************************************************** + topic "String" + ;***************************************************************** + + byteCode [""] + ; ================================ + ; DATA + ; ================================ + + ; ================================ + ; CODE + ; ================================ + ; consts + ; end + + byteCode ["Hello World!"] + ; ================================ + ; DATA + ; ================================ + ; 0: Hello World! :string + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; end + + ;***************************************************************** + topic "Regex" + ;***************************************************************** + + byteCode [{/hello/}] + ; ================================ + ; DATA + ; ================================ + ; 0: hello :regex + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; end + + byteCode [{/[A-Z]+\d/}] + ; ================================ + ; DATA + ; ================================ + ; 0: [A-Z]+\d :regex + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; end + + ;***************************************************************** + topic "Type" + ;***************************************************************** + + byteCode [:integer] + ; ================================ + ; DATA + ; ================================ + ; 0: integer :type + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; end + + byteCode [:string] + ; ================================ + ; DATA + ; ================================ + ; 0: string :type + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; end + + ;***************************************************************** + topic "Literal" + ;***************************************************************** + + byteCode ['a] + ; ================================ + ; DATA + ; ================================ + ; 0: a :literal + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; end + + byteCode ['a 'b 'c] + ; ================================ + ; DATA + ; ================================ + ; 0: a :literal + ; 1: b :literal + ; 2: c :literal + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; push1 + ; push2 + ; end + + ;***************************************************************** + topic "SymbolLiteral" + ;***************************************************************** + + byteCode ['+] + ; ================================ + ; DATA + ; ================================ + ; 0: + :symbolliteral + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; end + + byteCode ['-->] + ; ================================ + ; DATA + ; ================================ + ; 0: --> :symbolliteral + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; end + + ;***************************************************************** + topic "Color" + ;***************************************************************** + + byteCode [#red] + ; ================================ + ; DATA + ; ================================ + ; 0: #FF0000 :color + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; end + + byteCode [#00FF66] + ; ================================ + ; DATA + ; ================================ + ; 0: #00FF66 :color + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; end + + ;***************************************************************** + topic "Quantity" + ;***************************************************************** + + byteCode [1:EUR] + ; ================================ + ; DATA + ; ================================ + ; 0: 1:EUR :quantity + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; end + + byteCode [12:m] + ; ================================ + ; DATA + ; ================================ + ; 0: 12:m :quantity + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; end + + ;***************************************************************** + topic "Version" + ;***************************************************************** + + byteCode [0.9.82] + ; ================================ + ; DATA + ; ================================ + ; 0: 0.9.82 :version + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; end + + byteCode [2.0.0-rc1] + ; ================================ + ; DATA + ; ================================ + ; 0: 2.0.0-rc1 :version + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; end + + ;***************************************************************** + topic "Block" + ;***************************************************************** + + byteCode [[]] + ; ================================ + ; DATA + ; ================================ + + ; ================================ + ; CODE + ; ================================ + ; consta + ; end + + byteCode [[1 "hello" 3.14 true]] + ; ================================ + ; DATA + ; ================================ + ; 0: [ :block + ; 1 :integer + ; hello :string + ; 3.14 :floating + ; true :word + ; ] + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; end + + ;***************************************************************** + topic "Null" + ;***************************************************************** + + byteCode [ø] + ; ================================ + ; DATA + ; ================================ + + ; ================================ + ; CODE + ; ================================ + ; constn + ; end + + byteCode [null] + ; ================================ + ; DATA + ; ================================ + ; 0: null :word + + ; ================================ + ; CODE + ; ================================ + ; load0 + ; end + +;///////////////////////////////////////////////////////// +supersection "LABELS" +;///////////////////////////////////////////////////////// + + ;***************************************************************** + topic "Setting and getting variables" + ;***************************************************************** + + byteCode [ + a: 1 + ] + ; ================================ + ; DATA + ; ================================ + ; 0: a :label + + ; ================================ + ; CODE + ; ================================ + ; consti1 + ; store0 + ; end + + byteCode [ + b: 2 + c: b + ] + ; ================================ + ; DATA + ; ================================ + ; 0: b :label + ; 1: c :label + + ; ================================ + ; CODE + ; ================================ + ; consti2 + ; store0 + ; load0 + ; store1 + ; end + +;///////////////////////////////////////////////////////// +supersection "FUNCTION CALLS" +;///////////////////////////////////////////////////////// + + ;***************************************************************** + topic "Built-in function calls" + ;***************************************************************** + + byteCode [abs 10] + ; ================================ + ; DATA + ; ================================ + ; 0: abs :word + + ; ================================ + ; CODE + ; ================================ + ; consti10 + ; call0 + ; end + + byteCode [empty? []] + ; ================================ + ; DATA + ; ================================ + ; 0: empty? :word + + ; ================================ + ; CODE + ; ================================ + ; consta + ; call0 + ; end + + byteCode [couple [1 2] ["one" "two"]] + ; ================================ + ; DATA + ; ================================ + ; 0: [ :block + ; one :string + ; two :string + ; ] + ; 1: [ :block + ; 1 :integer + ; 2 :integer + ; ] + ; 2: couple :word + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; push1 + ; call2 + ; end + + ;***************************************************************** + topic "Opcoded built-in function calls" + ;***************************************************************** + + byteCode [print 2] + ; ================================ + ; DATA + ; ================================ + + ; ================================ + ; CODE + ; ================================ + ; consti2 + ; print + ; end + + byteCode [size [1 2]] + ; ================================ + ; DATA + ; ================================ + ; 0: [ :block + ; 1 :integer + ; 2 :integer + ; ] + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; size + ; end + + byteCode [and 1 123] + ; ================================ + ; DATA + ; ================================ + ; 0: 123 :integer + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; consti1 + ; band + ; end + + ;***************************************************************** + topic "Composite opcoded built-in function calls" + ;***************************************************************** + + byteCode [to :floating 1] + ; ================================ + ; DATA + ; ================================ + ; 0: floating :type + + ; ================================ + ; CODE + ; ================================ + ; consti1 + ; push0 + ; to + ; end + + byteCode [to :integer "10"] + ; ================================ + ; DATA + ; ================================ + ; 0: 10 :string + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; toi + ; end + + byteCode [to :string 5] + ; ================================ + ; DATA + ; ================================ + + ; ================================ + ; CODE + ; ================================ + ; consti5 + ; tos + ; end + + ;***************************************************************** + topic "Function calls with attributes" + ;***************************************************************** + + byteCode [split.words "hello world"] + ; ================================ + ; DATA + ; ================================ + ; 0: hello world :string + ; 1: words :attribute + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; constbt + ; attr1 + ; split + ; end + + byteCode [split.by:"X" "helloXworld"] + ; ================================ + ; DATA + ; ================================ + ; 0: helloXworld :string + ; 1: X :string + ; 2: by :attributelabel + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; push1 + ; attr2 + ; split + ; end + + byteCode [join.with:"-" ["hello" "world"]] + ; ================================ + ; DATA + ; ================================ + ; 0: [ :block + ; hello :string + ; world :string + ; ] + ; 1: - :string + ; 2: with :attributelabel + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; push1 + ; attr2 + ; join + ; end + + ;***************************************************************** + topic "Function calls via symbol aliases" + ;***************************************************************** + + byteCode [@[1 2 3]] + ; ================================ + ; DATA + ; ================================ + ; 0: [ :block + ; 1 :integer + ; 2 :integer + ; 3 :integer + ; ] + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; array + ; end + + byteCode ["hello " ++ x] + ; ================================ + ; DATA + ; ================================ + ; 0: x :word + ; 1: hello :string + + ; ================================ + ; CODE + ; ================================ + ; load0 + ; push1 + ; append + ; end + + byteCode [1..25] + ; ================================ + ; DATA + ; ================================ + ; 0: 25 :integer + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; consti1 + ; range + ; end + + ;***************************************************************** + topic "User function definition & calling" + ;***************************************************************** + + byteCode [ + h: function [][print "function called"] + + print "before" + h + print "after" + ] + ; ================================ + ; DATA + ; ================================ + ; 0: [ :block + ; print :word + ; function called :string + ; ] + ; 1: h :label + ; 2: before :string + ; 3: after :string + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; consta + ; func + ; store1 + ; push2 + ; print + ; call1 + ; push3 + ; print + ; end + + byteCode [ + f: function [x][x + 1] + + print "before" + print f 10 + print "after" + ] + ; ================================ + ; DATA + ; ================================ + ; 0: [ :block + ; x :word + ; + :symbol + ; 1 :integer + ; ] + ; 1: [ :block + ; x :word + ; ] + ; 2: f :label + ; 3: before :string + ; 4: after :string + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; push1 + ; func + ; store2 + ; push3 + ; print + ; consti10 + ; call2 + ; print + ; push4 + ; print + ; end + + byteCode [ + g: $[z w][2 * z * w] + + print "before" + print g 10 20 + print "after" + ] + + ; ================================ + ; DATA + ; ================================ + ; 0: [ :block + ; 2 :integer + ; * :symbol + ; z :word + ; * :symbol + ; w :word + ; ] + ; 1: [ :block + ; z :word + ; w :word + ; ] + ; 2: g :label + ; 3: before :string + ; 4: 20 :integer + ; 5: after :string + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; push1 + ; func + ; store2 + ; push3 + ; print + ; push4 + ; consti10 + ; call2 + ; print + ; push5 + ; print + ; end + +;///////////////////////////////////////////////////////// +supersection "PATHS" +;///////////////////////////////////////////////////////// + + ;***************************************************************** + topic "Path values" + ;***************************************************************** + + byteCode [a\0] + ; ================================ + ; DATA + ; ================================ + ; 0: a :word + + ; ================================ + ; CODE + ; ================================ + ; consti0 + ; load0 + ; get + ; end + + byteCode [user\name] + ; ================================ + ; DATA + ; ================================ + ; 0: name :literal + ; 1: user :word + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; load1 + ; get + ; end + + byteCode [user\grades\0] + ; ================================ + ; DATA + ; ================================ + ; 0: grades :literal + ; 1: user :word + + ; ================================ + ; CODE + ; ================================ + ; consti0 + ; push0 + ; load1 + ; get + ; get + ; end + + byteCode [user\address\country] + ; ================================ + ; DATA + ; ================================ + ; 0: country :literal + ; 1: address :literal + ; 2: user :word + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; push1 + ; load2 + ; get + ; get + ; end + + ;***************************************************************** + topic "PathLabel values" + ;***************************************************************** + + byteCode [a\0: 10] + ; ================================ + ; DATA + ; ================================ + ; 0: a :word + + ; ================================ + ; CODE + ; ================================ + ; consti10 + ; consti0 + ; load0 + ; set + ; end + + byteCode [user\name: "John"] + ; ================================ + ; DATA + ; ================================ + ; 0: John :string + ; 1: name :literal + ; 2: user :word + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; push1 + ; load2 + ; set + ; end + + byteCode [user\grades\0: 6] + ; ================================ + ; DATA + ; ================================ + ; 0: grades :literal + ; 1: user :word + + ; ================================ + ; CODE + ; ================================ + ; consti6 + ; consti0 + ; push0 + ; load1 + ; get + ; set + ; end + + byteCode [user\address\country: "USA"] + ; ================================ + ; DATA + ; ================================ + ; 0: USA :string + ; 1: country :literal + ; 2: address :literal + ; 3: user :word + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; push1 + ; push2 + ; load3 + ; get + ; set + ; end + +;///////////////////////////////////////////////////////// +supersection "BLOCKS & SYNTACTIC SUGAR" +;///////////////////////////////////////////////////////// + + ;***************************************************************** + topic "Inline blocks" + ;***************************************************************** + + byteCode [(print 2)] + ; ================================ + ; DATA + ; ================================ + + ; ================================ + ; CODE + ; ================================ + ; consti2 + ; print + ; end + + byteCode [(print 2) (print 3)] + ; ================================ + ; DATA + ; ================================ + + ; ================================ + ; CODE + ; ================================ + ; consti2 + ; print + ; consti3 + ; print + ; end + + byteCode [(print 2 print 3)] + ; ================================ + ; DATA + ; ================================ + + ; ================================ + ; CODE + ; ================================ + ; consti2 + ; print + ; consti3 + ; print + ; end + + ;***************************************************************** + topic "doublecolon syntactic sugar (`::`)" + ;***************************************************************** + + byteCode [ + print 2 + do :: + print 3 + ] + ; ================================ + ; DATA + ; ================================ + ; 0: [ :block + ; print :word + ; 3 :integer + ; ] + ; 1: do :word + + ; ================================ + ; CODE + ; ================================ + ; consti2 + ; print + ; push0 + ; call1 + ; end + + ;***************************************************************** + topic "arrowright syntactic sugar (`->`)" + ;***************************************************************** + + byteCode [-> "hello"] + ; ================================ + ; DATA + ; ================================ + ; 0: [ :block + ; hello :string + ; ] + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; end + + byteCode [ + do -> print "hello" + print "done" + ] + ; ================================ + ; DATA + ; ================================ + ; 0: [ :block + ; print :word + ; hello :string + ; ] + ; 1: do :word + ; 2: done :string + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; call1 + ; push2 + ; print + ; end + + byteCode [-> 1 -> 2] + ; ================================ + ; DATA + ; ================================ + ; 0: [ :block + ; 1 :integer + ; ] + ; 1: [ :block + ; 2 :integer + ; ] + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; push1 + ; end + + byteCode [ + a: -> upper "hello" + b: -> "hello " ++ world + ] + ; ================================ + ; DATA + ; ================================ + ; 0: [ :block + ; upper :word + ; hello :string + ; ] + ; 1: a :label + ; 2: [ :block + ; hello :string + ; ++ :symbol + ; world :word + ; ] + ; 3: b :label + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; store1 + ; push2 + ; store3 + ; end + + ;***************************************************************** + topic "thickarrowright syntactic sugar (`=>`)" + ;***************************************************************** + + byteCode [=> "hello"] + ; ================================ + ; DATA + ; ================================ + ; 0: [ :block + ; hello :string + ; ] + + ; ================================ + ; CODE + ; ================================ + ; consta + ; push0 + ; end + + byteCode [ + f: function => print + ] + ; ================================ + ; DATA + ; ================================ + ; 0: [ :block + ; print :word + ; _0 :word + ; ] + ; 1: _0 :literal + ; 2: f :label + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; push1 + ; func + ; store2 + ; end + + byteCode [ + adder: $ => add + ] + ; ================================ + ; DATA + ; ================================ + ; 0: [ :block + ; add :word + ; _0 :word + ; _1 :word + ; ] + ; 1: [ :block + ; _0 :word + ; _1 :word + ; ] + ; 2: adder :label + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; push1 + ; func + ; store2 + ; end + + byteCode [ + subOne: function => [& - 1] + ] + ; ================================ + ; DATA + ; ================================ + ; 0: [ :block + ; _0 :word + ; - :symbol + ; 1 :integer + ; ] + ; 1: _0 :literal + ; 2: subOne :label + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; push1 + ; func + ; store2 + ; end + + byteCode [ + mulAddOne: $ => [1 + & * &] + ] + ; ================================ + ; DATA + ; ================================ + ; 0: [ :block + ; 1 :integer + ; + :symbol + ; _0 :word + ; * :symbol + ; _1 :word + ; ] + ; 1: [ :block + ; _0 :word + ; _1 :word + ; ] + ; 2: mulAddOne :label + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; push1 + ; func + ; store2 + ; end + + ;***************************************************************** + topic "pipe operator (`|`)" + ;***************************************************************** + + byteCode [2 | print] + ; ================================ + ; DATA + ; ================================ + + ; ================================ + ; CODE + ; ================================ + ; consti2 + ; print + ; end + + byteCode ["hello" | upper | print] + ; ================================ + ; DATA + ; ================================ + ; 0: hello :string + ; 1: upper :word + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; call1 + ; print + ; end + + byteCode [x: "hello" | upper] + ; ================================ + ; DATA + ; ================================ + ; 0: hello :string + ; 1: upper :word + ; 2: x :label + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; call1 + ; store2 + ; end + + byteCode [1..10 | map 'x -> 2 * x] + ; ================================ + ; DATA + ; ================================ + ; 0: [ :block + ; 2 :integer + ; * :symbol + ; x :word + ; ] + ; 1: x :literal + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; push1 + ; consti10 + ; consti1 + ; range + ; map + ; end + + byteCode [1..10 | map 'x -> 2 * x | sum] + ; ================================ + ; DATA + ; ================================ + ; 0: [ :block + ; 2 :integer + ; * :symbol + ; x :word + ; ] + ; 1: x :literal + ; 2: sum :word + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; push1 + ; consti10 + ; consti1 + ; range + ; map + ; call2 + ; end + + byteCode [1..10 | map 'x -> 2 * x | sum | print] + ; ================================ + ; DATA + ; ================================ + ; 0: [ :block + ; 2 :integer + ; * :symbol + ; x :word + ; ] + ; 1: x :literal + ; 2: sum :word + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; push1 + ; consti10 + ; consti1 + ; range + ; map + ; call2 + ; print + ; end + + byteCode [ + 1..10 | map 'x -> 2 * x + | sum + | print + ] + ; ================================ + ; DATA + ; ================================ + ; 0: [ :block + ; 2 :integer + ; * :symbol + ; x :word + ; ] + ; 1: x :literal + ; 2: sum :word + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; push1 + ; consti10 + ; consti1 + ; range + ; map + ; call2 + ; print + ; end + + byteCode [ + filtered: 1..a | map 'x -> 3 * x + | select 'x -> odd? x + print filtered + ] + ; ================================ + ; DATA + ; ================================ + ; 0: [ :block + ; odd? :word + ; x :word + ; ] + ; 1: x :literal + ; 2: [ :block + ; 3 :integer + ; * :symbol + ; x :word + ; ] + ; 3: a :word + ; 4: filtered :label + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; push1 + ; push2 + ; push1 + ; load3 + ; consti1 + ; range + ; map + ; select + ; storl4 + ; print + ; end + + byteCode [ + filtered: 1..a | map 'x -> 3 * x + | select => odd? + print filtered + ] + ; ================================ + ; DATA + ; ================================ + ; 0: [ :block + ; odd? :word + ; _0 :word + ; ] + ; 1: _0 :literal + ; 2: [ :block + ; 3 :integer + ; * :symbol + ; x :word + ; ] + ; 3: x :literal + ; 4: a :word + ; 5: filtered :label + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; push1 + ; push2 + ; push3 + ; load4 + ; consti1 + ; range + ; map + ; select + ; storl5 + ; print + ; end + +;///////////////////////////////////////////////////////// +supersection "OPTIMIZATIONS" +;///////////////////////////////////////////////////////// + + ;***************************************************************** + topic "add (`+`) / constant folding" + ;***************************************************************** + + byteCode [add 2 3] + ; ================================ + ; DATA + ; ================================ + + ; ================================ + ; CODE + ; ================================ + ; consti5 + ; end + + byteCode [2 + 3] + ; ================================ + ; DATA + ; ================================ + + ; ================================ + ; CODE + ; ================================ + ; consti5 + ; end + + byteCode [2 + 3 + 4 + 5] + ; ================================ + ; DATA + ; ================================ + + ; ================================ + ; CODE + ; ================================ + ; consti14 + ; end + + + byteCode [1 + 2 + 3 + 4 + 5 + 6] + ; ================================ + ; DATA + ; ================================ + ; 0: 21 :integer + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; end + + byteCode [x: 3 + 7] + ; ================================ + ; DATA + ; ================================ + ; 0: x :label + + ; ================================ + ; CODE + ; ================================ + ; consti10 + ; store0 + ; end + + ;***************************************************************** + topic "add (`+`) / -> inc" + ;***************************************************************** + + byteCode [x + 1] + ; ================================ + ; DATA + ; ================================ + ; 0: x :word + + ; ================================ + ; CODE + ; ================================ + ; load0 + ; inc + ; end + + byteCode [1 + y] + ; ================================ + ; DATA + ; ================================ + ; 0: y :word + + ; ================================ + ; CODE + ; ================================ + ; load0 + ; inc + ; end + + ;***************************************************************** + topic "add (`+`) / distributive" + ;***************************************************************** + + byteCode [X + X * Y] + ; ================================ + ; DATA + ; ================================ + ; 0: Y :word + ; 1: X :word + + ; ================================ + ; CODE + ; ================================ + ; load0 + ; consti1 + ; add + ; load1 + ; mul + ; end + + byteCode [(X * Y) + X] + ; ================================ + ; DATA + ; ================================ + ; 0: X :word + ; 1: Y :word + + ; ================================ + ; CODE + ; ================================ + ; load0 + ; load1 + ; consti1 + ; add + ; mul + ; end + + ;***************************************************************** + topic "sub (`-`) / constant folding" + ;***************************************************************** + + byteCode [sub 2 3] + ; ================================ + ; DATA + ; ================================ + + ; ================================ + ; CODE + ; ================================ + ; consti1m + ; end + + byteCode [2 - 3] + ; ================================ + ; DATA + ; ================================ + + ; ================================ + ; CODE + ; ================================ + ; consti1m + ; end + + byteCode [8 - 2 - 1] + ; ================================ + ; DATA + ; ================================ + + ; ================================ + ; CODE + ; ================================ + ; consti7 + ; end + + byteCode [30 - 10 - 1] + ; ================================ + ; DATA + ; ================================ + ; 0: 21 :integer + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; end + + ;***************************************************************** + topic "sub (`-`) / -> dec" + ;***************************************************************** + + byteCode [x - 1] + ; ================================ + ; DATA + ; ================================ + ; 0: x :word + + ; ================================ + ; CODE + ; ================================ + ; load0 + ; dec + ; end + + ;***************************************************************** + topic "mul (`*`) / constant folding" + ;***************************************************************** + + byteCode [mul 2 3] + ; ================================ + ; DATA + ; ================================ + + ; ================================ + ; CODE + ; ================================ + ; consti6 + ; end + + byteCode [2 * 3] + ; ================================ + ; DATA + ; ================================ + + ; ================================ + ; CODE + ; ================================ + ; consti6 + ; end + + byteCode [2 * 2 * 3] + ; ================================ + ; DATA + ; ================================ + + ; ================================ + ; CODE + ; ================================ + ; consti12 + ; end + + byteCode [2 * 3 * 4 * 5] + ; ================================ + ; DATA + ; ================================ + ; 0: 120 :integer + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; end + + ;***************************************************************** + topic "div (`/`) / constant folding" + ;***************************************************************** + + byteCode [div 6 3] + ; ================================ + ; DATA + ; ================================ + + ; ================================ + ; CODE + ; ================================ + ; consti2 + ; end + + byteCode [6 / 3] + ; ================================ + ; DATA + ; ================================ + + ; ================================ + ; CODE + ; ================================ + ; consti2 + ; end + + byteCode [6 / 6 / 3] + ; ================================ + ; DATA + ; ================================ + + ; ================================ + ; CODE + ; ================================ + ; consti3 + ; end + + byteCode [50 / 4 / 4 / 2] + ; ================================ + ; DATA + ; ================================ + ; 0: 25 :integer + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; end + + ;***************************************************************** + topic "fdiv (`//`) / constant folding" + ;***************************************************************** + + byteCode [fdiv 6 3] + ; ================================ + ; DATA + ; ================================ + + ; ================================ + ; CODE + ; ================================ + ; constf2 + ; end + + byteCode [6 // 3] + ; ================================ + ; DATA + ; ================================ + + ; ================================ + ; CODE + ; ================================ + ; constf2 + ; end + + byteCode [4 // 8 // 2] + ; ================================ + ; DATA + ; ================================ + + ; ================================ + ; CODE + ; ================================ + ; constf1 + ; end + + byteCode [55 // 4 // 4 // 2] + ; ================================ + ; DATA + ; ================================ + ; 0: 27.5 :floating + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; end + + ;***************************************************************** + topic "mod (`%`) / constant folding" + ;***************************************************************** + + byteCode [mod 6 3] + ; ================================ + ; DATA + ; ================================ + + ; ================================ + ; CODE + ; ================================ + ; consti0 + ; end + + byteCode [6 % 3] + ; ================================ + ; DATA + ; ================================ + + ; ================================ + ; CODE + ; ================================ + ; consti0 + ; end + + byteCode [20 % 15 % 6] + ; ================================ + ; DATA + ; ================================ + + ; ================================ + ; CODE + ; ================================ + ; consti2 + ; end + + ;***************************************************************** + topic "pow (`^`) / constant folding" + ;***************************************************************** + + byteCode [pow 3 2] + ; ================================ + ; DATA + ; ================================ + + ; ================================ + ; CODE + ; ================================ + ; consti9 + ; end + + byteCode [3 ^ 2] + ; ================================ + ; DATA + ; ================================ + + ; ================================ + ; CODE + ; ================================ + ; consti9 + ; end + + byteCode [2 ^ 2 ^ 2] + ; ================================ + ; DATA + ; ================================ + ; 0: 16 :integer + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; end + + ;***************************************************************** + topic "if" + ;***************************************************************** + + byteCode [ + print "before" + if x [print "here", return true] + print "after" + ] + ; ================================ + ; DATA + ; ================================ + ; 0: before :string + ; 1: x :word + ; 2: here :string + ; 3: after :string + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; print + ; load1 + ; jmpifnot @4 + ; push2 + ; print + ; constbt + ; return + ; push3 + ; print + ; end + + byteCode [ + print "before" + if not? x [print "here", return false] + print "after" + ] + ; ================================ + ; DATA + ; ================================ + ; 0: before :string + ; 1: x :word + ; 2: here :string + ; 3: after :string + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; print + ; load1 + ; jmpif @4 + ; push2 + ; print + ; constbf + ; return + ; push3 + ; print + ; end + + byteCode [ + print "before" + if x=2 [print "here", return true] + print "after" + ] + ; ================================ + ; DATA + ; ================================ + ; 0: before :string + ; 1: x :word + ; 2: here :string + ; 3: after :string + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; print + ; consti2 + ; load1 + ; jmpifne @4 + ; push2 + ; print + ; constbt + ; return + ; push3 + ; print + ; end + + byteCode [ + print "before" + if x>2 [print "here", return true] + print "after" + ] + ; ================================ + ; DATA + ; ================================ + ; 0: before :string + ; 1: x :word + ; 2: here :string + ; 3: after :string + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; print + ; consti2 + ; load1 + ; jmpifle @4 + ; push2 + ; print + ; constbt + ; return + ; push3 + ; print + ; end + + byteCode [ + print "before" + if x=<2 [print "here", return true] + print "after" + ] + ; ================================ + ; DATA + ; ================================ + ; 0: before :string + ; 1: x :word + ; 2: here :string + ; 3: after :string + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; print + ; consti2 + ; load1 + ; jmpifgt @4 + ; push2 + ; print + ; constbt + ; return + ; push3 + ; print + ; end + + ;***************************************************************** + topic "unless" + ;***************************************************************** + + byteCode [ + print "before" + unless x [print "here", return false] + print "after" + ] + ; ================================ + ; DATA + ; ================================ + ; 0: before :string + ; 1: x :word + ; 2: here :string + ; 3: after :string + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; print + ; load1 + ; jmpif @4 + ; push2 + ; print + ; constbf + ; return + ; push3 + ; print + ; end + + byteCode [ + print "before" + unless not? x [print "here", return true] + print "after" + ] + ; ================================ + ; DATA + ; ================================ + ; 0: before :string + ; 1: x :word + ; 2: here :string + ; 3: after :string + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; print + ; load1 + ; jmpifnot @4 + ; push2 + ; print + ; constbt + ; return + ; push3 + ; print + ; end + + byteCode [ + print "before" + unless x=2 [print "here", return false] + print "after" + ] + ; ================================ + ; DATA + ; ================================ + ; 0: before :string + ; 1: x :word + ; 2: here :string + ; 3: after :string + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; print + ; consti2 + ; load1 + ; jmpifeq @4 + ; push2 + ; print + ; constbf + ; return + ; push3 + ; print + ; end + + byteCode [ + print "before" + unless x>2 [print "here", return false] + print "after" + ] + ; ================================ + ; DATA + ; ================================ + ; 0: before :string + ; 1: x :word + ; 2: here :string + ; 3: after :string + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; print + ; consti2 + ; load1 + ; jmpifgt @4 + ; push2 + ; print + ; constbf + ; return + ; push3 + ; print + ; end + + byteCode [ + print "before" + unless x=<2 [print "here", return false] + print "after" + ] + ; ================================ + ; DATA + ; ================================ + ; 0: before :string + ; 1: x :word + ; 2: here :string + ; 3: after :string + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; print + ; consti2 + ; load1 + ; jmpifle @4 + ; push2 + ; print + ; constbf + ; return + ; push3 + ; print + ; end + + ;***************************************************************** + topic "if?-else" + ;***************************************************************** + + byteCode [ + if? x [return true] + else [return false] + ] + ; ================================ + ; DATA + ; ================================ + ; 0: x :word + + ; ================================ + ; CODE + ; ================================ + ; load0 + ; jmpifnot @4 + ; constbt + ; return + ; goto @2 + ; constbf + ; return + ; end + + byteCode [ + print "before" + if? a <> 1 + 2 [print "here", return true] + else [print "there", return false] + print "after" + ] + ; ================================ + ; DATA + ; ================================ + ; 0: before :string + ; 1: a :word + ; 2: here :string + ; 3: there :string + ; 4: after :string + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; print + ; consti3 + ; load1 + ; jmpifeq @6 + ; push2 + ; print + ; constbt + ; return + ; goto @4 + ; push3 + ; print + ; constbf + ; return + ; push4 + ; print + ; end + + ;***************************************************************** + topic "switch (`?`)" + ;***************************************************************** + + byteCode [ + print "before" + switch a [1][2] + print "after" + ] + ; ================================ + ; DATA + ; ================================ + ; 0: before :string + ; 1: a :word + ; 2: after :string + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; print + ; load1 + ; jmpifnot @3 + ; consti1 + ; goto @1 + ; consti2 + ; push2 + ; print + ; end + + byteCode [(x = 1)? -> 1 -> 2] + ; ================================ + ; DATA + ; ================================ + ; 0: x :word + + ; ================================ + ; CODE + ; ================================ + ; consti1 + ; load0 + ; jmpifne @3 + ; consti1 + ; goto @1 + ; consti2 + ; end + + byteCode [ + print "before" + return (x<1)? -> true -> false + print "after" + ] + ; ================================ + ; DATA + ; ================================ + ; 0: before :string + ; 1: [ :block + ; false :word + ; ] + ; 2: [ :block + ; true :word + ; ] + ; 3: x :word + ; 4: after :string + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; print + ; push1 + ; push2 + ; consti1 + ; load3 + ; lt + ; switch + ; return + ; push4 + ; print + ; end + + byteCode [ + print "before" + z: (x<1)? -> true -> false + print "after" + ] + ; ================================ + ; DATA + ; ================================ + ; 0: before :string + ; 1: [ :block + ; false :word + ; ] + ; 2: [ :block + ; true :word + ; ] + ; 3: x :word + ; 4: z :label + ; 5: after :string + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; print + ; push1 + ; push2 + ; consti1 + ; load3 + ; lt + ; switch + ; store4 + ; push5 + ; print + ; end + + byteCode [ + print "before" + z: 3 + (x>=1)? -> 1 -> 2 + print "after" + ] + ; ================================ + ; DATA + ; ================================ + ; 0: before :string + ; 1: [ :block + ; 2 :integer + ; ] + ; 2: [ :block + ; 1 :integer + ; ] + ; 3: x :word + ; 4: z :label + ; 5: after :string + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; print + ; push1 + ; push2 + ; consti1 + ; load3 + ; ge + ; switch + ; consti3 + ; add + ; store4 + ; push5 + ; print + ; end + + ;***************************************************************** + topic "while" + ;***************************************************************** + + byteCode [ + while [x=1][print "hello"] + ] + ; ================================ + ; DATA + ; ================================ + ; 0: x :word + ; 1: hello :string + + ; ================================ + ; CODE + ; ================================ + ; consti1 + ; load0 + ; jmpifne @4 + ; push1 + ; print + ; goup @6 + ; end + + byteCode [ + while ø [print "hello"] + ] + ; ================================ + ; DATA + ; ================================ + ; 0: [ :block + ; print :word + ; hello :string + ; ] + + ; ================================ + ; CODE + ; ================================ + ; push0 + ; constn + ; while + ; end \ No newline at end of file diff --git a/tests/unittests/evaluator.res b/tests/unittests/evaluator.res new file mode 100644 index 0000000000..208cecbc71 --- /dev/null +++ b/tests/unittests/evaluator.res @@ -0,0 +1,785 @@ +************************************************** +* +* SIMPLE VALUES +* +************************************************** + + >-------------------------------------------------- + > Boolean + >-------------------------------------------------- + + input: [true] + data: [] + code: [21 218] (2 bytes) + + input: [false] + data: [] + code: [22 218] (2 bytes) + + input: [maybe] + data: [maybe] + code: [64 218] (2 bytes) + + + >-------------------------------------------------- + > Integer + >-------------------------------------------------- + + input: [1] + data: [] + code: [2 218] (2 bytes) + + input: [10] + data: [] + code: [11 218] (2 bytes) + + input: [123] + data: [123] + code: [32 218] (2 bytes) + + input: [1234567890123] + data: [1234567890123] + code: [32 218] (2 bytes) + + + >-------------------------------------------------- + > Floating + >-------------------------------------------------- + + input: [0.0] + data: [] + code: [18 218] (2 bytes) + + input: [1.0] + data: [] + code: [19 218] (2 bytes) + + input: [10.0] + data: [10.0] + code: [32 218] (2 bytes) + + input: [12345.1234567] + data: [12345.1234567] + code: [32 218] (2 bytes) + + + >-------------------------------------------------- + > Char + >-------------------------------------------------- + + input: [`a`] + data: [a] + code: [32 218] (2 bytes) + + input: [`😀`] + data: [😀] + code: [32 218] (2 bytes) + + + >-------------------------------------------------- + > String + >-------------------------------------------------- + + input: [""] + data: [] + code: [24 218] (2 bytes) + + input: ["Hello World!"] + data: [Hello World!] + code: [32 218] (2 bytes) + + + >-------------------------------------------------- + > Regex + >-------------------------------------------------- + + input: [{/hello/}] + data: [hello] + code: [32 218] (2 bytes) + + input: [{/[A-Z]+\d/}] + data: [[A-Z]+\d] + code: [32 218] (2 bytes) + + + >-------------------------------------------------- + > Type + >-------------------------------------------------- + + input: [:integer] + data: [:integer] + code: [32 218] (2 bytes) + + input: [:string] + data: [:string] + code: [32 218] (2 bytes) + + + >-------------------------------------------------- + > Literal + >-------------------------------------------------- + + input: ['a] + data: [a] + code: [32 218] (2 bytes) + + input: ['a 'b 'c] + data: [a b c] + code: [32 33 34 218] (4 bytes) + + + >-------------------------------------------------- + > SymbolLiteral + >-------------------------------------------------- + + input: ['+] + data: [+] + code: [32 218] (2 bytes) + + input: ['-->] + data: [-->] + code: [32 218] (2 bytes) + + + >-------------------------------------------------- + > Color + >-------------------------------------------------- + + input: [#FF0000] + data: [#FF0000] + code: [32 218] (2 bytes) + + input: [#00FF66] + data: [#00FF66] + code: [32 218] (2 bytes) + + + >-------------------------------------------------- + > Quantity + >-------------------------------------------------- + + input: [1:eur] + data: [1EUR] + code: [32 218] (2 bytes) + + input: [12:m] + data: [12m] + code: [32 218] (2 bytes) + + + >-------------------------------------------------- + > Version + >-------------------------------------------------- + + input: [0.9.82] + data: [0.9.82] + code: [32 218] (2 bytes) + + input: [2.0.0-rc1] + data: [2.0.0-rc1] + code: [32 218] (2 bytes) + + + >-------------------------------------------------- + > Block + >-------------------------------------------------- + + input: [[]] + data: [] + code: [25 218] (2 bytes) + + input: [[1 "hello" 3.14 true]] + data: [[1 hello 3.14 true]] + code: [32 218] (2 bytes) + + + >-------------------------------------------------- + > Null + >-------------------------------------------------- + + input: [∅] + data: [] + code: [27 218] (2 bytes) + + input: [null] + data: [null] + code: [64 218] (2 bytes) + + +************************************************** +* +* LABELS +* +************************************************** + + >-------------------------------------------------- + > Setting and getting variables + >-------------------------------------------------- + + input: [a: 1] + data: [a] + code: [2 48 218] (3 bytes) + + input: [b: 2 c: b] + data: [b c] + code: [3 48 64 49 218] (5 bytes) + + +************************************************** +* +* FUNCTION CALLS +* +************************************************** + + >-------------------------------------------------- + > Built-in function calls + >-------------------------------------------------- + + input: [abs 10] + data: [abs] + code: [11 96 218] (3 bytes) + + input: [empty? []] + data: [empty?] + code: [25 96 218] (3 bytes) + + input: [couple [1 2] ["one" "two"]] + data: [[one two] [1 2] couple] + code: [32 33 98 218] (4 bytes) + + + >-------------------------------------------------- + > Opcoded built-in function calls + >-------------------------------------------------- + + input: [print 2] + data: [] + code: [3 189 218] (3 bytes) + + input: [size [1 2]] + data: [[1 2]] + code: [32 183 218] (3 bytes) + + input: [and 1 123] + data: [123] + code: [32 2 139 218] (4 bytes) + + + >-------------------------------------------------- + > Composite opcoded built-in function calls + >-------------------------------------------------- + + input: [to :floating 1] + data: [:floating] + code: [2 32 170 218] (4 bytes) + + input: [to :integer "10"] + data: [10] + code: [32 172 218] (3 bytes) + + input: [to :string 5] + data: [] + code: [6 171 218] (3 bytes) + + + >-------------------------------------------------- + > Function calls with attributes + >-------------------------------------------------- + + input: [split .words "hello world"] + data: [hello world words] + code: [32 21 113 185 218] (5 bytes) + + input: [split .by: "X" "helloXworld"] + data: [helloXworld X by] + code: [32 33 114 185 218] (5 bytes) + + input: [join .with: "-" ["hello" "world"]] + data: [[hello world] - with] + code: [32 33 114 186 218] (5 bytes) + + + >-------------------------------------------------- + > Function calls via symbol aliases + >-------------------------------------------------- + + input: [@ [1 2 3]] + data: [[1 2 3]] + code: [32 176 218] (3 bytes) + + input: ["hello " ++ x] + data: [x hello ] + code: [64 33 188 218] (4 bytes) + + input: [1 .. 25] + data: [25] + code: [32 2 179 218] (4 bytes) + + + >-------------------------------------------------- + > User function definition & calling + >-------------------------------------------------- + + input: [h: function [] [print "function called"] print "before" h print "after"] + data: [[print function called] h before after] + code: [32 25 178 49 34 189 97 35 189 218] (10 bytes) + + input: [f: function [x] [x + 1] print "before" print f 10 print "after"] + data: [[x + 1] [x] f before after] + code: [32 33 178 50 35 189 11 98 189 36 189 218] (12 bytes) + + input: [g: $ [z w] [2 * z * w] print "before" print g 10 20 print "after"] + data: [[2 * z * w] [z w] g before 20 after] + code: [32 33 178 50 35 189 36 11 98 189 37 189 218] (13 bytes) + + +************************************************** +* +* PATHS +* +************************************************** + + >-------------------------------------------------- + > Path values + >-------------------------------------------------- + + input: [a\0] + data: [a] + code: [1 64 153 218] (4 bytes) + + input: [user\name] + data: [name user] + code: [32 65 153 218] (4 bytes) + + input: [user\grades\0] + data: [grades user] + code: [1 32 65 153 153 218] (6 bytes) + + input: [user\address\country] + data: [country address user] + code: [32 33 66 153 153 218] (6 bytes) + + + >-------------------------------------------------- + > PathLabel values + >-------------------------------------------------- + + input: [a\0: 10] + data: [a] + code: [11 1 64 154 218] (5 bytes) + + input: [user\name: "John"] + data: [John name user] + code: [32 33 66 154 218] (5 bytes) + + input: [user\grades\0: 6] + data: [grades user] + code: [7 1 32 65 153 154 218] (7 bytes) + + input: [user\address\country: "USA"] + data: [USA country address user] + code: [32 33 34 67 153 154 218] (7 bytes) + + +************************************************** +* +* BLOCKS & SYNTACTIC SUGAR +* +************************************************** + + >-------------------------------------------------- + > Inline blocks + >-------------------------------------------------- + + input: [(print 2)] + data: [] + code: [3 189 218] (3 bytes) + + input: [(print 2) (print 3)] + data: [] + code: [3 189 4 189 218] (5 bytes) + + input: [(print 2 print 3)] + data: [] + code: [3 189 4 189 218] (5 bytes) + + + >-------------------------------------------------- + > doublecolon syntactic sugar (`::`) + >-------------------------------------------------- + + input: [print 2 do :: print 3] + data: [[print 3] do] + code: [3 189 32 97 218] (5 bytes) + + + >-------------------------------------------------- + > arrowright syntactic sugar (`->`) + >-------------------------------------------------- + + input: [-> "hello"] + data: [[hello]] + code: [32 218] (2 bytes) + + input: [do -> print "hello" print "done"] + data: [[print hello] do done] + code: [32 97 34 189 218] (5 bytes) + + input: [-> 1 -> 2] + data: [[1] [2]] + code: [32 33 218] (3 bytes) + + input: [a: -> upper "hello" b: -> "hello " ++ world] + data: [[upper hello] a [hello ++ world] b] + code: [32 49 34 51 218] (5 bytes) + + + >-------------------------------------------------- + > thickarrowright syntactic sugar (`=>`) + >-------------------------------------------------- + + input: [=> "hello"] + data: [[hello]] + code: [25 32 218] (3 bytes) + + input: [f: function => print] + data: [[print _0] _0 f] + code: [32 33 178 50 218] (5 bytes) + + input: [adder: $ => add] + data: [[add _0 _1] [_0 _1] adder] + code: [32 33 178 50 218] (5 bytes) + + input: [subOne: function => [& - 1]] + data: [[_0 - 1] _0 subOne] + code: [32 33 178 50 218] (5 bytes) + + input: [mulAddOne: $ => [1 + & * &]] + data: [[1 + _0 * _1] [_0 _1] mulAddOne] + code: [32 33 178 50 218] (5 bytes) + + + >-------------------------------------------------- + > pipe operator (`|`) + >-------------------------------------------------- + + input: [2 | print] + data: [] + code: [3 189 218] (3 bytes) + + input: ["hello" | upper | print] + data: [hello upper] + code: [32 97 189 218] (4 bytes) + + input: [x: "hello" | upper] + data: [hello upper x] + code: [32 97 50 218] (4 bytes) + + input: [1 .. 10 | map 'x -> 2 * x] + data: [[2 * x] x] + code: [32 33 11 2 179 181 218] (7 bytes) + + input: [1 .. 10 | map 'x -> 2 * x | sum] + data: [[2 * x] x sum] + code: [32 33 11 2 179 181 98 218] (8 bytes) + + input: [1 .. 10 | map 'x -> 2 * x | sum | print] + data: [[2 * x] x sum] + code: [32 33 11 2 179 181 98 189 218] (9 bytes) + + input: [1 .. 10 | map 'x -> 2 * x | sum | print] + data: [[2 * x] x sum] + code: [32 33 11 2 179 181 98 189 218] (9 bytes) + + input: [filtered: 1 .. a | map 'x -> 3 * x | select 'x -> odd? x print filtered] + data: [[odd? x] x [3 * x] a filtered] + code: [32 33 34 33 67 2 179 181 182 84 189 218] (12 bytes) + + input: [filtered: 1 .. a | map 'x -> 3 * x | select => odd? print filtered] + data: [[odd? _0] _0 [3 * x] x a filtered] + code: [32 33 34 35 68 2 179 181 182 85 189 218] (12 bytes) + + +************************************************** +* +* OPTIMIZATIONS +* +************************************************** + + >-------------------------------------------------- + > add (`+`) / constant folding + >-------------------------------------------------- + + input: [add 2 3] + data: [] + code: [6 218] (2 bytes) + + input: [2 + 3] + data: [] + code: [6 218] (2 bytes) + + input: [2 + 3 + 4 + 5] + data: [] + code: [15 218] (2 bytes) + + input: [1 + 2 + 3 + 4 + 5 + 6] + data: [21] + code: [32 218] (2 bytes) + + input: [x: 3 + 7] + data: [x] + code: [11 48 218] (3 bytes) + + + >-------------------------------------------------- + > add (`+`) / -> inc + >-------------------------------------------------- + + input: [x + 1] + data: [x] + code: [64 136 218] (3 bytes) + + input: [1 + y] + data: [y] + code: [64 136 218] (3 bytes) + + + >-------------------------------------------------- + > add (`+`) / distributive + >-------------------------------------------------- + + input: [X + X * Y] + data: [Y X] + code: [64 2 128 65 130 218] (6 bytes) + + input: [(X * Y) + X] + data: [X Y] + code: [64 65 2 128 130 218] (6 bytes) + + + >-------------------------------------------------- + > sub (`-`) / constant folding + >-------------------------------------------------- + + input: [sub 2 3] + data: [] + code: [0 218] (2 bytes) + + input: [2 - 3] + data: [] + code: [0 218] (2 bytes) + + input: [8 - 2 - 1] + data: [] + code: [8 218] (2 bytes) + + input: [30 - 10 - 1] + data: [21] + code: [32 218] (2 bytes) + + + >-------------------------------------------------- + > sub (`-`) / -> dec + >-------------------------------------------------- + + input: [x - 1] + data: [x] + code: [64 137 218] (3 bytes) + + + >-------------------------------------------------- + > mul (`*`) / constant folding + >-------------------------------------------------- + + input: [mul 2 3] + data: [] + code: [7 218] (2 bytes) + + input: [2 * 3] + data: [] + code: [7 218] (2 bytes) + + input: [2 * 2 * 3] + data: [] + code: [13 218] (2 bytes) + + input: [2 * 3 * 4 * 5] + data: [120] + code: [32 218] (2 bytes) + + + >-------------------------------------------------- + > div (`/`) / constant folding + >-------------------------------------------------- + + input: [div 6 3] + data: [] + code: [3 218] (2 bytes) + + input: [6 / 3] + data: [] + code: [3 218] (2 bytes) + + input: [6 / 6 / 3] + data: [] + code: [4 218] (2 bytes) + + input: [50 / 4 / 4 / 2] + data: [25] + code: [32 218] (2 bytes) + + + >-------------------------------------------------- + > fdiv (`//`) / constant folding + >-------------------------------------------------- + + input: [fdiv 6 3] + data: [] + code: [20 218] (2 bytes) + + input: [6 // 3] + data: [] + code: [20 218] (2 bytes) + + input: [4 // 8 // 2] + data: [] + code: [19 218] (2 bytes) + + input: [55 // 4 // 4 // 2] + data: [27.5] + code: [32 218] (2 bytes) + + + >-------------------------------------------------- + > mod (`%`) / constant folding + >-------------------------------------------------- + + input: [mod 6 3] + data: [] + code: [1 218] (2 bytes) + + input: [6 % 3] + data: [] + code: [1 218] (2 bytes) + + input: [20 % 15 % 6] + data: [] + code: [3 218] (2 bytes) + + + >-------------------------------------------------- + > pow (`^`) / constant folding + >-------------------------------------------------- + + input: [pow 3 2] + data: [] + code: [10 218] (2 bytes) + + input: [3 ^ 2] + data: [] + code: [10 218] (2 bytes) + + input: [2 ^ 2 ^ 2] + data: [16] + code: [32 218] (2 bytes) + + + >-------------------------------------------------- + > if + >-------------------------------------------------- + + input: [print "before" if x [print "here" return true] print "after"] + data: [before x here after] + code: [32 189 65 199 4 34 189 21 167 35 189 218] (12 bytes) + + input: [print "before" if not? x [print "here" return false] print "after"] + data: [before x here after] + code: [32 189 65 197 4 34 189 22 167 35 189 218] (12 bytes) + + input: [print "before" if x = 2 [print "here" return true] print "after"] + data: [before x here after] + code: [32 189 3 65 203 4 34 189 21 167 35 189 218] (13 bytes) + + input: [print "before" if x > 2 [print "here" return true] print "after"] + data: [before x here after] + code: [32 189 3 65 211 4 34 189 21 167 35 189 218] (13 bytes) + + input: [print "before" if x =< 2 [print "here" return true] print "after"] + data: [before x here after] + code: [32 189 3 65 205 4 34 189 21 167 35 189 218] (13 bytes) + + + >-------------------------------------------------- + > unless + >-------------------------------------------------- + + input: [print "before" unless x [print "here" return false] print "after"] + data: [before x here after] + code: [32 189 65 197 4 34 189 22 167 35 189 218] (12 bytes) + + input: [print "before" unless not? x [print "here" return true] print "after"] + data: [before x here after] + code: [32 189 65 199 4 34 189 21 167 35 189 218] (12 bytes) + + input: [print "before" unless x = 2 [print "here" return false] print "after"] + data: [before x here after] + code: [32 189 3 65 201 4 34 189 22 167 35 189 218] (13 bytes) + + input: [print "before" unless x > 2 [print "here" return false] print "after"] + data: [before x here after] + code: [32 189 3 65 205 4 34 189 22 167 35 189 218] (13 bytes) + + input: [print "before" unless x =< 2 [print "here" return false] print "after"] + data: [before x here after] + code: [32 189 3 65 211 4 34 189 22 167 35 189 218] (13 bytes) + + + >-------------------------------------------------- + > if?-else + >-------------------------------------------------- + + input: [if? x [return true] else [return false]] + data: [x] + code: [64 199 4 21 167 213 2 22 167 218] (10 bytes) + + input: [print "before" if? a <> 1 + 2 [print "here" return true] else [print "there" return false] print "after"] + data: [before a here there after] + code: [32 189 4 65 201 6 34 189 21 167 213 4 35 189 22 167 36 189 218] (19 bytes) + + + >-------------------------------------------------- + > switch (`?`) + >-------------------------------------------------- + + input: [print "before" switch a [1] [2] print "after"] + data: [before a after] + code: [32 189 65 199 3 2 213 1 3 34 189 218] (12 bytes) + + input: [(x = 1) ? -> 1 -> 2] + data: [x] + code: [2 64 203 3 2 213 1 3 218] (9 bytes) + + input: [print "before" return (x < 1) ? -> true -> false print "after"] + data: [before [false] [true] x after] + code: [32 189 33 34 2 67 151 165 167 36 189 218] (12 bytes) + + input: [print "before" z: (x < 1) ? -> true -> false print "after"] + data: [before [false] [true] x z after] + code: [32 189 33 34 2 67 151 165 52 37 189 218] (12 bytes) + + input: [print "before" z: 3 + (x >= 1) ? -> 1 -> 2 print "after"] + data: [before [2] [1] x z after] + code: [32 189 33 34 2 67 150 165 4 128 52 37 189 218] (14 bytes) + + + >-------------------------------------------------- + > while + >-------------------------------------------------- + + input: [while [x = 1] [print "hello"]] + data: [x hello] + code: [2 64 203 4 33 189 215 6 218] (9 bytes) + + input: [while ∅ [print "hello"]] + data: [[print hello]] + code: [32 27 166 218] (4 bytes) \ No newline at end of file diff --git a/tests/unittests/lib.collections.res b/tests/unittests/lib.collections.res index 492b2ac297..24f7c399d1 100644 --- a/tests/unittests/lib.collections.res +++ b/tests/unittests/lib.collections.res @@ -208,7 +208,7 @@ one :string FD E8 :binary E8 :binary [hello] :block -[32 176 211] :block +[32 189 218] :block 15 :integer January :string John :string diff --git a/tests/unittests/thickarrowright.art b/tests/unittests/thickarrowright.art index da246b5df0..550e309a68 100644 --- a/tests/unittests/thickarrowright.art +++ b/tests/unittests/thickarrowright.art @@ -37,14 +37,14 @@ print l "hello" ; AS A FUNCTION WITH BLOCK -m: function => [& + 3] +m: function => [& - 3] print m 10 n: $ => [& + 3] print n 10 ; AS A FUNCTION WITH MULTIPLE PARAMS -p: function => [& * &] +p: function => [& + &] print p 3 6 q: function => [& * &] diff --git a/tests/unittests/thickarrowright.res b/tests/unittests/thickarrowright.res index ab7d9b59cc..91077f5d25 100644 --- a/tests/unittests/thickarrowright.res +++ b/tests/unittests/thickarrowright.res @@ -9,7 +9,7 @@ a a a 2 HELLO HELLO +7 13 -13 -18 +9 18 \ No newline at end of file diff --git a/tools/tester.art b/tools/tester.art index 346e1f1fc0..399ad1dbff 100755 --- a/tools/tester.art +++ b/tools/tester.art @@ -25,6 +25,7 @@ canExecute?: function [scri][ "ackermann function" "arbitrary-precision integers - included" "curzon numbers" + "hamming numbers" "iban" "integer overflow" "integer roots" @@ -32,17 +33,21 @@ canExecute?: function [scri][ "long multiplication" "loops - continue" "lucas-lehmer test" + "magic constant" "modular exponentation" "next special primes" + "pell's equation" + "permutations - derangements" "sorting algorithms - cycle sort" "sorting algorithms - pancake sort" + "square root by hand" "sylvester's sequence" "trigonometric functions" "ultra useful primes" "unicode strings" "wieferich primes" "xml - input" - ] -> return false + ] -> return false ] if sys\release='full [ diff --git a/version/build b/version/build index 2994099957..cc3c8d5704 100644 --- a/version/build +++ b/version/build @@ -1 +1 @@ -3471 \ No newline at end of file +4052 \ No newline at end of file