The former implementation was not correct regarding whiteouts in ?: conditional branches. The new one also parses a bit better, in effect on equal level than bash with its recursive descendent parser. --- All-in-one v4. Compares to v3 like
- * the entire condition, it's number stack position is used + * the entire condition, its number stack position is used I hope it is ok like this. Ciao! shell/Config.src | 8 + shell/ash.c | 6 +- shell/ash_test/ash-arith/bigbadbison.right | 880 ++++++++++++ shell/ash_test/ash-arith/bigbadbison.tests | 914 +++++++++++++ shell/hush.c | 23 +- shell/hush_test/hush-arith/bigbadbison.right | 880 ++++++++++++ shell/hush_test/hush-arith/bigbadbison.tests | 914 +++++++++++++ shell/math.c | 713 ++-------- shell/math.h | 7 +- shell/shexp-arith.h | 1292 ++++++++++++++++++ 10 files changed, 5001 insertions(+), 636 deletions(-) create mode 100644 shell/ash_test/ash-arith/bigbadbison.right create mode 100755 shell/ash_test/ash-arith/bigbadbison.tests create mode 100644 shell/hush_test/hush-arith/bigbadbison.right create mode 100755 shell/hush_test/hush-arith/bigbadbison.tests create mode 100644 shell/shexp-arith.h diff --git a/shell/Config.src b/shell/Config.src index 5efbf99959..32aaab58e8 100644 --- a/shell/Config.src +++ b/shell/Config.src @@ -108,6 +108,14 @@ config FEATURE_SH_MATH_BASE default y depends on FEATURE_SH_MATH +config FEATURE_SH_MATH_ERROR_TRACK + bool "Extend POSIX math support with error location tracking" + default y + depends on FEATURE_SH_MATH + help + Enable error location tracking in the shell's math support. + Without it only the type of error will be logged. + config FEATURE_SH_EXTRA_QUIET bool "Hide message on interactive shell startup" default y diff --git a/shell/ash.c b/shell/ash.c index 326f8b2a98..af3db6162e 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -6034,8 +6034,12 @@ ash_arith(const char *s) INT_OFF; result = arith(&math_state, s); - if (math_state.errmsg) + if (math_state.errmsg) { ash_msg_and_raise_error(math_state.errmsg); +# if ENABLE_FEATURE_SH_MATH_ERROR_TRACK + free(math_state.errmsg); +# endif + } INT_ON; return result; diff --git a/shell/ash_test/ash-arith/bigbadbison.right b/shell/ash_test/ash-arith/bigbadbison.right new file mode 100644 index 0000000000..a6446c81cd --- /dev/null +++ b/shell/ash_test/ash-arith/bigbadbison.right @@ -0,0 +1,880 @@ += BASE +<0> +<0> +<1> +<0> +<0> +<0> +<0> +<1> +<1> +<1> +<0> +<10> +<9191919191919> +<13> +<11> +<1023> +<1295> +<1295> +<9322365> +<16242915> +<10> +<33> +<10> +<33> +<1> +<1> +<1> +<33> +<33> +<33> +<33> += UNA PLUS/MINUS +<0> +<0> +<1> +<1> +<4221> +<16929> +<16242915> +<16242915> +<1> +<1> +<1> +<0> +<0> +<-1> +<-1> +<-4221> +<-16929> +<-16242915> +<-16242915> +<-1> +<-1> +<-1> +<-1> +<1> +<-1> += UNA ! +<1> +<1> +<0> +<0> +<1> +<0> += UNA ~ +<-1> +<-1> +<-2> +<-2> +<-2276> +<0> +<0> +<-1> +<-1> +<-1> +<-1> += BIN + +<0> +<0> +<1> +<1> +<1> +<1> +<2> +<2> +<2> +<-2> +<3333> +<3333> +<33> +<-33> +<-33> +<-1> +<33> +<-33> +<-33> +<1> +<9223372036854775807> +<-9223372036854775807> +<9223372036854775806> +<-9223372036854775808> +<-2> +<0> +<9223372036854775797> +<-9223372036854775797> +<9223372036854775796> +<-9223372036854775798> +<-12> +<10> += BIN - +<0> +<0> +<-1> +<-1> +<1> +<1> +<0> +<0> +<0> +<0> +<-1111> +<1111> +<-1> +<1> +<1> +<129> +<1> +<-1> +<-1> +<129> +<-9223372036854775807> +<9223372036854775807> +<-9223372036854775808> +<9223372036854775806> +<0> +<-2> +<-9223372036854775797> +<9223372036854775797> +<-9223372036854775798> +<9223372036854775796> +<10> +<-12> += BIN * +<0> +<0> +<0> +<0> +<0> +<0> +<1> +<1> +<2468642> +<2468642> +<272> +<272> +<272> +<-4160> +<272> +<272> +<272> +<-4160> +<-9223372036854775808> +<-9223372036854775808> +<-9223372036854775807> +<9223372036854775807> +<1> +<-1> +<-9223372036854775808> +<-9223372036854775808> +<-9223372036854775797> +<9223372036854775797> +<11> +<-11> += BIN / +<0> +<1> +<1> +<0> +<2> +<0> +<0> +<0> +<0> +<1> +<1> +<1> +<-1> +<2> +<3> +<1> +<1> +<0> +<0> +<-9223372036854775808> +<-9223372036854775808> +<-9223372036854775807> +<9223372036854775807> +<1> +<-1> +<838488366986797800> +<-838488366986797800> +<-838488366986797800> +<838488366986797800> +<0> +<0> += BIN % +<0> +<0> +<0> +<1111> +<0> +<16> +<-16> +<-16> +<64> +<1> +<-1> +<-1> +<1> +<0> +<0> +<1> +<0> +<3> +<-1> +<0> +<0> +<0> +<0> +<0> +<0> +<-8> +<-8> +<7> +<7> +<-1> +<-1> += BIN << +<0> +<0> +<0> +<0> +<1> +<1> +<2> +<2> +<78179674781384704> +<18639486976> +<2097152> +<-2251799813685248> +<-2251799813685248> +<0> +<1114112> +<-4785074604081152> +<-4785074604081152> +<65> +<64> +<0> +<0> +<-9223372036854775808> +<-2> +<-9223372036854775808> +<-2> +<0> +<0> +<-9007199254740992> +<-2048> +<-9007199254740992> +<-2048> += BIN >> +<0> +<0> +<0> +<0> +<1> +<1> +<0> +<0> +<0> +<0> +<0> +<0> +<0> +<0> +<0> +<-1> +<-1> +<131071> +<0> +<0> +<-1> +<-1> +<65> +<64> +<-1> +<-4611686018427387904> +<0> +<4611686018427387903> +<-1> +<-1> +<-1024> +<-4503599627370496> +<1023> +<4503599627370495> +<-1> +<-1> +<9007199254740991> += BIN ** +<0> +<2> +<4> +<8> +<16> +<10000> +<10000000000> +<100005> +<10000000000> += LOG OR +<0> +<0> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> += LOG AND +<0> +<0> +<1> +<1> +<1> +<1> +<1> +<1> +<0> +<0> +<1> +<1> +<1> +<1> +<1> +<1> +<0> +<0> += BIN BIT_OR +<0> +<0> +<1> +<1> +<1> +<1> +<1> +<1> +<3327> +<3327> +<17> +<-1> +<-1> +<-1> +<17> +<-1> +<-1> +<-63> +<1088> +<-1> +<-9223372036854775807> +<-1> +<9223372036854775807> +<-1> +<-1> +<-11> +<-9223372036854775797> +<-1> +<9223372036854775807> +<-1> +<-1> += BIN BIT_XOR +<0> +<0> +<1> +<1> +<1> +<1> +<0> +<0> +<3321> +<3321> +<1> +<31> +<31> +<-1> +<1> +<31> +<31> +<-127> +<1088> +<9223372036854775807> +<-9223372036854775807> +<-9223372036854775808> +<9223372036854775806> +<0> +<-2> +<9223372036854775797> +<-9223372036854775797> +<-9223372036854775798> +<9223372036854775796> +<10> +<-12> += BIN BIT_AND +<0> +<0> +<0> +<0> +<0> +<0> +<1> +<1> +<6> +<6> +<16> +<-32> +<-32> +<0> +<16> +<-32> +<-32> +<64> +<0> +<-9223372036854775808> +<0> +<9223372036854775807> +<1> +<-1> +<1> +<-9223372036854775808> +<0> +<9223372036854775797> +<11> +<-11> +<11> += BIN EQ +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<0> +<0> +<0> +<0> +<0> +<0> +<0> +<1> += BIN NE +<0> +<0> +<0> +<0> +<0> +<0> +<0> +<0> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<0> += BIN LE +<1> +<1> +<1> +<1> +<0> +<1> +<1> +<1> +<0> +<1> +<1> +<1> +<0> +<1> +<1> +<0> +<1> += BIN GE +<1> +<1> +<1> +<0> +<1> +<1> +<1> +<0> +<1> +<1> +<1> +<0> +<1> +<1> +<1> +<1> +<0> += BIN LT +<0> +<0> +<0> +<1> +<0> +<0> +<0> +<1> +<0> +<0> +<0> +<1> +<0> +<0> +<0> +<0> +<1> += BIN GT +<0> +<0> +<0> +<0> +<1> +<0> +<0> +<0> +<1> +<0> +<0> +<0> +<1> +<0> +<0> +<1> +<0> += PRECEDENCE I +<6> +<2> +<0> +<2> +<-1> +<1> +<1> +<2> +<8> +<7> +<12> +<5> +<10> +<1> +<72> +<48> +<288> +<1> +<3> +<1> +<4> +<76> +<1> +<1> +<1> +<1> +<2> +<2> += PARENS +<6> +<6> +<-4> +<-4> +<2> +<2> +<0> +<0> +<-3> +<-3> +<0> +<0> +<12> +<12> +<10> +<10> +<12> +<48> +<1> +<1> +<6> +<6> +<9> +<9> +<20> +<20> +<21> +<21> +<36864> +<36864> +<6> +<6> +<9> +<9> +<9> +<9> +<0> +<0> +<38> +<38> +<2> +<2> +<32> +<32> +<0> +<0> +<24> +<24> += ASSIGN I +<3><3> +<3><3> +<3><3> +<11><11> +<9><9> +<10><10> +<20><20> +<10><10> +<5><5> +<0><0> +<0><0> +<10><10> +<100><100> +<100><100> +<11><11> +<11><11> +<10><10> +<2><2> +<5><5> +<20><20> +<9223372036854775807><9223372036854775807> += ASSIGN II +<2><3> +<4><3> +<36><5><7> +<1501><100><15> +<3><3> +<10><1><2><3><10> += POSTFIX +<1><2> +<1><2><1> +<10><2><11> +<10><2><11> +<1><0> +<1><0><1> +<10><0><9> +<10><0><9> += PREFIX +<2><2> +<2><2><2> +<22><2><11> +<10><1><10> +<22><2><11> +<0><0> +<0><0><0> +<9><1><9> +<10><1><10> +<0><0><9> += VAR RECUR +<2><1 + 1> +<2><1 + 1> +<3><3> +<2><3> +<3><1 + 1> +<4><1 + 1 * 2> +<5><(1 + 1) * 2> +<3><3><3 / 2> +<2><2> +<2><2> +<3><3> +<4><4> +<5><5> +<5><5><3 / 2> += COMMA +<2> +<3> +<4> +<4> +<133><133> +<10><I2=10><10> += COND +<3> +<3> +<2> +<3> +<2> +<2> +<111> +<5> +<7> +<5> +<5> +<7> +<7> +<7> +<7> +<5> +-- COND .2 +<-1> +<0> +<1> +<1> +<32> +<32> +<1> +<1> +<32> +-- COND .3 +<3> +<4> +<3> +<3> +<3> +<4> +<3> +<3> +<3> +<4> +<1> +<4> +<5> +<2> +<10> +<10> +<10> +<10> +<10> +<2> +<10> +<10> +<10> +<10> +<10> +<2> +<5> +<5> +<8> +<8> +<10> +<10> +<10> +<10> +<10> +-- COND .4 +<5> +<6> +<7> +<8> +<9> +<12> +<10> +<12> +<10> +-- COND .5 +<12> +<10> +<12> +<10> +-- COND .6 +<12> +<9> +<-2> +<-1> +<23> +<26> +<24> +<0> +<23> +<23> +<23> +-- COND .7 +<16><2><3><16><5> +<16><2><3><16><5> +<16><2><3><16><5> +<25><2><3><4><25><> +<9><2><9><4><5> +<9><2><9><4><5> +<9><2><9><4><5> +<9><2><9><4><5> +<9><2><9><4><5> +<16><2><3><16><5> +<9><2><9><4><5><> +<4><4><3><4><5><> +-- COND .8 +<10><2> +<20><0> +<10><10> +<20><0> +<10><10> +<20><0> +<20><20> +-- COND .9 +<2><+E+><1+1> +<2><1+1><+E+> +<2><+E+><2> +<1><1><+E+> +<4><+E+><4> +<4><4><+E+> +-- COND .10 +<-1><+E+><+E+><+E+><-1> +<2><1><2><+E+><+E+> +<3><0><+E+><3><+E+> += WILD I +<14> +<1> +<1> +<1> +<3> +<87> +<2097152> +<20><I2=10><10> +<100><I2=10><10> +<0><I2=10><10> += WILD II +<36><11> +<33><11> +<36><12> +<39><12> +<39><12> +<-33><12> +<-27> +<20><10> +<21><11> +<20><10> +<21><11> +<21><11> +<20><10> +<20><10> +<21> +<21> +<21> += WILD RECUR +<20><20><10> +<10><I2=10><10><I2+=1> +<11><I2=10><11><I2+=1> +<21><I2=10><11><I2+=1> +<10><I2=10><10><I2+=1> +<1><I2=0><1><I2+=1> +<6><6><6><I2+=1> +<10><10><5><I2+=1> +<12><I2=(I2=10)+1><12><I2+=1> +<12><I2=(I2=(I2=10)+1)><12><I2+=1> +<10><I2=10><11><I2+=1> +<10><I2=10><11><I2+=1> +<10><10><5> +<6><I2=+E+><6> +<10><I2=10><10> +<10><0><10><20> +<10><6><10><20> +<10><10><10><20> +<50><50><10><20> +<50><50><10><20> +<500><500><10><20> diff --git a/shell/ash_test/ash-arith/bigbadbison.tests b/shell/ash_test/ash-arith/bigbadbison.tests new file mode 100755 index 0000000000..2b15fda7c9 --- /dev/null +++ b/shell/ash_test/ash-arith/bigbadbison.tests @@ -0,0 +1,914 @@ +# make this work with (ba)sh \ +command -v shopt && shopt -s expand_aliases;\ +alias p=printf;alias e=echo;alias s=export +s I=10 J=33 +e '= BASE' +e "<$(())>" +e "<$(( ))>" +e "<$((1))>" +e "<$((0))>" +e "<$((0x0))>" +e "<$((0X0))>" +e "<$((000))>" +e "<$((000000000000001))>" +e "<$((2#00000000000000000000000000000000000001))>" +e "<$((0X00000000000000000000000000000000000000000001))>" +e "<$((999999999999999999999999999999999999999999999))>" +e "<$(( 10 ))>" +e "<$((9191919191919))>" +e "<$((0xD))>" +e "<$((013))>" +e "<$((32#VV))>" +e "<$((36#ZZ))>" +e "<$((36#zz))>" +e "<$(( 64#zzZZ ))>" +e "<$((64#ZZzz))>" +e "<$((I))>" +e "<$((J))>" +e "<$(( I ))>" +e "<$(( J ))>" +e "<$(( (1) ))>" +e "<$((((1))))>" +e "<$(((((1)))))>" +e "<$(( (J) ))>" +e "<$((((J))))>" +e "<$(((((J)))))>" +e "<$(( ( ( ( J ) ) ) ))>" +e '= UNA PLUS/MINUS' +e "<$((+0))>" +e "<$(( + 0 ))>" +e "<$(( +1))>" +e "<$((+ 1 ))>" +e "<$(( + 4221 ))>" +e "<$(( +0x4221 ))>" +e "<$(( + 64#ZZzz ))>" +e "<$(( +64#ZZzz ))>" +e "<$((+ (1) ))>" +e "<$((+((1))))>" +e "<$((+(((1)))))>" +e "<$((-0))>" +e "<$(( - 0 ))>" +e "<$(( -1))>" +e "<$((- 1 ))>" +e "<$(( - 4221 ))>" +e "<$(( -0x4221 ))>" +e "<$(( - 64#ZZzz ))>" +e "<$(( -64#ZZzz ))>" +e "<$((- (1) ))>" +e "<$((-((1))))>" +e "<$((-(((1)))))>" +e "<$((+ -(1) ))>" +e "<$((+(-(-1))))>" +e "<$((+(-(-(-1)))))>" +e '= UNA !' +e "<$((!0))>" +e "<$((! 00000000))>" +e "<$((!1))>" +e "<$((! 0x00001))>" +e "<$((! - 0))>" +e "<$((!-1))>" +e '= UNA ~' +e "<$((~0))>" +e "<$((~ 00000000))>" +e "<$((~1))>" +e "<$((~ 0x00001))>" +e "<$((~ 64#zz))>" +e "<$((~-1))>" +e "<$((~ - 1))>" +e "<$((~-0))>" +e "<$((~ - 0))>" +e "<$((~(-0)))>" +e "<$((~((- 0))))>" +e '= BIN +' +e "<$((0+0))>" +e "<$(( 0 + 0 ))>" +e "<$((0+1))>" +e "<$(( 0 + 1 ))>" +e "<$((1+0))>" +e "<$(( 1 + 0 ))>" +e "<$((1+1))>" +e "<$(( 1 + 1 ))>" +e "<$(( (1 + 1) ))>" +e "<$(((((((-1)))) + (((-1))))))>" +e "<$((1111+2222))>" +e "<$((2222+1111))>" +e "<$(( +0x10 + +0x11 ))>" +e "<$(( -0x10 + -0x11 ))>" +e "<$(( -0x10 + -0x11 ))>" +e "<$(( +64#10 + -64#11 ))>" +e "<$(( +0x11 + +0x10 ))>" +e "<$(( -0x11 + -0x10 ))>" +e "<$(( -0x11 + -0x10 ))>" +e "<$(( +64#11 + -64#10 ))>" +e "<$((0x8000000000000000+-1))>" +e "<$((0x8000000000000000+1))>" +e "<$((0x7FFFFFFFFFFFFFFF+-1))>" +e "<$((0x7FFFFFFFFFFFFFFF+1))>" +e "<$((0xFFFFFFFFFFFFFFFF+-1))>" +e "<$((0xFFFFFFFFFFFFFFFF+1))>" +e "<$((0x8000000000000000+-11))>" +e "<$((0x8000000000000000+11))>" +e "<$((0x7FFFFFFFFFFFFFFF+-11))>" +e "<$((0x7FFFFFFFFFFFFFFF+11))>" +e "<$((0xFFFFFFFFFFFFFFFF+-11))>" +e "<$((0xFFFFFFFFFFFFFFFF+11))>" +e '= BIN -' +e "<$((0-0))>" +e "<$(( 0 - 0 ))>" +e "<$((0-1))>" +e "<$(( 0 - 1 ))>" +e "<$((1-0))>" +e "<$(( 1 - 0 ))>" +e "<$((1-1))>" +e "<$(( 1 - 1 ))>" +e "<$(( (1 - 1) ))>" +e "<$(((((((+1)))) - (((+1))))))>" +e "<$((1111-2222))>" +e "<$((2222-1111))>" +e "<$(( +0x10 - +0x11 ))>" +e "<$(( -0x10 - -0x11 ))>" +e "<$(( -0x10 - -0x11 ))>" +e "<$(( +64#10 - -64#11 ))>" +e "<$(( +0x11 - +0x10 ))>" +e "<$(( -0x11 - -0x10 ))>" +e "<$(( -0x11 - -0x10 ))>" +e "<$(( +64#11 - -64#10 ))>" +e "<$((0x8000000000000000--1))>" +e "<$((0x8000000000000000-1))>" +e "<$((0x7FFFFFFFFFFFFFFF--1))>" +e "<$((0x7FFFFFFFFFFFFFFF-1))>" +e "<$((0xFFFFFFFFFFFFFFFF--1))>" +e "<$((0xFFFFFFFFFFFFFFFF-1))>" +e "<$((0x8000000000000000--11))>" +e "<$((0x8000000000000000-11))>" +e "<$((0x7FFFFFFFFFFFFFFF--11))>" +e "<$((0x7FFFFFFFFFFFFFFF-11))>" +e "<$((0xFFFFFFFFFFFFFFFF--11))>" +e "<$((0xFFFFFFFFFFFFFFFF-11))>" +e '= BIN *' +e "<$((0*0))>" +e "<$(( 0 * 0 ))>" +e "<$((0*1))>" +e "<$(( 0 * 1 ))>" +e "<$((1*0))>" +e "<$(( 1 * 0 ))>" +e "<$((1*1))>" +e "<$(( 1 * 1 ))>" +e "<$((1111*2222))>" +e "<$((2222*1111))>" +e "<$(( +0x10 * +0x11 ))>" +e "<$(( -0x10 * -0x11 ))>" +e "<$(( -0x10 * -0x11 ))>" +e "<$(( +64#10 * -64#11 ))>" +e "<$(( +0x11 * +0x10 ))>" +e "<$(( -0x11 * -0x10 ))>" +e "<$(( -0x11 * -0x10 ))>" +e "<$(( +64#11 * -64#10 ))>" +e "<$((0x8000000000000000*-1))>" +e "<$((0x8000000000000000*1))>" +e "<$((0x7FFFFFFFFFFFFFFF*-1))>" +e "<$((0x7FFFFFFFFFFFFFFF*1))>" +e "<$((0xFFFFFFFFFFFFFFFF*-1))>" +e "<$((0xFFFFFFFFFFFFFFFF*1))>" +e "<$((0x8000000000000000*-11))>" +e "<$((0x8000000000000000*11))>" +e "<$((0x7FFFFFFFFFFFFFFF*-11))>" +e "<$((0x7FFFFFFFFFFFFFFF*11))>" +e "<$((0xFFFFFFFFFFFFFFFF*-11))>" +e "<$((0xFFFFFFFFFFFFFFFF*11))>" +e '= BIN /' +e "<$(( 0 / 1 ))>" +e "<$((1/1))>" +e "<$(( 1 / 1 ))>" +e "<$((1111/2222))>" +e "<$((2222/1111))>" +e "<$(( +0x10 / +0x11 ))>" +e "<$(( -0x10 / -0x11 ))>" +e "<$(( -0x10 / -0x11 ))>" +e "<$(( +64#10 / -64#11 ))>" +e "<$(( +0x11 / +0x10 ))>" +e "<$(( -0x11 / -0x10 ))>" +e "<$(( -0x11 / -0x10 ))>" +e "<$(( +64#11 / -64#10 ))>" +e "<$((2/1))>" +e "<$((3/1))>" +e "<$((3/2))>" +e "<$((3/3))>" +e "<$((3/4))>" +e "<$((-1/4))>" +e "<$((0x8000000000000000/-1))>" +e "<$((0x8000000000000000/1))>" +e "<$((0x7FFFFFFFFFFFFFFF/-1))>" +e "<$((0x7FFFFFFFFFFFFFFF/1))>" +e "<$((0xFFFFFFFFFFFFFFFF/-1))>" +e "<$((0xFFFFFFFFFFFFFFFF/1))>" +e "<$((0x8000000000000000/-11))>" +e "<$((0x8000000000000000/11))>" +e "<$((0x7FFFFFFFFFFFFFFF/-11))>" +e "<$((0x7FFFFFFFFFFFFFFF/11))>" +e "<$((0xFFFFFFFFFFFFFFFF/-11))>" +e "<$((0xFFFFFFFFFFFFFFFF/11))>" +e '= BIN %' +e "<$(( 0 % 1 ))>" +e "<$((1%1))>" +e "<$(( 1 % 1 ))>" +e "<$((1111%2222))>" +e "<$((2222%1111))>" +e "<$(( +0x10 % +0x11 ))>" +e "<$(( -0x10 % -0x11 ))>" +e "<$(( -0x10 % -0x11 ))>" +e "<$(( +64#10 % -64#11 ))>" +e "<$(( +0x11 % +0x10 ))>" +e "<$(( -0x11 % -0x10 ))>" +e "<$(( -0x11 % -0x10 ))>" +e "<$(( +64#11 % -64#10 ))>" +e "<$((2%1))>" +e "<$((3%1))>" +e "<$((3%2))>" +e "<$((3%3))>" +e "<$((3%4))>" +e "<$((-1%4))>" +e "<$((0x8000000000000000%-1))>" +e "<$((0x8000000000000000%1))>" +e "<$((0x7FFFFFFFFFFFFFFF%-1))>" +e "<$((0x7FFFFFFFFFFFFFFF%1))>" +e "<$((0xFFFFFFFFFFFFFFFF%-1))>" +e "<$((0xFFFFFFFFFFFFFFFF%1))>" +e "<$((0x8000000000000000%-11))>" +e "<$((0x8000000000000000%11))>" +e "<$((0x7FFFFFFFFFFFFFFF%-11))>" +e "<$((0x7FFFFFFFFFFFFFFF%11))>" +e "<$((0xFFFFFFFFFFFFFFFF%-11))>" +e "<$((0xFFFFFFFFFFFFFFFF%11))>" +e '= BIN <<' +e "<$((0<<0))>" +e "<$(( 0 << 0 ))>" +e "<$((0<<1))>" +e "<$(( 0 << 1 ))>" +e "<$((1<<0))>" +e "<$(( 1 << 0 ))>" +e "<$((1<<1))>" +e "<$(( 1 << 1 ))>" +e "<$((1111<<2222))>" +e "<$((2222<<1111))>" +e "<$(( +0x10 << +0x11 ))>" +e "<$(( -0x10 << -0x11 ))>" +e "<$(( -0x10 << -0x11 ))>" +e "<$(( +64#10 << -64#11 ))>" +e "<$(( +0x11 << +0x10 ))>" +e "<$(( -0x11 << -0x10 ))>" +e "<$(( -0x11 << -0x10 ))>" +e "<$(( +64#11 << -64#10 ))>" +e "<$(( +64 << +1024 ))>" +e "<$((0x8000000000000000<<-1))>" +e "<$((0x8000000000000000<<1))>" +e "<$((0x7FFFFFFFFFFFFFFF<<-1))>" +e "<$((0x7FFFFFFFFFFFFFFF<<1))>" +e "<$((0xFFFFFFFFFFFFFFFF<<-1))>" +e "<$((0xFFFFFFFFFFFFFFFF<<1))>" +e "<$((0x8000000000000000<<-11))>" +e "<$((0x8000000000000000<<11))>" +e "<$((0x7FFFFFFFFFFFFFFF<<-11))>" +e "<$((0x7FFFFFFFFFFFFFFF<<11))>" +e "<$((0xFFFFFFFFFFFFFFFF<<-11))>" +e "<$((0xFFFFFFFFFFFFFFFF<<11))>" +e '= BIN >>' +e "<$((0>>0))>" +e "<$(( 0 >> 0 ))>" +e "<$((0>>1))>" +e "<$(( 0 >> 1 ))>" +e "<$((1>>0))>" +e "<$(( 1 >> 0 ))>" +e "<$((1>>1))>" +e "<$(( 1 >> 1 ))>" +e "<$((1>>>1))>" +e "<$(( 1 >>> 1 ))>" +e "<$((1111>>2222))>" +e "<$((2222>>1111))>" +e "<$((1111>>>2222))>" +e "<$((2222>>>1111))>" +e "<$(( +0x10 >> +0x11 ))>" +e "<$(( -0x10 >> -0x11 ))>" +e "<$(( -0x10 >> -0x11 ))>" +e "<$(( -0x10 >>> -0x11 ))>" +e "<$(( +64#10 >> -64#11 ))>" +e "<$(( +0x11 >> +0x10 ))>" +e "<$(( -0x11 >> -0x10 ))>" +e "<$(( -0x11 >> -0x10 ))>" +e "<$(( +64#11 >> -64#10 ))>" +e "<$(( +64 >> +1024 ))>" +e "<$((0x8000000000000000>>-1))>" +e "<$((0x8000000000000000>>1))>" +e "<$((0x7FFFFFFFFFFFFFFF>>-1))>" +e "<$((0x7FFFFFFFFFFFFFFF>>1))>" +e "<$((0xFFFFFFFFFFFFFFFF>>-1))>" +e "<$((0xFFFFFFFFFFFFFFFF>>1))>" +e "<$((0x8000000000000000>>-11))>" +e "<$((0x8000000000000000>>11))>" +e "<$((0x7FFFFFFFFFFFFFFF>>-11))>" +e "<$((0x7FFFFFFFFFFFFFFF>>11))>" +e "<$((0xFFFFFFFFFFFFFFFF>>-11))>" +e "<$((0xFFFFFFFFFFFFFFFF>>11))>" +e "<$((0xFFFFFFFFFFFFFFFF>>>11))>" +e '= BIN **' +e "<$((0**1))>" +e "<$((2**1))>" +e "<$((2**2))>" +e "<$((2**3))>" +e "<$((2**4))>" +e "<$((10**4))>" +e "<$((10**10))>" +e "<$((10**5+5))>" +e "<$((10**(5+5)))>" +e '= LOG OR' +e "<$((0||0))>" +e "<$(( 000 || 0X0 ))>" +e "<$((01 || 64#1))>" +e "<$((01 || 64#1))>" +e "<$((0x1234 || 4660))>" +e "<$((0x1234 || 011064))>" +s I=33 J=33;e "<$((I||J))>" +s I=33 J=33;e "<$(( I || J ))>" +e "<$((0||1))>" +e "<$((0||0000000000000000000000001))>" +e "<$((1||2))>" +e "<$((0x1234 || 04660))>" +e "<$((0x1234 || 0x11064))>" +s I=10 J=33;e "<$((I||J))>" +s I=-10 J=-33;e "<$((I||J))>" +s I=-33 J=-33;e "<$((I||J))>" +s I=0 J=-33;e "<$((I||J))>" +s I=33 J=0;e "<$((I||J))>" +e '= LOG AND' +e "<$((0&&0))>" +e "<$(( 000 && 0X0 ))>" +e "<$((01 && 64#1))>" +e "<$((01 && 64#1))>" +e "<$((0x1234 && 4660))>" +e "<$((0x1234 && 011064))>" +s I=33 J=33;e "<$((I&&J))>" +s I=33 J=33;e "<$(( I && J ))>" +e "<$((0&&1))>" +e "<$((0&&0000000000000000000000001))>" +e "<$((1&&2))>" +e "<$((0x1234 && 04660))>" +e "<$((0x1234 && 0x11064))>" +s I=10 J=33;e "<$((I&&J))>" +s I=-10 J=-33;e "<$((I&&J))>" +s I=-33 J=-33;e "<$((I&&J))>" +s I=0 J=-33;e "<$((I&&J))>" +s I=33 J=0;e "<$((I&&J))>" +e '= BIN BIT_OR' +e "<$((0|0))>" +e "<$(( 0 | 0 ))>" +e "<$((0|1))>" +e "<$(( 0 | 1 ))>" +e "<$((1|0))>" +e "<$(( 1 | 0 ))>" +e "<$((1|1))>" +e "<$(( 1 | 1 ))>" +e "<$((1111|2222))>" +e "<$((2222|1111))>" +e "<$(( +0x10 | +0x11 ))>" +e "<$(( -0x10 | -0x11 ))>" +e "<$(( -0x10 | -0x11 ))>" +e "<$(( +64#10 | -64#11 ))>" +e "<$(( +0x11 | +0x10 ))>" +e "<$(( -0x11 | -0x10 ))>" +e "<$(( -0x11 | -0x10 ))>" +e "<$(( +64#11 | -64#10 ))>" +e "<$(( +64 | +1024 ))>" +e "<$((0x8000000000000000|-1))>" +e "<$((0x8000000000000000|1))>" +e "<$((0x7FFFFFFFFFFFFFFF|-1))>" +e "<$((0x7FFFFFFFFFFFFFFF|1))>" +e "<$((0xFFFFFFFFFFFFFFFF|-1))>" +e "<$((0xFFFFFFFFFFFFFFFF|1))>" +e "<$((0x8000000000000000|-11))>" +e "<$((0x8000000000000000|11))>" +e "<$((0x7FFFFFFFFFFFFFFF|-11))>" +e "<$((0x7FFFFFFFFFFFFFFF|11))>" +e "<$((0xFFFFFFFFFFFFFFFF|-11))>" +e "<$((0xFFFFFFFFFFFFFFFF|11))>" +e '= BIN BIT_XOR' +e "<$((0^0))>" +e "<$(( 0 ^ 0 ))>" +e "<$((0^1))>" +e "<$(( 0 ^ 1 ))>" +e "<$((1^0))>" +e "<$(( 1 ^ 0 ))>" +e "<$((1^1))>" +e "<$(( 1 ^ 1 ))>" +e "<$((1111^2222))>" +e "<$((2222^1111))>" +e "<$(( +0x10 ^ +0x11 ))>" +e "<$(( -0x10 ^ -0x11 ))>" +e "<$(( -0x10 ^ -0x11 ))>" +e "<$(( +64#10 ^ -64#11 ))>" +e "<$(( +0x11 ^ +0x10 ))>" +e "<$(( -0x11 ^ -0x10 ))>" +e "<$(( -0x11 ^ -0x10 ))>" +e "<$(( +64#11 ^ -64#10 ))>" +e "<$(( +64 ^ +1024 ))>" +e "<$((0x8000000000000000^-1))>" +e "<$((0x8000000000000000^1))>" +e "<$((0x7FFFFFFFFFFFFFFF^-1))>" +e "<$((0x7FFFFFFFFFFFFFFF^1))>" +e "<$((0xFFFFFFFFFFFFFFFF^-1))>" +e "<$((0xFFFFFFFFFFFFFFFF^1))>" +e "<$((0x8000000000000000^-11))>" +e "<$((0x8000000000000000^11))>" +e "<$((0x7FFFFFFFFFFFFFFF^-11))>" +e "<$((0x7FFFFFFFFFFFFFFF^11))>" +e "<$((0xFFFFFFFFFFFFFFFF^-11))>" +e "<$((0xFFFFFFFFFFFFFFFF^11))>" +e '= BIN BIT_AND' +e "<$((0&0))>" +e "<$(( 0 & 0 ))>" +e "<$((0&1))>" +e "<$(( 0 & 1 ))>" +e "<$((1&0))>" +e "<$(( 1 & 0 ))>" +e "<$((1&1))>" +e "<$(( 1 & 1 ))>" +e "<$((1111&2222))>" +e "<$((2222&1111))>" +e "<$(( +0x10 & +0x11 ))>" +e "<$(( -0x10 & -0x11 ))>" +e "<$(( -0x10 & -0x11 ))>" +e "<$(( +64#10 & -64#11 ))>" +e "<$(( +0x11 & +0x10 ))>" +e "<$(( -0x11 & -0x10 ))>" +e "<$(( -0x11 & -0x10 ))>" +e "<$(( +64#11 & -64#10 ))>" +e "<$(( +64 & +1024 ))>" +e "<$((0x8000000000000000&-1))>" +e "<$((0x8000000000000000&1))>" +e "<$((0x7FFFFFFFFFFFFFFF&-1))>" +e "<$((0x7FFFFFFFFFFFFFFF&1))>" +e "<$((0xFFFFFFFFFFFFFFFF&-1))>" +e "<$((0xFFFFFFFFFFFFFFFF&1))>" +e "<$((0x8000000000000000&-11))>" +e "<$((0x8000000000000000&11))>" +e "<$((0x7FFFFFFFFFFFFFFF&-11))>" +e "<$((0x7FFFFFFFFFFFFFFF&11))>" +e "<$((0xFFFFFFFFFFFFFFFF&-11))>" +e "<$((0xFFFFFFFFFFFFFFFF&11))>" +e '= BIN EQ' +e "<$((0==0))>" +e "<$(( 000 == 0X0 ))>" +e "<$((01 == 64#1))>" +e "<$((01 == 64#1))>" +e "<$((0x1234 == 4660))>" +e "<$((0x1234 == 011064))>" +s I=33 J=33;e "<$((I==J))>" +s I=33 J=33;e "<$(( I == J ))>" +e "<$((0==1))>" +e "<$((0==0000000000000000000000001))>" +e "<$((1==2))>" +e "<$((0x1234 == 04660))>" +e "<$((0x1234 == 0x11064))>" +s I=10 J=33;e "<$((I==J))>" +s I=-10 J=-33;e "<$((I==J))>" +s I=-33 J=-33;e "<$((I==J))>" +e '= BIN NE' +e "<$((0!=0))>" +e "<$(( 000 != 0X0 ))>" +e "<$((01 != 64#1))>" +e "<$((01 != 64#1))>" +e "<$((0x1234 != 4660))>" +e "<$((0x1234 != 011064))>" +s I=33 J=33;e "<$((I!=J))>" +s I=33 J=33;e "<$(( I != J ))>" +e "<$((0!=1))>" +e "<$((0!=0000000000000000000000001))>" +e "<$((1!=2))>" +e "<$((0x1234 != 04660))>" +e "<$((0x1234 != 0x11064))>" +s I=10 J=33;e "<$((I!=J))>" +s I=-10 J=-33;e "<$((I!=J))>" +s I=-33 J=-33;e "<$((I!=J))>" +e '= BIN LE' +e "<$((0<=0))>" +e "<$(( 000 <= 0X0 ))>" +e "<$((01 <= 64#1))>" +e "<$((01 <= 64#2))>" +e "<$((02 <= 64#1))>" +e "<$((0x1234 <= 4660))>" +e "<$((0x1234 <= 011064))>" +e "<$((0x1233 <= 011064))>" +e "<$((0x1235 <= 011064))>" +s I=33 J=33;e "<$((I<=J))>" +s I=33 J=33;e "<$((I<=J))>" +s I=32 J=33;e "<$((I<=J))>" +s I=34 J=33;e "<$((I<=J))>" +s I=-33 J=-33;e "<$((I<=J))>" +s I=-33 J=-33;e "<$((I<=J))>" +s I=-32 J=-33;e "<$((I<=J))>" +s I=-34 J=-33;e "<$((I<=J))>" +e '= BIN GE' +e "<$((0>=0))>" +e "<$(( 000 >= 0X0 ))>" +e "<$((01 >= 64#1))>" +e "<$((01 >= 64#2))>" +e "<$((02 >= 64#1))>" +e "<$((0x1234 >= 4660))>" +e "<$((0x1234 >= 011064))>" +e "<$((0x1233 >= 011064))>" +e "<$((0x1235 >= 011064))>" +s I=33 J=33;e "<$((I>=J))>" +s I=33 J=33;e "<$((I>=J))>" +s I=32 J=33;e "<$((I>=J))>" +s I=34 J=33;e "<$((I>=J))>" +s I=-33 J=-33;e "<$((I>=J))>" +s I=-33 J=-33;e "<$((I>=J))>" +s I=-32 J=-33;e "<$((I>=J))>" +s I=-34 J=-33;e "<$((I>=J))>" +e '= BIN LT' +e "<$((0<0))>" +e "<$(( 000 < 0X0 ))>" +e "<$((01 < 64#1))>" +e "<$((01 < 64#2))>" +e "<$((02 < 64#1))>" +e "<$((0x1234 < 4660))>" +e "<$((0x1234 < 011064))>" +e "<$((0x1233 < 011064))>" +e "<$((0x1235 < 011064))>" +s I=33 J=33;e "<$((I<J))>" +s I=33 J=33;e "<$((I<J))>" +s I=32 J=33;e "<$((I<J))>" +s I=34 J=33;e "<$((I<J))>" +s I=-33 J=-33;e "<$((I<J))>" +s I=-33 J=-33;e "<$((I<J))>" +s I=-32 J=-33;e "<$((I<J))>" +s I=-34 J=-33;e "<$((I<J))>" +e '= BIN GT' +e "<$((0>0))>" +e "<$(( 000 > 0X0 ))>" +e "<$((01 > 64#1))>" +e "<$((01 > 64#2))>" +e "<$((02 > 64#1))>" +e "<$((0x1234 > 4660))>" +e "<$((0x1234 > 011064))>" +e "<$((0x1233 > 011064))>" +e "<$((0x1235 > 011064))>" +s I=33 J=33;e "<$((I>J))>" +s I=33 J=33;e "<$((I>J))>" +s I=32 J=33;e "<$((I>J))>" +s I=34 J=33;e "<$((I>J))>" +s I=-33 J=-33;e "<$((I>J))>" +s I=-33 J=-33;e "<$((I>J))>" +s I=-32 J=-33;e "<$((I>J))>" +s I=-34 J=-33;e "<$((I>J))>" +# +# COMMA below +e '= PRECEDENCE I' +e "<$(( 1 + 2 + 3 ))>" +e "<$(( 1 - 2 + 3 ))>" +e "<$(( 3 - 2 - 1 ))>" +e "<$(( 3 - 2 + 1 ))>" +e "<$(( - 2 + 1 ))>" +e "<$(( 2 + -1 ))>" +e "<$(( ! 2 + 1 ))>" +e "<$(( 2 + !1 ))>" +e "<$(( 3 * 2 + 2 ))>" +e "<$(( 3 + 2 * 2 ))>" +e "<$(( 3 * 2 * 2 ))>" +e "<$(( 9 / 3 + 2 ))>" +e "<$(( 9 + 3 / 2 ))>" +e "<$(( 9 / 3 / 2 ))>" +e "<$(( 9 << 1 + 2 ))>" +e "<$(( 9 + 3 << 2 ))>" +e "<$(( 9 << 3 << 2 ))>" +e "<$(( 9 >> 1 + 2 ))>" +e "<$(( 9 + 3 >> 2 ))>" +e "<$(( 19 >> 3 >> 1 ))>" +e "<$(( 19 >> 3 << 1 ))>" +e "<$(( 19 << 3 >> 1 ))>" +e "<$(( 2 + 3 < 3 * 2 ))>" +e "<$(( 2 << 3 >= 3 << 2 ))>" +e "<$(( 0xfD & 0xF == 0xF ))>" +e "<$((0xfD&0xF==0xF))>" +e "<$(( 3 * 7 , 2 << 8 , 9 - 7 ))>" +e "<$((3*7,2<<8,9-7))>" +e '= PARENS' +e "<$(((1 + 2) + 3))>" +e "<$(((1+2)+3))>" +e "<$((1 - (2 + 3)))>" +e "<$((1-(2+3)))>" +e "<$((3 - (2 - 1)))>" +e "<$((3-(2-1)))>" +e "<$((3 - ( 2 + 1 )))>" +e "<$((3-(2+1)))>" +e "<$((- (2 + 1)))>" +e "<$((-(2+1)))>" +e "<$((! (2 + 1)))>" +e "<$((!(2+1)))>" +e "<$((3 * (2 + 2)))>" +e "<$((3*(2+2)))>" +e "<$(((3 + 2) * 2))>" +e "<$(((3+2)*2))>" +e "<$((3 * (2 * 2)))>" +e "<$((3*(2*8)))>" +e "<$((9 / (3 + 2)))>" +e "<$((9/(3+2)))>" +e "<$((( 9 + 3 ) / 2))>" +e "<$(((9+3)/2))>" +e "<$((9 / ( 3 / 2 )))>" +e "<$((9/(3/2)))>" +e "<$((( 9 << 1 ) + 2))>" +e "<$(((9<<1)+2))>" +e "<$((9 + (3 << 2)))>" +e "<$((9+(3<<2)))>" +e "<$((9 << (3 << 2)))>" +e "<$((9<<(3<<2)))>" +e "<$(((9 >> 1) + 2))>" +e "<$(((9>>1)+2))>" +e "<$((9 + (3 >> 2)))>" +e "<$((9+(3>>2)))>" +e "<$((19 >> (3 >> 1)))>" +e "<$((19>>(3>>1)))>" +e "<$((19 >> (3 << 1)))>" +e "<$((19>>(3<<1)))>" +e "<$((19 << (3 >> 1)))>" +e "<$((19<<(3>>1)))>" +e "<$((2 + (3 < 3) * 2))>" +e "<$((2+(3<3)*2))>" +e "<$((2 << ((3 >= 3) << 2)))>" +e "<$((2<<((3>=3)<<2)))>" +e "<$(((0xfD & 0xF) == 0xF))>" +e "<$(((0xfD&0xF)==0xF))>" +e "<$((3 * (7 , 2) << (8 , 9 - 7)))>" +e "<$((3*(7,2)<<(8,9-7)))>" +# +# COND BELOW +e '= ASSIGN I' +unset I;p "<$(( I = 3 ))>";e "<$I>" +unset I;p "<$((I=3))>";e "<$I>" +s I=10;p "<$((I=3))>";e "<$I>" +s I=10;p "<$((I+=1))>";e "<$I>" +s I=10;p "<$((I-=1))>";e "<$I>" +s I=10;p "<$((I*=1))>";e "<$I>" +s I=10;p "<$((I*=2))>";e "<$I>" +s I=10;p "<$((I/=1))>";e "<$I>" +s I=10;p "<$((I/=2))>";e "<$I>" +s I=10;p "<$((I%=1))>";e "<$I>" +s I=10;p "<$((I%=2))>";e "<$I>" +s I=10;p "<$((I**=1))>";e "<$I>" +s I=10;p "<$((I**=2))>";e "<$I>" +s I=10;p "<$((I**=1+1))>";e "<$I>" +s I=10;p "<$((I|=1))>";e "<$I>" +s I=10;p "<$((I^=1))>";e "<$I>";p "<$((I^=1))>";e "<$I>" +s I=10;p "<$((I&=2))>";e "<$I>" +s I=10;p "<$((I>>=1))>";e "<$I>" +s I=10;p "<$((I<<=1))>";e "<$I>" +s I=-1;p "<$((I>>>=1))>";e "<$I>" +e '= ASSIGN II' +s I=2;p "<$(((I+=1)-1))>";e "<$I>" +s I=4;p "<$(((I-=1)+1))>";e "<$I>" +s I=0 J=0;p "<$(((I=5)*(J=7)+1))>";e "<$I><$J>" +s I=99 J=17;p "<$(((I+=1)*(J-=2)+1))>";e "<$I><$J>" +s I=10;p "<$((I=2,I|=1))>";e "<$I>" +s I=0 J=0 Y=0 Z=0;p "<$((I=1,J=2,Y=3,Z=4,Z+=I+J+Y))>";e "<$I><$J><$Y><$Z>" +e '= POSTFIX' +s I=1;p "<$((I++))>";e "<$I>" +s I=1 J=0;p "<$((J=I++))>";e "<$I><$J>" +s I=1 J=10;p "<$((J++*I++))>";e "<$I><$J>" +s I=1 J=10;p "<$(((J++)*(I++)))>";e "<$I><$J>" +s I=1;p "<$((I--))>";e "<$I>" +s I=1 J=0;p "<$((J=I--))>";e "<$I><$J>" +s I=1 J=10;p "<$((J--*I--))>";e "<$I><$J>" +s I=1 J=10;p "<$(((J--)*(I--)))>";e "<$I><$J>" +e '= PREFIX' +s I=1;p "<$((++I))>";e "<$I>" +s I=1 J=0;p "<$((J=++I))>";e "<$I><$J>" +s I=1 J=10;p "<$((++J*++I))>";e "<$I><$J>" +s I=1 J=10;p "<$((++(J)*++(I)))>";e "<$I><$J>" +s I=1 J=10;p "<$(((++J)*(++I)))>";e "<$I><$J>" +s I=1;p "<$((--I))>";e "<$I>" +s I=1 J=0;p "<$((J=--I))>";e "<$I><$J>" +s I=2 J=10;p "<$((--J*--I))>";e "<$I><$J>" +s I=1 J=10;p "<$((--(J)*--(I)))>";e "<$I><$J>" +s I=1 J=10;p "<$(((--J)*(--I)))>";e "<$I><$J>" +e '= VAR RECUR' +s I='1 + 1';p "<$((I))>";e "<$I>" +s I='1 + 1';p "<$((+I))>";e "<$I>" +s I='1 + 1';p "<$((++I))>";e "<$I>" +s I='1 + 1';p "<$((I++))>";e "<$I>" +s I='1 + 1';p "<$((1+I))>";e "<$I>" +s I='1 + 1 * 2';p "<$((I+1))>";e "<$I>" +s I='(1 + 1) * 2';p "<$((I+1))>";e "<$I>" +s I='1 + 1' J='3 / 2';p "<$((I=I+J))>";e "<$I><$J>" +s I='1 + 1';p "<$((I=I))>";e "<$I>" +s I='1 + 1';p "<$((I=+I))>";e "<$I>" +s I='1 + 1';p "<$((I=1+I))>";e "<$I>" +s I='1 + 1 * 2';p "<$((I=I+1))>";e "<$I>" +s I='(1 + 1) * 2';p "<$((I=I+1))>";e "<$I>" +s I='1 + 1' J='3 / 2';p "<$((I+=I+J))>";e "<$I><$J>" +e '= COMMA' +e "<$(( 1 , 2 ))>" +e "<$(( 1 , 2 , 3 ))>" +e "<$(( 1 , 2 , 3 , 4 ))>" +e "<$((1,2,3,4))>" +s I='1 + 1';p "<$(( I=10 , I+=I, I=I**2, I/=3 ))>";e "<$I>" +s I1=I2=10 I2=3;p "<$((I1,I2))>";e "<$I1><$I2>" +e '= COND' +e "<$(( +0 ? 2 : 3 ))>" +e "<$((-0?2:3))>" +e "<$(( +1 ? 2 : 3 ))>" +e "<$(( 1-1 ? 2 : 3 ))>" +e "<$(( 1-0 ? 2 : 3 ))>" +e "<$((-1?2:3))>" +e "<$(( 0x1234 ? 111 : 222 ))>" +e "<$((1**2 ? 5 : 7))>" +e "<$((0**2 ? 5 : 7))>" +e "<$((0**2>=0?5:7))>" +e "<$((-1<=0**2?5:7))>" +e "<$((1<=0**2?5:7))>" +e "<$((1>2||1*0?5:7))>" +e "<$((1>2&&1*0?5:7))>" +e "<$((1<2&&1*0?5:7))>" +e "<$((1<2&&1*0+1?5:7))>" +e '-- COND .2' +e "<$(( 1 < 2 ? -1 : 1 > 2 ? 1 : 0 ))>" +e "<$((1 < 1 ? -1 : 1 > 1 ? 1 : 0))>" +e "<$((2<1?-1:2>1?1:0))>" +e "<$((4<5 ? 1 : 32))>" +e "<$((4>5 ? 1 : 32))>" +e "<$((4>(2+3) ? 1 : 32))>" +e "<$((4<(2+3) ? 1 : 32))>" +e "<$(((2+2)<(2+3) ? 1 : 32))>" +e "<$(((2+2)>(2+3) ? 1 : 32))>" +## grouping protects precedence in : parts (syntax error tests below) +e '-- COND .3' +e "<$((1-1 < 1 ? 2,4 : 1,3))>" +e "<$((0<1?2,4:(1,3)))>" +e "<$((0,1,2,0?2,4:1,3))>" +e "<$((0,1,2,1?2,4:1,3))>" +e "<$((0,1,2,0?2,4:(1,3)))>" +e "<$((0,1,2,1?2,4:(1,3)))>" +e "<$((0,1,2,0?(2,4):1,3))>" +e "<$((0,1,2,1?(2,4):1,3))>" +e "<$((0,1,2,0?(2,4):(1,3)))>" +e "<$((0,1,2,1?(2,4):(1,3)))>" +e "<$((0?2:((0,3)?1:4)))>" +e "<$((1?2:3,0?1:4))>" +e "<$((1?2:3,0?1:4?5:6))>" +e "<$((1?2:(3,0)?1:4?5:6))>" +e "<$((1?2:3,0?4,5:5,6?7,8:9,10))>" +e "<$((1?2:(3,0)?4,5:5,6?7,8:9,10))>" +e "<$((1?2:(3,0)?(4,5):5,6?7,8:9,10))>" +e "<$((1?2:(3,0)?(4,5):(5,6)?7,8:9,10))>" +e "<$((1?2:(3,0)?(4,5):(5,6)?(7,8):9,10))>" +e "<$((1?2:(3,0)?(4,5):(5,6)?(7,8):(9,10)))>" +e "<$((1?2:3,1?4,5:5,6?7,8:9,10))>" +e "<$((1?2:(3,1)?4,5:5,6?7,8:9,10))>" +e "<$((1?2:(3,1)?(4,5):5,6?7,8:9,10))>" +e "<$((1?2:(3,1)?(4,5):(5,6)?7,8:9,10))>" +e "<$((1?2:(3,1)?(4,5):(5,6)?(7,8):9,10))>" +e "<$((1?2:(3,1)?(4,5):(5,6)?(7,8):(9,10)))>" +e "<$((0?2:(3,1)?(4,5):(5,6)?(7,8):(9,10)))>" +e "<$((0?2:(3,1)?4,5:(5,6)?7,8:(9,10)))>" +e "<$((0?2:(3,0)?(4,5):(5,6)?(7,8):(9,10)))>" +e "<$((0?2:(3,0)?4,5:(5,6)?7,8:(9,10)))>" +e "<$((0?2:(3,0)?(4,5):(5,0)?(7,8):(9,10)))>" +e "<$((0?2:(3,0)?4,5:(5,0)?7,8:(9,10)))>" +e "<$((0?2:3,0?4,5:(5,0)?7,8:(9,10)))>" +e "<$((0?2:(3,0)?4,5:5,0?7,8:(9,10)))>" +e "<$((0?2:(3,0)?4,5:(5,0)?7,8:9,10))>" +e '-- COND .4' +e "<$((1?2?3?4?5:6:7:8:9))>" +e "<$((1?2?3?0?5:6:7:8:9))>" +e "<$((1?2?0?0?5:6:7:8:9))>" +e "<$((1?0?0?0?5:6:7:8:9))>" +e "<$((0?0?0?0?5:6:7:8:9))>" +e "<$((0?3+4?10:11:5+6?12:13))>" +e "<$((1?3+4?10:11:5+6?12:13))>" +e "<$((0?(3+4)?(10):(11):((5+6)?12:13)))>" +e "<$((1?(3+4)?(10):(11):((5+6)?12:13)))>" +e '-- COND .5' +e "<$((0?3+4?10:11?20+1:22*1:5+6?12:13))>" +e "<$((1?3+4?10:11?20+1:22*1:5+6?12:13))>" +e "<$((0?(3+4)?(10):(11)?(20+1):(22*1):((5+6)?12:13)))>" +e "<$((1?(3+4)?(10):(11)?(20+1):(22*1):((5+6)?12:13)))>" +e '-- COND .6' +e "<$((0?3+4?9:11?20+1:22*1:5+6?12:13))>" +e "<$((1?3+4?9:11?20+1:22*1:5+6?12:13))>" +e "<$((0?10+11?20+1?22*1?23**1:24**1:25/1?26%27:56>>1:-1:-2))>" +e "<$((1?0?20+1?22*1?23**1:24**1:25/1?26%27:56>>1:-1:-2))>" +e "<$((1?10?20+1?22*1?23**1:24**1:25/1?26%27:56>>1:-1:-2))>" +e "<$((1?10?0?22*1?23**1:24**1:25/1?26%27:56>>1:-1:-2))>" +e "<$((1?10?20?0?23**1:24**1:25/1?26%27:56>>1:-1:-2))>" +e "<$((1?10?20?22*1?0:24**1:25/1?26%27:56>>1:-1:-2))>" +e "<$((1?10?20?22*1?23**1:0:25/1?26%27:56>>1:-1:-2))>" +e "<$((1?10?20?22*1?23**1:24**1:0?26%27:56>>1:-1:-2))>" +e "<$((1?10?20?22*1?23**1:24**1:25/1?0:56>>1:-1:-2))>" +e '-- COND .7' +s I1=2 I2=3 I3=4 I4=5;\ + p "<$(( (I1 < I2) ? (I2 < I3) ? I3 *= I3 : (I2 *= I2) : (I1 *= I1)))>";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ + p "<$(( (I1 < I2) ? ((I2 < I3) ? I3 *= I3 : (I2 *= I2)) : (I1 *= I1)))>";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ + p "<$((((I1<I2)?((I2<I3)?(I3*=I3):(I2*=I2)):(I1*=I1))))>";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ +p "<$(((I1<I2)?(I2<I3)?(I3<I4)?I4*=I4:(I3*=I3):(I2*=I2):(I1*=I1)))>";\ + e "<$I1><$I2><$I3><$I4><$I5>" +# only first +s I1=2 I2=3 I3=4 I4=5;\ + p "<$(( (I1 < I2) ? (I2 > I3) ? I3 *= I3 : (I2 *= I2) : (I1 *= I1)))>";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ + p "<$(((I1<I2)?(I2>I3)?I3*=I3:(I2*=I2):(I1*=I1)))>";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ + p "<$(( (I1 < I2) ? ((I2 > I3) ? I3 *= I3 : (I2 *= I2)) : (I1 *= I1)))>";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ + p "<$(( ((I1 < I2) ? ((I2 > I3) ? (I3 *= I3):(I2 *= I2)):(I1 *= I1))))>";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ +p "<$(((I1<I2)?(I2>I3)?(I3>I4)?I4*=I4:(I3*=I3):(I2*=I2):(I1*=I1)))>";\ + e "<$I1><$I2><$I3><$I4>" +# last not etc. +s I1=2 I2=3 I3=4 I4=5;\ +p "<$(((I1<I2)?(I2<I3)?(I3>I4)?I4*=I4:(I3*=I3):(I2*=I2):(I1*=I1)))>";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ +p "<$(((I1<I2)?(I2>I3)?(I3<I4)?I4*=I4:(I3*=I3):(I2*=I2):(I1*=I1)))>";\ + e "<$I1><$I2><$I3><$I4><$I5>" +s I1=2 I2=3 I3=4 I4=5;\ +p "<$(((I1>I2)?(I2<I3)?(I3<I4)?I4*=I4:(I3*=I3):(I2*=I2):(I1*=I1)))>";\ + e "<$I1><$I2><$I3><$I4><$I5>" +e '-- COND .8' +s I=0;p "<$((1?I=2:(I=3),8,10))>";e "<$I>" +s I=0;p "<$((1?20:(I+=2)))>";e "<$I>" +s I=0;p "<$((1?I+=10:(I+=2)))>";e "<$I>" +s I=0;p "<$((0?I+=2:20))>";e "<$I>" +s I=0;p "<$((0?I+=2:(I+=10)))>";e "<$I>" +s I=0;p "<$((0?(I+=2):(20)))>";e "<$I>" +s I=0;p "<$((0?(I+=2):(I+=20)))>";e "<$I>" +e '-- COND .9' +s I1=+E+ I2=1+1;p "<$((0?I1:I2))>";e "<$I1><$I2>" +s I1=1+1 I2=+E+;p "<$((1?I1:I2))>";e "<$I1><$I2>" +s I1=+E+ I2=1+1;p "<$((0?I1=1:(I2=2)))>";e "<$I1><$I2>" +s I1=1+1 I2=+E+;p "<$((1?I1=1:(I2=2)))>";e "<$I1><$I2>" +s I1=+E+ I2=1+1;p "<$((0?I1*=I1:(I2*=I2)))>";e "<$I1><$I2>" +s I1=1+1 I2=+E+;p "<$((1?I1*=I1:(I2*=I2)))>";e "<$I1><$I2>" +e '-- COND .10' +s I1=+E+ I2=+E+ I3=+E+ I4=-1;p "<$((0?I1?I2:I3:I4))>";e "<$I1><$I2><$I3><$I4>" +s I1=1 I2=2 I3=+E+ I4=+E+;p "<$((1?I1?I2:I3:I4))>";e "<$I1><$I2><$I3><$I4>" +s I1=0 I2=+E+ I3=3 I4=+E+;p "<$((1?I1?I2:I3:I4))>";e "<$I1><$I2><$I3><$I4>" +e '= WILD I' +e "<$(( 3 + ( 11 ) ))>" +e "<$((1 + (2 - 2)))>" +e "<$((1 + (2 - 2)))>" +e "<$(( (( 3 / 3 )) + ((1*1*1)) - (( 7 % 6 ))))>" +e "<$(( 3+((2 * 2))/6 ))>" +e "<$(( 1 + 1 - 3 * 3 + 99-88 / 17))>" +e "<$(( 1 << 2 % 1+2 * 4 - (2 + 2 + 1) * 6 / 7 + 4 * 2 + (81/9)))>" +s I1=I2=10 I2=3;p "<$((I1 + I2))>";e "<$I1><$I2>" +s I1=I2=10 I2=3;p "<$((I1 * I2))>";e "<$I1><$I2>" +s I1=I2=10 I2=3;p "<$((I1 % I2))>";e "<$I1><$I2>" +e '= WILD II' +s I=10;p "<$((3+(3*(I=11))))>";e "<$I>" +s I=10;p "<$((3+(3*(I++))))>";e "<$I>" +s I=10;p "<$((3+(3*(I=11,I++))))>";e "<$I>" +s I=10;p "<$((3+(3*(I=11,++I))))>";e "<$I>" +s I=10;p "<$((3+(3*(I=11,++++I))))>";e "<$I>" +s I=10;p "<$((3+(3*(I=11,+++++++++++++++++++++++-+++++I))))>";e "<$I>" +e "<$((3+(3*(+++++++++++++++++++++++-+++++10))))>" +s I=10;p "<$(( +10 + + +I ))>";e "<$I>" +s I=10;p "<$(( +10 + ++I ))>";e "<$I>" +s I=10;p "<$(( +10 ++ +I ))>";e "<$I>" +s I=10;p "<$(( +10 +++ I ))>";e "<$I>" +s I=10;p "<$(( +10+++I ))>";e "<$I>" +s I=10;p "<$((+10++I))>";e "<$I>" +s I=10;p "<$((+10 + + + ++++ +I))>";e "<$I>" +e "<$(( +10 + + + ++++ +11 ))>" +e "<$(( +10 + + + ++++ ++11 ))>" +e "<$((+10++++++++11))>" +e '= WILD RECUR' # (some yet) +s I1=I2=10 I2=5;p "<$((I1+=I2))>";e "<$I1><$I2>" +s I1=I2=10 I2=5 I3=I2+=1;p "<$((I1))>";e "<$I1><$I2><$I3>" +s I1=I2=10 I2=5 I3=I2+=1;p "<$((I1,I3))>";e "<$I1><$I2><$I3>" +s I1=I2=10 I2=5 I3=I2+=1;p "<$((I1+I3))>";e "<$I1><$I2><$I3>" +s I1=I2=10 I2=5 I3=I2+=1;p "<$((I1?I1:I3))>";e "<$I1><$I2><$I3>" +s I1=I2=0 I2=5 I3=I2+=1;p "<$((I1?I1:I3))>";e "<$I1><$I2><$I3>" +s I1=I1=10 I2=5 I3=I2+=1;p "<$((I1=0?I1:I3))>";e "<$I1><$I2><$I3>" +s I1=I1=10 I2=5 I3=I2+=1;p "<$((I1=1?I1:I3))>";e "<$I1><$I2><$I3>" +s I1=I2='(I2=10)+1' I2=5 I3=I2+=1;p "<$((I1,I3))>";e "<$I1><$I2><$I3>" +s I1=I2='(I2=(I2=10)+1)' I2=5 I3=I2+=1;p "<$((I1,I3))>";e "<$I1><$I2><$I3>" +s I1=I2=10 I2=5 I3=I2+=1;p "<$((I1+I3*I1*I3/I1%I3))>";e "<$I1><$I2><$I3>" +s I1=I2=10 I2=5 I3=I2+=1;p "<$((I1+I3*I1*I3/I1%I3))>";e "<$I1><$I2><$I3>" +s I1=I2=+E+ I2=5;p "<$((I1=10))>";e "<$I1><$I2>" +s I1=I2=+E+ I2=5;p "<$((0?I1:++I2))>";e "<$I1><$I2>" +s I1=I2=10 I2=5;p "<$((I2,(1?I1:++I2)))>";e "<$I1><$I2>" +s I1=5 I2=10 I3=20;p "<$((I1-=5,1?I2:I3))>";e "<$I1><$I2><$I3>" +s I1=5 Ix=6 I2=10 I3=20;p "<$((I1=Ix,1?I2:I3))>";e "<$I1><$I2><$I3>" +s I1=5 Ix=6 I2=10 I3=20;p "<$((I1=Ix?I2:I3))>";e "<$I1><$I2><$I3>" +s I1=5 Ix=6 I2=10 I3=20;p "<$((I1*=Ix?I2:I3))>";e "<$I1><$I2><$I3>" +s I1=5 Ix=6 I2=10 I3=20;p "<$((0,I1*=Ix?I2:I3))>";e "<$I1><$I2><$I3>" +s I1=5 Ix=6 I2=10 I3=20;p "<$((I1*=Ix?I2:I3,Ix=21,I1*=Ix?I2:I3))>";e "<$I1><$I2><$I3>" diff --git a/shell/hush.c b/shell/hush.c index 051b123e78..be01ed035c 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -6475,7 +6475,7 @@ static NOINLINE int encode_then_append_var_plusminus(o_string *output, int n, } #if ENABLE_FEATURE_SH_MATH -static arith_t expand_and_evaluate_arith(const char *arg, const char **errmsg_p) +static arith_t expand_and_evaluate_arith(const char *arg, char **errmsg_p) { arith_state_t math_state; arith_t res; @@ -6489,8 +6489,13 @@ static arith_t expand_and_evaluate_arith(const char *arg, const char **errmsg_p) free(exp_str); if (errmsg_p) *errmsg_p = math_state.errmsg; - if (math_state.errmsg) + if (math_state.errmsg) { msg_and_die_if_script(math_state.errmsg); +# if ENABLE_FEATURE_SH_MATH_ERROR_TRACK + if (errmsg_p == NULL) + free(math_state.errmsg); +# endif + } return res; } #endif @@ -6814,11 +6819,15 @@ static NOINLINE int expand_one_var(o_string *output, int n, */ arith_t beg, len; unsigned vallen; - const char *errmsg; + char *errmsg; beg = expand_and_evaluate_arith(exp_word, &errmsg); - if (errmsg) + if (errmsg) { +# if ENABLE_FEATURE_SH_MATH_ERROR_TRACK + free(errmsg); +# endif goto empty_result; + } debug_printf_varexp("beg:'%s'=%lld\n", exp_word, (long long)beg); *p++ = SPECIAL_VAR_SYMBOL; exp_word = p; @@ -6838,8 +6847,12 @@ static NOINLINE int expand_one_var(o_string *output, int n, goto empty_result; } len = expand_and_evaluate_arith(exp_word, &errmsg); - if (errmsg) + if (errmsg) { +# if ENABLE_FEATURE_SH_MATH_ERROR_TRACK + free(errmsg); +# endif goto empty_result; + } debug_printf_varexp("len:'%s'=%lld\n", exp_word, (long long)len); debug_printf_varexp("from val:'%s'\n", val); if (len < 0) { diff --git a/shell/hush_test/hush-arith/bigbadbison.right b/shell/hush_test/hush-arith/bigbadbison.right new file mode 100644 index 0000000000..a6446c81cd --- /dev/null +++ b/shell/hush_test/hush-arith/bigbadbison.right @@ -0,0 +1,880 @@ += BASE +<0> +<0> +<1> +<0> +<0> +<0> +<0> +<1> +<1> +<1> +<0> +<10> +<9191919191919> +<13> +<11> +<1023> +<1295> +<1295> +<9322365> +<16242915> +<10> +<33> +<10> +<33> +<1> +<1> +<1> +<33> +<33> +<33> +<33> += UNA PLUS/MINUS +<0> +<0> +<1> +<1> +<4221> +<16929> +<16242915> +<16242915> +<1> +<1> +<1> +<0> +<0> +<-1> +<-1> +<-4221> +<-16929> +<-16242915> +<-16242915> +<-1> +<-1> +<-1> +<-1> +<1> +<-1> += UNA ! +<1> +<1> +<0> +<0> +<1> +<0> += UNA ~ +<-1> +<-1> +<-2> +<-2> +<-2276> +<0> +<0> +<-1> +<-1> +<-1> +<-1> += BIN + +<0> +<0> +<1> +<1> +<1> +<1> +<2> +<2> +<2> +<-2> +<3333> +<3333> +<33> +<-33> +<-33> +<-1> +<33> +<-33> +<-33> +<1> +<9223372036854775807> +<-9223372036854775807> +<9223372036854775806> +<-9223372036854775808> +<-2> +<0> +<9223372036854775797> +<-9223372036854775797> +<9223372036854775796> +<-9223372036854775798> +<-12> +<10> += BIN - +<0> +<0> +<-1> +<-1> +<1> +<1> +<0> +<0> +<0> +<0> +<-1111> +<1111> +<-1> +<1> +<1> +<129> +<1> +<-1> +<-1> +<129> +<-9223372036854775807> +<9223372036854775807> +<-9223372036854775808> +<9223372036854775806> +<0> +<-2> +<-9223372036854775797> +<9223372036854775797> +<-9223372036854775798> +<9223372036854775796> +<10> +<-12> += BIN * +<0> +<0> +<0> +<0> +<0> +<0> +<1> +<1> +<2468642> +<2468642> +<272> +<272> +<272> +<-4160> +<272> +<272> +<272> +<-4160> +<-9223372036854775808> +<-9223372036854775808> +<-9223372036854775807> +<9223372036854775807> +<1> +<-1> +<-9223372036854775808> +<-9223372036854775808> +<-9223372036854775797> +<9223372036854775797> +<11> +<-11> += BIN / +<0> +<1> +<1> +<0> +<2> +<0> +<0> +<0> +<0> +<1> +<1> +<1> +<-1> +<2> +<3> +<1> +<1> +<0> +<0> +<-9223372036854775808> +<-9223372036854775808> +<-9223372036854775807> +<9223372036854775807> +<1> +<-1> +<838488366986797800> +<-838488366986797800> +<-838488366986797800> +<838488366986797800> +<0> +<0> += BIN % +<0> +<0> +<0> +<1111> +<0> +<16> +<-16> +<-16> +<64> +<1> +<-1> +<-1> +<1> +<0> +<0> +<1> +<0> +<3> +<-1> +<0> +<0> +<0> +<0> +<0> +<0> +<-8> +<-8> +<7> +<7> +<-1> +<-1> += BIN << +<0> +<0> +<0> +<0> +<1> +<1> +<2> +<2> +<78179674781384704> +<18639486976> +<2097152> +<-2251799813685248> +<-2251799813685248> +<0> +<1114112> +<-4785074604081152> +<-4785074604081152> +<65> +<64> +<0> +<0> +<-9223372036854775808> +<-2> +<-9223372036854775808> +<-2> +<0> +<0> +<-9007199254740992> +<-2048> +<-9007199254740992> +<-2048> += BIN >> +<0> +<0> +<0> +<0> +<1> +<1> +<0> +<0> +<0> +<0> +<0> +<0> +<0> +<0> +<0> +<-1> +<-1> +<131071> +<0> +<0> +<-1> +<-1> +<65> +<64> +<-1> +<-4611686018427387904> +<0> +<4611686018427387903> +<-1> +<-1> +<-1024> +<-4503599627370496> +<1023> +<4503599627370495> +<-1> +<-1> +<9007199254740991> += BIN ** +<0> +<2> +<4> +<8> +<16> +<10000> +<10000000000> +<100005> +<10000000000> += LOG OR +<0> +<0> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> += LOG AND +<0> +<0> +<1> +<1> +<1> +<1> +<1> +<1> +<0> +<0> +<1> +<1> +<1> +<1> +<1> +<1> +<0> +<0> += BIN BIT_OR +<0> +<0> +<1> +<1> +<1> +<1> +<1> +<1> +<3327> +<3327> +<17> +<-1> +<-1> +<-1> +<17> +<-1> +<-1> +<-63> +<1088> +<-1> +<-9223372036854775807> +<-1> +<9223372036854775807> +<-1> +<-1> +<-11> +<-9223372036854775797> +<-1> +<9223372036854775807> +<-1> +<-1> += BIN BIT_XOR +<0> +<0> +<1> +<1> +<1> +<1> +<0> +<0> +<3321> +<3321> +<1> +<31> +<31> +<-1> +<1> +<31> +<31> +<-127> +<1088> +<9223372036854775807> +<-9223372036854775807> +<-9223372036854775808> +<9223372036854775806> +<0> +<-2> +<9223372036854775797> +<-9223372036854775797> +<-9223372036854775798> +<9223372036854775796> +<10> +<-12> += BIN BIT_AND +<0> +<0> +<0> +<0> +<0> +<0> +<1> +<1> +<6> +<6> +<16> +<-32> +<-32> +<0> +<16> +<-32> +<-32> +<64> +<0> +<-9223372036854775808> +<0> +<9223372036854775807> +<1> +<-1> +<1> +<-9223372036854775808> +<0> +<9223372036854775797> +<11> +<-11> +<11> += BIN EQ +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<0> +<0> +<0> +<0> +<0> +<0> +<0> +<1> += BIN NE +<0> +<0> +<0> +<0> +<0> +<0> +<0> +<0> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<0> += BIN LE +<1> +<1> +<1> +<1> +<0> +<1> +<1> +<1> +<0> +<1> +<1> +<1> +<0> +<1> +<1> +<0> +<1> += BIN GE +<1> +<1> +<1> +<0> +<1> +<1> +<1> +<0> +<1> +<1> +<1> +<0> +<1> +<1> +<1> +<1> +<0> += BIN LT +<0> +<0> +<0> +<1> +<0> +<0> +<0> +<1> +<0> +<0> +<0> +<1> +<0> +<0> +<0> +<0> +<1> += BIN GT +<0> +<0> +<0> +<0> +<1> +<0> +<0> +<0> +<1> +<0> +<0> +<0> +<1> +<0> +<0> +<1> +<0> += PRECEDENCE I +<6> +<2> +<0> +<2> +<-1> +<1> +<1> +<2> +<8> +<7> +<12> +<5> +<10> +<1> +<72> +<48> +<288> +<1> +<3> +<1> +<4> +<76> +<1> +<1> +<1> +<1> +<2> +<2> += PARENS +<6> +<6> +<-4> +<-4> +<2> +<2> +<0> +<0> +<-3> +<-3> +<0> +<0> +<12> +<12> +<10> +<10> +<12> +<48> +<1> +<1> +<6> +<6> +<9> +<9> +<20> +<20> +<21> +<21> +<36864> +<36864> +<6> +<6> +<9> +<9> +<9> +<9> +<0> +<0> +<38> +<38> +<2> +<2> +<32> +<32> +<0> +<0> +<24> +<24> += ASSIGN I +<3><3> +<3><3> +<3><3> +<11><11> +<9><9> +<10><10> +<20><20> +<10><10> +<5><5> +<0><0> +<0><0> +<10><10> +<100><100> +<100><100> +<11><11> +<11><11> +<10><10> +<2><2> +<5><5> +<20><20> +<9223372036854775807><9223372036854775807> += ASSIGN II +<2><3> +<4><3> +<36><5><7> +<1501><100><15> +<3><3> +<10><1><2><3><10> += POSTFIX +<1><2> +<1><2><1> +<10><2><11> +<10><2><11> +<1><0> +<1><0><1> +<10><0><9> +<10><0><9> += PREFIX +<2><2> +<2><2><2> +<22><2><11> +<10><1><10> +<22><2><11> +<0><0> +<0><0><0> +<9><1><9> +<10><1><10> +<0><0><9> += VAR RECUR +<2><1 + 1> +<2><1 + 1> +<3><3> +<2><3> +<3><1 + 1> +<4><1 + 1 * 2> +<5><(1 + 1) * 2> +<3><3><3 / 2> +<2><2> +<2><2> +<3><3> +<4><4> +<5><5> +<5><5><3 / 2> += COMMA +<2> +<3> +<4> +<4> +<133><133> +<10><I2=10><10> += COND +<3> +<3> +<2> +<3> +<2> +<2> +<111> +<5> +<7> +<5> +<5> +<7> +<7> +<7> +<7> +<5> +-- COND .2 +<-1> +<0> +<1> +<1> +<32> +<32> +<1> +<1> +<32> +-- COND .3 +<3> +<4> +<3> +<3> +<3> +<4> +<3> +<3> +<3> +<4> +<1> +<4> +<5> +<2> +<10> +<10> +<10> +<10> +<10> +<2> +<10> +<10> +<10> +<10> +<10> +<2> +<5> +<5> +<8> +<8> +<10> +<10> +<10> +<10> +<10> +-- COND .4 +<5> +<6> +<7> +<8> +<9> +<12> +<10> +<12> +<10> +-- COND .5 +<12> +<10> +<12> +<10> +-- COND .6 +<12> +<9> +<-2> +<-1> +<23> +<26> +<24> +<0> +<23> +<23> +<23> +-- COND .7 +<16><2><3><16><5> +<16><2><3><16><5> +<16><2><3><16><5> +<25><2><3><4><25><> +<9><2><9><4><5> +<9><2><9><4><5> +<9><2><9><4><5> +<9><2><9><4><5> +<9><2><9><4><5> +<16><2><3><16><5> +<9><2><9><4><5><> +<4><4><3><4><5><> +-- COND .8 +<10><2> +<20><0> +<10><10> +<20><0> +<10><10> +<20><0> +<20><20> +-- COND .9 +<2><+E+><1+1> +<2><1+1><+E+> +<2><+E+><2> +<1><1><+E+> +<4><+E+><4> +<4><4><+E+> +-- COND .10 +<-1><+E+><+E+><+E+><-1> +<2><1><2><+E+><+E+> +<3><0><+E+><3><+E+> += WILD I +<14> +<1> +<1> +<1> +<3> +<87> +<2097152> +<20><I2=10><10> +<100><I2=10><10> +<0><I2=10><10> += WILD II +<36><11> +<33><11> +<36><12> +<39><12> +<39><12> +<-33><12> +<-27> +<20><10> +<21><11> +<20><10> +<21><11> +<21><11> +<20><10> +<20><10> +<21> +<21> +<21> += WILD RECUR +<20><20><10> +<10><I2=10><10><I2+=1> +<11><I2=10><11><I2+=1> +<21><I2=10><11><I2+=1> +<10><I2=10><10><I2+=1> +<1><I2=0><1><I2+=1> +<6><6><6><I2+=1> +<10><10><5><I2+=1> +<12><I2=(I2=10)+1><12><I2+=1> +<12><I2=(I2=(I2=10)+1)><12><I2+=1> +<10><I2=10><11><I2+=1> +<10><I2=10><11><I2+=1> +<10><10><5> +<6><I2=+E+><6> +<10><I2=10><10> +<10><0><10><20> +<10><6><10><20> +<10><10><10><20> +<50><50><10><20> +<50><50><10><20> +<500><500><10><20> diff --git a/shell/hush_test/hush-arith/bigbadbison.tests b/shell/hush_test/hush-arith/bigbadbison.tests new file mode 100755 index 0000000000..2b15fda7c9 --- /dev/null +++ b/shell/hush_test/hush-arith/bigbadbison.tests @@ -0,0 +1,914 @@ +# make this work with (ba)sh \ +command -v shopt && shopt -s expand_aliases;\ +alias p=printf;alias e=echo;alias s=export +s I=10 J=33 +e '= BASE' +e "<$(())>" +e "<$(( ))>" +e "<$((1))>" +e "<$((0))>" +e "<$((0x0))>" +e "<$((0X0))>" +e "<$((000))>" +e "<$((000000000000001))>" +e "<$((2#00000000000000000000000000000000000001))>" +e "<$((0X00000000000000000000000000000000000000000001))>" +e "<$((999999999999999999999999999999999999999999999))>" +e "<$(( 10 ))>" +e "<$((9191919191919))>" +e "<$((0xD))>" +e "<$((013))>" +e "<$((32#VV))>" +e "<$((36#ZZ))>" +e "<$((36#zz))>" +e "<$(( 64#zzZZ ))>" +e "<$((64#ZZzz))>" +e "<$((I))>" +e "<$((J))>" +e "<$(( I ))>" +e "<$(( J ))>" +e "<$(( (1) ))>" +e "<$((((1))))>" +e "<$(((((1)))))>" +e "<$(( (J) ))>" +e "<$((((J))))>" +e "<$(((((J)))))>" +e "<$(( ( ( ( J ) ) ) ))>" +e '= UNA PLUS/MINUS' +e "<$((+0))>" +e "<$(( + 0 ))>" +e "<$(( +1))>" +e "<$((+ 1 ))>" +e "<$(( + 4221 ))>" +e "<$(( +0x4221 ))>" +e "<$(( + 64#ZZzz ))>" +e "<$(( +64#ZZzz ))>" +e "<$((+ (1) ))>" +e "<$((+((1))))>" +e "<$((+(((1)))))>" +e "<$((-0))>" +e "<$(( - 0 ))>" +e "<$(( -1))>" +e "<$((- 1 ))>" +e "<$(( - 4221 ))>" +e "<$(( -0x4221 ))>" +e "<$(( - 64#ZZzz ))>" +e "<$(( -64#ZZzz ))>" +e "<$((- (1) ))>" +e "<$((-((1))))>" +e "<$((-(((1)))))>" +e "<$((+ -(1) ))>" +e "<$((+(-(-1))))>" +e "<$((+(-(-(-1)))))>" +e '= UNA !' +e "<$((!0))>" +e "<$((! 00000000))>" +e "<$((!1))>" +e "<$((! 0x00001))>" +e "<$((! - 0))>" +e "<$((!-1))>" +e '= UNA ~' +e "<$((~0))>" +e "<$((~ 00000000))>" +e "<$((~1))>" +e "<$((~ 0x00001))>" +e "<$((~ 64#zz))>" +e "<$((~-1))>" +e "<$((~ - 1))>" +e "<$((~-0))>" +e "<$((~ - 0))>" +e "<$((~(-0)))>" +e "<$((~((- 0))))>" +e '= BIN +' +e "<$((0+0))>" +e "<$(( 0 + 0 ))>" +e "<$((0+1))>" +e "<$(( 0 + 1 ))>" +e "<$((1+0))>" +e "<$(( 1 + 0 ))>" +e "<$((1+1))>" +e "<$(( 1 + 1 ))>" +e "<$(( (1 + 1) ))>" +e "<$(((((((-1)))) + (((-1))))))>" +e "<$((1111+2222))>" +e "<$((2222+1111))>" +e "<$(( +0x10 + +0x11 ))>" +e "<$(( -0x10 + -0x11 ))>" +e "<$(( -0x10 + -0x11 ))>" +e "<$(( +64#10 + -64#11 ))>" +e "<$(( +0x11 + +0x10 ))>" +e "<$(( -0x11 + -0x10 ))>" +e "<$(( -0x11 + -0x10 ))>" +e "<$(( +64#11 + -64#10 ))>" +e "<$((0x8000000000000000+-1))>" +e "<$((0x8000000000000000+1))>" +e "<$((0x7FFFFFFFFFFFFFFF+-1))>" +e "<$((0x7FFFFFFFFFFFFFFF+1))>" +e "<$((0xFFFFFFFFFFFFFFFF+-1))>" +e "<$((0xFFFFFFFFFFFFFFFF+1))>" +e "<$((0x8000000000000000+-11))>" +e "<$((0x8000000000000000+11))>" +e "<$((0x7FFFFFFFFFFFFFFF+-11))>" +e "<$((0x7FFFFFFFFFFFFFFF+11))>" +e "<$((0xFFFFFFFFFFFFFFFF+-11))>" +e "<$((0xFFFFFFFFFFFFFFFF+11))>" +e '= BIN -' +e "<$((0-0))>" +e "<$(( 0 - 0 ))>" +e "<$((0-1))>" +e "<$(( 0 - 1 ))>" +e "<$((1-0))>" +e "<$(( 1 - 0 ))>" +e "<$((1-1))>" +e "<$(( 1 - 1 ))>" +e "<$(( (1 - 1) ))>" +e "<$(((((((+1)))) - (((+1))))))>" +e "<$((1111-2222))>" +e "<$((2222-1111))>" +e "<$(( +0x10 - +0x11 ))>" +e "<$(( -0x10 - -0x11 ))>" +e "<$(( -0x10 - -0x11 ))>" +e "<$(( +64#10 - -64#11 ))>" +e "<$(( +0x11 - +0x10 ))>" +e "<$(( -0x11 - -0x10 ))>" +e "<$(( -0x11 - -0x10 ))>" +e "<$(( +64#11 - -64#10 ))>" +e "<$((0x8000000000000000--1))>" +e "<$((0x8000000000000000-1))>" +e "<$((0x7FFFFFFFFFFFFFFF--1))>" +e "<$((0x7FFFFFFFFFFFFFFF-1))>" +e "<$((0xFFFFFFFFFFFFFFFF--1))>" +e "<$((0xFFFFFFFFFFFFFFFF-1))>" +e "<$((0x8000000000000000--11))>" +e "<$((0x8000000000000000-11))>" +e "<$((0x7FFFFFFFFFFFFFFF--11))>" +e "<$((0x7FFFFFFFFFFFFFFF-11))>" +e "<$((0xFFFFFFFFFFFFFFFF--11))>" +e "<$((0xFFFFFFFFFFFFFFFF-11))>" +e '= BIN *' +e "<$((0*0))>" +e "<$(( 0 * 0 ))>" +e "<$((0*1))>" +e "<$(( 0 * 1 ))>" +e "<$((1*0))>" +e "<$(( 1 * 0 ))>" +e "<$((1*1))>" +e "<$(( 1 * 1 ))>" +e "<$((1111*2222))>" +e "<$((2222*1111))>" +e "<$(( +0x10 * +0x11 ))>" +e "<$(( -0x10 * -0x11 ))>" +e "<$(( -0x10 * -0x11 ))>" +e "<$(( +64#10 * -64#11 ))>" +e "<$(( +0x11 * +0x10 ))>" +e "<$(( -0x11 * -0x10 ))>" +e "<$(( -0x11 * -0x10 ))>" +e "<$(( +64#11 * -64#10 ))>" +e "<$((0x8000000000000000*-1))>" +e "<$((0x8000000000000000*1))>" +e "<$((0x7FFFFFFFFFFFFFFF*-1))>" +e "<$((0x7FFFFFFFFFFFFFFF*1))>" +e "<$((0xFFFFFFFFFFFFFFFF*-1))>" +e "<$((0xFFFFFFFFFFFFFFFF*1))>" +e "<$((0x8000000000000000*-11))>" +e "<$((0x8000000000000000*11))>" +e "<$((0x7FFFFFFFFFFFFFFF*-11))>" +e "<$((0x7FFFFFFFFFFFFFFF*11))>" +e "<$((0xFFFFFFFFFFFFFFFF*-11))>" +e "<$((0xFFFFFFFFFFFFFFFF*11))>" +e '= BIN /' +e "<$(( 0 / 1 ))>" +e "<$((1/1))>" +e "<$(( 1 / 1 ))>" +e "<$((1111/2222))>" +e "<$((2222/1111))>" +e "<$(( +0x10 / +0x11 ))>" +e "<$(( -0x10 / -0x11 ))>" +e "<$(( -0x10 / -0x11 ))>" +e "<$(( +64#10 / -64#11 ))>" +e "<$(( +0x11 / +0x10 ))>" +e "<$(( -0x11 / -0x10 ))>" +e "<$(( -0x11 / -0x10 ))>" +e "<$(( +64#11 / -64#10 ))>" +e "<$((2/1))>" +e "<$((3/1))>" +e "<$((3/2))>" +e "<$((3/3))>" +e "<$((3/4))>" +e "<$((-1/4))>" +e "<$((0x8000000000000000/-1))>" +e "<$((0x8000000000000000/1))>" +e "<$((0x7FFFFFFFFFFFFFFF/-1))>" +e "<$((0x7FFFFFFFFFFFFFFF/1))>" +e "<$((0xFFFFFFFFFFFFFFFF/-1))>" +e "<$((0xFFFFFFFFFFFFFFFF/1))>" +e "<$((0x8000000000000000/-11))>" +e "<$((0x8000000000000000/11))>" +e "<$((0x7FFFFFFFFFFFFFFF/-11))>" +e "<$((0x7FFFFFFFFFFFFFFF/11))>" +e "<$((0xFFFFFFFFFFFFFFFF/-11))>" +e "<$((0xFFFFFFFFFFFFFFFF/11))>" +e '= BIN %' +e "<$(( 0 % 1 ))>" +e "<$((1%1))>" +e "<$(( 1 % 1 ))>" +e "<$((1111%2222))>" +e "<$((2222%1111))>" +e "<$(( +0x10 % +0x11 ))>" +e "<$(( -0x10 % -0x11 ))>" +e "<$(( -0x10 % -0x11 ))>" +e "<$(( +64#10 % -64#11 ))>" +e "<$(( +0x11 % +0x10 ))>" +e "<$(( -0x11 % -0x10 ))>" +e "<$(( -0x11 % -0x10 ))>" +e "<$(( +64#11 % -64#10 ))>" +e "<$((2%1))>" +e "<$((3%1))>" +e "<$((3%2))>" +e "<$((3%3))>" +e "<$((3%4))>" +e "<$((-1%4))>" +e "<$((0x8000000000000000%-1))>" +e "<$((0x8000000000000000%1))>" +e "<$((0x7FFFFFFFFFFFFFFF%-1))>" +e "<$((0x7FFFFFFFFFFFFFFF%1))>" +e "<$((0xFFFFFFFFFFFFFFFF%-1))>" +e "<$((0xFFFFFFFFFFFFFFFF%1))>" +e "<$((0x8000000000000000%-11))>" +e "<$((0x8000000000000000%11))>" +e "<$((0x7FFFFFFFFFFFFFFF%-11))>" +e "<$((0x7FFFFFFFFFFFFFFF%11))>" +e "<$((0xFFFFFFFFFFFFFFFF%-11))>" +e "<$((0xFFFFFFFFFFFFFFFF%11))>" +e '= BIN <<' +e "<$((0<<0))>" +e "<$(( 0 << 0 ))>" +e "<$((0<<1))>" +e "<$(( 0 << 1 ))>" +e "<$((1<<0))>" +e "<$(( 1 << 0 ))>" +e "<$((1<<1))>" +e "<$(( 1 << 1 ))>" +e "<$((1111<<2222))>" +e "<$((2222<<1111))>" +e "<$(( +0x10 << +0x11 ))>" +e "<$(( -0x10 << -0x11 ))>" +e "<$(( -0x10 << -0x11 ))>" +e "<$(( +64#10 << -64#11 ))>" +e "<$(( +0x11 << +0x10 ))>" +e "<$(( -0x11 << -0x10 ))>" +e "<$(( -0x11 << -0x10 ))>" +e "<$(( +64#11 << -64#10 ))>" +e "<$(( +64 << +1024 ))>" +e "<$((0x8000000000000000<<-1))>" +e "<$((0x8000000000000000<<1))>" +e "<$((0x7FFFFFFFFFFFFFFF<<-1))>" +e "<$((0x7FFFFFFFFFFFFFFF<<1))>" +e "<$((0xFFFFFFFFFFFFFFFF<<-1))>" +e "<$((0xFFFFFFFFFFFFFFFF<<1))>" +e "<$((0x8000000000000000<<-11))>" +e "<$((0x8000000000000000<<11))>" +e "<$((0x7FFFFFFFFFFFFFFF<<-11))>" +e "<$((0x7FFFFFFFFFFFFFFF<<11))>" +e "<$((0xFFFFFFFFFFFFFFFF<<-11))>" +e "<$((0xFFFFFFFFFFFFFFFF<<11))>" +e '= BIN >>' +e "<$((0>>0))>" +e "<$(( 0 >> 0 ))>" +e "<$((0>>1))>" +e "<$(( 0 >> 1 ))>" +e "<$((1>>0))>" +e "<$(( 1 >> 0 ))>" +e "<$((1>>1))>" +e "<$(( 1 >> 1 ))>" +e "<$((1>>>1))>" +e "<$(( 1 >>> 1 ))>" +e "<$((1111>>2222))>" +e "<$((2222>>1111))>" +e "<$((1111>>>2222))>" +e "<$((2222>>>1111))>" +e "<$(( +0x10 >> +0x11 ))>" +e "<$(( -0x10 >> -0x11 ))>" +e "<$(( -0x10 >> -0x11 ))>" +e "<$(( -0x10 >>> -0x11 ))>" +e "<$(( +64#10 >> -64#11 ))>" +e "<$(( +0x11 >> +0x10 ))>" +e "<$(( -0x11 >> -0x10 ))>" +e "<$(( -0x11 >> -0x10 ))>" +e "<$(( +64#11 >> -64#10 ))>" +e "<$(( +64 >> +1024 ))>" +e "<$((0x8000000000000000>>-1))>" +e "<$((0x8000000000000000>>1))>" +e "<$((0x7FFFFFFFFFFFFFFF>>-1))>" +e "<$((0x7FFFFFFFFFFFFFFF>>1))>" +e "<$((0xFFFFFFFFFFFFFFFF>>-1))>" +e "<$((0xFFFFFFFFFFFFFFFF>>1))>" +e "<$((0x8000000000000000>>-11))>" +e "<$((0x8000000000000000>>11))>" +e "<$((0x7FFFFFFFFFFFFFFF>>-11))>" +e "<$((0x7FFFFFFFFFFFFFFF>>11))>" +e "<$((0xFFFFFFFFFFFFFFFF>>-11))>" +e "<$((0xFFFFFFFFFFFFFFFF>>11))>" +e "<$((0xFFFFFFFFFFFFFFFF>>>11))>" +e '= BIN **' +e "<$((0**1))>" +e "<$((2**1))>" +e "<$((2**2))>" +e "<$((2**3))>" +e "<$((2**4))>" +e "<$((10**4))>" +e "<$((10**10))>" +e "<$((10**5+5))>" +e "<$((10**(5+5)))>" +e '= LOG OR' +e "<$((0||0))>" +e "<$(( 000 || 0X0 ))>" +e "<$((01 || 64#1))>" +e "<$((01 || 64#1))>" +e "<$((0x1234 || 4660))>" +e "<$((0x1234 || 011064))>" +s I=33 J=33;e "<$((I||J))>" +s I=33 J=33;e "<$(( I || J ))>" +e "<$((0||1))>" +e "<$((0||0000000000000000000000001))>" +e "<$((1||2))>" +e "<$((0x1234 || 04660))>" +e "<$((0x1234 || 0x11064))>" +s I=10 J=33;e "<$((I||J))>" +s I=-10 J=-33;e "<$((I||J))>" +s I=-33 J=-33;e "<$((I||J))>" +s I=0 J=-33;e "<$((I||J))>" +s I=33 J=0;e "<$((I||J))>" +e '= LOG AND' +e "<$((0&&0))>" +e "<$(( 000 && 0X0 ))>" +e "<$((01 && 64#1))>" +e "<$((01 && 64#1))>" +e "<$((0x1234 && 4660))>" +e "<$((0x1234 && 011064))>" +s I=33 J=33;e "<$((I&&J))>" +s I=33 J=33;e "<$(( I && J ))>" +e "<$((0&&1))>" +e "<$((0&&0000000000000000000000001))>" +e "<$((1&&2))>" +e "<$((0x1234 && 04660))>" +e "<$((0x1234 && 0x11064))>" +s I=10 J=33;e "<$((I&&J))>" +s I=-10 J=-33;e "<$((I&&J))>" +s I=-33 J=-33;e "<$((I&&J))>" +s I=0 J=-33;e "<$((I&&J))>" +s I=33 J=0;e "<$((I&&J))>" +e '= BIN BIT_OR' +e "<$((0|0))>" +e "<$(( 0 | 0 ))>" +e "<$((0|1))>" +e "<$(( 0 | 1 ))>" +e "<$((1|0))>" +e "<$(( 1 | 0 ))>" +e "<$((1|1))>" +e "<$(( 1 | 1 ))>" +e "<$((1111|2222))>" +e "<$((2222|1111))>" +e "<$(( +0x10 | +0x11 ))>" +e "<$(( -0x10 | -0x11 ))>" +e "<$(( -0x10 | -0x11 ))>" +e "<$(( +64#10 | -64#11 ))>" +e "<$(( +0x11 | +0x10 ))>" +e "<$(( -0x11 | -0x10 ))>" +e "<$(( -0x11 | -0x10 ))>" +e "<$(( +64#11 | -64#10 ))>" +e "<$(( +64 | +1024 ))>" +e "<$((0x8000000000000000|-1))>" +e "<$((0x8000000000000000|1))>" +e "<$((0x7FFFFFFFFFFFFFFF|-1))>" +e "<$((0x7FFFFFFFFFFFFFFF|1))>" +e "<$((0xFFFFFFFFFFFFFFFF|-1))>" +e "<$((0xFFFFFFFFFFFFFFFF|1))>" +e "<$((0x8000000000000000|-11))>" +e "<$((0x8000000000000000|11))>" +e "<$((0x7FFFFFFFFFFFFFFF|-11))>" +e "<$((0x7FFFFFFFFFFFFFFF|11))>" +e "<$((0xFFFFFFFFFFFFFFFF|-11))>" +e "<$((0xFFFFFFFFFFFFFFFF|11))>" +e '= BIN BIT_XOR' +e "<$((0^0))>" +e "<$(( 0 ^ 0 ))>" +e "<$((0^1))>" +e "<$(( 0 ^ 1 ))>" +e "<$((1^0))>" +e "<$(( 1 ^ 0 ))>" +e "<$((1^1))>" +e "<$(( 1 ^ 1 ))>" +e "<$((1111^2222))>" +e "<$((2222^1111))>" +e "<$(( +0x10 ^ +0x11 ))>" +e "<$(( -0x10 ^ -0x11 ))>" +e "<$(( -0x10 ^ -0x11 ))>" +e "<$(( +64#10 ^ -64#11 ))>" +e "<$(( +0x11 ^ +0x10 ))>" +e "<$(( -0x11 ^ -0x10 ))>" +e "<$(( -0x11 ^ -0x10 ))>" +e "<$(( +64#11 ^ -64#10 ))>" +e "<$(( +64 ^ +1024 ))>" +e "<$((0x8000000000000000^-1))>" +e "<$((0x8000000000000000^1))>" +e "<$((0x7FFFFFFFFFFFFFFF^-1))>" +e "<$((0x7FFFFFFFFFFFFFFF^1))>" +e "<$((0xFFFFFFFFFFFFFFFF^-1))>" +e "<$((0xFFFFFFFFFFFFFFFF^1))>" +e "<$((0x8000000000000000^-11))>" +e "<$((0x8000000000000000^11))>" +e "<$((0x7FFFFFFFFFFFFFFF^-11))>" +e "<$((0x7FFFFFFFFFFFFFFF^11))>" +e "<$((0xFFFFFFFFFFFFFFFF^-11))>" +e "<$((0xFFFFFFFFFFFFFFFF^11))>" +e '= BIN BIT_AND' +e "<$((0&0))>" +e "<$(( 0 & 0 ))>" +e "<$((0&1))>" +e "<$(( 0 & 1 ))>" +e "<$((1&0))>" +e "<$(( 1 & 0 ))>" +e "<$((1&1))>" +e "<$(( 1 & 1 ))>" +e "<$((1111&2222))>" +e "<$((2222&1111))>" +e "<$(( +0x10 & +0x11 ))>" +e "<$(( -0x10 & -0x11 ))>" +e "<$(( -0x10 & -0x11 ))>" +e "<$(( +64#10 & -64#11 ))>" +e "<$(( +0x11 & +0x10 ))>" +e "<$(( -0x11 & -0x10 ))>" +e "<$(( -0x11 & -0x10 ))>" +e "<$(( +64#11 & -64#10 ))>" +e "<$(( +64 & +1024 ))>" +e "<$((0x8000000000000000&-1))>" +e "<$((0x8000000000000000&1))>" +e "<$((0x7FFFFFFFFFFFFFFF&-1))>" +e "<$((0x7FFFFFFFFFFFFFFF&1))>" +e "<$((0xFFFFFFFFFFFFFFFF&-1))>" +e "<$((0xFFFFFFFFFFFFFFFF&1))>" +e "<$((0x8000000000000000&-11))>" +e "<$((0x8000000000000000&11))>" +e "<$((0x7FFFFFFFFFFFFFFF&-11))>" +e "<$((0x7FFFFFFFFFFFFFFF&11))>" +e "<$((0xFFFFFFFFFFFFFFFF&-11))>" +e "<$((0xFFFFFFFFFFFFFFFF&11))>" +e '= BIN EQ' +e "<$((0==0))>" +e "<$(( 000 == 0X0 ))>" +e "<$((01 == 64#1))>" +e "<$((01 == 64#1))>" +e "<$((0x1234 == 4660))>" +e "<$((0x1234 == 011064))>" +s I=33 J=33;e "<$((I==J))>" +s I=33 J=33;e "<$(( I == J ))>" +e "<$((0==1))>" +e "<$((0==0000000000000000000000001))>" +e "<$((1==2))>" +e "<$((0x1234 == 04660))>" +e "<$((0x1234 == 0x11064))>" +s I=10 J=33;e "<$((I==J))>" +s I=-10 J=-33;e "<$((I==J))>" +s I=-33 J=-33;e "<$((I==J))>" +e '= BIN NE' +e "<$((0!=0))>" +e "<$(( 000 != 0X0 ))>" +e "<$((01 != 64#1))>" +e "<$((01 != 64#1))>" +e "<$((0x1234 != 4660))>" +e "<$((0x1234 != 011064))>" +s I=33 J=33;e "<$((I!=J))>" +s I=33 J=33;e "<$(( I != J ))>" +e "<$((0!=1))>" +e "<$((0!=0000000000000000000000001))>" +e "<$((1!=2))>" +e "<$((0x1234 != 04660))>" +e "<$((0x1234 != 0x11064))>" +s I=10 J=33;e "<$((I!=J))>" +s I=-10 J=-33;e "<$((I!=J))>" +s I=-33 J=-33;e "<$((I!=J))>" +e '= BIN LE' +e "<$((0<=0))>" +e "<$(( 000 <= 0X0 ))>" +e "<$((01 <= 64#1))>" +e "<$((01 <= 64#2))>" +e "<$((02 <= 64#1))>" +e "<$((0x1234 <= 4660))>" +e "<$((0x1234 <= 011064))>" +e "<$((0x1233 <= 011064))>" +e "<$((0x1235 <= 011064))>" +s I=33 J=33;e "<$((I<=J))>" +s I=33 J=33;e "<$((I<=J))>" +s I=32 J=33;e "<$((I<=J))>" +s I=34 J=33;e "<$((I<=J))>" +s I=-33 J=-33;e "<$((I<=J))>" +s I=-33 J=-33;e "<$((I<=J))>" +s I=-32 J=-33;e "<$((I<=J))>" +s I=-34 J=-33;e "<$((I<=J))>" +e '= BIN GE' +e "<$((0>=0))>" +e "<$(( 000 >= 0X0 ))>" +e "<$((01 >= 64#1))>" +e "<$((01 >= 64#2))>" +e "<$((02 >= 64#1))>" +e "<$((0x1234 >= 4660))>" +e "<$((0x1234 >= 011064))>" +e "<$((0x1233 >= 011064))>" +e "<$((0x1235 >= 011064))>" +s I=33 J=33;e "<$((I>=J))>" +s I=33 J=33;e "<$((I>=J))>" +s I=32 J=33;e "<$((I>=J))>" +s I=34 J=33;e "<$((I>=J))>" +s I=-33 J=-33;e "<$((I>=J))>" +s I=-33 J=-33;e "<$((I>=J))>" +s I=-32 J=-33;e "<$((I>=J))>" +s I=-34 J=-33;e "<$((I>=J))>" +e '= BIN LT' +e "<$((0<0))>" +e "<$(( 000 < 0X0 ))>" +e "<$((01 < 64#1))>" +e "<$((01 < 64#2))>" +e "<$((02 < 64#1))>" +e "<$((0x1234 < 4660))>" +e "<$((0x1234 < 011064))>" +e "<$((0x1233 < 011064))>" +e "<$((0x1235 < 011064))>" +s I=33 J=33;e "<$((I<J))>" +s I=33 J=33;e "<$((I<J))>" +s I=32 J=33;e "<$((I<J))>" +s I=34 J=33;e "<$((I<J))>" +s I=-33 J=-33;e "<$((I<J))>" +s I=-33 J=-33;e "<$((I<J))>" +s I=-32 J=-33;e "<$((I<J))>" +s I=-34 J=-33;e "<$((I<J))>" +e '= BIN GT' +e "<$((0>0))>" +e "<$(( 000 > 0X0 ))>" +e "<$((01 > 64#1))>" +e "<$((01 > 64#2))>" +e "<$((02 > 64#1))>" +e "<$((0x1234 > 4660))>" +e "<$((0x1234 > 011064))>" +e "<$((0x1233 > 011064))>" +e "<$((0x1235 > 011064))>" +s I=33 J=33;e "<$((I>J))>" +s I=33 J=33;e "<$((I>J))>" +s I=32 J=33;e "<$((I>J))>" +s I=34 J=33;e "<$((I>J))>" +s I=-33 J=-33;e "<$((I>J))>" +s I=-33 J=-33;e "<$((I>J))>" +s I=-32 J=-33;e "<$((I>J))>" +s I=-34 J=-33;e "<$((I>J))>" +# +# COMMA below +e '= PRECEDENCE I' +e "<$(( 1 + 2 + 3 ))>" +e "<$(( 1 - 2 + 3 ))>" +e "<$(( 3 - 2 - 1 ))>" +e "<$(( 3 - 2 + 1 ))>" +e "<$(( - 2 + 1 ))>" +e "<$(( 2 + -1 ))>" +e "<$(( ! 2 + 1 ))>" +e "<$(( 2 + !1 ))>" +e "<$(( 3 * 2 + 2 ))>" +e "<$(( 3 + 2 * 2 ))>" +e "<$(( 3 * 2 * 2 ))>" +e "<$(( 9 / 3 + 2 ))>" +e "<$(( 9 + 3 / 2 ))>" +e "<$(( 9 / 3 / 2 ))>" +e "<$(( 9 << 1 + 2 ))>" +e "<$(( 9 + 3 << 2 ))>" +e "<$(( 9 << 3 << 2 ))>" +e "<$(( 9 >> 1 + 2 ))>" +e "<$(( 9 + 3 >> 2 ))>" +e "<$(( 19 >> 3 >> 1 ))>" +e "<$(( 19 >> 3 << 1 ))>" +e "<$(( 19 << 3 >> 1 ))>" +e "<$(( 2 + 3 < 3 * 2 ))>" +e "<$(( 2 << 3 >= 3 << 2 ))>" +e "<$(( 0xfD & 0xF == 0xF ))>" +e "<$((0xfD&0xF==0xF))>" +e "<$(( 3 * 7 , 2 << 8 , 9 - 7 ))>" +e "<$((3*7,2<<8,9-7))>" +e '= PARENS' +e "<$(((1 + 2) + 3))>" +e "<$(((1+2)+3))>" +e "<$((1 - (2 + 3)))>" +e "<$((1-(2+3)))>" +e "<$((3 - (2 - 1)))>" +e "<$((3-(2-1)))>" +e "<$((3 - ( 2 + 1 )))>" +e "<$((3-(2+1)))>" +e "<$((- (2 + 1)))>" +e "<$((-(2+1)))>" +e "<$((! (2 + 1)))>" +e "<$((!(2+1)))>" +e "<$((3 * (2 + 2)))>" +e "<$((3*(2+2)))>" +e "<$(((3 + 2) * 2))>" +e "<$(((3+2)*2))>" +e "<$((3 * (2 * 2)))>" +e "<$((3*(2*8)))>" +e "<$((9 / (3 + 2)))>" +e "<$((9/(3+2)))>" +e "<$((( 9 + 3 ) / 2))>" +e "<$(((9+3)/2))>" +e "<$((9 / ( 3 / 2 )))>" +e "<$((9/(3/2)))>" +e "<$((( 9 << 1 ) + 2))>" +e "<$(((9<<1)+2))>" +e "<$((9 + (3 << 2)))>" +e "<$((9+(3<<2)))>" +e "<$((9 << (3 << 2)))>" +e "<$((9<<(3<<2)))>" +e "<$(((9 >> 1) + 2))>" +e "<$(((9>>1)+2))>" +e "<$((9 + (3 >> 2)))>" +e "<$((9+(3>>2)))>" +e "<$((19 >> (3 >> 1)))>" +e "<$((19>>(3>>1)))>" +e "<$((19 >> (3 << 1)))>" +e "<$((19>>(3<<1)))>" +e "<$((19 << (3 >> 1)))>" +e "<$((19<<(3>>1)))>" +e "<$((2 + (3 < 3) * 2))>" +e "<$((2+(3<3)*2))>" +e "<$((2 << ((3 >= 3) << 2)))>" +e "<$((2<<((3>=3)<<2)))>" +e "<$(((0xfD & 0xF) == 0xF))>" +e "<$(((0xfD&0xF)==0xF))>" +e "<$((3 * (7 , 2) << (8 , 9 - 7)))>" +e "<$((3*(7,2)<<(8,9-7)))>" +# +# COND BELOW +e '= ASSIGN I' +unset I;p "<$(( I = 3 ))>";e "<$I>" +unset I;p "<$((I=3))>";e "<$I>" +s I=10;p "<$((I=3))>";e "<$I>" +s I=10;p "<$((I+=1))>";e "<$I>" +s I=10;p "<$((I-=1))>";e "<$I>" +s I=10;p "<$((I*=1))>";e "<$I>" +s I=10;p "<$((I*=2))>";e "<$I>" +s I=10;p "<$((I/=1))>";e "<$I>" +s I=10;p "<$((I/=2))>";e "<$I>" +s I=10;p "<$((I%=1))>";e "<$I>" +s I=10;p "<$((I%=2))>";e "<$I>" +s I=10;p "<$((I**=1))>";e "<$I>" +s I=10;p "<$((I**=2))>";e "<$I>" +s I=10;p "<$((I**=1+1))>";e "<$I>" +s I=10;p "<$((I|=1))>";e "<$I>" +s I=10;p "<$((I^=1))>";e "<$I>";p "<$((I^=1))>";e "<$I>" +s I=10;p "<$((I&=2))>";e "<$I>" +s I=10;p "<$((I>>=1))>";e "<$I>" +s I=10;p "<$((I<<=1))>";e "<$I>" +s I=-1;p "<$((I>>>=1))>";e "<$I>" +e '= ASSIGN II' +s I=2;p "<$(((I+=1)-1))>";e "<$I>" +s I=4;p "<$(((I-=1)+1))>";e "<$I>" +s I=0 J=0;p "<$(((I=5)*(J=7)+1))>";e "<$I><$J>" +s I=99 J=17;p "<$(((I+=1)*(J-=2)+1))>";e "<$I><$J>" +s I=10;p "<$((I=2,I|=1))>";e "<$I>" +s I=0 J=0 Y=0 Z=0;p "<$((I=1,J=2,Y=3,Z=4,Z+=I+J+Y))>";e "<$I><$J><$Y><$Z>" +e '= POSTFIX' +s I=1;p "<$((I++))>";e "<$I>" +s I=1 J=0;p "<$((J=I++))>";e "<$I><$J>" +s I=1 J=10;p "<$((J++*I++))>";e "<$I><$J>" +s I=1 J=10;p "<$(((J++)*(I++)))>";e "<$I><$J>" +s I=1;p "<$((I--))>";e "<$I>" +s I=1 J=0;p "<$((J=I--))>";e "<$I><$J>" +s I=1 J=10;p "<$((J--*I--))>";e "<$I><$J>" +s I=1 J=10;p "<$(((J--)*(I--)))>";e "<$I><$J>" +e '= PREFIX' +s I=1;p "<$((++I))>";e "<$I>" +s I=1 J=0;p "<$((J=++I))>";e "<$I><$J>" +s I=1 J=10;p "<$((++J*++I))>";e "<$I><$J>" +s I=1 J=10;p "<$((++(J)*++(I)))>";e "<$I><$J>" +s I=1 J=10;p "<$(((++J)*(++I)))>";e "<$I><$J>" +s I=1;p "<$((--I))>";e "<$I>" +s I=1 J=0;p "<$((J=--I))>";e "<$I><$J>" +s I=2 J=10;p "<$((--J*--I))>";e "<$I><$J>" +s I=1 J=10;p "<$((--(J)*--(I)))>";e "<$I><$J>" +s I=1 J=10;p "<$(((--J)*(--I)))>";e "<$I><$J>" +e '= VAR RECUR' +s I='1 + 1';p "<$((I))>";e "<$I>" +s I='1 + 1';p "<$((+I))>";e "<$I>" +s I='1 + 1';p "<$((++I))>";e "<$I>" +s I='1 + 1';p "<$((I++))>";e "<$I>" +s I='1 + 1';p "<$((1+I))>";e "<$I>" +s I='1 + 1 * 2';p "<$((I+1))>";e "<$I>" +s I='(1 + 1) * 2';p "<$((I+1))>";e "<$I>" +s I='1 + 1' J='3 / 2';p "<$((I=I+J))>";e "<$I><$J>" +s I='1 + 1';p "<$((I=I))>";e "<$I>" +s I='1 + 1';p "<$((I=+I))>";e "<$I>" +s I='1 + 1';p "<$((I=1+I))>";e "<$I>" +s I='1 + 1 * 2';p "<$((I=I+1))>";e "<$I>" +s I='(1 + 1) * 2';p "<$((I=I+1))>";e "<$I>" +s I='1 + 1' J='3 / 2';p "<$((I+=I+J))>";e "<$I><$J>" +e '= COMMA' +e "<$(( 1 , 2 ))>" +e "<$(( 1 , 2 , 3 ))>" +e "<$(( 1 , 2 , 3 , 4 ))>" +e "<$((1,2,3,4))>" +s I='1 + 1';p "<$(( I=10 , I+=I, I=I**2, I/=3 ))>";e "<$I>" +s I1=I2=10 I2=3;p "<$((I1,I2))>";e "<$I1><$I2>" +e '= COND' +e "<$(( +0 ? 2 : 3 ))>" +e "<$((-0?2:3))>" +e "<$(( +1 ? 2 : 3 ))>" +e "<$(( 1-1 ? 2 : 3 ))>" +e "<$(( 1-0 ? 2 : 3 ))>" +e "<$((-1?2:3))>" +e "<$(( 0x1234 ? 111 : 222 ))>" +e "<$((1**2 ? 5 : 7))>" +e "<$((0**2 ? 5 : 7))>" +e "<$((0**2>=0?5:7))>" +e "<$((-1<=0**2?5:7))>" +e "<$((1<=0**2?5:7))>" +e "<$((1>2||1*0?5:7))>" +e "<$((1>2&&1*0?5:7))>" +e "<$((1<2&&1*0?5:7))>" +e "<$((1<2&&1*0+1?5:7))>" +e '-- COND .2' +e "<$(( 1 < 2 ? -1 : 1 > 2 ? 1 : 0 ))>" +e "<$((1 < 1 ? -1 : 1 > 1 ? 1 : 0))>" +e "<$((2<1?-1:2>1?1:0))>" +e "<$((4<5 ? 1 : 32))>" +e "<$((4>5 ? 1 : 32))>" +e "<$((4>(2+3) ? 1 : 32))>" +e "<$((4<(2+3) ? 1 : 32))>" +e "<$(((2+2)<(2+3) ? 1 : 32))>" +e "<$(((2+2)>(2+3) ? 1 : 32))>" +## grouping protects precedence in : parts (syntax error tests below) +e '-- COND .3' +e "<$((1-1 < 1 ? 2,4 : 1,3))>" +e "<$((0<1?2,4:(1,3)))>" +e "<$((0,1,2,0?2,4:1,3))>" +e "<$((0,1,2,1?2,4:1,3))>" +e "<$((0,1,2,0?2,4:(1,3)))>" +e "<$((0,1,2,1?2,4:(1,3)))>" +e "<$((0,1,2,0?(2,4):1,3))>" +e "<$((0,1,2,1?(2,4):1,3))>" +e "<$((0,1,2,0?(2,4):(1,3)))>" +e "<$((0,1,2,1?(2,4):(1,3)))>" +e "<$((0?2:((0,3)?1:4)))>" +e "<$((1?2:3,0?1:4))>" +e "<$((1?2:3,0?1:4?5:6))>" +e "<$((1?2:(3,0)?1:4?5:6))>" +e "<$((1?2:3,0?4,5:5,6?7,8:9,10))>" +e "<$((1?2:(3,0)?4,5:5,6?7,8:9,10))>" +e "<$((1?2:(3,0)?(4,5):5,6?7,8:9,10))>" +e "<$((1?2:(3,0)?(4,5):(5,6)?7,8:9,10))>" +e "<$((1?2:(3,0)?(4,5):(5,6)?(7,8):9,10))>" +e "<$((1?2:(3,0)?(4,5):(5,6)?(7,8):(9,10)))>" +e "<$((1?2:3,1?4,5:5,6?7,8:9,10))>" +e "<$((1?2:(3,1)?4,5:5,6?7,8:9,10))>" +e "<$((1?2:(3,1)?(4,5):5,6?7,8:9,10))>" +e "<$((1?2:(3,1)?(4,5):(5,6)?7,8:9,10))>" +e "<$((1?2:(3,1)?(4,5):(5,6)?(7,8):9,10))>" +e "<$((1?2:(3,1)?(4,5):(5,6)?(7,8):(9,10)))>" +e "<$((0?2:(3,1)?(4,5):(5,6)?(7,8):(9,10)))>" +e "<$((0?2:(3,1)?4,5:(5,6)?7,8:(9,10)))>" +e "<$((0?2:(3,0)?(4,5):(5,6)?(7,8):(9,10)))>" +e "<$((0?2:(3,0)?4,5:(5,6)?7,8:(9,10)))>" +e "<$((0?2:(3,0)?(4,5):(5,0)?(7,8):(9,10)))>" +e "<$((0?2:(3,0)?4,5:(5,0)?7,8:(9,10)))>" +e "<$((0?2:3,0?4,5:(5,0)?7,8:(9,10)))>" +e "<$((0?2:(3,0)?4,5:5,0?7,8:(9,10)))>" +e "<$((0?2:(3,0)?4,5:(5,0)?7,8:9,10))>" +e '-- COND .4' +e "<$((1?2?3?4?5:6:7:8:9))>" +e "<$((1?2?3?0?5:6:7:8:9))>" +e "<$((1?2?0?0?5:6:7:8:9))>" +e "<$((1?0?0?0?5:6:7:8:9))>" +e "<$((0?0?0?0?5:6:7:8:9))>" +e "<$((0?3+4?10:11:5+6?12:13))>" +e "<$((1?3+4?10:11:5+6?12:13))>" +e "<$((0?(3+4)?(10):(11):((5+6)?12:13)))>" +e "<$((1?(3+4)?(10):(11):((5+6)?12:13)))>" +e '-- COND .5' +e "<$((0?3+4?10:11?20+1:22*1:5+6?12:13))>" +e "<$((1?3+4?10:11?20+1:22*1:5+6?12:13))>" +e "<$((0?(3+4)?(10):(11)?(20+1):(22*1):((5+6)?12:13)))>" +e "<$((1?(3+4)?(10):(11)?(20+1):(22*1):((5+6)?12:13)))>" +e '-- COND .6' +e "<$((0?3+4?9:11?20+1:22*1:5+6?12:13))>" +e "<$((1?3+4?9:11?20+1:22*1:5+6?12:13))>" +e "<$((0?10+11?20+1?22*1?23**1:24**1:25/1?26%27:56>>1:-1:-2))>" +e "<$((1?0?20+1?22*1?23**1:24**1:25/1?26%27:56>>1:-1:-2))>" +e "<$((1?10?20+1?22*1?23**1:24**1:25/1?26%27:56>>1:-1:-2))>" +e "<$((1?10?0?22*1?23**1:24**1:25/1?26%27:56>>1:-1:-2))>" +e "<$((1?10?20?0?23**1:24**1:25/1?26%27:56>>1:-1:-2))>" +e "<$((1?10?20?22*1?0:24**1:25/1?26%27:56>>1:-1:-2))>" +e "<$((1?10?20?22*1?23**1:0:25/1?26%27:56>>1:-1:-2))>" +e "<$((1?10?20?22*1?23**1:24**1:0?26%27:56>>1:-1:-2))>" +e "<$((1?10?20?22*1?23**1:24**1:25/1?0:56>>1:-1:-2))>" +e '-- COND .7' +s I1=2 I2=3 I3=4 I4=5;\ + p "<$(( (I1 < I2) ? (I2 < I3) ? I3 *= I3 : (I2 *= I2) : (I1 *= I1)))>";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ + p "<$(( (I1 < I2) ? ((I2 < I3) ? I3 *= I3 : (I2 *= I2)) : (I1 *= I1)))>";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ + p "<$((((I1<I2)?((I2<I3)?(I3*=I3):(I2*=I2)):(I1*=I1))))>";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ +p "<$(((I1<I2)?(I2<I3)?(I3<I4)?I4*=I4:(I3*=I3):(I2*=I2):(I1*=I1)))>";\ + e "<$I1><$I2><$I3><$I4><$I5>" +# only first +s I1=2 I2=3 I3=4 I4=5;\ + p "<$(( (I1 < I2) ? (I2 > I3) ? I3 *= I3 : (I2 *= I2) : (I1 *= I1)))>";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ + p "<$(((I1<I2)?(I2>I3)?I3*=I3:(I2*=I2):(I1*=I1)))>";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ + p "<$(( (I1 < I2) ? ((I2 > I3) ? I3 *= I3 : (I2 *= I2)) : (I1 *= I1)))>";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ + p "<$(( ((I1 < I2) ? ((I2 > I3) ? (I3 *= I3):(I2 *= I2)):(I1 *= I1))))>";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ +p "<$(((I1<I2)?(I2>I3)?(I3>I4)?I4*=I4:(I3*=I3):(I2*=I2):(I1*=I1)))>";\ + e "<$I1><$I2><$I3><$I4>" +# last not etc. +s I1=2 I2=3 I3=4 I4=5;\ +p "<$(((I1<I2)?(I2<I3)?(I3>I4)?I4*=I4:(I3*=I3):(I2*=I2):(I1*=I1)))>";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ +p "<$(((I1<I2)?(I2>I3)?(I3<I4)?I4*=I4:(I3*=I3):(I2*=I2):(I1*=I1)))>";\ + e "<$I1><$I2><$I3><$I4><$I5>" +s I1=2 I2=3 I3=4 I4=5;\ +p "<$(((I1>I2)?(I2<I3)?(I3<I4)?I4*=I4:(I3*=I3):(I2*=I2):(I1*=I1)))>";\ + e "<$I1><$I2><$I3><$I4><$I5>" +e '-- COND .8' +s I=0;p "<$((1?I=2:(I=3),8,10))>";e "<$I>" +s I=0;p "<$((1?20:(I+=2)))>";e "<$I>" +s I=0;p "<$((1?I+=10:(I+=2)))>";e "<$I>" +s I=0;p "<$((0?I+=2:20))>";e "<$I>" +s I=0;p "<$((0?I+=2:(I+=10)))>";e "<$I>" +s I=0;p "<$((0?(I+=2):(20)))>";e "<$I>" +s I=0;p "<$((0?(I+=2):(I+=20)))>";e "<$I>" +e '-- COND .9' +s I1=+E+ I2=1+1;p "<$((0?I1:I2))>";e "<$I1><$I2>" +s I1=1+1 I2=+E+;p "<$((1?I1:I2))>";e "<$I1><$I2>" +s I1=+E+ I2=1+1;p "<$((0?I1=1:(I2=2)))>";e "<$I1><$I2>" +s I1=1+1 I2=+E+;p "<$((1?I1=1:(I2=2)))>";e "<$I1><$I2>" +s I1=+E+ I2=1+1;p "<$((0?I1*=I1:(I2*=I2)))>";e "<$I1><$I2>" +s I1=1+1 I2=+E+;p "<$((1?I1*=I1:(I2*=I2)))>";e "<$I1><$I2>" +e '-- COND .10' +s I1=+E+ I2=+E+ I3=+E+ I4=-1;p "<$((0?I1?I2:I3:I4))>";e "<$I1><$I2><$I3><$I4>" +s I1=1 I2=2 I3=+E+ I4=+E+;p "<$((1?I1?I2:I3:I4))>";e "<$I1><$I2><$I3><$I4>" +s I1=0 I2=+E+ I3=3 I4=+E+;p "<$((1?I1?I2:I3:I4))>";e "<$I1><$I2><$I3><$I4>" +e '= WILD I' +e "<$(( 3 + ( 11 ) ))>" +e "<$((1 + (2 - 2)))>" +e "<$((1 + (2 - 2)))>" +e "<$(( (( 3 / 3 )) + ((1*1*1)) - (( 7 % 6 ))))>" +e "<$(( 3+((2 * 2))/6 ))>" +e "<$(( 1 + 1 - 3 * 3 + 99-88 / 17))>" +e "<$(( 1 << 2 % 1+2 * 4 - (2 + 2 + 1) * 6 / 7 + 4 * 2 + (81/9)))>" +s I1=I2=10 I2=3;p "<$((I1 + I2))>";e "<$I1><$I2>" +s I1=I2=10 I2=3;p "<$((I1 * I2))>";e "<$I1><$I2>" +s I1=I2=10 I2=3;p "<$((I1 % I2))>";e "<$I1><$I2>" +e '= WILD II' +s I=10;p "<$((3+(3*(I=11))))>";e "<$I>" +s I=10;p "<$((3+(3*(I++))))>";e "<$I>" +s I=10;p "<$((3+(3*(I=11,I++))))>";e "<$I>" +s I=10;p "<$((3+(3*(I=11,++I))))>";e "<$I>" +s I=10;p "<$((3+(3*(I=11,++++I))))>";e "<$I>" +s I=10;p "<$((3+(3*(I=11,+++++++++++++++++++++++-+++++I))))>";e "<$I>" +e "<$((3+(3*(+++++++++++++++++++++++-+++++10))))>" +s I=10;p "<$(( +10 + + +I ))>";e "<$I>" +s I=10;p "<$(( +10 + ++I ))>";e "<$I>" +s I=10;p "<$(( +10 ++ +I ))>";e "<$I>" +s I=10;p "<$(( +10 +++ I ))>";e "<$I>" +s I=10;p "<$(( +10+++I ))>";e "<$I>" +s I=10;p "<$((+10++I))>";e "<$I>" +s I=10;p "<$((+10 + + + ++++ +I))>";e "<$I>" +e "<$(( +10 + + + ++++ +11 ))>" +e "<$(( +10 + + + ++++ ++11 ))>" +e "<$((+10++++++++11))>" +e '= WILD RECUR' # (some yet) +s I1=I2=10 I2=5;p "<$((I1+=I2))>";e "<$I1><$I2>" +s I1=I2=10 I2=5 I3=I2+=1;p "<$((I1))>";e "<$I1><$I2><$I3>" +s I1=I2=10 I2=5 I3=I2+=1;p "<$((I1,I3))>";e "<$I1><$I2><$I3>" +s I1=I2=10 I2=5 I3=I2+=1;p "<$((I1+I3))>";e "<$I1><$I2><$I3>" +s I1=I2=10 I2=5 I3=I2+=1;p "<$((I1?I1:I3))>";e "<$I1><$I2><$I3>" +s I1=I2=0 I2=5 I3=I2+=1;p "<$((I1?I1:I3))>";e "<$I1><$I2><$I3>" +s I1=I1=10 I2=5 I3=I2+=1;p "<$((I1=0?I1:I3))>";e "<$I1><$I2><$I3>" +s I1=I1=10 I2=5 I3=I2+=1;p "<$((I1=1?I1:I3))>";e "<$I1><$I2><$I3>" +s I1=I2='(I2=10)+1' I2=5 I3=I2+=1;p "<$((I1,I3))>";e "<$I1><$I2><$I3>" +s I1=I2='(I2=(I2=10)+1)' I2=5 I3=I2+=1;p "<$((I1,I3))>";e "<$I1><$I2><$I3>" +s I1=I2=10 I2=5 I3=I2+=1;p "<$((I1+I3*I1*I3/I1%I3))>";e "<$I1><$I2><$I3>" +s I1=I2=10 I2=5 I3=I2+=1;p "<$((I1+I3*I1*I3/I1%I3))>";e "<$I1><$I2><$I3>" +s I1=I2=+E+ I2=5;p "<$((I1=10))>";e "<$I1><$I2>" +s I1=I2=+E+ I2=5;p "<$((0?I1:++I2))>";e "<$I1><$I2>" +s I1=I2=10 I2=5;p "<$((I2,(1?I1:++I2)))>";e "<$I1><$I2>" +s I1=5 I2=10 I3=20;p "<$((I1-=5,1?I2:I3))>";e "<$I1><$I2><$I3>" +s I1=5 Ix=6 I2=10 I3=20;p "<$((I1=Ix,1?I2:I3))>";e "<$I1><$I2><$I3>" +s I1=5 Ix=6 I2=10 I3=20;p "<$((I1=Ix?I2:I3))>";e "<$I1><$I2><$I3>" +s I1=5 Ix=6 I2=10 I3=20;p "<$((I1*=Ix?I2:I3))>";e "<$I1><$I2><$I3>" +s I1=5 Ix=6 I2=10 I3=20;p "<$((0,I1*=Ix?I2:I3))>";e "<$I1><$I2><$I3>" +s I1=5 Ix=6 I2=10 I3=20;p "<$((I1*=Ix?I2:I3,Ix=21,I1*=Ix?I2:I3))>";e "<$I1><$I2><$I3>" diff --git a/shell/math.c b/shell/math.c index 76d22c9bd5..8ba0d2f7fb 100644 --- a/shell/math.c +++ b/shell/math.c @@ -116,398 +116,6 @@ #include "libbb.h" #include "math.h" -typedef unsigned char operator; - -/* An operator's token id is a bit of a bitfield. The lower 5 bits are the - * precedence, and 3 high bits are an ID unique across operators of that - * precedence. The ID portion is so that multiple operators can have the - * same precedence, ensuring that the leftmost one is evaluated first. - * Consider * and / - */ -#define tok_decl(prec,id) (((id)<<5) | (prec)) -#define PREC(op) ((op) & 0x1F) - -#define TOK_LPAREN tok_decl(0,0) - -#define TOK_COMMA tok_decl(1,0) - -/* All assignments are right associative and have the same precedence, - * but there are 11 of them, which doesn't fit into 3 bits for unique id. - * Abusing another precedence level: - */ -#define TOK_ASSIGN tok_decl(2,0) -#define TOK_AND_ASSIGN tok_decl(2,1) -#define TOK_OR_ASSIGN tok_decl(2,2) -#define TOK_XOR_ASSIGN tok_decl(2,3) -#define TOK_PLUS_ASSIGN tok_decl(2,4) -#define TOK_MINUS_ASSIGN tok_decl(2,5) -#define TOK_LSHIFT_ASSIGN tok_decl(2,6) -#define TOK_RSHIFT_ASSIGN tok_decl(2,7) - -#define TOK_MUL_ASSIGN tok_decl(3,0) -#define TOK_DIV_ASSIGN tok_decl(3,1) -#define TOK_REM_ASSIGN tok_decl(3,2) - -#define fix_assignment_prec(prec) do { if (prec == 3) prec = 2; } while (0) - -/* Ternary conditional operator is right associative too */ -#define TOK_CONDITIONAL tok_decl(4,0) -#define TOK_CONDITIONAL_SEP tok_decl(4,1) - -#define TOK_OR tok_decl(5,0) - -#define TOK_AND tok_decl(6,0) - -#define TOK_BOR tok_decl(7,0) - -#define TOK_BXOR tok_decl(8,0) - -#define TOK_BAND tok_decl(9,0) - -#define TOK_EQ tok_decl(10,0) -#define TOK_NE tok_decl(10,1) - -#define TOK_LT tok_decl(11,0) -#define TOK_GT tok_decl(11,1) -#define TOK_GE tok_decl(11,2) -#define TOK_LE tok_decl(11,3) - -#define TOK_LSHIFT tok_decl(12,0) -#define TOK_RSHIFT tok_decl(12,1) - -#define TOK_ADD tok_decl(13,0) -#define TOK_SUB tok_decl(13,1) - -#define TOK_MUL tok_decl(14,0) -#define TOK_DIV tok_decl(14,1) -#define TOK_REM tok_decl(14,2) - -/* Exponent is right associative */ -#define TOK_EXPONENT tok_decl(15,1) - -/* Unary operators */ -#define UNARYPREC 16 -#define TOK_BNOT tok_decl(UNARYPREC,0) -#define TOK_NOT tok_decl(UNARYPREC,1) - -#define TOK_UMINUS tok_decl(UNARYPREC+1,0) -#define TOK_UPLUS tok_decl(UNARYPREC+1,1) - -#define PREC_PRE (UNARYPREC+2) - -#define TOK_PRE_INC tok_decl(PREC_PRE, 0) -#define TOK_PRE_DEC tok_decl(PREC_PRE, 1) - -#define PREC_POST (UNARYPREC+3) - -#define TOK_POST_INC tok_decl(PREC_POST, 0) -#define TOK_POST_DEC tok_decl(PREC_POST, 1) - -#define SPEC_PREC (UNARYPREC+4) - -#define TOK_NUM tok_decl(SPEC_PREC, 0) -#define TOK_RPAREN tok_decl(SPEC_PREC, 1) - -static int -is_assign_op(operator op) -{ - operator prec = PREC(op); - fix_assignment_prec(prec); - return prec == PREC(TOK_ASSIGN) - || prec == PREC_PRE - || prec == PREC_POST; -} - -static int -is_right_associative(operator prec) -{ - return prec == PREC(TOK_ASSIGN) - || prec == PREC(TOK_EXPONENT) - || prec == PREC(TOK_CONDITIONAL); -} - - -typedef struct { - arith_t val; - /* We acquire second_val only when "expr1 : expr2" part - * of ternary ?: op is evaluated. - * We treat ?: as two binary ops: (expr ? (expr1 : expr2)). - * ':' produces a new value which has two parts, val and second_val; - * then '?' selects one of them based on its left side. - */ - arith_t second_val; - char second_val_present; - /* If NULL then it's just a number, else it's a named variable */ - char *var; -} var_or_num_t; - -typedef struct remembered_name { - struct remembered_name *next; - const char *var; -} remembered_name; - - -static arith_t -evaluate_string(arith_state_t *math_state, const char *expr); - -static const char* -arith_lookup_val(arith_state_t *math_state, var_or_num_t *t) -{ - if (t->var) { - const char *p = math_state->lookupvar(t->var); - if (p) { - remembered_name *cur; - remembered_name cur_save; - - /* did we already see this name? - * testcase: a=b; b=a; echo $((a)) - */ - for (cur = math_state->list_of_recursed_names; cur; cur = cur->next) { - if (strcmp(cur->var, t->var) == 0) { - /* Yes */ - return "expression recursion loop detected"; - } - } - - /* push current var name */ - cur = math_state->list_of_recursed_names; - cur_save.var = t->var; - cur_save.next = cur; - math_state->list_of_recursed_names = &cur_save; - - /* recursively evaluate p as expression */ - t->val = evaluate_string(math_state, p); - - /* pop current var name */ - math_state->list_of_recursed_names = cur; - - return math_state->errmsg; - } - /* treat undefined var as 0 */ - t->val = 0; - } - return 0; -} - -/* "Applying" a token means performing it on the top elements on the integer - * stack. For an unary operator it will only change the top element, but a - * binary operator will pop two arguments and push the result */ -static NOINLINE const char* -arith_apply(arith_state_t *math_state, operator op, var_or_num_t *numstack, var_or_num_t **numstackptr) -{ -#define NUMPTR (*numstackptr) - - var_or_num_t *top_of_stack; - arith_t rez; - const char *err; - - /* There is no operator that can work without arguments */ - if (NUMPTR == numstack) - goto err; - - top_of_stack = NUMPTR - 1; - - /* Resolve name to value, if needed */ - err = arith_lookup_val(math_state, top_of_stack); - if (err) - return err; - - rez = top_of_stack->val; - if (op == TOK_UMINUS) - rez = -rez; - else if (op == TOK_NOT) - rez = !rez; - else if (op == TOK_BNOT) - rez = ~rez; - else if (op == TOK_POST_INC || op == TOK_PRE_INC) - rez++; - else if (op == TOK_POST_DEC || op == TOK_PRE_DEC) - rez--; - else if (op != TOK_UPLUS) { - /* Binary operators */ - arith_t right_side_val; - char bad_second_val; - - /* Binary operators need two arguments */ - if (top_of_stack == numstack) - goto err; - /* ...and they pop one */ - NUMPTR = top_of_stack; /* this decrements NUMPTR */ - - bad_second_val = top_of_stack->second_val_present; - if (op == TOK_CONDITIONAL) { /* ? operation */ - /* Make next if (...) protect against - * $((expr1 ? expr2)) - that is, missing ": expr" */ - bad_second_val = !bad_second_val; - } - if (bad_second_val) { - /* Protect against $((expr <not_?_op> expr1 : expr2)) */ - return "malformed ?: operator"; - } - - top_of_stack--; /* now points to left side */ - - if (op != TOK_ASSIGN) { - /* Resolve left side value (unless the op is '=') */ - err = arith_lookup_val(math_state, top_of_stack); - if (err) - return err; - } - - right_side_val = rez; - rez = top_of_stack->val; - if (op == TOK_CONDITIONAL) /* ? operation */ - rez = (rez ? right_side_val : top_of_stack[1].second_val); - else if (op == TOK_CONDITIONAL_SEP) { /* : operation */ - if (top_of_stack == numstack) { - /* Protect against $((expr : expr)) */ - return "malformed ?: operator"; - } - top_of_stack->second_val_present = op; - top_of_stack->second_val = right_side_val; - } - else if (op == TOK_BOR || op == TOK_OR_ASSIGN) - rez |= right_side_val; - else if (op == TOK_OR) - rez = right_side_val || rez; - else if (op == TOK_BAND || op == TOK_AND_ASSIGN) - rez &= right_side_val; - else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN) - rez ^= right_side_val; - else if (op == TOK_AND) - rez = rez && right_side_val; - else if (op == TOK_EQ) - rez = (rez == right_side_val); - else if (op == TOK_NE) - rez = (rez != right_side_val); - else if (op == TOK_GE) - rez = (rez >= right_side_val); - else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN) - rez >>= right_side_val; - else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN) - rez <<= right_side_val; - else if (op == TOK_GT) - rez = (rez > right_side_val); - else if (op == TOK_LT) - rez = (rez < right_side_val); - else if (op == TOK_LE) - rez = (rez <= right_side_val); - else if (op == TOK_MUL || op == TOK_MUL_ASSIGN) - rez *= right_side_val; - else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN) - rez += right_side_val; - else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN) - rez -= right_side_val; - else if (op == TOK_ASSIGN || op == TOK_COMMA) - rez = right_side_val; - else if (op == TOK_EXPONENT) { - arith_t c; - if (right_side_val < 0) - return "exponent less than 0"; - c = 1; - while (--right_side_val >= 0) - c *= rez; - rez = c; - } - else if (right_side_val == 0) - return "divide by zero"; - else if (op == TOK_DIV || op == TOK_DIV_ASSIGN - || op == TOK_REM || op == TOK_REM_ASSIGN) { - /* - * bash 4.2.45 x86 64bit: SEGV on 'echo $((2**63 / -1))' - * - * MAX_NEGATIVE_INT / -1 = MAX_POSITIVE_INT+1 - * and thus is not representable. - * Some CPUs segfault trying such op. - * Others overflow MAX_POSITIVE_INT+1 to - * MAX_NEGATIVE_INT (0x7fff+1 = 0x8000). - * Make sure to at least not SEGV here: - */ - if (right_side_val == -1 - && rez << 1 == 0 /* MAX_NEGATIVE_INT or 0 */ - ) { - right_side_val = 1; - } - if (op == TOK_DIV || op == TOK_DIV_ASSIGN) - rez /= right_side_val; - else { - rez %= right_side_val; - } - } - } - - if (is_assign_op(op)) { - char buf[sizeof(arith_t)*3 + 2]; - - if (top_of_stack->var == NULL) { - /* Hmm, 1=2 ? */ - goto err; - } - /* Save to shell variable */ - sprintf(buf, ARITH_FMT, rez); - math_state->setvar(top_of_stack->var, buf); - /* After saving, make previous value for v++ or v-- */ - if (op == TOK_POST_INC) - rez--; - if (op == TOK_POST_DEC) - rez++; - } - - top_of_stack->val = rez; - /* Erase var name, it is just a number now */ - top_of_stack->var = NULL; - return NULL; - err: - return "arithmetic syntax error"; -#undef NUMPTR -} - -/* longest must be first */ -static const char op_tokens[] ALIGN1 = { - '<','<','=',0, TOK_LSHIFT_ASSIGN, - '>','>','=',0, TOK_RSHIFT_ASSIGN, - '<','<', 0, TOK_LSHIFT, - '>','>', 0, TOK_RSHIFT, - '|','|', 0, TOK_OR, - '&','&', 0, TOK_AND, - '!','=', 0, TOK_NE, - '<','=', 0, TOK_LE, - '>','=', 0, TOK_GE, - '=','=', 0, TOK_EQ, - '|','=', 0, TOK_OR_ASSIGN, - '&','=', 0, TOK_AND_ASSIGN, - '*','=', 0, TOK_MUL_ASSIGN, - '/','=', 0, TOK_DIV_ASSIGN, - '%','=', 0, TOK_REM_ASSIGN, - '+','=', 0, TOK_PLUS_ASSIGN, - '-','=', 0, TOK_MINUS_ASSIGN, - '-','-', 0, TOK_POST_DEC, - '^','=', 0, TOK_XOR_ASSIGN, - '+','+', 0, TOK_POST_INC, - '*','*', 0, TOK_EXPONENT, - '!', 0, TOK_NOT, - '<', 0, TOK_LT, - '>', 0, TOK_GT, - '=', 0, TOK_ASSIGN, - '|', 0, TOK_BOR, - '&', 0, TOK_BAND, - '*', 0, TOK_MUL, - '/', 0, TOK_DIV, - '%', 0, TOK_REM, - '+', 0, TOK_ADD, - '-', 0, TOK_SUB, - '^', 0, TOK_BXOR, - /* uniq */ - '~', 0, TOK_BNOT, - ',', 0, TOK_COMMA, - '?', 0, TOK_CONDITIONAL, - ':', 0, TOK_CONDITIONAL_SEP, - ')', 0, TOK_RPAREN, - '(', 0, TOK_LPAREN, - 0 -}; -#define ptr_to_rparen (&op_tokens[sizeof(op_tokens)-7]) - #if ENABLE_FEATURE_SH_MATH_BASE static arith_t strto_arith_t(const char *nptr, char **endptr) { @@ -577,250 +185,99 @@ static arith_t strto_arith_t(const char *nptr, char **endptr) # endif #endif -static arith_t -evaluate_string(arith_state_t *math_state, const char *expr) -{ - operator lasttok; - const char *errmsg; - const char *start_expr = expr = skip_whitespace(expr); - unsigned expr_len = strlen(expr) + 2; - /* Stack of integers */ - /* The proof that there can be no more than strlen(startbuf)/2+1 - * integers in any given correct or incorrect expression - * is left as an exercise to the reader. */ - var_or_num_t *const numstack = alloca((expr_len / 2) * sizeof(numstack[0])); - var_or_num_t *numstackptr = numstack; - /* Stack of operator tokens */ - operator *const stack = alloca(expr_len * sizeof(stack[0])); - operator *stackptr = stack; - - /* Start with a left paren */ - *stackptr++ = lasttok = TOK_LPAREN; - errmsg = NULL; - - while (1) { - const char *p; - operator op; - operator prec; - - expr = skip_whitespace(expr); - if (*expr == '\0') { - if (expr == start_expr) { - /* Null expression */ - numstack->val = 0; - goto ret; - } - - /* This is only reached after all tokens have been extracted from the - * input stream. If there are still tokens on the operator stack, they - * are to be applied in order. At the end, there should be a final - * result on the integer stack */ - - if (expr != ptr_to_rparen + 1) { - /* If we haven't done so already, - * append a closing right paren - * and let the loop process it */ - expr = ptr_to_rparen; -//bb_error_msg("expr=')'"); - continue; - } - /* At this point, we're done with the expression */ - if (numstackptr != numstack + 1) { - /* ...but if there isn't, it's bad */ - goto err; - } - goto ret; - } - - p = endofname(expr); - if (p != expr) { - /* Name */ - size_t var_name_size = (p - expr) + 1; /* +1 for NUL */ - numstackptr->var = alloca(var_name_size); - safe_strncpy(numstackptr->var, expr, var_name_size); -//bb_error_msg("var:'%s'", numstackptr->var); - expr = p; - num: - numstackptr->second_val_present = 0; - numstackptr++; - lasttok = TOK_NUM; - continue; - } - - if (isdigit(*expr)) { - /* Number */ - numstackptr->var = NULL; - errno = 0; - numstackptr->val = strto_arith_t(expr, (char**) &expr); -//bb_error_msg("val:%lld", numstackptr->val); - if (errno) - numstackptr->val = 0; /* bash compat */ - goto num; - } - - /* Should be an operator */ - - /* Special case: XYZ--, XYZ++, --XYZ, ++XYZ are recognized - * only if XYZ is a variable name, not a number or EXPR. IOW: - * "a+++v" is a++ + v. - * "(a)+++7" is ( a ) + + + 7. - * "7+++v" is 7 + ++v, not 7++ + v. - * "--7" is - - 7, not --7. - * "++++a" is + + ++a, not ++ ++a. - */ - if ((expr[0] == '+' || expr[0] == '-') - && (expr[1] == expr[0]) - ) { - if (numstackptr == numstack || !numstackptr[-1].var) { /* not a VAR++ */ - char next = skip_whitespace(expr + 2)[0]; - if (!(isalpha(next) || next == '_')) { /* not a ++VAR */ - //bb_error_msg("special %c%c", expr[0], expr[0]); - op = (expr[0] == '+' ? TOK_ADD : TOK_SUB); - expr++; - goto tok_found1; - } - } - } - - p = op_tokens; - while (1) { - /* Compare expr to current op_tokens[] element */ - const char *e = expr; - while (1) { - if (*p == '\0') { - /* Match: operator is found */ - expr = e; - goto tok_found; - } - if (*p != *e) - break; - p++; - e++; - } - /* No match, go to next element of op_tokens[] */ - while (*p) - p++; - p += 2; /* skip NUL and TOK_foo bytes */ - if (*p == '\0') { - /* No next element, operator not found */ - //math_state->syntax_error_at = expr; - goto err; - } - } - tok_found: - op = p[1]; /* fetch TOK_foo value */ - tok_found1: - /* NB: expr now points past the operator */ - - /* post grammar: a++ reduce to num */ - if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC) - lasttok = TOK_NUM; - - /* Plus and minus are binary (not unary) _only_ if the last - * token was a number, or a right paren (which pretends to be - * a number, since it evaluates to one). Think about it. - * It makes sense. */ - if (lasttok != TOK_NUM) { - switch (op) { - case TOK_ADD: - op = TOK_UPLUS; - break; - case TOK_SUB: - op = TOK_UMINUS; - break; - case TOK_POST_INC: - op = TOK_PRE_INC; - break; - case TOK_POST_DEC: - op = TOK_PRE_DEC; - break; - } - } - /* We don't want an unary operator to cause recursive descent on the - * stack, because there can be many in a row and it could cause an - * operator to be evaluated before its argument is pushed onto the - * integer stack. - * But for binary operators, "apply" everything on the operator - * stack until we find an operator with a lesser priority than the - * one we have just extracted. If op is right-associative, - * then stop "applying" on the equal priority too. - * Left paren is given the lowest priority so it will never be - * "applied" in this way. - */ - prec = PREC(op); -//bb_error_msg("prec:%02x", prec); - if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) { - /* not left paren or unary */ - if (lasttok != TOK_NUM) { - /* binary op must be preceded by a num */ - goto err; - } - /* The algorithm employed here is simple: while we don't - * hit an open paren nor the bottom of the stack, pop - * tokens and apply them */ - while (stackptr != stack) { - operator prev_op = *--stackptr; - if (op == TOK_RPAREN) { -//bb_error_msg("op == TOK_RPAREN"); - if (prev_op == TOK_LPAREN) { -//bb_error_msg("prev_op == TOK_LPAREN"); -//bb_error_msg(" %p %p numstackptr[-1].var:'%s'", numstack, numstackptr-1, numstackptr[-1].var); - if (numstackptr[-1].var) { - /* Expression is (var), lookup now */ - errmsg = arith_lookup_val(math_state, &numstackptr[-1]); - if (errmsg) - goto err_with_custom_msg; - /* Erase var name: (var) is just a number, for example, (var) = 1 is not valid */ - numstackptr[-1].var = NULL; - } - /* Any operator directly after a - * close paren should consider itself binary */ - lasttok = TOK_NUM; - goto next; - } -//bb_error_msg("prev_op != TOK_LPAREN"); - } else { - operator prev_prec = PREC(prev_op); -//bb_error_msg("op != TOK_RPAREN"); - fix_assignment_prec(prec); - fix_assignment_prec(prev_prec); - if (prev_prec < prec - || (prev_prec == prec && is_right_associative(prec)) - ) { - stackptr++; - break; - } - } -//bb_error_msg("arith_apply(prev_op:%02x)", prev_op); - errmsg = arith_apply(math_state, prev_op, numstack, &numstackptr); - if (errmsg) - goto err_with_custom_msg; - } - if (op == TOK_RPAREN) - goto err; - } +#define a_SHEXP_ARITH_COMPAT_SHIMS +# define s64 arith_t +# if ENABLE_FEATURE_SH_MATH_64 +# define S64_MIN LLONG_MIN +# define u64 unsigned long long +# else +# define S64_MIN LONG_MIN +# define u64 unsigned long +# endif +# define savestr(X) xstrdup(X) +# define su_IDEC_STATE_EMASK (1u<<0) +# define su_IDEC_STATE_CONSUMED (1u<<1) +#define a_SHEXP_ARITH_COOKIE arith_state_t * +#if ENABLE_FEATURE_SH_MATH_ERROR_TRACK +# define a_SHEXP_ARITH_ERROR_TRACK +#endif - /* Push this operator to the stack and remember it */ -//bb_error_msg("push op:%02x", op); - *stackptr++ = lasttok = op; - next: ; - } /* while (1) */ +#define su_ienc_s64(X,Y,Z) (sprintf(X, ARITH_FMT, Y), X) +#define n_var_vlook(X,Y) (*self->sac_cookie->lookupvar)(X) +#define n_var_vset(X,Y,Z) (*self->sac_cookie->setvar)(X, (char*)(Y)) +#define su_idec_cp(A,B,C,D,E) a_idec_x(A, B, E) + +static inline uint32_t a_idec_x(void *resp, char const *cbuf, + char const **endptr_or_nil){ + uint32_t rv; + arith_t res; + char const *eptr; + + if(endptr_or_nil == NULL) + endptr_or_nil = &eptr; + + errno = 0; + res = strto_arith_t(cbuf, (char**)endptr_or_nil); + rv = 0; + if(errno == 0){ + if(**endptr_or_nil == '\0') + rv = su_IDEC_STATE_CONSUMED; + }else{ + rv = su_IDEC_STATE_EMASK; + res = 0; + } - err: - errmsg = "arithmetic syntax error"; - err_with_custom_msg: - numstack->val = -1; - ret: - math_state->errmsg = errmsg; - return numstack->val; + *(arith_t*)resp = res; + return rv; } +#include "shexp-arith.h" + arith_t FAST_FUNC arith(arith_state_t *math_state, const char *expr) { +#if ENABLE_FEATURE_SH_MATH_ERROR_TRACK + char *err_rest; +#endif + char const *emsg; + s64 res; + math_state->errmsg = NULL; - math_state->list_of_recursed_names = NULL; - return evaluate_string(math_state, expr); + + switch(a_shexp_arith_eval(math_state, &res, expr, UZ_MAX +#if ENABLE_FEATURE_SH_MATH_ERROR_TRACK + , &err_rest +#endif + )){ + default: + return res; +#undef a_X +#define a_X(X,N) case CONCAT(a_SHEXP_ARITH_ERR_,X): emsg = N_(N); break + a_X(NOMEM, "out of memory"); + a_X(SYNTAX, "syntax error"); + a_X(ASSIGN_NO_VAR, "assignment without variable"); + a_X(DIV_BY_ZERO, "division by zero"); + a_X(EXP_INVALID, "invalid exponent"); + a_X(NO_OP, "syntax error, expected operand"); + a_X(COND_NO_COLON, "syntax error, incomplete ?: condition"); + a_X(COND_PREC_INVALID, "?: condition, invalid precedence (1:v2:v3=3)"); + a_X(NAME_LOOP, "recursive variable name reference"); + a_X(OP_INVALID, "unknown operator"); + } +#undef a_X + + math_state->errmsg = +#if ENABLE_FEATURE_SH_MATH_ERROR_TRACK + xasprintf("%s (rest: %s)", emsg, err_rest); +#else + emsg +#endif + ; +#if ENABLE_FEATURE_SH_MATH_ERROR_TRACK + free(err_rest); +#endif + + return -1; } /* diff --git a/shell/math.h b/shell/math.h index 41ef6e8dfa..4bb8d5cdfa 100644 --- a/shell/math.h +++ b/shell/math.h @@ -76,11 +76,14 @@ typedef void FAST_FUNC (*arith_var_set_t)(const char *name, const char *v //typedef const char* FAST_FUNC (*arith_var_endofname_t)(const char *name); typedef struct arith_state_t { - const char *errmsg; + /* ENABLE_FEATURE_SH_MATH_ERROR_TRACK: must be free(3)d if !NULL */ +#if !ENABLE_FEATURE_SH_MATH_ERROR_TRACK + const +#endif + char *errmsg; arith_var_lookup_t lookupvar; arith_var_set_t setvar; // arith_var_endofname_t endofname; - void *list_of_recursed_names; } arith_state_t; arith_t FAST_FUNC arith(arith_state_t *state, const char *expr); diff --git a/shell/shexp-arith.h b/shell/shexp-arith.h new file mode 100644 index 0000000000..5f3655b456 --- /dev/null +++ b/shell/shexp-arith.h @@ -0,0 +1,1292 @@ +/*@ S-nail - a mail user agent derived from Berkeley Mail. + *@ Signed 64-bit sh(1)ell-style $(( ARITH ))metic expression evaluator. + *@ POW2 bases are parsed as unsigned, operation overflow -> limit constant(s), + *@ saturated mode is not supported, division by zero is handled via error. + *@ The expression length limit is ~100.000.000 on 32-bit, U32_MAX otherwise. + *@ After reading on Dijkstra's two stack algorithm, as well as bash:expr.c. + *@ Most heavily inspired by busybox -- conclusion: the Dijkstra algorithm + *@ scales very badly to ternary as are used to implement conditionals and + *@ their ignored sub-expressions. + *@ + *@ #define's: + *@ - a_SHEXP_ARITH_COMPAT_SHIMS: for inclusion in other code bases, setting + *@ this defines most necessary compat macros. + *@ We still need s64, u64, S64_MIN, savestr(CP) <> strdup(3) that does not + *@ return NIL (only with _ERROR_TRACK). Plus stdint.h, ctype.h, string.h. + *@ We need su_idec_cp(), su_ienc_s64(), n_var_vlook() and n_var_vset(). + *@ We need su_IDEC_STATE_EMASK (= 1) and su_IDEC_STATE_CONSUMED (= 2), e.g.: + *@ errno = 0; + *@ res = strto_arith_t(cbuf, (char**)endptr_or_nil); + *@ rv = 0; + *@ if(errno == 0){ + *@ if(**endptr_or_nil == '\0') + *@ rv = su_IDEC_STATE_CONSUMED; + *@ }else{ + *@ rv = su_IDEC_STATE_EMASK; + *@ res = 0; + *@ } + *@ *S(s64*,resp) = res; + *@ - a_SHEXP_ARITH_COOKIE: adds struct a_shexp_arith_ctx:sac_cookie, and + *@ a cookie arg to a_shexp_arith_eval(). + *@ - a_SHEXP_ARITH_ERROR_TRACK: add "char **error_track_or_nil" to + *@ a_shexp_arith_eval(), and according error stack handling, so that users + *@ can be given hint where an error occurred. ("Three stack algorithm.") + * + * Copyright (c) 2022 Steffen Nurpmeso <stef...@sdaoden.eu>. + * SPDX-License-Identifier: ISC + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* Tracking of error location in input */ +#ifdef a_SHEXP_ARITH_ERROR_TRACK +# undef a_SHEXP_ARITH_ERROR_TRACK +# define a_SHEXP_ARITH_ERROR_TRACK(X) X +#else +# define a_SHEXP_ARITH_ERROR_TRACK(X) +#endif + +/* IFS whitespace removal */ +#undef a_SHEXP_ARITH_IFS +#ifdef mx_SOURCE +# define a_SHEXP_ARITH_IFS(X) X +#else +# define a_SHEXP_ARITH_IFS(X) +#endif + +/* (Most necessary) Compat shims */ +#ifdef a_SHEXP_ARITH_COMPAT_SHIMS +# define boole bool +# define FAL0 false +# define TRU1 true +# define u8 uint8_t +# define u16 uint16_t +# define u32 uint32_t +# define U32_MAX UINT32_MAX +# define ul unsigned long +# define up uintptr_t +# define UZ_MAX SIZE_MAX +# define uz size_t +# define a_SHEXP_ISVARC(C) ((C) == '_' || isalnum(S(unsigned char,C))) +# define a_SHEXP_ISVARC_BAD1ST(C) su_cs_is_digit(C) +# define a_SHEXP_ISVARC_BADNST(C) FAL0 +# define ASSERT(X) +# define ASSERT_NYD_EXEC(X,Y) +# define BITENUM_IS(X,Y) X +# define CONCAT(S1,S2) su__CONCAT_1(S1, S2) +# define su__CONCAT_1(S1,S2) su__CONCAT_2(S1, S2) +# define su__CONCAT_2(S1,S2) S1 ## S2 +# define DBGX(X) +# define FALLTHRU +# define N_(X) X +# define NIL NULL +# define NYD_IN S(void,0) +# define NYD2_IN S(void,0) +# define NYD_OU S(void,0) +# define NYD2_OU S(void,0) +# define P2UZ(X) S(size_t,X) +# define S(X,Y) ((X)(Y)) +# define su_ALIGNOF(X) ((sizeof(X) + 15) & ~15) +# define su_COMMA , +# define su_cs_cmp(X,Y) strcmp(X, Y) +# define su_cs_is_digit(X) isdigit(S(unsigned char,X)) +# define su_cs_is_space(X) isspace(S(unsigned char,X)) +# define su_empty "" +# define su_IDEC_STATE_EBASE 0 /* (could cause $CC optimiz.) */ +# define su_IENC_BUFFER_SIZE 80u +# define su_LOFI_ALLOC(X) alloca(X) +# define su_LOFI_FREE(X) +# define su_mem_move(X,Y,Z) memmove(X, Y, Z) +# define STRUCT_ZERO(X,Y) memset(Y, 0, sizeof(X)) +# define UNLIKELY(X) X +# define UNUSED(X) S(void,X) +# if LONG_MAX - 1 > 0x7FFFFFFFl - 1 +# define su_64(X) X +# else +# define su_64(X) +# endif +#endif /* a_SHEXP_ARITH_COMPAT_SHIMS */ + +/* -- >8 -- 8< -- */ + +#if 1 +# define a_SHEXP_ARITH_DBG 0 +# define a_SHEXP_ARITH_L(X) +#else +# define a_SHEXP_ARITH_DBG 1 +# define a_SHEXP_ARITH_L(X) a_shexp__arith_log X +#endif + +/* We parse with base 0: set _RESCAN to allow "I=' -10';$((10#$I))" */ +#define a_SHEXP_ARITH_IDEC_MODE (su_IDEC_MODE_SIGNED_TYPE |\ + su_IDEC_MODE_POW2BASE_UNSIGNED | su_IDEC_MODE_LIMIT_NOERROR |\ + su_IDEC_MODE_BASE0_NUMBER_SIGN_RESCAN) + +enum a_shexp_arith_error{ + a_SHEXP_ARITH_ERR_NONE, + a_SHEXP_ARITH_ERR_NOMEM, /* Out of memory */ + a_SHEXP_ARITH_ERR_SYNTAX, /* General syntax error */ + a_SHEXP_ARITH_ERR_ASSIGN_NO_VAR, /* Assignment without variable */ + a_SHEXP_ARITH_ERR_DIV_BY_ZERO, + a_SHEXP_ARITH_ERR_EXP_INVALID, /* Invalid exponent */ + a_SHEXP_ARITH_ERR_NO_OP, /* Expected an argument here */ + a_SHEXP_ARITH_ERR_COND_NO_COLON, /* Incomplete ?: condition */ + a_SHEXP_ARITH_ERR_COND_PREC_INVALID, /* 1 ? VAR1 : VAR2 = 3 */ + a_SHEXP_ARITH_ERR_NAME_LOOP, /* Variable self-reference loop */ + a_SHEXP_ARITH_ERR_OP_INVALID /* Unknown operator */ +}; + +/* Operators and precedences in increasing precedence order. + * (The operator stack as such is u16: [OP_FLAGS |] (OP<<8) | PREC.) */ +enum a_shexp_arith_ops{ +#undef a_X +#define a_X(N,P,O) \ + CONCAT(a_SHEXP_ARITH_PREC_,N) = CONCAT(P,u),\ + CONCAT(a_SHEXP_ARITH_OP_,N) =\ + (CONCAT(O,u) << 8) | CONCAT(a_SHEXP_ARITH_PREC_,N) + + a_X(PAREN_LEFT, 0, 0), + + a_X(COMMA, 1, 0), + + a_X(ASSIGN, 2, 0), + a_X(ASSIGN_BIT_OR, 2, 1), + a_X(ASSIGN_BIT_XOR, 2, 2), + a_X(ASSIGN_BIT_AND, 2, 3), + a_X(ASSIGN_SHIFT_LEFT, 2, 4), a_X(ASSIGN_SHIFT_RIGHT, 2, 5), + a_X(ASSIGN_SHIFT_RIGHTU, 2, 6), + a_X(ASSIGN_ADD, 2, 7), a_X(ASSIGN_SUB, 2, 8), + a_X(ASSIGN_MUL, 2, 9), a_X(ASSIGN_DIV, 2, 10), a_X(ASSIGN_MOD, 2, 11), + a_X(ASSIGN_EXP, 2, 12), + + a_X(COND, 3, 0), + a_X(COND_COLON, 3, 1), + + a_X(OR, 4, 0), + a_X(AND, 5, 0), + a_X(BIT_OR, 6, 0), + a_X(BIT_XOR, 7, 0), + a_X(BIT_AND, 8, 0), + a_X(EQ, 9, 0), a_X(NE, 9, 1), + a_X(LE, 10, 0), a_X(GE, 10, 1), a_X(LT, 10, 2), a_X(GT, 10, 3), + a_X(SHIFT_LEFT, 11, 0), a_X(SHIFT_RIGHT, 11, 1), a_X(SHIFT_RIGHTU, 11, 2), + a_X(ADD, 12, 0), a_X(SUB, 12, 1), + a_X(MUL, 13, 0), a_X(DIV, 13, 1), a_X(MOD, 13, 2), + a_X(EXP, 14, 0), + + /* Further operators are unary, pre- or postfix */ + a_SHEXP_ARITH_PREC_UNARY = 15, + a_SHEXP_ARITH_PREC_PREFIX = 16, + a_SHEXP_ARITH_PREC_POSTFIX = 18, + + a_X(UNARY_NOT, 15, 0), a_X(UNARY_BIT_NOT, 15, 1), + a_X(PREFIX_INC, 16, 0), a_X(PREFIX_DEC, 16, 1), + a_X(UNARY_PLUS, 17, 1), a_X(UNARY_MINUS, 17, 0), + a_X(POSTFIX_INC, 18, 0), a_X(POSTFIX_DEC, 18, 1), + + /* Beyond operator profanity; the first "is a number" */ + a_SHEXP_ARITH_PREC_SKY = 19, + a_X(NUM, 19, 0), a_X(PAREN_RIGHT, 19, 1), + +#undef a_X +}; + +enum arith_op_flags{ + /* Mask off operator and precision */ + a_SHEXP_ARITH_OP_MASK = 0x1FFF, + a_SHEXP_ARITH_OP_FLAG_COND_SAW_COLON = 1u<<13, + a_SHEXP_ARITH_OP_FLAG_OUTER_WHITEOUT = 1u<<14, + a_SHEXP_ARITH_OP_FLAG_WHITEOUT = 1u<<15, + a_SHEXP_ARITH_OP_FLAG_WHITE_MASK = a_SHEXP_ARITH_OP_FLAG_OUTER_WHITEOUT | + a_SHEXP_ARITH_OP_FLAG_WHITEOUT, + a_SHEXP_ARITH_OP_FLAG_MASK = a_SHEXP_ARITH_OP_FLAG_COND_SAW_COLON | + a_SHEXP_ARITH_OP_FLAG_WHITE_MASK +}; + +struct a_shexp_arith_name_stack{ + struct a_shexp_arith_name_stack *sans_last; + char const *sans_var; +}; + +struct a_shexp_arith_val{ + s64 sav_val; + char *sav_var; /* Named variable or NIL */ +}; + +struct a_shexp_arith_stack{ + struct a_shexp_arith_val *sas_nums; + struct a_shexp_arith_val *sas_nums_top; + u16 *sas_ops; + u16 *sas_ops_top; + a_SHEXP_ARITH_ERROR_TRACK( + char **sas_error_track; + char **sas_error_track_top; + ) +}; + +struct a_shexp_arith_ctx{ + enum a_shexp_arith_error sac_error; + boole sac_have_error_track; + u8 sac__pad[3]; + s64 sac_rv; + struct a_shexp_arith_stack *sac_stack; + struct a_shexp_arith_name_stack *sac_name_stack; + a_SHEXP_ARITH_ERROR_TRACK( char **sac_error_track_or_nil; ) + a_SHEXP_ARITH_IFS( char const *sac_ifs_ws; ) +#ifdef a_SHEXP_ARITH_COOKIE + a_SHEXP_ARITH_COOKIE sac_cookie; +#endif +}; + +/* Sort by ~expected usage -- however, longest first if ambiguous! + * Follow busybox, save space by compressing data in char[] not struct[]! + * (XXX Instead use 1-st byte jump table like for commands) */ +static char const a_shexp_arith_op_toks[] = { +#undef a_X +#define a_X(X) \ + S(char,(CONCAT(a_SHEXP_ARITH_OP_,X) & 0xFF00u) >> 8),\ + S(char,CONCAT(a_SHEXP_ARITH_PREC_,X)) + + '+','+','\0', a_X(POSTFIX_INC), + '+','=','\0', a_X(ASSIGN_ADD), + '+','\0', a_X(ADD), + '-','-','\0', a_X(POSTFIX_DEC), + '-','=','\0', a_X(ASSIGN_SUB), + '-','\0', a_X(SUB), + '*','*','=','\0', a_X(ASSIGN_EXP), + '*','*','\0', a_X(EXP), + '*','=','\0', a_X(ASSIGN_MUL), + '*','\0', a_X(MUL), + '/','=','\0', a_X(ASSIGN_DIV), + '/','\0', a_X(DIV), + '%','=','\0', a_X(ASSIGN_MOD), + '%','\0', a_X(MOD), + '|','|','\0', a_X(OR), + '|','=','\0', a_X(ASSIGN_BIT_OR), + '|','\0', a_X(BIT_OR), + '^','=','\0', a_X(ASSIGN_BIT_XOR), + '^','\0', a_X(BIT_XOR), + '&','&','\0', a_X(AND), + '&','=','\0', a_X(ASSIGN_BIT_AND), + '&','\0', a_X(BIT_AND), + '<','<','=',0, a_X(ASSIGN_SHIFT_LEFT), + '<','<','\0', a_X(SHIFT_LEFT), + '>','>','>','=',0, a_X(ASSIGN_SHIFT_RIGHTU), + '>','>','>','\0', a_X(SHIFT_RIGHTU), + '>','>','=',0, a_X(ASSIGN_SHIFT_RIGHT), + '>','>','\0', a_X(SHIFT_RIGHT), + + '~','\0', a_X(UNARY_BIT_NOT), + '!','=','\0', a_X(NE), + '!','\0', a_X(UNARY_NOT), + + ')','\0', a_X(PAREN_RIGHT), + '(','\0', a_X(PAREN_LEFT), + ',','\0', a_X(COMMA), + + '<','=','\0', a_X(LE), + '>','=','\0', a_X(GE), + '=','=','\0', a_X(EQ), + '<','\0', a_X(LT), + '>','\0', a_X(GT), + '=','\0', a_X(ASSIGN), + + '?','\0', a_X(COND), + ':','\0', a_X(COND_COLON), + + '\0' +#undef a_X +}; + +/* Our "public" entry point. exp_buf can be NIL if exp_len is 0, it need not + * be NUL terminated (stop for NUL or out of length). + * Upon error *error_track_or_nil is set to a "newly allocated" string that + * points to where parse stopped, or NIL upon initial setup failure. */ +static enum a_shexp_arith_error a_shexp_arith_eval( +#ifdef a_SHEXP_ARITH_COOKIE + a_SHEXP_ARITH_COOKIE cookie, +#endif + s64 *resp, char const *exp_buf, uz exp_len + a_SHEXP_ARITH_ERROR_TRACK( su_COMMA char **error_track_or_nil )); + +static void a_shexp__arith_eval(struct a_shexp_arith_ctx *self, + char const *exp_buf, uz exp_len); + +/* Count non-WS as well as normalized WS ([:"space":]+ -> ' ') in exp_buf, + * return count. If store!=NIL, also copy normalization. + * An all-WS exp_buf returns 0 */ +static uz a_shexp__arith_ws_squeeze(struct a_shexp_arith_ctx *self, + char const *exp_buf, uz exp_len, char *store_or_nil); + +/* Resolve and evaluate the "self-contained string" savp->sav_var. + * Take care to avoid name lookup loops */ +static boole a_shexp__arith_val_eval(struct a_shexp_arith_ctx *self, + struct a_shexp_arith_val *savp); + +/* Work top of the stack, which may pop & push etc */ +static boole a_shexp__arith_op_apply(struct a_shexp_arith_ctx *self); + +static boole a_shexp__arith_op_apply_colons(struct a_shexp_arith_ctx *self); + +#if a_SHEXP_ARITH_DBG +static void a_shexp__arith_log(char const *fmt, ...); +#endif + +static enum a_shexp_arith_error +a_shexp_arith_eval( +#ifdef a_SHEXP_ARITH_COOKIE + a_SHEXP_ARITH_COOKIE cookie, +#endif + s64 *resp, char const *exp_buf, uz exp_len + a_SHEXP_ARITH_ERROR_TRACK( su_COMMA char **error_track_or_nil )){ + struct a_shexp_arith_stack sas_stack; + struct a_shexp_arith_ctx self; + NYD_IN; + + a_SHEXP_ARITH_L(("> arith_eval %zu <%.*s>\n", + exp_len, S(int,exp_len != UZ_MAX ? exp_len : su_cs_len(exp_buf)), + exp_buf)); + + a_SHEXP_ARITH_ERROR_TRACK(DBGX( + if(error_track_or_nil != NIL) + *error_track_or_nil = NIL; + )); + + STRUCT_ZERO(struct a_shexp_arith_ctx, &self); +#ifdef a_SHEXP_ARITH_COOKIE + self.sac_cookie = cookie; +#endif + a_SHEXP_ARITH_ERROR_TRACK( + if((self.sac_error_track_or_nil = error_track_or_nil) != NIL) + self.sac_have_error_track = TRU1; + ) + + ASSERT_NYD_EXEC(resp != NIL, + self.sac_error = a_SHEXP_ARITH_ERR_NO_OP); + DBGX( *resp = 0; ) + ASSERT_NYD_EXEC(exp_len == 0 || exp_buf != NIL, + self.sac_error = a_SHEXP_ARITH_ERR_NO_OP); + + a_SHEXP_ARITH_IFS( self.sac_ifs_ws = ok_vlook(ifs_ws); ) + self.sac_stack = &sas_stack; + a_shexp__arith_eval(&self, exp_buf, exp_len); + *resp = self.sac_rv; + + a_SHEXP_ARITH_L(("< arith_eval %zu <%.*s> -> <%lld> ERR<%d>\n", + exp_len, S(int,exp_len != UZ_MAX ? exp_len : su_cs_len(exp_buf)), + exp_buf, self.sac_rv, self.sac_error)); + + NYD_OU; + return self.sac_error; +} + +static void +a_shexp__arith_eval(struct a_shexp_arith_ctx *self, + char const *exp_buf, uz exp_len){ + char *ep, *varp, *cp, c; + u16 lop; + struct a_shexp_arith_stack *sasp; + void *mem_p; + NYD2_IN; + + a_SHEXP_ARITH_L((" > _arith_eval %zu <%.*s>\n", + exp_len, S(int,exp_len != UZ_MAX ? exp_len : su_cs_len(exp_buf)), + exp_buf)); + + mem_p = NIL; + sasp = self->sac_stack; + + /* Create a single continuous allocation for anything */ + /* C99 */{ + union {void *v; char *c;} p; + uz i, j, a; + + /* Done for empty expression */ + if((i = a_shexp__arith_ws_squeeze(self, exp_buf, exp_len, NIL)) == 0) + goto jleave; + ++i; + + /* Overflow check: since arithmetic expressions are rarely long enough + * to come near this limit, xxx laxe & fuzzy, not exact; max U32_MAX! */ + if(su_64( i > U32_MAX || ) i >= UZ_MAX / 2 || + i >= ((UZ_MAX - (i a_SHEXP_ARITH_ERROR_TRACK( * 2))) / + ((su_ALIGNOF(*sasp->sas_nums) + sizeof(*sasp->sas_ops) * 2) + a_SHEXP_ARITH_ERROR_TRACK( + + sizeof(*sasp->sas_error_track) * 2 )) + )){ + self->sac_error = a_SHEXP_ARITH_ERR_NOMEM; + goto jleave; + } + + ++i; + j = su_ALIGNOF(*sasp->sas_nums) * (i >> 1); + a = j + (sizeof(*sasp->sas_ops) * i) + + a_SHEXP_ARITH_ERROR_TRACK( (sizeof(*sasp->sas_error_track) * i) + ) + 1 + (i a_SHEXP_ARITH_ERROR_TRACK( * 2 )); + mem_p = p.v = su_LOFI_ALLOC(a); + if(p.v == NIL){ + /* (For MX LOFI has _MUSTFAIL set though) */ + self->sac_error = a_SHEXP_ARITH_ERR_NOMEM; + goto jleave; + } + sasp->sas_nums = sasp->sas_nums_top = S(struct a_shexp_arith_val*,p.v); + p.c += j; + sasp->sas_ops = sasp->sas_ops_top = S(u16*,p.v); + p.c += sizeof(*sasp->sas_ops) * i; + a_SHEXP_ARITH_ERROR_TRACK( + sasp->sas_error_track_top = sasp->sas_error_track = S(char**,p.v); + p.c += sizeof(*sasp->sas_error_track) * i; + ) + + ep = ++p.c; /* ++ to copy varnames in !_ARITH_ERROR cases */ + i = a_shexp__arith_ws_squeeze(self, exp_buf, exp_len, ep); + varp = &ep[ +#if 0 a_SHEXP_ARITH_ERROR_TRACK( + 1) + i + 1 +#else + -1 +#endif + ]; + + a_SHEXP_ARITH_L((" ! _arith_eval ALLOC <%lu> " + "nums=%p (%lu) ops=%p varp=%p %lu <%s>\n", + S(ul,a), sasp->sas_nums, S(ul,j / su_ALIGNOF(*sasp->sas_nums)), + sasp->sas_ops, varp, S(ul,i - 1), ep)); + } + + /* Start with a left paren */ + a_SHEXP_ARITH_ERROR_TRACK( *sasp->sas_error_track_top++ = ep; ) + *sasp->sas_ops_top++ = lop = a_SHEXP_ARITH_OP_PAREN_LEFT; + + for(;;) Jouter:{ + u16 op; + + a_SHEXP_ARITH_L((" = _arith_eval TICK LOP <0x%02X %u> " + "nums=%lu ops=%lu DATA %lu <%s>\n", + lop, lop & 0xFF, S(ul,sasp->sas_nums_top - sasp->sas_nums), + S(ul,sasp->sas_ops_top - sasp->sas_ops), S(ul,su_cs_len(ep)), ep)); + + if(*ep == '\0'){ + /* At the end of the expression pop anything left. + * Assume we have read PAREN_RIGHT */ + if(exp_buf != NIL){ + exp_buf = NIL; + op = a_SHEXP_ARITH_OP_PAREN_RIGHT; + /* Could fail for "1)" (how could that enter at all?) + * ASSERT(sasp->sas_ops_top > sasp->sas_ops); + * Can only be a syntax error! */ + if(sasp->sas_ops_top == sasp->sas_ops){ + self->sac_error = a_SHEXP_ARITH_ERR_SYNTAX; + break; + } + goto jtok_go; + } + + /* After PAREN_RIGHT, we must be finished */ + if(sasp->sas_nums_top != &sasp->sas_nums[1]) + self->sac_error = a_SHEXP_ARITH_ERR_SYNTAX; + break; + } + + /* Skip (normalized) WS now */ + if(*ep == ' ') + ++ep; + ASSERT(!su_cs_is_space(*ep)); + + /* A number? */ + if(su_cs_is_digit(*ep)){ + BITENUM_IS(u32,su_idec_state) is; + + is = su_idec_cp(&sasp->sas_nums_top->sav_val, ep, 0, + a_SHEXP_ARITH_IDEC_MODE, S(char const**,&ep)); + if((is &= su_IDEC_STATE_EMASK) && is != su_IDEC_STATE_EBASE) + sasp->sas_nums_top->sav_val = 0; + sasp->sas_nums_top->sav_var = NIL; + + ++sasp->sas_nums_top; + lop = a_SHEXP_ARITH_OP_NUM; + a_SHEXP_ARITH_L((" + _arith_eval NUM <%lld>\n", + sasp->sas_nums_top[-1].sav_val)); + continue; + } + + /* Is it a variable name? */ + for(cp = ep; (c = *cp, a_SHEXP_ISVARC(c)); ++cp) + if(cp == ep && a_SHEXP_ISVARC_BAD1ST(c)) + break; + + if(cp != ep){ + for(;;){ + c = cp[-1]; + /* (For example, hyphen-minus as a sh(1) extension!) */ + if(!a_SHEXP_ISVARC_BADNST(c)) + break; + if(--cp == ep){ + self->sac_error = a_SHEXP_ARITH_ERR_SYNTAX; + goto jleave; + } + } + + /* Copy over to pre-allocated var storage */ + /* C99 */{ + uz i; + + i = P2UZ(cp - ep); + /* (move not copy for !_ARITH_ERROR cases (says ISO C?)) */ + su_mem_move(sasp->sas_nums_top->sav_var = varp, ep, i); + varp += i; + *varp++ = '\0'; + } + ep = cp; + + ++sasp->sas_nums_top; + lop = a_SHEXP_ARITH_OP_NUM; + + a_SHEXP_ARITH_L((" + _arith_eval VAR <%s>\n", + sasp->sas_nums_top[-1].sav_var)); + continue; + } + + /* An operator. + * We turn prefix operators to multiple unary plus/minus if + * not pre- or post-attached to a variable name (++10 -> + + 10). + * (We adjust postfix to prefix below) */ + if((ep[0] == '+' || ep[0] == '-') && (ep[1] == ep[0])){ + if(sasp->sas_nums_top == sasp->sas_nums || + sasp->sas_nums_top[-1].sav_var == NIL){ + if((c = ep[2]) == ' ') + c = ep[3]; + + if(c != '\0' && (!a_SHEXP_ISVARC(c) || a_SHEXP_ISVARC_BAD1ST(c))){ + op = (ep[0] == '+') ? a_SHEXP_ARITH_OP_ADD + : a_SHEXP_ARITH_OP_SUB; + ++ep; + a_SHEXP_ARITH_L((" + _arith_eval OP PREFIX INC/DEC SPLIT " + "<%c%c> -> <%c>\n", ep[0], ep[0], ep[0])); + goto jtok_go; + } + } + } + + /* Operator search */ + /* C99 */{ + char const *tokp; + + /* 3=NUL+OP+PREC */ + for(tokp = a_shexp_arith_op_toks; *tokp != '\0'; tokp += 3){ + for(cp = ep;; ++tokp, ++cp){ + if(*tokp == '\0'){ + ep = cp; + op = (S(u16,tokp[1]) << 8) | S(u8,tokp[2]); + goto jtok_go; + }else if(*tokp != *cp) + break; + } + + while(*tokp != '\0') + ++tokp; + } + self->sac_error = a_SHEXP_ARITH_ERR_OP_INVALID; + goto jleave; + } + +jtok_go:/* C99 */{ + u8 prec; + + prec = op & 0xFF; + a_SHEXP_ARITH_L((" + _arith_eval OP <0x%02X %u> LOP <0x%02X %u> " + "nums=%lu ops=%lu %lu <%s>\n", + op, prec, lop, lop & 0xFF, S(ul,sasp->sas_nums_top - sasp->sas_nums), + S(ul,sasp->sas_ops_top - sasp->sas_ops), S(ul,su_cs_len(ep)), ep)); + + if(op == a_SHEXP_ARITH_OP_UNARY_PLUS){ + a_SHEXP_ARITH_L((" + _arith_eval IGNORE UNARY PLUS\n")); + continue; + } + + /* Correct our understanding of what there is. + * Post grammar: VAR++ reduces to num */ + if((lop & 0xFF) == a_SHEXP_ARITH_PREC_POSTFIX){ + lop = a_SHEXP_ARITH_OP_NUM; + a_SHEXP_ARITH_L((" + _arith_eval LOP POSTFIX REDUCED to NUM\n")); + } + /* Adjust some binary/postfix operators to make them flow */ + else if(lop != a_SHEXP_ARITH_OP_NUM){ + switch(op){ + case a_SHEXP_ARITH_OP_ADD: + a_SHEXP_ARITH_L((" + _arith_eval OP ADJUST: IGNORE UNARY PLUS\n")); + continue; + case a_SHEXP_ARITH_OP_SUB: + op = a_SHEXP_ARITH_OP_UNARY_MINUS; + goto junapre; + case a_SHEXP_ARITH_OP_POSTFIX_INC: + op = a_SHEXP_ARITH_OP_PREFIX_INC; + goto junapre; + case a_SHEXP_ARITH_OP_POSTFIX_DEC: + op = a_SHEXP_ARITH_OP_PREFIX_DEC; +junapre: + prec = a_SHEXP_ARITH_PREC_PREFIX; + a_SHEXP_ARITH_L((" + _arith_eval OP ADJUST TO UNARY/PREFIX\n")); + break; + } + } + /* Special: +10++VAR -> +10 + +VAR. (Since we do handle +10++11 + * correctly via "prefix split", we should also handle this) */ + else if(prec == a_SHEXP_ARITH_PREC_POSTFIX){ + ASSERT(lop == a_SHEXP_ARITH_OP_NUM); + if((c = ep[0]) == ' ') + c = ep[1]; + if(c != '\0' && (a_SHEXP_ISVARC(c) && !a_SHEXP_ISVARC_BAD1ST(c))){ + c = *--ep; + op = (c == '+') ? a_SHEXP_ARITH_OP_ADD : a_SHEXP_ARITH_OP_SUB; + prec = op & 0xFF; + a_SHEXP_ARITH_L((" + _arith_eval OP POSTFIX INC/DEC SPLIT " + "<%c%c> -> <%c>\n", c, c, c)); + } + } + + /* Check whether we can work it a bit */ + if((prec > a_SHEXP_ARITH_PREC_PAREN_LEFT && + prec < a_SHEXP_ARITH_PREC_UNARY) || + prec >= a_SHEXP_ARITH_PREC_SKY){ + if(lop != a_SHEXP_ARITH_OP_NUM){ + self->sac_error = a_SHEXP_ARITH_ERR_NO_OP; + goto jleave; + } + + /* Pop as much as possible */ + while(sasp->sas_ops_top != sasp->sas_ops){ + a_SHEXP_ARITH_ERROR_TRACK( --sasp->sas_error_track_top; ) + lop = *--sasp->sas_ops_top & a_SHEXP_ARITH_OP_MASK; + + a_SHEXP_ARITH_L((" + _arith_eval TRY POP - OP " + "<0x%02X %u>, NEW LOP <0x%02X %u 0x%X> nums=%lu ops=%lu\n", + op, op & 0xFF, lop, lop & 0xFF, + (*sasp->sas_ops_top & a_SHEXP_ARITH_OP_FLAG_MASK), + S(ul,sasp->sas_nums_top - sasp->sas_nums), + S(ul,sasp->sas_ops_top - sasp->sas_ops))); + + /* Special-case parenthesis groups */ + if(op == a_SHEXP_ARITH_OP_PAREN_RIGHT){ + if(lop == a_SHEXP_ARITH_OP_PAREN_LEFT){ + ASSERT(sasp->sas_nums_top > sasp->sas_nums); + /* Resolve VAR to NUM */ + if(sasp->sas_nums_top[-1].sav_var != NIL){ + ASSERT(!(*sasp->sas_ops_top & + a_SHEXP_ARITH_OP_FLAG_WHITE_MASK)); + if(!a_shexp__arith_val_eval(self, + &sasp->sas_nums_top[-1])) + goto jleave; + } + sasp->sas_nums_top[-1].sav_var = NIL; + a_SHEXP_ARITH_L((" + _arith_eval OP () RESOLVED <%lld>\n", + sasp->sas_nums_top[-1].sav_val)); + lop = a_SHEXP_ARITH_OP_NUM; + goto Jouter; + } + }else{ + u8 lprec; + + lprec = lop & 0xFF; + + /* */ + if(op == a_SHEXP_ARITH_OP_COND){ + u16 x; + + x = *sasp->sas_ops_top; + x &= a_SHEXP_ARITH_OP_FLAG_MASK; + if(x & a_SHEXP_ARITH_OP_FLAG_WHITEOUT){ + x ^= a_SHEXP_ARITH_OP_FLAG_WHITEOUT; + x |= a_SHEXP_ARITH_OP_FLAG_OUTER_WHITEOUT; + } + op |= x; + + /* Resolve as resolve can, need to assert our condition! */ + while(lprec > a_SHEXP_ARITH_PREC_COND){ + if(!a_shexp__arith_op_apply(self)) + goto jleave; + a_SHEXP_ARITH_ERROR_TRACK( --sasp->sas_error_track_top; ) + lop = *--sasp->sas_ops_top & a_SHEXP_ARITH_OP_MASK; + lprec = lop & 0xFF; + } + + /* Evaluate condition assertion */ + ASSERT(sasp->sas_nums_top > sasp->sas_nums); + --sasp->sas_nums_top; + + if(sasp->sas_nums_top->sav_var != NIL){ + if(!(op & a_SHEXP_ARITH_OP_FLAG_WHITE_MASK) && + !a_shexp__arith_val_eval(self, sasp->sas_nums_top)) + goto jleave; + sasp->sas_nums_top->sav_var = NIL; + } + + if((sasp->sas_nums_top)->sav_val == 0) + op |= a_SHEXP_ARITH_OP_FLAG_WHITEOUT; + op |= *sasp->sas_ops_top & a_SHEXP_ARITH_OP_FLAG_MASK; + + /* Delay ternary: this ? op will last until we can resolve + * the entire condition, its number stack position is used + * as storage for the actual condition result */ + a_SHEXP_ARITH_ERROR_TRACK( ++sasp->sas_error_track_top; ) + ++sasp->sas_ops_top; + break; + }else if(op == a_SHEXP_ARITH_OP_COND_COLON){ + uz recur; + u16 *opsp, x; + boole delay; + + delay = TRU1; + + /* Find our counterpart ? so we can toggle whiteout */ + opsp = sasp->sas_ops_top; + for(recur = 1;; --opsp){ + if(opsp == sasp->sas_ops){ + self->sac_error = a_SHEXP_ARITH_ERR_SYNTAX; + goto jleave; + } + + x = *opsp & a_SHEXP_ARITH_OP_MASK; + if(x == a_SHEXP_ARITH_OP_COND_COLON) + ++recur; + else if(x == a_SHEXP_ARITH_OP_COND && --recur == 0){ + *opsp |= a_SHEXP_ARITH_OP_FLAG_COND_SAW_COLON; + break; + } + } + op |= *opsp & a_SHEXP_ARITH_OP_FLAG_MASK; + op ^= a_SHEXP_ARITH_OP_FLAG_WHITEOUT; + + /* Resolve innermost condition asap. + * In "1?0?5:6:3", resolve innermost upon :3 */ + while(lprec > a_SHEXP_ARITH_PREC_PAREN_LEFT && + lprec != a_SHEXP_ARITH_PREC_COND){ + if(!a_shexp__arith_op_apply(self)) + goto jleave; + a_SHEXP_ARITH_ERROR_TRACK( --sasp->sas_error_track_top; ) + lop = *--sasp->sas_ops_top & a_SHEXP_ARITH_OP_MASK; + lprec = lop & 0xFF; + } + + /* If at a COLON we have to resolve further, otherwise syntax + * error would happen for 1?2?3:6:7 (due to how Dijkstra's + * algorithm applies, and our squeezing of ?: constructs) */ + if(lop == a_SHEXP_ARITH_OP_COND_COLON){ + delay = FAL0; + if(!a_shexp__arith_op_apply_colons(self)) + goto jleave; + lop = *sasp->sas_ops_top & a_SHEXP_ARITH_OP_MASK; + } + + if(lop != a_SHEXP_ARITH_OP_COND){ + self->sac_error = a_SHEXP_ARITH_ERR_SYNTAX; + goto jleave; + } + + if(delay){ + a_SHEXP_ARITH_ERROR_TRACK( ++sasp->sas_error_track_top; ) + ++sasp->sas_ops_top; + } + a_SHEXP_ARITH_L((" + _arith_eval %sTERNARY ?:%s\n", + (delay ? "DELAY " : su_empty), + ((op & a_SHEXP_ARITH_OP_FLAG_WHITE_MASK) + ? " WHITEOUT" : su_empty))); + break; + } + /* Is this a right-associative operation? */ + else{ + boole doit; + + doit = FAL0; + if(lprec < prec){ + doit = TRU1; + a_SHEXP_ARITH_L((" + _arith_eval DELAY PRECEDENCE\n")); + }else if(lprec == prec && prec == a_SHEXP_ARITH_PREC_ASSIGN){ + doit = TRU1; + a_SHEXP_ARITH_L((" + _arith_eval DELAY RIGHT ASSOC\n")); + }else if(lprec == a_SHEXP_ARITH_PREC_COND){ + if(lop == a_SHEXP_ARITH_OP_COND){ + doit = TRU1; + a_SHEXP_ARITH_L((" + _arith_eval DELAY CONDITION\n")); + } + /* Without massive rewrite this is the location to detect + * in-whiteout precedence bugs as in + * $((0?I1=10:(1?I3:I2=12))) + * which would be parsed like (1?I3:I2)=12 without error + * (different to 0?I3:I2=12) otherwise */ + else if(op != a_SHEXP_ARITH_OP_COMMA){ + self->sac_error = a_SHEXP_ARITH_ERR_COND_PREC_INVALID; + goto jleave; + } + } + + if(doit){ + /* If we are about to delay and LHV is a VAR, expand that + * immediately to expand in correct order things like + * I1=I2=10 I2=3; echo $((I1,I2)) + * I1=I2=10 I2=3; echo $((I1+=I2)) */ + if(sasp->sas_nums_top[-1].sav_var != NIL){ + if(op != a_SHEXP_ARITH_OP_ASSIGN && + !(*sasp->sas_ops_top & + a_SHEXP_ARITH_OP_FLAG_WHITE_MASK) && + !a_shexp__arith_val_eval(self, + &sasp->sas_nums_top[-1])) + goto jleave; + if(prec != a_SHEXP_ARITH_PREC_ASSIGN) + sasp->sas_nums_top[-1].sav_var = NIL; + } + + a_SHEXP_ARITH_ERROR_TRACK( ++sasp->sas_error_track_top; ) + ++sasp->sas_ops_top; + break; + } + } + } + + /* */ + if(!a_shexp__arith_op_apply(self)) + goto jleave; + + if(lop == a_SHEXP_ARITH_OP_COND_COLON){ + ASSERT(sasp->sas_ops_top > sasp->sas_ops && + &sasp->sas_ops_top[-1] > sasp->sas_ops); + ASSERT((sasp->sas_ops_top[-1] & a_SHEXP_ARITH_OP_MASK + ) == a_SHEXP_ARITH_OP_COND); + a_SHEXP_ARITH_ERROR_TRACK( --sasp->sas_error_track_top; ) + --sasp->sas_ops_top; + } + } + + /* Should have been catched in *ep==\0,exp_buf!=NIL case */ + ASSERT(op != a_SHEXP_ARITH_OP_PAREN_RIGHT); + } + + /* Push this operator to the stack and remember it */ + a_SHEXP_ARITH_ERROR_TRACK( *sasp->sas_error_track_top++ = ep; ) + if(sasp->sas_ops_top > sasp->sas_ops && + (op & 0xFF) != a_SHEXP_ARITH_PREC_COND) + op |= sasp->sas_ops_top[-1] & a_SHEXP_ARITH_OP_FLAG_MASK; + *sasp->sas_ops_top++ = op; + lop = op & a_SHEXP_ARITH_OP_MASK; + a_SHEXP_ARITH_L((" + _arith_eval OP PUSH <0x%02X %u> nums=%lu ops=%lu\n", + op, (op & 0xFF), S(ul,sasp->sas_nums_top - sasp->sas_nums), + S(ul,sasp->sas_ops_top - sasp->sas_ops))); + } + } + + self->sac_rv = sasp->sas_nums->sav_val; + +jleave: +#if 0 a_SHEXP_ARITH_ERROR_TRACK( + 1 ) + if(self->sac_error != a_SHEXP_ARITH_ERR_NONE && mem_p != NIL && + self->sac_have_error_track){ + if(sasp->sas_error_track_top > sasp->sas_error_track) + --sasp->sas_error_track_top; + *self->sac_error_track_or_nil = savestr(*sasp->sas_error_track_top); + } +#endif + + if(mem_p != NIL) + su_LOFI_FREE(mem_p); + + a_SHEXP_ARITH_L((" < _arith_eval <%lld> ERR<%d>\n", + self->sac_rv, self->sac_error)); + NYD2_OU; +} + +static uz +a_shexp__arith_ws_squeeze(struct a_shexp_arith_ctx *self, + char const *exp_buf, uz exp_len, char *store_or_nil){ + a_SHEXP_ARITH_IFS( char const *ifs_ws; ) + char c; + boole last_ws, ws; + uz rv; + NYD2_IN; + UNUSED(self); + + rv = 0; + a_SHEXP_ARITH_IFS( ifs_ws = self->sac_ifs_ws; ) + + for(;; ++exp_buf, --exp_len){ + if(UNLIKELY(exp_len == 0) || UNLIKELY((c = *exp_buf) == '\0')) + goto jleave; + if(!(su_cs_is_space(c) + a_SHEXP_ARITH_IFS( || su_cs_find_c(ifs_ws, c) != NIL ) + )) + break; + } + + for(last_ws = FAL0;; ++exp_buf, --exp_len){ + if(UNLIKELY(exp_len == 0) || UNLIKELY((c = *exp_buf) == '\0')) + break; + + ws = (su_cs_is_space(c) + a_SHEXP_ARITH_IFS( || su_cs_find_c(ifs_ws, c) != NIL ) + ); + if(ws){ + if(last_ws) + continue; + c = ' '; + } + last_ws = ws; + + ++rv; + if(store_or_nil != NIL) + *store_or_nil++ = c; + } + + if(last_ws){ + --rv; + if(store_or_nil != NIL) + --store_or_nil; + } + +jleave: + if(store_or_nil != NIL) + *store_or_nil = '\0'; + + NYD2_OU; + return rv; +} + +static boole +a_shexp__arith_val_eval(struct a_shexp_arith_ctx *self, + struct a_shexp_arith_val *savp){ + struct a_shexp_arith_name_stack sans_stack, *sansp; + struct a_shexp_arith_stack sas_stack, *sasp; + char const *cp; + NYD_IN; + ASSERT(savp->sav_var != NIL); + + a_SHEXP_ARITH_L(("> _arith_val_eval %p <%s>\n", savp, savp->sav_var)); + + savp->sav_val = 0; + + /* Also look in program environment XXX configurable? */ + cp = n_var_vlook(savp->sav_var, TRU1); + if(cp == NIL) + goto jleave; + + for(sansp = self->sac_name_stack; sansp != NIL; sansp = sansp->sans_last){ + if(!su_cs_cmp(sansp->sans_var, savp->sav_var)){ + self->sac_error = a_SHEXP_ARITH_ERR_NAME_LOOP; + goto jleave; + } + } + + /* cp must be a self-contained expression. + * However, in most cases it solely consists of an integer, shortcut that */ + if(su_idec_cp(&savp->sav_val, cp, 0, a_SHEXP_ARITH_IDEC_MODE, NIL + ) & su_IDEC_STATE_CONSUMED){ + a_SHEXP_ARITH_L((" + _arith_val_eval NUM DIRECT <%lld>\n", + savp->sav_val)); + }else{ + sasp = self->sac_stack; + self->sac_stack = &sas_stack; + + sans_stack.sans_last = sansp = self->sac_name_stack; + sans_stack.sans_var = savp->sav_var; + self->sac_name_stack = &sans_stack; + + a_shexp__arith_eval(self, cp, UZ_MAX); + savp->sav_val = self->sac_rv; + /* .sav_var may be needed further on for updating purposes */ + + self->sac_stack = sasp; + self->sac_name_stack = sansp; + } + + cp = NIL; +jleave: + a_SHEXP_ARITH_L(("< _arith_val_eval %p <%s> <%lld> -> OK <%d>\n", + savp, savp->sav_var, savp->sav_val, + (cp == NIL && self->sac_error == a_SHEXP_ARITH_ERR_NONE))); + + NYD_OU; + return (cp == NIL && self->sac_error == a_SHEXP_ARITH_ERR_NONE); +} + +static boole +a_shexp__arith_op_apply(struct a_shexp_arith_ctx *self){ + struct a_shexp_arith_val *nums_top; + u8 prec; + u16 op; + struct a_shexp_arith_stack *sasp; + s64 val; + boole rv, ign; + NYD_IN; + + rv = FAL0; + val = 0; + sasp = self->sac_stack; + op = *sasp->sas_ops_top & a_SHEXP_ARITH_OP_MASK; + ign = ((*sasp->sas_ops_top & a_SHEXP_ARITH_OP_FLAG_WHITE_MASK) != 0); + + a_SHEXP_ARITH_L((" > _arith_op_apply %s<0x%02X %u> " + "nums_top=%p (%lu) ops_top=%p (%lu)\n", + (ign ? "WHITEOUT " : su_empty), op, (op & 0xFF), sasp->sas_nums_top, + S(ul,sasp->sas_nums_top - sasp->sas_nums), + sasp->sas_ops_top, S(ul,sasp->sas_ops_top - sasp->sas_ops))); + + /* At least one argument is always needed */ + if((nums_top = sasp->sas_nums_top) == sasp->sas_nums){ + self->sac_error = a_SHEXP_ARITH_ERR_NO_OP; + goto jleave; + } + --nums_top; + + /* Resolve name ([R]VAL) to value as necessary */ + if(!ign && nums_top->sav_var != NIL && + !a_shexp__arith_val_eval(self, nums_top)) + goto jleave; + + val = nums_top->sav_val; + prec = op & 0xFF; + + /* Not a binary operator? */ + if(prec >= a_SHEXP_ARITH_PREC_UNARY && prec < a_SHEXP_ARITH_PREC_SKY){ + if(ign) + goto jquick; + + switch(op){ + default: break; + case a_SHEXP_ARITH_OP_UNARY_NOT: val = !val; break; + case a_SHEXP_ARITH_OP_UNARY_BIT_NOT: val = ~val; break; + case a_SHEXP_ARITH_OP_UNARY_MINUS: val = -val; break; + case a_SHEXP_ARITH_OP_PREFIX_INC: FALLTHRU + case a_SHEXP_ARITH_OP_POSTFIX_INC: ++val; break; + case a_SHEXP_ARITH_OP_PREFIX_DEC: FALLTHRU + case a_SHEXP_ARITH_OP_POSTFIX_DEC: --val; break; + } + }else if(op == a_SHEXP_ARITH_OP_COND){ + if(!(*sasp->sas_ops_top & a_SHEXP_ARITH_OP_FLAG_COND_SAW_COLON)){ + self->sac_error = a_SHEXP_ARITH_ERR_COND_NO_COLON; + goto jleave; + } + goto jquick; + }else if(op == a_SHEXP_ARITH_OP_COND_COLON){ + ASSERT(sasp->sas_ops_top > sasp->sas_ops); + ASSERT(nums_top > sasp->sas_nums); + + if(!ign){ + /* Move the ternary value over to LHV where we find it as a result, + * and ensure LHV's name is forgotten so not to evaluate it (for + * example in 0?I1:I2 I1 would be evaluated when resolving the virtual + * outer group, because it still exists on number stack) */ + nums_top[-1].sav_val = nums_top[0].sav_val; + nums_top[-1].sav_var = NIL; + } + DBGX( else val = -1; ) + + sasp->sas_nums_top = nums_top; + + if((sasp->sas_ops_top[-1] & a_SHEXP_ARITH_OP_MASK + ) == a_SHEXP_ARITH_OP_COND_COLON){ + a_SHEXP_ARITH_ERROR_TRACK( --sasp->sas_error_track_top; ) + --sasp->sas_ops_top; + if(!a_shexp__arith_op_apply_colons(self)) + goto jleave; + ASSERT(sasp->sas_nums_top > sasp->sas_nums); + if(!ign) + sasp->sas_nums_top[-1].sav_val = val; + } + }else{ + /* Binaries need two numbers: one is popped, the other replaced */ + s64 rval; + + if(nums_top == sasp->sas_nums){ + self->sac_error = a_SHEXP_ARITH_ERR_NO_OP; + goto jleave; + } + sasp->sas_nums_top = nums_top--; + + if(ign) + goto jquick; + + /* Resolve LHV as necessary */ + if(op != a_SHEXP_ARITH_OP_ASSIGN && nums_top->sav_var != NIL && + !a_shexp__arith_val_eval(self, nums_top)) + goto jleave; + + rval = val; + val = nums_top->sav_val; /* (may be bogus for assign, fixed soon) */ + + /* In precedence order (excluding assignments) */ + switch(op){ + default: break; + case a_SHEXP_ARITH_OP_COMMA: FALLTHRU + + case a_SHEXP_ARITH_OP_ASSIGN: val = rval; break; + + case a_SHEXP_ARITH_OP_OR: val = (val != 0 || rval != 0); break; + case a_SHEXP_ARITH_OP_AND: val = (val != 0 && rval != 0); break; + + case a_SHEXP_ARITH_OP_BIT_OR: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_BIT_OR: val |= rval; break; + case a_SHEXP_ARITH_OP_BIT_XOR: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_BIT_XOR: val ^= rval; break; + case a_SHEXP_ARITH_OP_BIT_AND: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_BIT_AND: val &= rval; break; + + case a_SHEXP_ARITH_OP_EQ: val = (val == rval); break; + case a_SHEXP_ARITH_OP_NE: val = (val != rval); break; + + case a_SHEXP_ARITH_OP_LE: val = (val <= rval); break; + case a_SHEXP_ARITH_OP_GE: val = (val >= rval); break; + case a_SHEXP_ARITH_OP_LT: val = (val < rval); break; + case a_SHEXP_ARITH_OP_GT: val = (val > rval); break; + + case a_SHEXP_ARITH_OP_SHIFT_LEFT: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_SHIFT_LEFT: val <<= rval; break; + + case a_SHEXP_ARITH_OP_SHIFT_RIGHT: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_SHIFT_RIGHT: val >>= rval; break; + + case a_SHEXP_ARITH_OP_SHIFT_RIGHTU: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_SHIFT_RIGHTU: + val = S(s64,S(u64,val) >> rval); + break; + + case a_SHEXP_ARITH_OP_ADD: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_ADD: val += rval; break; + case a_SHEXP_ARITH_OP_SUB: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_SUB: val -= rval; break; + + case a_SHEXP_ARITH_OP_MUL: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_MUL: val *= rval; break; + /* For /,%, avoid lvh=S64_MIN, rhv=-1: + * CHANGES, bash 4.3 [ac50fbac377e32b98d2de396f016ea81e8ee9961]: + * Fixed a bug that caused floating-point exceptions and + * overflow errors for the / and % arithmetic operators when + * using INTMAX_MIN and -1. */ + case a_SHEXP_ARITH_OP_DIV: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_DIV: + if(rval == 0){ + self->sac_error = a_SHEXP_ARITH_ERR_DIV_BY_ZERO; + goto jleave; + }else if(val != S64_MIN || rval != -1) + val /= rval; + break; + case a_SHEXP_ARITH_OP_MOD: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_MOD: + if(rval == 0){ + self->sac_error = a_SHEXP_ARITH_ERR_DIV_BY_ZERO; + goto jleave; + }else if(val == S64_MIN && rval == -1) + val = 0; + else + val %= rval; + break; + + case a_SHEXP_ARITH_OP_EXP: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_EXP: + if(rval < 0){ + self->sac_error = a_SHEXP_ARITH_ERR_EXP_INVALID; + goto jleave; + }else{ + s64 i; + + for(i = 1; rval > 0; --rval) + i *= val; + val = i; + } + break; + } + } + + /* Assignment updates a variable, which must exist. + * For prefix and postfix operators, too: we already turned them into + * multiple unary plus/minus unless we had seen a variable name */ +jquick: + if(prec == a_SHEXP_ARITH_PREC_ASSIGN || prec == a_SHEXP_ARITH_PREC_PREFIX || + prec == a_SHEXP_ARITH_PREC_POSTFIX){ + char buf[su_IENC_BUFFER_SIZE], *bp; + + if(nums_top->sav_var == NIL){ + self->sac_error = a_SHEXP_ARITH_ERR_ASSIGN_NO_VAR; + goto jleave; + } + + if(!ign){ + bp = su_ienc_s64(buf, val, 10); + n_var_vset(nums_top->sav_var, S(up,bp), FAL0); + } + + /* And restore the stack value again for postfix operators */ + if(op == a_SHEXP_ARITH_OP_POSTFIX_INC) + --val; + else if(op == a_SHEXP_ARITH_OP_POSTFIX_DEC) + ++val; + + if(!ign) + a_SHEXP_ARITH_L((" + _arith_op_apply VAR <%s> SET <%s> VAL <%lld>\n", + nums_top->sav_var, bp, val)); + } + + nums_top->sav_val = val; + nums_top->sav_var = NIL; + + rv = TRU1; +jleave: + a_SHEXP_ARITH_L((" < _arith_op_apply RV %d <0x%02X %u> RES<%lld> ERR<%d> " + "nums=%lu ops=%lu\n", + rv, op, op & 0xFF, val, self->sac_error, + S(ul,sasp->sas_nums_top - sasp->sas_nums), + S(ul,sasp->sas_ops_top - sasp->sas_ops))); + + NYD_OU; + return rv; +} + +static boole +a_shexp__arith_op_apply_colons(struct a_shexp_arith_ctx *self){ + u16 lop, lprec; + boole next_stop; + NYD_IN; + + for(next_stop = FAL0;;){ + if(!a_shexp__arith_op_apply(self)){ + next_stop = FAL0; + break; + } + if(next_stop) + break; + a_SHEXP_ARITH_ERROR_TRACK( --self->sac_stack->sas_error_track_top; ) + lop = *--self->sac_stack->sas_ops_top & a_SHEXP_ARITH_OP_MASK; + lprec = lop & 0xFF; + next_stop = (lprec == a_SHEXP_ARITH_PREC_PAREN_LEFT || + lop == a_SHEXP_ARITH_OP_COND); + } + + NYD_OU; + return next_stop; +} + +#if a_SHEXP_ARITH_DBG +static void +a_shexp__arith_log(char const *fmt, ...){ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); +} +#endif + +#undef a_SHEXP_ARITH_ERROR_TRACK +#undef a_SHEXP_ARITH_IFS +#undef a_SHEXP_ARITH_DBG +#undef a_SHEXP_ARITH_L +#undef a_SHEXP_ARITH_IDEC_MODE + +/* s-it-mode */ -- 2.37.2 --steffen | |Der Kragenbaer, The moon bear, |der holt sich munter he cheerfully and one by one |einen nach dem anderen runter wa.ks himself off |(By Robert Gernhardt) _______________________________________________ busybox mailing list busybox@busybox.net http://lists.busybox.net/mailman/listinfo/busybox