Post Reply 
bc (basic calculator) and libraries of functions
07-21-2018, 11:30 PM (This post was last modified: 04-17-2020 12:14 PM by Albert Chan.)
Post: #11
RE: bc (basic calculator) and libraries of functions
For trying things out, unlike bc, gawk lack a REPL

I made a simple REPL for gawk, for trying things out.
Code:
# igawk.py
import sys
from subprocess import Popen, PIPE

class UserInput:
    def __init__(self, inp):
        self.inp = inp
        self.output_chars = [0,0]

class Runner:
    def __init__(self, name, args):
        self.user_input = []
        self.compile_error = ""
        self.input_num = 0
        self.prog = name + args
        self.prompt = name + ":%d> "
        self.output_chars = [0,0]
        
    def read_line(self):
        while 1:
            prompt = self.prompt
            if prompt: prompt = prompt % (self.input_num + 1)
            line = raw_input(prompt)
            if line == '\\': line = '\t'.join(sys.stdin.readlines())
            line = line.strip()
            if line: return line

    def do_run(self):
        while 1:
            line = self.read_line()
            type = dot_cmd_process(self, line)
            if type is None: continue   # DOT cmd processed

            if type == 'INPUT':         # add line + run_awk
                if line[0] == '=':      # shorthand for print ...
                    line = 'print ' + line[1:]
                line += ')' * (line.count('(') - line.count(')'))
                last = UserInput(line)
                try:   self.user_input[self.input_num] = last
                except IndexError: self.user_input.append(last)
                self.input_num += 1
            else:
                last = self.user_input[self.input_num - 1]

            run = Popen(self.prog, stdin=PIPE, stdout=PIPE, stderr=PIPE)
            outputs = run.communicate(self.get_source())

            if run.returncode:
                self.compile_error = '\n'.join(outputs)
                print "[Compile error - type .e to see it.]"
                dot_u(self)
                continue

            for i, output in enumerate(outputs):
                n = len(output) - self.output_chars[i]
                if not n: continue
                last.output_chars[i] = n
                self.output_chars[i] += n
                print output[-n:]

    def get_user_input(self):
        return self.user_input[:self.input_num]

    def get_source(self):
        code = [ line.inp for line in self.get_user_input() ]
        return 'BEGIN {\n%s\n}' % ';\n'.join(code)

def dot_q(runner): raise EOFError()
def dot_e(runner): print runner.compile_error

def dot_l(runner, i=0):
    for line in runner.get_user_input():
        i = i+1
        print "%d\t%s" % (i, line.inp)

def dot_r(runner):
    n = runner.input_num
    if n < len(runner.user_input):
        runner.input_num = n + 1
        redo = runner.user_input[n]
        print "[Redo '%s'.]" % redo.inp
        return redo     # execute command again!
    else:
        print "[Nothing to redo.]"

def dot_u(runner):
    n = runner.input_num
    if n:
        runner.input_num = n = n - 1
        undone = runner.user_input[n]
        runner.output_chars[0] -= undone.output_chars[0]
        runner.output_chars[1] -= undone.output_chars[1]
        print "[Undone '%s'.]" % undone.inp
    else:
        print "[Nothing to undo.]"

def dot_U(runner):
    runner.compile_error = ""
    runner.input_num = 0
    runner.output_chars = [0,0]
    print "[Undo ALL]"

def dot_h(runner):
    for cmd in sorted(dot_cmds, key=str.lower):
        print cmd, dot_cmds[cmd][0]

def dot_cmd_process(runner, inp):
    if inp not in dot_cmds: return 'INPUT'
    return dot_cmds[inp][1]( runner )

dot_cmds = {
    ".h" : ( "Show this help message", dot_h ),
    ".q" : ( "Quit", dot_q ),
    ".e" : ( "Show the last compile errors/warnings", dot_e ),
    ".l" : ( "List the code you have entered", dot_l ),
    ".r" : ( "Redo undone command", dot_r ),
    ".u" : ( "Undo previous command", dot_u ),
    ".U" : ( "Undo all commands", dot_U),
    }

if __name__ == '__main__':
    prog = 'gawk' if len(sys.argv)==1 else sys.argv[1]
    args = ' -vOFMT="%.17g" -vORS="" -f -'
    
    if   prog == 'perl': args = ' -'
    elif prog == 'awk' : pass           # NOTE: 53-bits rounding
    elif prog != 'gawk': prog = None    # Force TypeError
    
    try: Runner(prog, args).do_run()
    except EOFError: pass               # Ctrl-Z to exit
    except TypeError: print "Usage: igawk.py [perl | awk | gawk]"

Example, below trick returns base^(-precision), both decimal and binary system

> igawk.py
gawk:1> x = 1/3
gawk:2> = 1-x-x-x
1.1102230246251565e-016
Find all posts by this user
Quote this message in a reply
Post Reply 


Messages In This Thread
RE: bc (basic calculator) and libraries of functions - Albert Chan - 07-21-2018 11:30 PM



User(s) browsing this thread: 6 Guest(s)