#!/bin/sed -nf

x ; s/^$/

/ ; 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]*\)

\(.*\)

\([0-9]\{1,\}\)\([^0-9]\( *[^[:cntrl:]]*\)*\)\{0,1\}$/\1

\3

\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\}

\(00*\)

/\1

0

/ ; t loop2

s/^P*\(NP*NP*\)*NP*\([0-9A-MOQ-Za-z][0-9A-Za-z]*\)\{0,1\}

\([0-9]*\)

/\2

-\3

/ ; t loop2

s/^P*\(NP*NP*\)*NP*\([0-9A-MOQ-Za-z][0-9A-Za-z]*\)\{0,1\}

-\([0-9]*\)

/\2

\3

/ ; t loop2

s/^P*\(NP*NP*\)*\([0-9A-MOQ-Za-z][0-9A-Za-z]*\)\{0,1\}

\([0-9]*\)

/\2

\3

/ ; t loop2

s/^P*\(NP*NP*\)*\([0-9A-MOQ-Za-z][0-9A-Za-z]*\)\{0,1\}

-\([0-9]*\)

/\2

-\3

/ ; 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]*

0*

/{

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]*\)

1\(0*\)

-\([0-9]\{1,\}\)\2

/\1

-\3

/ ; t op_mul

s/^D\([0-9A-Za-z]*\)

-1\(0*\)

\([0-9]\{1,\}\)\2

/\1

-\3

/ ; t op_mul

# special case |divisor| = 10^n AND dividend / divisor > 0

s/^D\([0-9A-Za-z]*\)

\(-\{0,1\}\)1\(0*\)

\2\([0-9]\{1,\}\)\3

/\1

\4

/ ; t op_mul

# special case dividend = 0

s/^D\([0-9A-Za-z]*\)

\(-\{0,1\}[0-9]\{1,\}\)

00*

/\1

0

/ ; t op_mul

# special case divisor * 10^n = dividend

s/^D\([0-9A-Za-z]*\)

\(-\{0,1\}[0-9]*[1-9]\)

\2\(0*\)

/\1

1\3

/ ; t op_mul

# special case divisor * 10^n = -dividend (n is in N_1) AND divisor > 0

s/^D\([0-9A-Za-z]*\)

\([0-9]*[1-9]\)

-\2\(0*\)

/\1

-1\3

/ ; t op_mul

# special case divisor * 10^n = -dividend (n is in N_1) AND divisor < 0

s/^D\([0-9A-Za-z]*\)

-\([0-9]*[1-9]\)

\2\(0*\)

/\1

-1\3

/ ; t op_mul

# normalize signs

s/^D\([0-9A-Za-z]*\)

\([0-9]\{1,\}\)

-\([0-9]\{1,\}\)

/&/ ; t do_div

s/^D\([0-9A-Za-z]*\)

-\([0-9]\{1,\}\)

\([0-9]\{1,\}\)

/&/ ; t do_div

s/^D\([0-9A-Za-z]*\)

\(-\{0,1\}\)\([0-9]\{1,\}\)

\2\([0-9]\{1,\}\)

/&/ ; 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]*\)

-\{0,1\}[0-9]\{1,\}

0\{1,\}

/\1

0

/ ; t op_mul

s/^M\([0-9A-Za-z]*\)

0\{1,\}

-\{0,1\}[0-9]\{1,\}

/\1

0

/ ; t op_mul

# special case (|A| = 1 OR |B| = 1) AND A * B > 0

s/^M\([0-9A-Za-z]*\)

\(-\{0,1\}\)\([0-9]\{1,\}\)

\21

/\1

\3

/ ; t op_mul

s/^M\([0-9A-Za-z]*\)

\(-\{0,1\}\)1

\2\([0-9]\{1,\}\)

/\1

\3

/ ; t op_mul

# special case (|A| = 1 OR |B| = 1) AND A * B < 0

s/^M\([0-9A-Za-z]*\)

-1

\([0-9]\{1,\}\)

/\1

-\2

/ ; t op_mul

s/^M\([0-9A-Za-z]*\)

1

-\([0-9]\{1,\}\)

/\1

-\2

/ ; t op_mul

s/^M\([0-9A-Za-z]*\)

-\([0-9]\{1,\}\)

1

/\1

-\2

/ ; t op_mul

s/^M\([0-9A-Za-z]*\)

\([0-9]\{1,\}\)

-1

/\1

-\2

/ ; t op_mul

# normalize signs (-?<multiplicand> <multiplier>)

s/^M\([0-9A-Za-z]*\)

-\([0-9]*[1-9]\)\(0*\)

\([0-9]\{1,\}\)

/S\4\3 \2

\1

0

/ ; t do_mul

s/^M\([0-9A-Za-z]*\)

\([0-9]*[1-9]\)\(0*\)

-\([0-9]\{1,\}\)

/S\4\3 \2

\1

0

/ ; t do_mul

s/^M\([0-9A-Za-z]*\)

\(-\{0,1\}\)\([0-9]*[1-9]\)\(0*\)

\2\([0-9]\{1,\}\)

/A\5\4 \3

\1

0

/ ; 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

\([0-9A-Za-z]*\)

\(-\{0,1\}[0-9]*\)

/\1Rm\6

\2

\7

\1\2\50

\4

/ ; t op_add_1

# 1sign 2mult.nd 3multiplier 4stack 5accumulator

s/^\([SA]\)\([0-9]*\) \([0-9]*\)2

\([0-9A-Za-z]*\)

\(-\{0,1\}[0-9]*\)

/\1Rm\4

\2

\5

\1\2

\31

/ ; t op_add_1

s/^\([SA]\)\([0-9]*\) \([0-9]*\)3

\([0-9A-Za-z]*\)

\(-\{0,1\}[0-9]*\)

/\1Rm\4

\2

\5

\1\2

\32

/ ; t op_add_1

s/^\([SA]\)\([0-9]*\) \([0-9]*\)4

\([0-9A-Za-z]*\)

\(-\{0,1\}[0-9]*\)

/\1Rm\4

\2

\5

\1\2

\33

/ ; t op_add_1

s/^\([SA]\)\([0-9]*\) \([0-9]*\)5

\([0-9A-Za-z]*\)

\(-\{0,1\}[0-9]*\)

/\1Rm\4

\2

\5

\1\2

\34

/ ; t op_add_1

s/^\([SA]\)\([0-9]*\) \([0-9]*\)6

\([0-9A-Za-z]*\)

\(-\{0,1\}[0-9]*\)

/\1Rm\4

-\2

\5

\1\2

\37

/ ; t op_add_1

s/^\([SA]\)\([0-9]*\) \([0-9]*\)7

\([0-9A-Za-z]*\)

\(-\{0,1\}[0-9]*\)

/\1Rm\4

-\2

\5

\1\2

\38

/ ; t op_add_1

s/^\([SA]\)\([0-9]*\) \([0-9]*\)8

\([0-9A-Za-z]*\)

\(-\{0,1\}[0-9]*\)

/\1Rm\4

-\2

\5

\1\2

\39

/ ; t op_add_1

# 1sign 2mult.nd 3 4multiplier 5zeroes 6stack 7accumulator

s/^\([SA]\)\([0-9]*\) \(\([0-9]*[1-9]\)\(0*\)\)\{0,1\}9

\([0-9A-Za-z]*\)

\(-\{0,1\}[0-9]*\)

/S\1Rm\6

\2

\20

\7

\1\2\50

\4

/ ; 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]*\)

\(-\{0,1\}[0-9]\{1,\}\)

[SA][0-9]\{1,\}



/\1

\2

/ ; t op_mul

# 1stack 2accumulator 3multiplicand 4multiplier

s/^Rm\([0-9A-Za-z]*\)

\(-\{0,1\}[0-9]\{1,\}\)

\([SA][0-9]\{1,\}\)

\([0-9]\{1,\}\)

/\3 \4

\1

\2

/ ; t do_mul

/^[SA]/{

:op_add_1

# normalize signs

s/^S\([0-9A-Za-z]*\)

-\([0-9]*\)

-\([0-9]*\)

/\3 \2

\3q\1

/ ; t do_dec

s/^A\([0-9A-Za-z]*\)

\([0-9]*\)

-\([0-9]*\)

/\3 \2

\3q\1

/ ; t do_dec

s/^S\([0-9A-Za-z]*\)

\([0-9]*\)

\([0-9]*\)

/\2 \3

\2q\1

/ ; t do_dec

s/^A\([0-9A-Za-z]*\)

-\([0-9]*\)

\([0-9]*\)

/\2 \3

\2q\1

/ ; t do_dec

s/^S\([0-9A-Za-z]*\)

\([0-9]*\)

-\([0-9]*\)

/ \2 \3

y\1

/ ; t do_inc

s/^A\([0-9A-Za-z]*\)

-\([0-9]*\)

-\([0-9]*\)

/ \2 \3

y\1

/ ; t do_inc

s/^S\([0-9A-Za-z]*\)

-\([0-9]*\)

\([0-9]*\)

/ \2 \3

z\1

/ ; t do_inc

s/^A\([0-9A-Za-z]*\)

\([0-9]*\)

\([0-9]*\)

/ \2 \3

z\1

/ ; 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]*\)

/1\1

/ ; t cse_fin

s/^\([0-9]*\) \([0-9]*\)

/\2\1

/ ; t cse_fin

s/^\([0-9]*\) \([0-9]\{1,\}\)

/\2\1

/ ; t cse_fin

# 1 2 3 4 (extract digits, pad left)

s/^\(c\{0,1\}\)\([0-9]*\) \([0-9]*\)\([0-9]\)

/\10\4\2 \3

/ ; t cse_2

# 1 2 3 4 (extract digits, pad right)

s/^\(c\{0,1\}\)\([0-9]*\) \([0-9]*\)\([0-9]\)

/\1\40\2 \3

/ ; 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]\)

/\1\4\6\2 \3 \5

/ ; 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,\}\)

z\([0-9A-Za-z]*\)

/\2

\1

/ ; t op_add

s/^\([0-9]\{1,\}\)

y\([0-9A-Za-z]*\)

/\2

-\1

/ ; t op_add

s/^\([0-9]\{1,\}\)

\([0-9]\{1,\}\)q/\1 \2

/ ; 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*

\([0-9A-Za-z]*\)

/\1

0

/ ; t op_add

s/^k 0* 0*\([1-9][0-9]*\)

\([0-9A-Za-z]*\)

/\2

\1

/ ; t op_add

s/^k 0*\([1-9][0-9]*\) \([0-9]*\)

\([0-9A-Za-z]*\)

/\3

\1\2

/ ; 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]*\)

\([0-9A-Za-z]*\)

/\2

-1\1

/ ; t op_add

s/^0* b

\([0-9A-Za-z]*\)

/\1

0

/ ; t op_add

s/^0*\([1-9][0-9]*\) b

\([0-9A-Za-z]*\)

/\2

-\1

/ ; t op_add

s/^\([0-9]*\) \([0-9]*\)0

/0\1 \2

/ ; t cse_neg

s/^\([0-9]*\) \([0-9]*\)b

/\1 \2 0123456789876543210

/ ; t cse_neg_2

s/^\([0-9]*\) \([0-9]*\)

/\1 \2 0123456789987654321

/ ; 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\}

/\4\1 \2b

/ ; 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/^

\(-\{0,1\}[0-9]\{1,\}\)

$/\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]*\)

/\1

0

/ ; 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').