#!/bin/sh # INTERCAL unary and binary operators written in dd/sh (base 2 only) # with an example simple calculator # usage: type simple numbers with an optional operation, for example: # #1 # #V1 # #&1 # #1~#2 # /bin/rm is not really required, but it is nice to clean up temporary files PATH= dd=/bin/dd rm=/bin/rm # temporary files we might need tmp=/tmp/silly.$$ trap "$rm -f $tmp $tmp.*; exit" 0 HUP INT QUIT # from now on, no more rm - the above trap is enough unset rm # we do interesting things with IFS, but better save it... saveIFS="$IFS" # in case "echo" is not a shell builtin... Echo () { case "$1" in -n) shift $dd of=$tmp 2>/dev/null <&1` IFS="$saveIFS" $dd if=$tmp bs=1 count=$1 2>/dev/null ;; *) $dd 2>/dev/null </dev/null } # we use this during recursion stash () { eval sname=\$stash_$1 case "$sname" in ?*) sname="s$sname" ;; *) sname="s_$1" ;; esac eval stash_$1="$sname" eval $sname=\$$1 } retrieve () { eval fname=\$stash_$1 eval value=\$$fname eval $1=\$$fname eval sv=\$stash_$1 value="`Echo -n $sv | $dd bs=1 skip=1 2>/dev/null`" eval stash_$1=$value } # parse simple INTERCAL expressions parse () { if ! parse_term then return 1 fi while true do case "$text" in '¢'*) f=f_interleave ;; '$'*) f=f_interleave ;; '£'*) f=f_interleave ;; '~'*) f=f_select ;; *) return 0 ;; esac text="`Echo -n "$text" | $dd bs=1 skip=1 2>/dev/null`" left="$result" stash left stash f if ! parse_term then retrieve f retrieve left return 1 fi retrieve f retrieve left right="$result" if ! $f result "$left" "$right" then return 1 fi done } parse_term () { code="`Echo -n "$text" | $dd bs=1 count=1 2>/dev/null`" text="`Echo -n "$text" | $dd bs=1 skip=1 2>/dev/null`" case "$code" in '#') if parse_number then return 0 else return 1 fi ;; '"') if ! parse then return 1 fi case "$text" in '"'*) ;; *) return 1 ;; esac text="`Echo -n "$text" | $dd bs=1 skip=1 2>/dev/null`" return 0 ;; "'") if ! parse then return 1 fi case "$text" in "'"*) ;; *) return 1 ;; esac text="`Echo -n "$text" | $dd bs=1 skip=1 2>/dev/null`" return 0 ;; *) return 1 ;; esac } parse_number () { case "$text" in 'V'*) f=f_or; c=true ;; '¥'*) f=f_xor; c=true ;; '?'*) f=f_xor; c=true ;; '&'*) f=f_and; c=true ;; *) f=true; c=false ;; esac stash f problem=true if $c then text="`Echo -n "$text" | $dd bs=1 skip=1 2>/dev/null`" if parse_number then problem=false fi else result=0000000000000000 while parse_n1 do problem=false done fi retrieve f if $problem then return 1 fi arg="$result" $f result "$arg" return 0 } parse_n1 () { digit="`Echo -n "$text" | $dd bs=1 count=1 2>/dev/null`" case "$digit" in 0) num=0000000000000000 ;; 1) num=1000000000000000 ;; 2) num=0100000000000000 ;; 3) num=1100000000000000 ;; 4) num=0010000000000000 ;; 5) num=1010000000000000 ;; 6) num=0110000000000000 ;; 7) num=1110000000000000 ;; 8) num=0001000000000000 ;; 9) num=1001000000000000 ;; *) return 1 ;; esac text="`Echo -n "$text" | $dd bs=1 skip=1 2>/dev/null`" # check if result is already too big case "$result" in *1) return 1 ;; *01) return 1 ;; *001) return 1 ;; esac # shift left result by 1 and 3 res1="0`Echo -n "$result" | $dd bs=1 count=15 2>/dev/null`" res3="000`Echo -n "$result" | $dd bs=1 count=13 2>/dev/null`" # now add the numbers c1=0 c2=0 temp='' for digit in 0 1 2 3 4 5 6 7 8 9 A B C D E F do dig1="`Echo -n "$res1" | $dd bs=1 count=1 2>/dev/null`" res1="`Echo -n "$res1" | $dd bs=1 skip=1 2>/dev/null`" dig3="`Echo -n "$res3" | $dd bs=1 count=1 2>/dev/null`" res3="`Echo -n "$res3" | $dd bs=1 skip=1 2>/dev/null`" dign="`Echo -n "$num" | $dd bs=1 count=1 2>/dev/null`" num="`Echo -n "$num" | $dd bs=1 skip=1 2>/dev/null`" case "$dig1$dign$dig3$c1" in 0000) add=0; carry=0 ;; 0001) add=1; carry=0 ;; 0010) add=1; carry=0 ;; 0011) add=0; carry=1 ;; 0100) add=1; carry=0 ;; 0101) add=0; carry=1 ;; 0110) add=0; carry=1 ;; 0111) add=1; carry=1 ;; 1000) add=1; carry=0 ;; 1001) add=0; carry=1 ;; 1010) add=0; carry=1 ;; 1011) add=1; carry=1 ;; 1100) add=0; carry=1 ;; 1101) add=1; carry=1 ;; 1110) add=1; carry=1 ;; 1111) add=1; carry=2 ;; esac temp="$temp$add" case "$c2$carry" in 00) c1=0; c2=0 ;; 01) c1=1; c2=0 ;; 02) c1=0; c2=1 ;; 10) c1=1; c2=0 ;; 11) c1=0; c2=1 ;; 12) c1=1; c2=1 ;; esac done case "$c1" in 1) return 1 ;; esac case "$c2" in 1) return 1 ;; esac result="$temp" return 0 } reverse () { text="$1" result='' while true do case "$text" in ?*) ;; *) return ;; esac first="`Echo -n "$text" | $dd bs=1 count=1 2>/dev/null`" text="`Echo -n "$text" | $dd bs=1 skip=1 2>/dev/null`" result="$first$result" done } num2wimp () { reverse "$1" num="$result" result=0 while true do case "$num" in ?*) ;; *) break ;; esac carry="`Echo -n "$num" | $dd bs=1 count=1 2>/dev/null`" num="`Echo -n "$num" | $dd bs=1 skip=1 2>/dev/null`" temp="$result" result='' while true do case "$temp" in ?*) ;; *) break ;; esac dig="`Echo -n "$temp" | $dd bs=1 count=1 2>/dev/null`" temp="`Echo -n "$temp" | $dd bs=1 skip=1 2>/dev/null`" > $tmp zero | $dd bs=2 count=$dig 2>/dev/null >> $tmp zero | $dd bs=1 count=$carry 2>/dev/null >> $tmp IFS="+" set `$dd if=$tmp bs=1 of=/dev/null 2>&1` IFS="$saveIFS" case "$1" in ??*) carry="`Echo -n "$1" | $dd bs=1 count=1 2>/dev/null`" ndig="`Echo -n "$1" | $dd bs=1 skip=1 2>/dev/null`" ;; ?) carry=0 ndig="$1" ;; *) carry=0 ndig=0 ;; esac result="$result$ndig" done case "$carry" in 0) ;; *) result="$result$carry" ;; esac done reverse "$result" } # INTERCAL operators f_interleave () { res="$1" num1="$2" num2="$3" case "$num1" in ????????????????) ;; ????????????????0000000000000000) num1="`Echo "$num1" | $dd bs=1 count=16 2>/dev/null`" ;; *) Echo "Too many spots" 2>&1 return 1 ;; esac case "$num2" in ????????????????) ;; ????????????????0000000000000000) num2="`Echo "$num2" | $dd bs=1 count=16 2>/dev/null`" ;; *) Echo "Too many spots" 2>&1 return 1 ;; esac val='' for bits in 0 1 2 3 4 5 6 7 8 9 A B C D E F do dig1="`Echo -n "$num1" | $dd bs=1 count=1 2>/dev/null`" num1="`Echo -n "$num1" | $dd bs=1 skip=1 2>/dev/null`" dig2="`Echo -n "$num2" | $dd bs=1 count=1 2>/dev/null`" num2="`Echo -n "$num2" | $dd bs=1 skip=1 2>/dev/null`" val="$val$dig2$dig1" done eval $res=$val return 0 } f_select () { res="$1" num1="$2" num2="$3" case "$num2" in ????????????????) case "$num1" in ????????????????) ;; ????????????????????????????????) num1="`Echo "$num1" | $dd bs=1 count=16 skip=16 2>/dev/null`" ;; esac ;; ????????????????????????????????) case "$num1" in ????????????????) num1="${num1}0000000000000000" ;; ????????????????????????????????) ;; esac ;; esac val='' end='' while true do case "$num1" in ?*) ;; *) break ;; esac dig1="`Echo -n "$num1" | $dd bs=1 count=1 2>/dev/null`" num1="`Echo -n "$num1" | $dd bs=1 skip=1 2>/dev/null`" dig2="`Echo -n "$num2" | $dd bs=1 count=1 2>/dev/null`" num2="`Echo -n "$num2" | $dd bs=1 skip=1 2>/dev/null`" case "$dig2" in 0) end="${end}0" ;; 1) val="${val}$dig1" ;; esac done eval $res=$val$end return 0 } f_or () { unary "$1" "$2" 0 1 1 1 } f_xor () { unary "$1" "$2" 0 1 1 0 } f_and () { unary "$1" "$2" 0 0 0 1 } unary () { res="$1" num="$2" d1="$3" d2="$4" d3="$5" d4="$6" case "$num" in 0*) num="${num}0" ;; 1*) num="${num}1" ;; *) Echo "Something bad happened, bit value is wrong" >&2 ; return 1 ;; esac val='' while true do case "$num" in ?) break ;; esac dig="`Echo -n "$num" | $dd bs=1 count=2 2>/dev/null`" num="`Echo -n "$num" | $dd bs=1 skip=1 2>/dev/null`" case "$dig" in 00) val="${val}$d1" ;; 01) val="${val}$d2" ;; 10) val="${val}$d3" ;; 11) val="${val}$d4" ;; esac done eval $res="$val" } # the main program Echo -n "? " while read line do case "$line" in ?*) ;; *) Echo -n "?? " continue ;; esac text="$line" parse case "$result" in *?) case "$text" in ?*) Echo "Parse error just before $text" ;; *) num2wimp "$result" Echo "$line -> $result" ;; esac ;; *) Echo "Something went wrong" ;; esac Echo -n "? " done