Skip to content

Instantly share code, notes, and snippets.

@matyklug18
Created March 16, 2022 00:49
Show Gist options
  • Save matyklug18/1f8130c4022815a680ae135fd487aae7 to your computer and use it in GitHub Desktop.
Save matyklug18/1f8130c4022815a680ae135fd487aae7 to your computer and use it in GitHub Desktop.
class ExitError(Exception):
def __init__(self, trace=False):
self.trace = trace
def error(msg, trace=True):
print(msg)
raise ExitError(trace=trace)
#!/usr/bin/env python
import re
from errpy import ExitError, error
import traceback
digexpr = r'-?\d+(?:\.\d+)?'
strexpr = r'(?:".*?")|(?:\'.*?\')'
symexpr = r'(?:\\\s|\\\(|\\\)|\\\[|\\\]|[^\s()\[\]])+'
regx = fr'\(|\)|\[|\]|{digexpr}|{strexpr}|{symexpr}'
def parse_list(lst, end):
if len(lst) == 0: error(f"Parsing error, expected an expression or {end}")
res = []
e = lst[0]
if e == end:
lst.pop(0)
return res
else: res.append(parse_expr(lst))
while True:
if len(lst) == 0: error(f"Parsing error, expected an expression or {end}")
e = lst[0]
if e == end:
lst.pop(0)
return res
res.append(parse_expr(lst))
return res
def parse_expr(lst):
if len(lst) == 0: error("Parsing error, exected an expression")
e = lst.pop(0)
if e == '(':
return {"type": "list", "eval": True, "value": parse_list(lst, ')')}
if e == '[':
return {"type": "list", "eval": False, "value": parse_list(lst, ']')}
elif re.match(strexpr, e):
return {"type": "string", "value": e[1:-1]}
elif re.match(digexpr, e):
return {"type": "number", "value": e}
elif re.match(symexpr, e):
return {"type": "symbol", "value": e}
from infloats import Float
from infloats import Fraction
def g_add(args):
res = args[0]["value"]
for a in args[1:]:
res += a["value"]
if type(res) == Float or type(res) == Fraction:
return {"type":"number", "value": res}
elif type(res) == str:
return {"type":"string", "value": res}
def g_sub(args):
res = args[0]["value"]
for a in args[1:]:
res -= a["value"]
return {"type":"number", "value": res}
def g_mul(args):
res = args[0]["value"]
for a in args[1:]:
res *= a["value"]
return {"type":"number", "value": res}
def g_div(args):
res = args[0]["value"]
for a in args[1:]:
res /= a["value"]
return {"type":"number", "value": res}
def do_if(a, b, c):
return eval_expr(b) if eval_expr(a)["value"] else eval_expr(c)
def do_fn(args):
return {"type": "function", "eval": True, "value": lambda: eval_expr(args) }
prompt = "-> "
prompt_exec_raw = lambda: {"type":"string", "value":prompt}
prompt_exec = lambda: deal_exec_arg(prompt_exec_raw()).encode("utf-8").decode("unicode_escape")
def set_prompt(a):
global prompt, prompt_exec_raw
if a["type"] == "function":
prompt_exec_raw = a["value"]
else:
prompt = deal_exec_arg(a)
import os
import sys
from os.path import expanduser
import pathlib
import getpass
os.environ["SHELL"] = str(pathlib.Path(__file__))
os.environ["PATH"] += ':'+str(pathlib.Path(__file__).parent.absolute())
def g_set_env(v, e):
os.environ[v["value"]] = str(e["value"])
def g_add_env(v, e):
os.environ[v["value"]] += (":"+str(e["value"]))
return {"type":"string", "value":os.environ[v["value"]]}
def g_path(v):
os.environ["PATH"] += (":"+expanduser(v["value"]))
return {"type":"string", "value":os.environ["PATH"]}
def parse_pipe(args):
res = [[]]
i = 0
remov_stdout = False
is_file = False
filedesc = '0'
mode = "r"
if deal_exec_arg(args[-1]) == '!>':
remov_stdout = True
filedesc = '2'
a_args = args[:-1]
elif deal_exec_arg(args[-2]) == '>' or deal_exec_arg(args[-2]) == '>>':
is_file = True
if deal_exec_arg(args[-2]) == '>':
mode = "w"
elif deal_exec_arg(args[-2]) == '>>':
mode = "a"
a_args = args[:-2]
else:
a_args = args
for a in a_args:
if deal_exec_arg(a) == '|':
i+=1
res.append([])
else:
res[i].append(deal_exec_arg(a))
return res, is_file, deal_exec_arg(args[-1]), mode, remov_stdout, filedesc
def argv_to_arg(arg):
return arg
def g_pipe_top(*arg):
args, is_file, filename, mode, remov_stdout, filedesc = parse_pipe(arg)
if len(args) > 1:
fst = subprocess.Popen(args[0], stdout=subprocess.PIPE).stdout
lst_a = fst
else:
lst_a = None
for a in args[1:-1]:
lst_a = subprocess.Popen(a, stdin=lst_a, stdout=subprocess.PIPE).stdout
if remov_stdout:
if filedesc == '1':
lst = subprocess.Popen(args[-1], stdin=lst_a, stdout=subprocess.DEVNULL)
if filedesc == '2':
lst = subprocess.Popen(args[-1], stdin=lst_a, stderr=subprocess.DEVNULL)
if filedesc == '3':
lst = subprocess.Popen(args[-1], stdin=lst_a, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
lst.wait()
if is_file:
lst = subprocess.Popen(args[-1], stdin=lst_a, stdout=subprocess.PIPE)
lst.wait()
fff = open(filename, mode+'b')
fff.write(lst.stdout.read())
fff.close()
else:
lst = subprocess.Popen(args[-1], stdin=lst_a)
lst.wait()
return lst.returncode
def is_number(s):
return s.strip('\n').strip(' ').lstrip('-').replace('.', '', 1).isdigit()
def g_pipe(*arg):
args, is_file, filename, mode, remov_stdout, filedesc = parse_pipe(arg)
fst = subprocess.Popen(args[0], stdout=subprocess.PIPE).stdout
lst_a = fst
for a in args[1:-1]:
lst_a = subprocess.Popen(a, stdin=lst_a, stdout=subprocess.PIPE).stdout
if remov_stdout:
if filedesc == '1':
lst = subprocess.Popen(args[-1], stdin=lst_a, stdout=None)
lst.wait()
return lst.returncode
if filedesc == '2':
lst = subprocess.Popen(args[-1], stdin=lst_a, stderr=None)
sout = lst.stdout.read().decode("utf-8")
lst.wait()
if is_number(sout):
return {"type":"number", "value":Float(sout)}
else:
return {"type":"raw_string", "value":sout}
if filedesc == '3':
lst = subprocess.Popen(args[-1], stdin=lst_a, stdout=None, stderr=None)
lst.wait()
return lst.returncode
elif is_file:
lst = subprocess.Popen(args[-1], stdin=lst_a, stdout=subprocess.PIPE)
lst.wait()
fff = open(filename, mode+'b')
fff.write(lst.stdout.read())
fff.close()
return None
else:
lst = subprocess.Popen(args[-1], stdin=lst_a, stdout=subprocess.PIPE)
sout = lst.stdout.read().decode("utf-8")
lst.wait()
if is_number(sout):
return {"type":"number", "value":Float(sout)}
else:
return {"type":"raw_string", "value":sout}
def g_ex(*args):
fnc = find_sym(args[0]["value"])
arg = deal_exec_list(args[1:])
return {"type":"number", "value":Float(spawn_proc(fnc, arg))}
def g_alias(a, *rest):
ex = find_sym(rest[0]["value"])
gsyms[a["value"]] = {"type": "function", "eval": True, "value": lambda *ag: g_ex(*([ex]+list(rest[1:])+list(ag)))}
def g_unfree(*args):
fnc = find_sym(args[0]["value"])
arg = deal_exec_list(args[1:])
return {"type":"number", "value":Float(spawn_proc_env(fnc, arg, {"NIXPKGS_ALLOW_UNFREE": "1"}))}
import glob
toplevel_sym = {
'pipe': {"type": "function", "eval": True, "value": g_pipe_top}
}
gsyms = {
"println": {"type": "function", "eval": True, "value": lambda *a: print(*[e["value"] for e in a])},
"+": {"type": "function", "eval": True, "value": lambda *a: g_add(a) },
"-": {"type": "function", "eval": True, "value": lambda *a: g_sub(a) },
"*": {"type": "function", "eval": True, "value": lambda *a: g_mul(a) },
"/": {"type": "function", "eval": True, "value": lambda *a: g_div(a) },
"==": {"type": "function", "eval": True, "value": lambda a,b: {"type":"boolean", "value":a["value"]==b["value"]} },
">": {"type": "function", "eval": True, "value": lambda a,b: {"type":"boolean", "value":a["value"]>b["value"]} },
"<": {"type": "function", "eval": True, "value": lambda a,b: {"type":"boolean", "value":a["value"]<b["value"]} },
">=": {"type": "function", "eval": True, "value": lambda a,b: {"type":"boolean", "value":a["value"]>=b["value"]} },
"<=": {"type": "function", "eval": True, "value": lambda a,b: {"type":"boolean", "value":a["value"]<=b["value"]} },
"!=": {"type": "function", "eval": True, "value": lambda a,b: {"type":"boolean", "value":a["value"]!=b["value"]} },
"!": {"type": "function", "eval": True, "value": lambda a: {"type":"boolean", "value":not a["value"]} },
"&&": {"type": "function", "eval": True, "value": lambda a,b: {"type":"boolean", "value":a["value"] and b["value"]} },
"||": {"type": "function", "eval": True, "value": lambda a,b: {"type":"boolean", "value":a["value"] or b["value"]} },
'if': {"type": "function", "eval": False, "value": do_if },
'sh-if': {"type": "function", "eval": False, "value": lambda a,b,c: eval_expr(b) if eval_expr(a)["value"] == Float(0) else eval_expr(c) },
'fn': {"type": "function", "eval": False, "value": do_fn },
'prompt': {"type": "function", "eval": True, "value": set_prompt },
'cd': {"type": "function", "eval": True, "value": lambda dir={"value":expanduser("~")}: os.chdir(expanduser(dir["value"])) },
'pwd': {"type": "function", "eval": True, "value": lambda: {"type":"string", "value":os.getcwd()} },
'user': {"type": "function", "eval": True, "value": lambda: {"type":"string", "value":getpass.getuser()} },
'set-env': {"type": "function", "eval": True, "value": g_set_env },
'add-env': {"type": "function", "eval": True, "value": g_add_env },
'get-env': {"type": "function", "eval": True, "value": lambda v: {"type":"string", "value":os.environ[v["value"]]} },
'is-env': {"type": "function", "eval": True, "value": lambda v: {"type":"boolean", "value":v["value"] in os.environ} },
'path': {"type": "function", "eval": True, "value": g_path },
'pipe': {"type": "function", "eval": False, "value": g_pipe },
'glob': {"type": "function", "eval": False, "value": lambda s: {"type":"string", "value":' '.join(glob.glob(expanduser(s["value"])))} },
'ex': {"type": "function", "eval": False, "value": g_ex },
'unfree': {"type": "function", "eval": False, "value": g_unfree },
'alias': {"type": "function", "eval": False, "value": g_alias },
'do': {"type": "function", "eval": True, "value": lambda *a: a[-1]},
#'startswith': {"type": "function", "eval": True, "value": lambda a, b: {"type":"boolean", "value":a["value"].startswith(b["value"])} },
#'replace': {"type": "function", "eval": True, "value": lambda src, what, with: {"type":"string", "value":src["value"].replace(what["value"], with["value"])} },
'replace': {"type": "function", "eval": True, "value": lambda s,w,i: {"type":"string", "value":re.sub(w["value"], i["value"], s["value"])} },
}
import shutil
def path_query(sym):
return shutil.which(sym) != None
def path_get(sym):
return {"type":"executable", "value": shutil.which(sym), "original": sym}
def to_num(val):
return Float(val)
def opt_sym(sym):
if sym in gsyms: return gsyms[sym]
elif path_query(sym): return path_get(sym)
elif sym[0] == "$" and sym[1:] in os.environ: return {"type":"string", "value":os.getenv(sym[1:])}
#elif sym == '|': return {"type":"string", "value":'|'}
elif all([c == '.' for c in sym]): return {"type":"path-change", "value": "./" if len(sym) == 1 else (len(sym)-1)*'../'}
elif os.path.isdir(expanduser(sym)): return {"type":"path-change", "value": expanduser(sym)}
else: return None
def find_sym(sym):
ssym = opt_sym(sym)
if ssym: return ssym
else: error(f'Symbol not found: {sym}', trace=False)
import subprocess
def spawn_proc(exec, args):
proc = subprocess.Popen([exec["original"]]+args, executable=exec["value"])
proc.wait()
return proc.returncode
def spawn_proc_env(exec, args, env):
proc = subprocess.Popen([exec["original"]]+args, executable=exec["value"], env=os.environ.copy() | env)
proc.wait()
return proc.returncode
def get_proc_out(exec, args):
proc = subprocess.Popen([exec["original"]]+args, executable=exec["value"], stdout=subprocess.PIPE)
proc.wait()
res = proc.stdout.read().decode("utf-8")
return res
def deal_exec_arg(e):
if type(e) == dict:
if e["type"] == "string":
return e["value"]
elif e["type"] == "boolean":
return "true" if e["value"] else "false"
elif e["type"] == "raw_string":
if e["value"][-1] != '\n': return e["value"] + '\n'
else: return e["value"]
elif e["type"] == "number":
return str(e["value"])
elif e["type"] == "symbol":
if e["value"][0] == "$": return os.environ[e["value"][1:]]
else: return e["value"].replace("\\ ", " ").replace('\\_', " ").replace("\\(", "(").replace("\\)", ")").replace("\\[", "[").replace("\\]", "]")
elif e["type"] == "executable":
return e["value"]
else: error(f"TODO {e}")
else: return e
def deal_exec_list(lst):
res = []
for e in lst:
if e["type"] == "symbol":
res.append(e["value"])
else:
res.append(eval_expr(e))
dres = []
for e in res:
dres.append(deal_exec_arg(e))
return dres
def exec_exec(func, lst):
fnc = find_sym(func)
arg = []
if fnc["type"] == "executable":
arg = deal_exec_list(lst[1:])
elif not fnc["eval"]:
arg = lst[1:]
else:
for e in lst[1:]:
arg.append(eval_expr(e))
if fnc["type"] == "function":
return fnc["value"](*arg)
elif fnc["type"] == "executable":
return {"type":"raw_string", "value":get_proc_out(fnc, arg)}
else: error(f'Symbol not a function: {fnc["value"]}')
def eval_expr(expr):
if expr["type"] == "list":
return eval_list(expr)
elif expr["type"] == "symbol":
return find_sym(expr["value"])
elif expr["type"] == "string":
return {"type":"string", "value": expr["value"]}
elif expr["type"] == "number":
return {"type":"number", "value": to_num(expr["value"])}
else: return expr # TODO
def eval_list(lst):
if lst["eval"]:
if len(lst["value"]) == 0: return {"type":"list", "eval": True, "value": []}
fst0 = lst["value"][0]
if fst0["type"] == "list" and fst0["eval"]:
fst = eval_list(fst0)
else:
fst = fst0
if fst["type"] == "symbol":
return exec_exec(fst["value"], lst["value"])
elif fst["type"] == "function":
if fst["eval"]:
argl = [eval_expr(e) for e in lst["value"][1:]]
else:
argl = lst["value"][1:]
return fst["value"](*argl)
else: error(f'Not a symbol or function: {fst["value"]}')
else: return lst
def to_string(exp):
if type(exp) == dict:
if exp["type"] == "list":
res = []
for v in exp["value"]:
res.append(to_string(v))
if exp["eval"]: return f"({' '.join(res)})"
if not exp["eval"]: return f"[{' '.join(res)}]"
elif exp["type"] == "string":
return f'"{exp["value"]}"'
elif exp["type"] == "number":
return exp["value"]
elif exp["type"] == "function":
return exp["value"]
elif exp["type"] == "symbol":
return exp["value"]
elif exp["type"] == "number":
return exp["value"]
elif exp["type"] == "executable":
return exp["value"]
elif exp["type"] == "raw_string":
return exp["value"]
elif exp["type"] == "boolean":
return "true" if exp["value"] else "false"
else: error("TODO")
elif type(exp) == float:
return exp
elif type(exp) == int:
return exp
elif type(exp) == str:
return f'"{exp}"'
import signal
import sys
from termios import tcflush, TCIFLUSH
def signal_handler(signal, frame):
pass
#tcflush(sys.stdin, TCIFLUSH)
#print(f'\n{prompt_exec()}', end='')
def deal_builtin_exec(a):
if a["type"] == "symbol":
sym = a["value"]
if sym[0] == "$" and sym[1:] in os.environ:
return {"type":"string", "value":os.getenv(sym[1:])}
else:
return a
elif a["type"] == "string":
return eval_expr(a)
else: return eval_expr(a)
def deal_shell_exec(args):
toplevel = args[0]["value"]
root = find_sym(toplevel)
if toplevel in toplevel_sym:
root = toplevel_sym[toplevel]
arg = []
for a in args[1:]:
if a["type"] == "symbol":
sym = a["value"]
if sym[0] == "$" and sym[1:] in os.environ:
arg.append(os.getenv(sym[1:]))
else:
arg.append(a["value"])
else:
arg.append(deal_exec_arg(eval_expr(a)))
return root["value"](*arg)
elif root["type"] == "path-change":
os.chdir(root["value"])
elif root["type"] == "executable":
arg = []
is_pipe = False
for a in args[1:]:
if a["value"] == '|' or a["value"] == '>' or a["value"] == '>>' or a["value"] == '!>':
is_pipe = True
arg.append(a["value"])
elif a["type"] == "symbol":
sym = a["value"]
if sym[0] == "$" and sym[1:] in os.environ:
arg.append(os.getenv(sym[1:]))
else:
arg.append(a["value"].replace("\\ ", " ").replace('\\_', " ").replace("\\(", "(").replace("\\)", ")").replace("\\[", "[").replace("\\]", "]").replace("\\$", "$"))
else:
arg.append(deal_exec_arg(eval_expr(a)))
if is_pipe:
return g_pipe_top(*[root["value"]]+arg)
else:
return spawn_proc(root, arg)
else:
return root["value"](*([deal_builtin_exec(e) for e in args[1:]]))
#signal.signal(signal.SIGINT, signal_handler)
from os.path import expanduser
home = expanduser("~")
initrc = open(home+"/.config/ilisp/sh.il", 'r')
digexpr = r'-?\d+(?:\.\d+)?'
strexpr = r'(?:".*?")|(?:\'.*?\')'
symexpr = r'(?:\\\s|\\\(|\\\)|\\\[|\\\]|[^\s()\[\]])+'
regx = fr'\(|\)|\[|\]|{digexpr}|{strexpr}|{symexpr}'
def tokenize(inp):
res = []
curr = ""
esc = False
strm = False
for c in inp:
if c == '\n': pass
elif c == '\\': # escape: begin escape
esc = True
elif esc and c == '_':
curr += ' '
elif esc: # escaped: append unmodified
if strm or True: # TODO please die.
curr += ('\\'+c)
else: pass
# curr += c
esc = False
elif not strm and c in ['\'', '"']: # begin string: begin string, append current, begin new
strm = True
if curr != "": res.append(curr)
curr = c
elif strm and c in ['\'', '"']: # end string: end string, append current, begin new
strm = False
if curr != "": res.append(curr+c)
curr = ""
elif strm:
curr += c
elif c in ['[', ']', '(', ')']: # list: append current, append character, begin new
if curr != "": res.append(curr)
res.append(c)
curr = ""
elif (len(curr) == 0 and c in "-.0123456789") or (len(curr) > 1 and (curr[0] in "-.0123456789") and all([(ch in ".0123456789") for ch in curr[1:]]) and c in ".0123456789"): # digit: append character
curr += c
elif c == ' ' and not strm: # space: append current, begin new
if curr != "": res.append(curr)
curr = ""
else:
curr += c
if len(curr) > 0: res.append(curr)
return res
def exec_line(line, silent=False):
line_r = '\n'.join(paren_split(line))
if len(line_r) == 0: return
lst = re.findall(regx, line_r, re.MULTILINE)
#lst = tokenize(line_r)
#print(tokenize(line_r))
res = parse_expr(lst)
if len(lst) == 0 and type(res) == dict and res["type"] == "symbol":
t_str = to_string(deal_shell_exec([res]))
if t_str != None:
print(t_str)
elif len(lst) == 0:
e_res = eval_expr(res)
if not silent:
if e_res != None:
print(to_string(e_res))
else:
ress = [res]
while True:
if len(lst) == 0:
break
o_len = len(lst)
ress.append(parse_expr(lst))
if o_len == len(lst):
if len(lst) != 0:
print(f'Unexpected "{" ".join(lst)}", Expected EOF')
break
else:
o_len = len(lst)
print(to_string(deal_shell_exec(ress)))
def paren_split(inp):
pi = 0
res = [""]
ix = 0
w = False
e = False
for i,c in enumerate(inp):
if e:
e = False
res[ix] += c
continue
if w:
if c == '\n':
w = False
continue
else: continue
if c == '(': pi += 1
elif c == ')': pi -= 1
elif c == '\\':
if inp[i+1] in []: # TODO why in the actual *FUCK* does emptying this array fix escaping???
e = True
continue
elif c == ';':
w = True
continue
res[ix] += c
if pi == 0:
if res[ix][0] == '(' and res[ix][-1] == ')':
ix += 1
res.append("")
elif res[ix] == '\n':
res[ix] = ""
if len(res[ix]) == 0: res.pop()
return res
initrctxt = initrc.readlines()
if len(initrctxt) != 0:
try:
for l in paren_split('\n'.join(initrctxt)):
exec_line(l, silent=True)
except ExitError:
pass
except Exception as e:
print("Unknown Error During Startup: \n"+str(e))
traceback.print_tb(e.__traceback__)
initrc.close()
import readline
def gsym_complete(text):
gsymm = []
for k in gsyms.keys():
if k.startswith(text):
gsymm.append(k)
gsymm.append(None)
return gsymm
def file_complete(text):
try:
t_text = text.replace('\\_', " ").replace("\\(", "(").replace("\\)", ")").replace("\\[", "[").replace("\\]", "]")
t_text = re.sub(r"([\[\]])", r"[\1]", t_text)
res_t = glob.glob(expanduser(t_text)+'*')
if len(res_t) > 1:
res = []
for r in res_t:
res.append(r.replace(" ", "\\_").replace("(", "\\(").replace(")", "\\)").replace("[", "\\[").replace("]", "\\]"))
#res.append(f"'{r}'")
return res+[None]
else:
res = res_t[0].replace(" ", "\\_").replace("(", "\\(").replace(")", "\\)").replace("[", "\\[").replace("]", "\\]")
#res = f"'{res_t[0]}'"
return [res,None]
except Exception as e:
#traceback.print_tb(e.__traceback__)
pass
binpath = os.environ["PATH"].split(':')
allfiles = []
for dir in binpath:
if os.path.exists(dir):
allfiles.extend([f for f in os.listdir(dir) if os.path.isfile(os.path.join(dir, f))])
def binary_complete(text):
res = []
for f in allfiles:
if f.startswith(text):
res.append(f)
return res+[None]
def complete(text, state):
bc = binary_complete(text)
if len(bc) > 1: return bc[state]
fc = file_complete(text)
if len(fc) > 1:
fcp = fc[state]
#fcpr = os.path.basename(fcp) TODO figure out
fcpr = fcp
#if os.path.isdir(fcp.replace("\\ ", " ").replace("\\(", "(").replace("\\)", ")").replace("\\[", "[").replace("\\]", "]")): fcpr += '/'
if fcp[0] == "'" and fcp[-1] == "'":
fcppr = fcp[1:-1]
if os.path.isdir(fcppr):
fcpr = fcpr[:-1] + "/'"
else:
fcppr = fcp
if os.path.isdir(fcppr):
fcpr = fcpr + "/"
return fcpr
gc = gsym_complete(text)
if len(gc) > 1: return gc[state]
readline.set_completer_delims(' ()\t\n;')
readline.parse_and_bind("tab: complete")
readline.set_completer(complete)
histfile = expanduser("~/.ilisphist")
if not os.path.exists(histfile):
os.mknod(histfile)
readline.read_history_file(histfile)
# TODO WTF PYTHON
## import atexit
## def exit_handler():
## readline.write_history_file(histfile)
## signal.signal(signal.SIGTERM, exit_handler)
## signal.signal(signal.SIGINT, exit_handler)
## atexit.register(exit_handler)
def f_input(prompt):
try:
return input(prompt)
except KeyboardInterrupt:
print('^C')
return ''
def repl():
try:
while True:
exec_line(f_input(prompt_exec()))
readline.write_history_file(histfile)
except EOFError:
sys.exit("\nExit")
except KeyboardInterrupt:
pass
except ExitError:
pass
except Exception as e:
print("Unknown Error: \n"+str(e))
traceback.print_tb(e.__traceback__)
while True:
repl()
import math
from errpy import ExitError, error
def simplify(val, exp):
tval = val
texp = exp
while (divs := math.gcd(tval, texp)) != 1:
tval //= divs
texp //= divs
return tval, texp
def prime_factors(n):
i = 2
factors = []
while i * i <= n:
if n % i:
i += 1
else:
n //= i
factors.append(i)
if n > 1:
factors.append(n)
return factors
def is_float(val, exp):
_, den = simplify(val, exp)
return all((e in [2, 5]) for e in prime_factors(den))
def get_float(val, exp):
if(is_float(val, exp)):
num, dem = simplify(val, exp)
eexp = 1
while(num*(10**eexp) % dem != 0): eexp += 1
return num*(10**eexp)//dem, eexp, True
else: return 0, 0, False
class Fraction:
def __init__(self, val, exp):
self.val, self.exp = simplify(val, exp)
def invert(self):
val = self.val
self.val = self.exp
self.exp = val
return self
def sign_flip(self):
self.val = -self.val
return self
def __mul__(self, other):
if type(other) == Fraction:
res = (self.val * other.val, self.exp * other.exp)
v, e, i = get_float(res[0], res[1])
if i: return Float(v, e)
else: return Fraction(res[0], res[1])
elif type(other) == Float:
ofrac = other.get_fraction()
res = (self.val * ofrac.val, self.exp * ofrac.exp)
v, e, i = get_float(res[0], res[1])
if i: return Float(v, e)
else: return Fraction(res[0], res[1])
def __truediv__(self, other):
if type(other) == Fraction:
return self.__mul__(other.invert())
elif type(other) == Float:
return self.__mul__(other.get_fraction().invert())
def __add__(self, other):
if type(other) == Fraction:
flcm = math.lcm(self.exp, other.exp)
sv = self.val * (flcm//self.exp)
ov = other.val * (flcm//other.exp)
res = (sv + ov, flcm)
v, e, i = get_float(res[0], res[1])
if i: return Float(v, e)
else: return Fraction(res[0], res[1])
elif type(other) == Float:
ofrac = other.get_fraction()
flcm = math.lcm(self.exp, ofrac.exp)
sv = self.val * (flcm//self.exp)
ov = ofrac.val * (flcm//ofrac.exp)
res = (sv + ov, flcm)
v, e, i = get_float(res[0], res[1])
if i: return Float(v, e)
else: return Fraction(res[0], res[1])
def __sub__(self, other):
if type(other) == Fraction:
return self.__mul__(other.sign_flip())
elif type(other) == Float:
return self.__mul__(other.get_fraction().sign_flip())
def __eq__(self, other):
if type(other) == Float:
othfr = other.get_fraction()
return self.val == othfr.val and self.exp == othfr.exp
elif type(other) == Fraction:
return self.val == other.val and self.exp == other.exp
def __gt__(self, other):
if type(other) == Fraction:
flcm = math.lcm(self.exp, other.exp)
sv = self.val * (flcm//self.exp)
ov = other.val * (flcm//other.exp)
return sv > ov
elif type(other) == Float:
othfr = other.get_fraction()
flcm = math.lcm(self.exp, othfr.exp)
sv = self.val * (flcm//self.exp)
ov = othfr.val * (flcm//othfr.exp)
return sv > ov
def __ge__(self, other):
if self == other: return True
else: return self > other
def __lt__(self, other):
if type(other) == Fraction:
flcm = math.lcm(self.exp, other.exp)
sv = self.val * (flcm//self.exp)
ov = other.val * (flcm//other.exp)
return sv < ov
elif type(other) == Float:
othfr = other.get_fraction()
flcm = math.lcm(self.exp, othfr.exp)
sv = self.val * (flcm//self.exp)
ov = othfr.val * (flcm//othfr.exp)
return sv < ov
def __le__(self, other):
if self == other: return True
else: return self < other
def __str__(self):
return f'{self.val}/{self.exp}'
def __repr__(self):
return self.__str__()
class Float:
def __init__(self, val, exp = 0):
vals = str(val).split('.')
if len(vals) == 1 and vals[0] == '': vals[0] = '0'
if len(vals) == 1: vals.append("0")
vals[0].lstrip('0')
vals[1].rstrip('0')
zrs = len(vals[1])
iexp = int(exp)
ovals = ''.join(vals)
rvals = ''.join(vals).rstrip('0')
if len(rvals) == 0: rvals = '0'
ordif = len(ovals)-len(rvals)
self.exp = iexp+zrs-ordif
self.val = int(rvals)
def _frac(self):
if self.exp == 0:
sv = self.val
se = 1
if self.exp > 0:
sv = self.val
se = 10 ** self.exp
if self.exp < 0:
sv = self.val * (10 ** (-self.exp))
se = 1
return sv, se
def get_fraction(self):
v, e = self._frac()
return Fraction(v, e)
def __mul__(self, other):
if type(other) == Float:
return Float(self.val * other.val, self.exp + other.exp)
elif type(other) == Fraction:
return self.get_fraction() * other
def __truediv__(self, other):
if type(other) == Float:
sv, se = self._frac()
ov, oe = other._frac()
rfrac = (sv * oe, se * ov)
val, exp, isflt = get_float(rfrac[0], rfrac[1])
if isflt:
return Float(val, exp)
else:
return Fraction(rfrac[0], rfrac[1])
elif type(other) == Fraction:
return self.get_fraction() / other
def __add__(self, other):
if type(other) == Float:
if self.exp < other.exp:
return Float(other.val + (self.val * (10 ** (other.exp - self.exp))), other.exp)
else:
return Float(self.val + (other.val * (10 ** (self.exp - other.exp))), self.exp)
elif type(other) == Fraction:
return self.get_fraction() + other
def __sub__(self, other):
if type(other) == Float:
return self.__add__(Float(-other.val, other.exp))
elif type(other) == Fraction:
return self.get_fraction() - other
def __eq__(self, other):
if type(other) == Fraction:
return self.get_fraction() == other
elif type(other) == Float:
return self.val == other.val and self.exp == other.exp
def __gt__(self, other):
if type(other) == Float:
return self.get_fraction() > other.get_fraction()
elif type(other) == Fraction:
return self.get_fraction() > other
def __ge__(self, other):
if self == other: return True
else: return self > other
def __lt__(self, other):
if type(other) == Float:
return self.get_fraction() < other.get_fraction()
elif type(other) == Fraction:
return self.get_fraction() < other
def __le__(self, other):
if self == other: return True
else: return self < other
def __str__(self):
#return f'{self.val} {self.exp}'
sval = str(self.val)
iexp = len(sval)-self.exp
if self.exp == 0:
return sval
elif self.exp < 0:
return sval + ('0' * (-self.exp))
elif iexp == 0:
return '0.' + sval
elif iexp < 0:
return '0.' + ('0'*(-iexp)) + sval
else:
return sval[:iexp]+'.'+sval[iexp:]
def __repr__(self):
return self.__str__()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment