Last active
June 19, 2022 17:21
-
-
Save iamahuman/eb248337803f86483f99f5fa9f4752b5 to your computer and use it in GitHub Desktop.
infix expression evaluator in sed (integers in decimal base only)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sed -nf | |
x ; s/^$/\n/ ; x ; t scan_unop | |
:scan_unop | |
/^[ ]*[-+][ ]*)/{i\ | |
unary op expected an operand, got ')' | |
x ; b hold_bailout | |
} | |
/^[ ]*[-+][ ]*$/{i\ | |
unary op expected an operand, got EOF | |
x ; b hold_bailout | |
} | |
s/^[ ]*-// ; t push_neg | |
s/^[ ]*+// ; t push_pos | |
:scan | |
s/^[ ]*// ; t scan2 | |
:scan2 | |
s/^0$// ; t zero_end | |
/^0[^0-9]/b token_digit | |
/^[1-9][0-9]*/{ | |
:token_digit | |
H ; x ; s/^\([0-9A-Za-z]*\)\n\(.*\)\n\([0-9]\{1,\}\)\([^0-9]\( *[^[:cntrl:]]*\)*\)\{0,1\}$/\1\n\3\n\2/ | |
t num_discard ; i\ | |
input contains invalid characters | |
b hold_bailout | |
:num_discard | |
x ; s/^[0-9]\{1,\}// ; t scan_op ; i\ | |
assertion failure: remove integer token from pattern | |
b reset | |
} | |
:scan_op | |
s/^[ ]*// ; t scan3 | |
:scan3 | |
/^[ ]*([ ]*)/{i\ | |
'(' expected an operand, got ')' | |
x ; b hold_bailout | |
} | |
/^[ ]*([ ]*$/{i\ | |
'(' expected an operand, got EOF | |
x ; b hold_bailout | |
} | |
s/^(// ; t opn | |
/^$/{ | |
x ; b loop1 | |
} | |
/^[-+/*)]/{x | |
:loop1 | |
/^[NP]/{ | |
s/^[NP][NP]*\([^NP][0-9A-Za-z]*\)\{0,1\}\n\(00*\)\n/\1\n0\n/ ; t loop2 | |
s/^P*\(NP*NP*\)*NP*\([0-9A-MOQ-Za-z][0-9A-Za-z]*\)\{0,1\}\n\([0-9]*\)\n/\2\n-\3\n/ ; t loop2 | |
s/^P*\(NP*NP*\)*NP*\([0-9A-MOQ-Za-z][0-9A-Za-z]*\)\{0,1\}\n-\([0-9]*\)\n/\2\n\3\n/ ; t loop2 | |
s/^P*\(NP*NP*\)*\([0-9A-MOQ-Za-z][0-9A-Za-z]*\)\{0,1\}\n\([0-9]*\)\n/\2\n\3\n/ ; t loop2 | |
s/^P*\(NP*NP*\)*\([0-9A-MOQ-Za-z][0-9A-Za-z]*\)\{0,1\}\n-\([0-9]*\)\n/\2\n-\3\n/ ; t loop2 | |
/^N/{ | |
i\ | |
operand missing for - (unary op) | |
x ; b hold_bailout | |
} | |
/^P/{ | |
i\ | |
operand missing for + (unary op) | |
x ; b hold_bailout | |
} | |
x ; b assert | |
} | |
:loop2 | |
:op_mul | |
/^D[0-9A-Za-z]*\n0*\n/{ | |
i\ | |
division by zero | |
b hold_bailout | |
} | |
/^D/{ | |
# special case |divisor| = 10^n (n is in N_1) AND dividend / divisor < 0 | |
s/^D\([0-9A-Za-z]*\)\n1\(0*\)\n-\([0-9]\{1,\}\)\2\n/\1\n-\3\n/ ; t op_mul | |
s/^D\([0-9A-Za-z]*\)\n-1\(0*\)\n\([0-9]\{1,\}\)\2\n/\1\n-\3\n/ ; t op_mul | |
# special case |divisor| = 10^n AND dividend / divisor > 0 | |
s/^D\([0-9A-Za-z]*\)\n\(-\{0,1\}\)1\(0*\)\n\2\([0-9]\{1,\}\)\3\n/\1\n\4\n/ ; t op_mul | |
# special case dividend = 0 | |
s/^D\([0-9A-Za-z]*\)\n\(-\{0,1\}[0-9]\{1,\}\)\n00*\n/\1\n0\n/ ; t op_mul | |
# special case divisor * 10^n = dividend | |
s/^D\([0-9A-Za-z]*\)\n\(-\{0,1\}[0-9]*[1-9]\)\n\2\(0*\)\n/\1\n1\3\n/ ; t op_mul | |
# special case divisor * 10^n = -dividend (n is in N_1) AND divisor > 0 | |
s/^D\([0-9A-Za-z]*\)\n\([0-9]*[1-9]\)\n-\2\(0*\)\n/\1\n-1\3\n/ ; t op_mul | |
# special case divisor * 10^n = -dividend (n is in N_1) AND divisor < 0 | |
s/^D\([0-9A-Za-z]*\)\n-\([0-9]*[1-9]\)\n\2\(0*\)\n/\1\n-1\3\n/ ; t op_mul | |
# normalize signs | |
s/^D\([0-9A-Za-z]*\)\n\([0-9]\{1,\}\)\n-\([0-9]\{1,\}\)\n/&/ ; t do_div | |
s/^D\([0-9A-Za-z]*\)\n-\([0-9]\{1,\}\)\n\([0-9]\{1,\}\)\n/&/ ; t do_div | |
s/^D\([0-9A-Za-z]*\)\n\(-\{0,1\}\)\([0-9]\{1,\}\)\n\2\([0-9]\{1,\}\)\n/&/ ; t do_div | |
i\ | |
operands missing for / | |
b hold_bailout | |
:do_div | |
i\ | |
division is not implemented | |
b hold_bailout | |
} | |
/^M/{ | |
# special case zero operand | |
s/^M\([0-9A-Za-z]*\)\n-\{0,1\}[0-9]\{1,\}\n0\{1,\}\n/\1\n0\n/ ; t op_mul | |
s/^M\([0-9A-Za-z]*\)\n0\{1,\}\n-\{0,1\}[0-9]\{1,\}\n/\1\n0\n/ ; t op_mul | |
# special case (|A| = 1 OR |B| = 1) AND A * B > 0 | |
s/^M\([0-9A-Za-z]*\)\n\(-\{0,1\}\)\([0-9]\{1,\}\)\n\21\n/\1\n\3\n/ ; t op_mul | |
s/^M\([0-9A-Za-z]*\)\n\(-\{0,1\}\)1\n\2\([0-9]\{1,\}\)\n/\1\n\3\n/ ; t op_mul | |
# special case (|A| = 1 OR |B| = 1) AND A * B < 0 | |
s/^M\([0-9A-Za-z]*\)\n-1\n\([0-9]\{1,\}\)\n/\1\n-\2\n/ ; t op_mul | |
s/^M\([0-9A-Za-z]*\)\n1\n-\([0-9]\{1,\}\)\n/\1\n-\2\n/ ; t op_mul | |
s/^M\([0-9A-Za-z]*\)\n-\([0-9]\{1,\}\)\n1\n/\1\n-\2\n/ ; t op_mul | |
s/^M\([0-9A-Za-z]*\)\n\([0-9]\{1,\}\)\n-1\n/\1\n-\2\n/ ; t op_mul | |
# normalize signs (-?<multiplicand> <multiplier>) | |
s/^M\([0-9A-Za-z]*\)\n-\([0-9]*[1-9]\)\(0*\)\n\([0-9]\{1,\}\)\n/S\4\3 \2\n\1\n0\n/ ; t do_mul | |
s/^M\([0-9A-Za-z]*\)\n\([0-9]*[1-9]\)\(0*\)\n-\([0-9]\{1,\}\)\n/S\4\3 \2\n\1\n0\n/ ; t do_mul | |
s/^M\([0-9A-Za-z]*\)\n\(-\{0,1\}\)\([0-9]*[1-9]\)\(0*\)\n\2\([0-9]\{1,\}\)\n/A\5\4 \3\n\1\n0\n/ ; t do_mul | |
i\ | |
operands missing for * | |
b hold_bailout | |
# decompose into series of addition | |
:do_mul | |
# 1sign 2mult.nd 3 4multiplier 5zeroes 6stack 7accumulator | |
s/^\([SA]\)\([0-9]*\) \(\([0-9]*[1-9]\)\(0*\)\)\{0,1\}1\n\([0-9A-Za-z]*\)\n\(-\{0,1\}[0-9]*\)\n/\1Rm\6\n\2\n\7\n\1\2\50\n\4\n/ ; t op_add_1 | |
# 1sign 2mult.nd 3multiplier 4stack 5accumulator | |
s/^\([SA]\)\([0-9]*\) \([0-9]*\)2\n\([0-9A-Za-z]*\)\n\(-\{0,1\}[0-9]*\)\n/\1Rm\4\n\2\n\5\n\1\2\n\31\n/ ; t op_add_1 | |
s/^\([SA]\)\([0-9]*\) \([0-9]*\)3\n\([0-9A-Za-z]*\)\n\(-\{0,1\}[0-9]*\)\n/\1Rm\4\n\2\n\5\n\1\2\n\32\n/ ; t op_add_1 | |
s/^\([SA]\)\([0-9]*\) \([0-9]*\)4\n\([0-9A-Za-z]*\)\n\(-\{0,1\}[0-9]*\)\n/\1Rm\4\n\2\n\5\n\1\2\n\33\n/ ; t op_add_1 | |
s/^\([SA]\)\([0-9]*\) \([0-9]*\)5\n\([0-9A-Za-z]*\)\n\(-\{0,1\}[0-9]*\)\n/\1Rm\4\n\2\n\5\n\1\2\n\34\n/ ; t op_add_1 | |
s/^\([SA]\)\([0-9]*\) \([0-9]*\)6\n\([0-9A-Za-z]*\)\n\(-\{0,1\}[0-9]*\)\n/\1Rm\4\n-\2\n\5\n\1\2\n\37\n/ ; t op_add_1 | |
s/^\([SA]\)\([0-9]*\) \([0-9]*\)7\n\([0-9A-Za-z]*\)\n\(-\{0,1\}[0-9]*\)\n/\1Rm\4\n-\2\n\5\n\1\2\n\38\n/ ; t op_add_1 | |
s/^\([SA]\)\([0-9]*\) \([0-9]*\)8\n\([0-9A-Za-z]*\)\n\(-\{0,1\}[0-9]*\)\n/\1Rm\4\n-\2\n\5\n\1\2\n\39\n/ ; t op_add_1 | |
# 1sign 2mult.nd 3 4multiplier 5zeroes 6stack 7accumulator | |
s/^\([SA]\)\([0-9]*\) \(\([0-9]*[1-9]\)\(0*\)\)\{0,1\}9\n\([0-9A-Za-z]*\)\n\(-\{0,1\}[0-9]*\)\n/S\1Rm\6\n\2\n\20\n\7\n\1\2\50\n\4\n/ ; t op_add_1 | |
# aaa * 9 = aaa0 - aaa | |
i\ | |
assertion failure: no match for do_mul | |
x ; b reset | |
} | |
:loop3 | |
x | |
s/^\/// ; t push_div | |
s/^\*// ; t push_mul | |
x | |
:op_add | |
# subroutine return | |
# 1stack 2accumulator | |
s/^Rm\([0-9A-Za-z]*\)\n\(-\{0,1\}[0-9]\{1,\}\)\n[SA][0-9]\{1,\}\n\n/\1\n\2\n/ ; t op_mul | |
# 1stack 2accumulator 3multiplicand 4multiplier | |
s/^Rm\([0-9A-Za-z]*\)\n\(-\{0,1\}[0-9]\{1,\}\)\n\([SA][0-9]\{1,\}\)\n\([0-9]\{1,\}\)\n/\3 \4\n\1\n\2\n/ ; t do_mul | |
/^[SA]/{ | |
:op_add_1 | |
# normalize signs | |
s/^S\([0-9A-Za-z]*\)\n-\([0-9]*\)\n-\([0-9]*\)\n/\3 \2\n\3q\1\n/ ; t do_dec | |
s/^A\([0-9A-Za-z]*\)\n\([0-9]*\)\n-\([0-9]*\)\n/\3 \2\n\3q\1\n/ ; t do_dec | |
s/^S\([0-9A-Za-z]*\)\n\([0-9]*\)\n\([0-9]*\)\n/\2 \3\n\2q\1\n/ ; t do_dec | |
s/^A\([0-9A-Za-z]*\)\n-\([0-9]*\)\n\([0-9]*\)\n/\2 \3\n\2q\1\n/ ; t do_dec | |
s/^S\([0-9A-Za-z]*\)\n\([0-9]*\)\n-\([0-9]*\)\n/ \2 \3\ny\1\n/ ; t do_inc | |
s/^A\([0-9A-Za-z]*\)\n-\([0-9]*\)\n-\([0-9]*\)\n/ \2 \3\ny\1\n/ ; t do_inc | |
s/^S\([0-9A-Za-z]*\)\n-\([0-9]*\)\n\([0-9]*\)\n/ \2 \3\nz\1\n/ ; t do_inc | |
s/^A\([0-9A-Za-z]*\)\n\([0-9]*\)\n\([0-9]*\)\n/ \2 \3\nz\1\n/ ; t do_inc | |
/^A/i\ | |
operands missing for + | |
/^S/i\ | |
operands missing for - | |
b hold_bailout | |
:do_dec | |
s/^ /c / ; t do_inc | |
s/^\([0-9]*[0-9]\) /\1;0123456789876543210;/ ; t do_dec_2 ; x ; b assert | |
:do_dec_2 | |
s/^\([0-9]*\)\([0-9]\);[0-9]\{0,9\}\2[0-9]\{8\}\([0-9]\)[0-9]\{0,10\};/\1 \3/ ; t do_dec | |
i\ | |
assertion failure: 10's complement | |
x ; b reset | |
:do_inc | |
s/^c\([0-9]*\) \n/1\1\n/ ; t cse_fin | |
s/^\([0-9]*\) \([0-9]*\) \n/\2\1\n/ ; t cse_fin | |
s/^\([0-9]*\) \([0-9]\{1,\}\)\n/\2\1\n/ ; t cse_fin | |
# 1 2 3 4 (extract digits, pad left) | |
s/^\(c\{0,1\}\)\([0-9]*\) \([0-9]*\)\([0-9]\)\n/\10\4\2 \3\n/ ; t cse_2 | |
# 1 2 3 4 (extract digits, pad right) | |
s/^\(c\{0,1\}\)\([0-9]*\) \([0-9]*\)\([0-9]\) \n/\1\40\2 \3 \n/ ; t cse_2 | |
# 1 2 3 4 5 6 (extract digits) | |
s/^\(c\{0,1\}\)\([0-9]*\) \([0-9]*\)\([0-9]\) \([0-9]*\)\([0-9]\)\n/\1\4\6\2 \3 \5\n/ ; t cse_2 | |
i\ | |
assertion failure: digit extraction | |
x ; b reset | |
:cse_2 | |
s/^c\([0-9]\{2\}\)/\10123456789;/ ; t cse_3 | |
s/^\([0-9]\{2\}\)/\10123456789 ;/ ; t cse_3 | |
i\ | |
assertion failure: table insertion | |
x ; b reset | |
:cse_3 | |
s/^\([0-9]\)\([0-9]\)[0-9 ]\{0,9\}\1\([0-9 ]\{0,10\}\);/\20123456789;\3;/ ; t cse_4 | |
i\ | |
assertion failure: 1st operand | |
x ; b reset | |
:cse_4 | |
s/^\([0-9]\)[0-9]\{0,9\}\1\([0-9]\{0,9\}\);\([0-9 ]*\);/\2\3,01234567890123456789;/ ; t cse_5 | |
i\ | |
assertion failure: 2nd operand | |
x ; b reset | |
:cse_5 | |
/^[0-9 ]\{0,9\},/{ | |
s/^[0-9 ,]\{20\}\([0-9]\)[0-9]\{0,19\};/c\1/ ; t do_inc | |
i\ | |
assertion failure: carry | |
x ; b reset | |
} | |
s/^[0-9 ,]\{20\}\([0-9]\)[0-9]\{0,19\};/\1/ ; t do_inc | |
i\ | |
assertion failure: no carry | |
x ; b reset | |
:cse_fin | |
s/^\([0-9]\{1,\}\)\nz\([0-9A-Za-z]*\)\n/\2\n\1\n/ ; t op_add | |
s/^\([0-9]\{1,\}\)\ny\([0-9A-Za-z]*\)\n/\2\n-\1\n/ ; t op_add | |
s/^\([0-9]\{1,\}\)\n\([0-9]\{1,\}\)q/\1 \2 \n/ ; t cse_post | |
i\ | |
assertion failure: addition post-process | |
x ; b reset | |
# Given N = (number of characters preceding 'q'), subtract 10^N | |
:cse_post | |
# divide by 10^N - that is, <quo><space><space><rem> | |
s/^\([0-9]*\)\([0-9]\) \([0-9]*\)[0-9] /\1 \3 \2/ | |
s/^ \([0-9]*\)[0-9] / \1 0/ | |
t cse_post | |
s/^0* // ; t cse_neg | |
# subtract by 1 - that is, <space><quo-1><space><rem> | |
:cse_pos | |
s/^k 0* 0*\n\([0-9A-Za-z]*\)\n/\1\n0\n/ ; t op_add | |
s/^k 0* 0*\([1-9][0-9]*\)\n\([0-9A-Za-z]*\)\n/\2\n\1\n/ ; t op_add | |
s/^k 0*\([1-9][0-9]*\) \([0-9]*\)\n\([0-9A-Za-z]*\)\n/\3\n\1\2\n/ ; t op_add | |
s/^\([0-9]*\)0 \([0-9]*\)/\1 9\2/ ; t cse_pos ; s/^\([0-9]*\)\([0-9]\)k \([0-9]*\)/\1k \2\3/ ; t cse_pos | |
s/^/9876543210 / ; t cse_pos_2 | |
x ; b assert | |
:cse_pos_2 | |
s/^[0-9]*\([0-9]\)\([0-9]\)[0-9]* \([0-9]*\)\1 /\3k \2/ ; t cse_pos | |
x ; b assert | |
i\ | |
assertion failure: positive outcome on subtraction | |
x ; b reset | |
:cse_neg | |
# hold: <space><rem> | |
# If quo == 0, evaluate -(10^N - rem) - that is, <absval><space>b? | |
s/^\([0-9]*\) \n\([0-9A-Za-z]*\)\n/\2\n-1\1\n/ ; t op_add | |
s/^0* b\n\([0-9A-Za-z]*\)\n/\1\n0\n/ ; t op_add | |
s/^0*\([1-9][0-9]*\) b\n\([0-9A-Za-z]*\)\n/\2\n-\1\n/ ; t op_add | |
s/^\([0-9]*\) \([0-9]*\)0\n/0\1 \2\n/ ; t cse_neg | |
s/^\([0-9]*\) \([0-9]*\)b\n/\1 \2 0123456789876543210\n/ ; t cse_neg_2 | |
s/^\([0-9]*\) \([0-9]*\)\n/\1 \2 0123456789987654321\n/ ; t cse_neg_2 | |
x ; b assert | |
:cse_neg_2 | |
s/^\([0-9]*\) \([0-9]*\)\([0-9]\) [0-9]\{0,9\}\3[0-9]\{8\}\([0-9]\)[0-9]\{0,10\}\n/\4\1 \2b\n/ ; t cse_neg | |
i\ | |
assertion failure: negative outcome on subtraction | |
x ; b reset | |
} | |
x | |
s/^+// ; t push_add | |
s/^-// ; t push_sub | |
s/^)// ; t cse | |
/^$/{ | |
x ; s/^\n\(-\{0,1\}[0-9]\{1,\}\)\n$/\1/ ; t eof_ok ; i\ | |
unexpected EOF | |
b hold_bailout | |
:eof_ok ; p ; b hold_bailout | |
} | |
i\ | |
assertion failure: no tokens match | |
b reset | |
} | |
s/^/unknown token: / ; p ; x | |
b hold_bailout | |
:assert ; i\ | |
assertion failure | |
:reset | |
s/^.*$/pattern: &/ ; l ; x ; s/^.*$/hold: &/ ; l | |
b hold_bailout | |
:push_neg ; x ; s/^/N/ ; x ; t scan_unop ; b assert | |
:push_pos ; x ; s/^/P/ ; x ; t scan_unop ; b assert | |
:push_div ; x ; s/^/D/ ; x ; t scan_unop ; b assert | |
:push_mul ; x ; s/^/M/ ; x ; t scan_unop ; b assert | |
:push_sub ; x ; s/^/S/ ; x ; t scan_unop ; b assert | |
:push_add ; x ; s/^/A/ ; x ; t scan_unop ; b assert | |
:zero_end ; x ; s/^\([0-9A-Za-z]*\)\n/\1\n0\n/ ; t loop1 ; x ; b assert | |
# TODO handle functions | |
:opn ; x ; s/^/O/ ; x ; t scan_unop ; b assert | |
:cse ; x ; s/^O// ; x ; t scan ; i\ | |
brackets mismatch | |
x | |
:hold_bailout | |
# There seem no portable way to clear pattern space | |
# with invalid multibyte sequence other than 'd' (GNU sed provides 'z'). | |
s/^.*$/\n/ ; x ; d |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment