From 5c0b3c39c12185fcf8cc00b74a2b8a9bbb421e55 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Thu, 28 Apr 2022 16:25:27 -0400 Subject: [PATCH 001/121] add quackery state --- quackery.py | 1357 +++++++++++++++++++++++++-------------------------- 1 file changed, 675 insertions(+), 682 deletions(-) diff --git a/quackery.py b/quackery.py index 42678f8..1d1e6ed 100644 --- a/quackery.py +++ b/quackery.py @@ -9,67 +9,81 @@ except: pass +def isNest(item): + return isinstance(item, list) -class QuackeryError(Exception): - pass - - -def quackery(source_string): - - """ Perform a Quackery program. Return the stack as a string. """ - - def failed(message): - traverse(build(""" stacksize pack - decimal unbuild - return$ - nestdepth ]bailby[ """)) - returnstack = string_from_stack() - thestack = string_from_stack() - raise QuackeryError('\n Problem: ' + message + - '\nQuackery Stack: ' + str(thestack)[2:-2] + - '\n Return stack: ' + str(returnstack)) - - def isNest(item): - return isinstance(item, list) - - def isNumber(item): - return isinstance(item, int) - - def isOperator(item): - return isinstance(item, types.FunctionType) - - def expect_something(): - nonlocal qstack - if qstack == []: - failed('Stack unexpectedly empty.') - - def top_of_stack(): - nonlocal qstack - return(qstack[-1]) +def isNumber(item): + return isinstance(item, int) - def expect_nest(): - expect_something() - if not isNest(top_of_stack()): - failed('Expected nest on stack.') +def isOperator(item): + return isinstance(item, types.FunctionType) - def expect_number(): - expect_something() - if not isNumber(top_of_stack()): - failed('Expected number on stack.') +def isinteger(string): + numstr = string + if len(numstr) > 0 and numstr[0] == '-': + numstr = numstr[1:] + return numstr.isdigit() - def to_stack(item): - nonlocal qstack - qstack.append(item) +def ishex(string): + hexstr = string + if len(hexstr) > 1 and hexstr[0] == '-': + hexstr = hexstr[1:] + for char in hexstr: + if char.lower() not in '0123456789abcdef': + return False + return True - def from_stack(): - nonlocal qstack - expect_something() - return qstack.pop() +class QuackeryError(Exception): + pass - def string_from_stack(): - expect_nest() +class QuackeryState: + def __init__(self, qstack = None, operators = None, builders = None): + self.qstack = [] if qstack is None else qstack + self.rstack = [] + self.operators = predefined_operators.copy() if operators is None else operators + self.builders = predefined_builders.copy() if builders is None else builders + self.program_counter = 0 + self.current_nest = [] + self.source = '' + self.current_build = [] + + def copy(self): + new = QuackeryState(self.qstack.copy(), self.operators.copy(), self.builders.copy()) + new.rstack = self.rstack.copy() + new.program_counter = self.program_counter + new.current_nest = self.current_nest.copy() + new.source = self.source + new.current_build = self.current_build.copy() + return new + + def expect_something(self): + if self.qstack == []: + self.failed('Stack unexpectedly empty.') + + def top_of_stack(self): + return self.qstack[-1] + + def expect_nest(self): + self.expect_something() + if not isNest(self.top_of_stack()): + self.failed('Expected nest on stack.') + + def expect_number(self): + self.expect_something() + if not isNumber(self.top_of_stack()): + self.failed('Expected number on stack.') + + def to_stack(self, item): + self.qstack.append(item) + + def from_stack(self): + self.expect_something() + return self.qstack.pop() + + def string_from_stack(self): + self.expect_nest() result = '' - for ch in from_stack(): + for ch in self.from_stack(): if ch == 13: # \r result += '\n' elif 31 < ch < 127: @@ -78,657 +92,114 @@ def string_from_stack(): result += '?' # XXX @dragoncoder047 maybe use \uFFFD on platforms that support unicode? return result - def string_to_stack(str): + def string_to_stack(self, string): result = [] - for ch in str: + for ch in string: if ch == '\n': result.append(13) elif 31 < ord(ch) < 127: result.append(ord(ch)) else: result.append(ord('?')) # XXX @dragoncoder047 maybe \0 or NULL to signify bad char? - to_stack(result) - - def python(): - nonlocal to_stack - nonlocal from_stack - nonlocal string_to_stack - nonlocal string_from_stack - try: - exec(string_from_stack()) - except QuackeryError: - raise - except Exception as diagnostics: - failed('Python reported: "' + str(diagnostics) + '"') - - def qfail(): - message = string_from_stack() - failed(message) - - def stack_size(): - nonlocal qstack - to_stack(len(qstack)) - - def qreturn(): - nonlocal rstack - to_stack(rstack) - - def dup(): - a = from_stack() - to_stack(a) - to_stack(a) - - def drop(): - from_stack() - - def swap(): - a = from_stack() - b = from_stack() - to_stack(a) - to_stack(b) - - def rot(): # XXX @dragoncoder047 maybe simplify to [ dip swap swap ] is rot ? There are no cyclic references that would prevent this - a = from_stack() - swap() - to_stack(a) - swap() - - def over(): # XXX @dragoncoder047 maybe simplify to [ dip dup swap ] is over ? same reason as above - a = from_stack() - dup() - to_stack(a) - swap() - - def nest_depth(): - nonlocal rstack - to_stack(len(rstack) // 2) - - def to_return(item): - nonlocal rstack - rstack.append(item) - - def from_return(): - nonlocal rstack - if rstack == []: - failed('Return stack unexpectedly empty.') - return rstack.pop() - - true = 1 - - false = 0 - - def bool_to_stack(qbool): - to_stack(true if qbool else false) - - def nand(): - expect_number() - a = from_stack() - expect_number() - bool_to_stack(from_stack() == false or a == false) - - def equal(): - expect_something() - a = from_stack() - expect_something() - bool_to_stack(a == from_stack()) - - def greater(): - expect_number() - a = from_stack() - expect_number() - bool_to_stack(from_stack() > a) - - def inc(): - expect_number() - to_stack(1 + from_stack()) - - def plus(): - expect_number() - a = from_stack() - expect_number() - to_stack(a + from_stack()) - - def negate(): - expect_number() - to_stack(-from_stack()) - - def multiply(): - expect_number() - a = from_stack() - expect_number() - to_stack(a * from_stack()) - - def qdivmod(): - expect_number() - a = from_stack() - if a == 0: - failed('Division by zero.') - expect_number() - results = divmod(from_stack(), a) - to_stack(results[0]) - to_stack(results[1]) - - def exponentiate(): - expect_number() - a = from_stack() - if a < 0: - failed('Tried to raise to a negative power: ' + str(a)) - expect_number() - to_stack(from_stack() ** a) - - def shift_left(): - expect_number() - a = from_stack() - if a < 0: - failed('Cannot << by a negative amount: ' + str(a)) - expect_number() - to_stack(from_stack() << a) - - def shift_right(): - expect_number() - a = from_stack() - if a < 0: - failed('Cannot >> by a negative amount: ' + str(a)) - expect_number() - to_stack(from_stack() >> a) - - def bitwise_and(): - expect_number() - a = from_stack() - expect_number() - to_stack(a & from_stack()) - - def bitwise_or(): - expect_number() - a = from_stack() - expect_number() - to_stack(a | from_stack()) - - def bitwise_xor(): - expect_number() - a = from_stack() - expect_number() - to_stack(a ^ from_stack()) - - def bitwise_not(): - expect_number() - to_stack(~from_stack()) - - def qtime(): - to_stack(int(time.time()*1000000)) - - def meta_done(): - from_return() - from_return() - - def meta_again(): - from_return() - to_return(-1) - - def meta_if(): - expect_number() - if from_stack() == 0: - to_return(from_return() + 1) - - def meta_iff(): - expect_number() - if from_stack() == 0: - to_return(from_return() + 2) - - def meta_else(): - to_return(from_return() + 1) - - def meta_literal(): - pc = from_return() + 1 - return_nest = from_return() - if len(return_nest) == pc: - failed('''Found a "'" at the end of a nest.''') - to_stack(return_nest[pc]) - to_return(return_nest) - to_return(pc) - - def meta_this(): - pc = from_return() - return_nest = from_return() - to_stack(return_nest) - to_return(return_nest) - to_return(pc) - - def meta_do(): - expect_something() - the_thing = from_stack() - if not isNest(the_thing): - the_thing = [the_thing] - to_return(the_thing) - to_return(-1) - - def meta_bail_by(): - expect_number() - a = 2*(from_stack()) - if a <= len(rstack): - for _ in range(a): - from_return() - else: - failed('Bailed out of Quackery.') - - def qput(): - expect_nest() - a = from_stack() - expect_something() - b = from_stack() - a.append(b) - - def immovable(): - pass - - def take(): - expect_nest() - a = from_stack() - if len(a) == 0: - failed('Unexpectedly empty nest.') - if len(a) == 1: - if isNest(a[0]) and len(a[0]) > 0 and a[0][0] == immovable: - failed('Cannot remove an immovable item.') - to_stack(a.pop()) - - def create_nest(): - to_stack([]) - - def qsplit(): - expect_number() - a = from_stack() - expect_nest() - b = from_stack() - to_stack(b[:a]) - to_stack(b[a:]) - - def join(): - expect_something() - b = from_stack() - if not isNest(b): - b = [b] - expect_something() - a = from_stack() - if not isNest(a): - a = [a] - to_stack(a + b) - - def qsize(): - expect_nest() - to_stack(len(from_stack())) - - def qfind(): - expect_nest() - nest = from_stack() - expect_something() - a = from_stack() - if a in nest: - to_stack(nest.index(a)) - else: - to_stack(len(nest)) - - def peek(): - expect_number() - index = from_stack() - expect_nest() - nest = from_stack() - if index >= len(nest) or ( - index < 0 and len(nest) < abs(index)): - failed('Cannot peek an item outside a nest.') - else: - to_stack(nest[index]) - - def poke(): - expect_number() - index = from_stack() - expect_nest() - nest = from_stack().copy() - expect_something() - value = from_stack() - if index >= len(nest) or ( - index < 0 and len(nest) < abs(index)): - failed('Cannot poke an item outside a nest.') - else: - nest[index] = value - to_stack(nest) - - def qnest(): - expect_something() - bool_to_stack(isNest(from_stack())) - - def qnumber(): - expect_something() - bool_to_stack(isNumber(from_stack())) - - def qoperator(): - expect_something() - bool_to_stack(isOperator(from_stack())) - - def quid(): - expect_something() - to_stack(id(from_stack())) - - def qemit(): - expect_number() - char = from_stack() - if char == 13: - sys.stdout.write('\n') - elif 31 < char < 127: - sys.stdout.write(chr(char)) - else: - sys.stdout.write('?') # XXX @dragoncoder047 maybe use \uFFFD on platforms that support unicode? - - def ding(): - sys.stdout.write('\a') - - def qinput(): - prompt = string_from_stack() - string_to_stack(input(prompt)) - - filepath = [] - - def putfile(): - nonlocal filepath - filename = string_from_stack() - if len(filepath) > 1: - to_stack(filepath[-1]) - filename = string_from_stack() + filename - filetext = string_from_stack() - try: - with open(filename, 'x'): pass - except FileExistsError: - to_stack(false) - except: - raise - else: - try: - with open(filename, 'w') as f: f.write(filetext) - except: - raise - else: - to_stack(true) - - def releasefile(): - nonlocal filepath - filename = string_from_stack() - if len(filepath) > 1: - to_stack(filepath[-1]) - filename = string_from_stack() + filename - try: - os.remove(filename) - except FileNotFoundError: - to_stack(false) - except: - raise - else: - to_stack(true) - - def sharefile(): - nonlocal filepath - dup() - filename = string_from_stack() - if len(filepath) > 1: - to_stack(filepath[-1]) - filename = string_from_stack() + filename - try: - with open(filename) as f: filetext = f.read() - except FileNotFoundError: - to_stack(false) - except: - raise + self.to_stack(result) + + def bool_to_stack(self, qbool): + self.to_stack(true if qbool else false) + + def to_return(self, item): + self.rstack.append(item) + + def from_return(self.): + if len(self.rstack) == 0: + self.failed('Return stack unexpectedly empty.') + return self.rstack.pop() + + def failed(self, message): + self.traverse(self.build("stacksize pack decimal unbuild return$ nestdepth ]bailby[")) + returnstack = self.string_from_stack() + thestack = self.string_from_stack() + raise QuackeryError('\n Problem: ' + message + + '\nQuackery Stack: ' + str(thestack)[2:-2] + + '\n Return stack: ' + str(returnstack)) + + def tick(self): + if self.program_counter >= len(self.current_nest): + self.program_counter = self.from_return() + self.current_nest = self.from_return() + self.program_counter += 1 + return + current_item = self.current_nest[self.program_counter] + if isNest(current_item): + self.to_return(current_nest) + self.to_return(program_counter) + self.current_nest = current_item + self.program_counter = 0 + elif isOperator(current_item): + current_item(self) + self.program_counter += 1 + elif isNumber(current_item): + self.to_stack(current_item) + self.program_counter += 1 else: - drop() - string_to_stack(filetext) - to_stack(true) - - operators = { - 'python': python, # ( $ --> ) - 'fail': qfail, # ( $ --> ) - 'nand': nand, # ( b b --> b ) - '=': equal, # ( x x --> b ) - '>': greater, # ( n n --> b ) - '1+': inc, # ( n --> n ) - '+': plus, # ( n n --> n ) - 'negate': negate, # ( n --> n ) - '*': multiply, # ( n n --> n ) - '/mod': qdivmod, # ( n n --> n n ) - '**': exponentiate, # ( n n --> n ) - '<<': shift_left, # ( f n --> f ) - '>>': shift_right, # ( f n --> f ) - '&': bitwise_and, # ( f f --> f ) - '|': bitwise_or, # ( f f --> f ) - '^': bitwise_xor, # ( f f --> f ) - '~': bitwise_not, # ( f --> f ) - 'time': qtime, # ( --> n ) - 'stacksize': stack_size, # ( --> n ) - 'nestdepth': nest_depth, # ( --> n ) - 'return': qreturn, # ( --> [ ) - 'dup': dup, # ( x --> x x ) - 'drop': drop, # ( x --> ) - 'swap': swap, # ( x x --> x x ) - 'rot': rot, # ( x x x --> x x x ) - 'over': over, # ( x x --> x x x ) - ']done[': meta_done, # ( --> ) - ']again[': meta_again, # ( --> ) - ']if[': meta_if, # ( b --> ) - ']iff[': meta_iff, # ( b --> ) - ']else[': meta_else, # ( --> ) - "]'[": meta_literal, # ( --> x ) - ']this[': meta_this, # ( --> [ ) - ']do[': meta_do, # ( x --> ) - ']bailby[': meta_bail_by, # ( n --> ) - 'put': qput, # ( x [ --> ) - 'immovable': immovable, # ( --> ) - 'take': take, # ( [ --> x ) - '[]': create_nest, # ( --> n ) - 'split': qsplit, # ( [ n --> [ [ ) - 'join': join, # ( x x --> [ ) - 'find': qfind, # ( x --> b ) - 'peek': peek, # ( [ n --> x ) - 'poke': poke, # ( x [ n --> ) - 'size': qsize, # ( [ --> n ) - 'nest?': qnest, # ( x --> b ) - 'number?': qnumber, # ( x --> b ) - 'operator?': qoperator, # ( x --> b ) - 'quid': quid, # ( x --> n ) - 'emit': qemit, # ( c --> ) - 'ding': ding, # ( --> ) - 'input': qinput, # ( $ --> $ ) - 'filepath': filepath, # ( --> s ) - 'putfile': putfile, # ( $ --> b ) - 'releasefile': releasefile, # ( $ --> b ) - 'sharefile': sharefile} # ( $ --> $ b ) - - qstack = [] - - rstack = [] - - current_nest = [] - - program_counter = 0 - - def traverse(the_nest): - nonlocal current_nest - nonlocal program_counter - nonlocal rstack - current_nest = the_nest - program_counter = 0 - while True: - if program_counter >= len(current_nest): - if len(rstack) == 0: - break - else: - program_counter = from_return() - current_nest = from_return() - program_counter += 1 - continue - current_item = current_nest[program_counter] - if isNest(current_item): - to_return(current_nest) - to_return(program_counter) - current_nest = current_item - program_counter = 0 - elif isOperator(current_item): - current_item() - program_counter += 1 - elif isNumber(current_item): - to_stack(current_item) - program_counter += 1 - else: - failed('Quackery was worried by a python.') - - def isinteger(string): - numstr = string - if len(numstr) > 0 and numstr[0] == '-': - numstr = numstr[1:] - return numstr.isdigit() - - def next_char(): - nonlocal source - if len(source) > 0: - char = source[0] - source = source[1:] + self.failed('Quackery was worried by a python.') + + + def traverse(self, the_nest): + self.current_nest = the_nest + self.program_counter = 0 + orig_depth = len(self.rstack) + while len(self.rstack) > orig_depth: + self.tick() + + def next_char(self): + if len(self.source) > 0: + char = self.source[0] + self.source = self.source[1:] return char else: return '' - def next_word(): + def next_word(self): result = '' while True: - char = next_char() + char = self.next_char() if char == '': - return(result) + return result if ord(char) < 33: if result == '': continue return result result += char - def one_char(): + def one_char(self): while True: - char = next_char() + char = self.next_char() if char == '': return char if ord(char) < 33: continue return char - def get_name(): - name = next_word() + def get_name(self): + name = selfnext_word() if name == '': raise EOFError('Unexpected end of program text.') return name - def check_build(): - nonlocal current_build - if len(current_build) == 0: + def check_build(self): + if len(self.current_build) == 0: raise IndexError('Nothing to name.') - - def qis(): - nonlocal operators - nonlocal current_build - check_build() - name = get_name() - operators[name] = current_build.pop() - - def qcomment(): - word = '' - while word != ')': - word = next_word() - if word == '': - raise EOFError('Unclosed comment.') - - def endcomment(): - raise SyntaxError('Too many end of comments.') - - def unresolved(): - raise TypeError('Unresolved forward reference.') - - def forward(): - nonlocal current_build - current_build.append([unresolved]) - - def resolves(): - nonlocal current_build - name = get_name() - if name in operators: - if operators[name][0] != unresolved: - raise TypeError(name + ' is not a forward reference.') - check_build() - operators[name][0] = current_build.pop() - else: - raise NameError('Unrecognised word: ' + name) - - def char_literal(): - nonlocal current_build - char = one_char() - if char == '': - raise SyntaxError('No character found.') - current_build.append(ord(char)) - - def string_literal(): - nonlocal current_build - delimiter = '' - result = [] - while delimiter == '': - char = next_char() - if char == '': - raise EOFError('No string found.') - if ord(char) > 32: - delimiter = char - char = '' - while char != delimiter: - char = next_char() - if char == '': - raise EOFError('Endless string discovered.') - if char != delimiter: - result.append(ord(char)) - current_build.append([[meta_literal], result]) - - def ishex(string): - hexstr = string - if len(hexstr) > 1 and hexstr[0] == '-': - hexstr = hexstr[1:] - for char in hexstr: - if char.lower() not in '0123456789abcdef': - return False - return True - - def hexnum(): - nonlocal current_build - word = get_name() - if not ishex(word): - raise SyntaxError(word + " is not hexadecimal.") - current_build.append(int(word, 16)) - - builders = {'is': qis, - '(': qcomment, - ')': endcomment, - 'forward': forward, - 'resolves': resolves, - 'char': char_literal, - '$': string_literal, - 'hex': hexnum} - - current_build = [] - - source = '' - - the_nest = [] - - def build(source_string): - nonlocal source - nonlocal the_nest - source = source_string + + def build(self, source_string): + self.source = source_string nesting = 0 def sub_build(): nonlocal nesting - nonlocal current_build the_nest = [] while True: - current_build = the_nest - word = next_word() + self.current_build = the_nest + word = self.next_word() if word == '': return the_nest elif word == '[': @@ -739,10 +210,10 @@ def sub_build(): if nesting < 0: raise SyntaxError('Unexpected end of nest.') return(the_nest) - elif word in builders.keys(): - builders[word]() - elif word in operators.keys(): - the_nest.append(operators[word]) + elif word in self.builders.keys(): + self.builders[word](self) + elif word in self.operators.keys(): + the_nest.append(self.operators[word]) elif isinteger(word): the_nest.append(int(word, 10)) else: @@ -751,9 +222,526 @@ def sub_build(): the_nest = sub_build() if nesting > 0: raise SyntaxError('Unfinished nest.') - return(the_nest) + return the_nest + + def run(self, source_string): + self.traverse(self.build(source_string)) + + def bootstrap(self): + self.run(predefined_qky) + + + +def python(qs): + """For backwards compatibility only""" + scope = { + "to_stack": qs.to_stack, + "from_stack": qs.from_stack, + "string_to_stack": qs.string_to_stack, + "string_from_stack": qs.string_from_stack, + "qs": qs, + } + try: + exec(qs.string_from_stack(), scope, globals()) + except QuackeryError: + raise + except Exception as diagnostics: + qs.failed('Python reported: "' + str(diagnostics) + '"') + +def qfail(qs): + qs.failed(qs.string_from_stack()) + +def stack_size(qs): + qs.to_stack(len(qs.qstack)) + +def qreturn(qs): + qs.to_stack(qs.rstack) + +def dup(qs): + a = qs.from_stack() + qs.to_stack(a) + qs.to_stack(a) + +def drop(qs): + qs.from_stack() + +def swap(qs): + a = qs.from_stack() + b = qs.from_stack() + qs.to_stack(a) + qs.to_stack(b) + +def rot(qs): # XXX @dragoncoder047 maybe simplify to [ dip swap swap ] is rot ? There are no cyclic references that would prevent this + a = qs.from_stack() + swap(qs) + qs.to_stack(a) + swap(qs) + +def over(qs): # XXX @dragoncoder047 maybe simplify to [ dip dup swap ] is over ? same reason as above + a = qs.from_stack() + dup(qs) + qs.to_stack(a) + swap(qs) + +def nest_depth(qs): + qs.to_stack(len(qs.rstack) // 2) + +true = 1 + +false = 0 + +def nand(qs): + qs.expect_number() + a = qs.from_stack() + qs.expect_number() + qs.bool_to_stack(qs.from_stack() == false or a == false) + +def equal(qs): + qs.expect_something() + a = qs.from_stack() + qs.expect_something() + qs.bool_to_stack(a == qs.from_stack()) + +def greater(qs): + qs.expect_number() + a = qs.from_stack() + qs.expect_number() + qs.bool_to_stack(qs.from_stack() > a) + +def inc(qs): + qs.expect_number() + qs.to_stack(1 + qs.from_stack()) + +def plus(qs): + qs.expect_number() + a = qs.from_stack() + qs.expect_number() + qs.to_stack(a + qs.from_stack()) + +def negate(qs): + qs.expect_number() + qs.to_stack(-qs.from_stack()) + +def multiply(qs): + qs.expect_number() + a = qs.from_stack() + qs.expect_number() + qs.to_stack(a * qs.from_stack()) + +def qdivmod(qs): + qs.expect_number() + a = qs.from_stack() + if a == 0: + qs.failed('Division by zero.') + qs.expect_number() + results = divmod(qs.from_stack(), a) + qs.to_stack(results[0]) + qs.to_stack(results[1]) + +def exponentiate(qs): + qs.expect_number() + a = qs.from_stack() + if a < 0: + qs.failed('Tried to raise to a negative power: ' + str(a)) + qs.expect_number() + qs.to_stack(qs.from_stack() ** a) + +def shift_left(qs): + qs.expect_number() + a = qs.from_stack() + if a < 0: + qs.failed('Cannot << by a negative amount: ' + str(a)) + qs.expect_number() + qs.to_stack(qs.from_stack() << a) + +def shift_right(qs): + qs.expect_number() + a = qs.from_stack() + if a < 0: + qs.failed('Cannot >> by a negative amount: ' + str(a)) + qs.expect_number() + qs.to_stack(qs.from_stack() >> a) + +def bitwise_and(qs): + qs.expect_number() + a = qs.from_stack() + qs.expect_number() + qs.to_stack(a & qs.from_stack()) + +def bitwise_or(qs): + qs.expect_number() + a = qs.from_stack() + qs.expect_number() + qs.to_stack(a | qs.from_stack()) + +def bitwise_xor(qs): + qs.expect_number() + a = qs.from_stack() + qs.expect_number() + qs.to_stack(a ^ qs.from_stack()) + +def bitwise_not(qs): + qs.expect_number() + qs.to_stack(~qs.from_stack()) + +def qtime(qs): + qs.to_stack(int(time.time()*1000000)) + +def meta_done(qs): + qs.from_return() + qs.from_return() + +def meta_again(qs): + qs.from_return() + qs.to_return(-1) + +def meta_if(qs): + qs.expect_number() + if qs.from_stack() == 0: + qs.to_return(qs.from_return() + 1) + +def meta_iff(qs): + qs.expect_number() + if qs.from_stack() == 0: + qs.to_return(qs.from_return() + 2) + +def meta_else(qs): + qs.to_return(qs.from_return() + 1) + +def meta_literal(qs): + pc = qs.from_return() + 1 + return_nest = qs.from_return() + if len(return_nest) == pc: + qs.failed('Found a "\'" at the end of a nest.') + qs.to_stack(return_nest[pc]) + qs.to_return(return_nest) + qs.to_return(pc) + +def meta_this(qs): + pc = qs.from_return() + return_nest = qs.from_return() + qs.to_stack(return_nest) + qs.to_return(return_nest) + qs.to_return(pc) + +def meta_do(qs): + qs.expect_something() + the_thing = qs.from_stack() + if not isNest(the_thing): + the_thing = [the_thing] + qs.to_return(the_thing) + qs.to_return(-1) + +def meta_bail_by(qs): + qs.expect_number() + a = 2 * qs.from_stack() + if a <= len(qs.rstack): + for _ in range(a): + qs.from_return() + else: + qs.failed('Bailed out of Quackery.') - predefined = r""" +def qput(qs): + qs.expect_nest() + a = qs.from_stack() + qs.expect_something() + b = qs.from_stack() + a.append(b) + +def immovable(qs): + pass + +def take(qs): + qs.expect_nest() + a = qs.from_stack() + if len(a) == 0: + qs.failed('Unexpectedly empty nest.') + if len(a) == 1: + if isNest(a[0]) and len(a[0]) > 0 and a[0][0] == immovable: + qs.failed('Cannot remove an immovable item.') + qs.to_stack(a.pop()) + +def create_nest(qs): + qs.to_stack([]) + +def qsplit(qs): + qs.expect_number() + a = qs.from_stack() + qs.expect_nest() + b = qs.from_stack() + qs.to_stack(b[:a]) + qs.to_stack(b[a:]) + +def join(qs): + qs.expect_something() + b = qs.from_stack() + if not isNest(b): + b = [b] + qs.expect_something() + a = from_stack() + if not isNest(a): + a = [a] + qs.to_stack(a + b) + +def qsize(qs): + qs.expect_nest() + qs.to_stack(len(qs.from_stack())) + +def qfind(qs): + qs.expect_nest() + nest = qs.from_stack() + qs.expect_something() + a = qs.from_stack() + if a in nest: + qs.to_stack(nest.index(a)) + else: + qs.to_stack(len(nest)) + +def peek(qs): + qs.expect_number() + index = qs.from_stack() + qs.expect_nest() + nest = qs.from_stack() + if index >= len(nest) or ( + index < 0 and len(nest) < abs(index)): + failed('Cannot peek an item outside a nest.') + else: + to_stack(nest[index]) + +def poke(qs): + qs.expect_number() + index = qs.from_stack() + qs.expect_nest() + nest = qs.from_stack().copy() + qs.expect_something() + value = qs.from_stack() + if index >= len(nest) or ( + index < 0 and len(nest) < abs(index)): + qs.failed('Cannot poke an item outside a nest.') + else: + nest[index] = value + qs.to_stack(nest) + +def qnest(qs): + qs.expect_something() + qs.bool_to_stack(isNest(qs.from_stack())) + +def qnumber(qs): + qs.expect_something() + qs.bool_to_stack(isNumber(qs.from_stack())) + +def qoperator(qs): + qs.expect_something() + qs.bool_to_stack(isOperator(qs.from_stack())) + +def quid(qs): + qs.expect_something() + qs.to_stack(id(qs.from_stack())) + +def qemit(qs): + qs.expect_number() + char = qs.from_stack() + if char == 13: + sys.stdout.write('\n') + elif 31 < char < 127: + sys.stdout.write(chr(char)) + else: + sys.stdout.write('?') # XXX @dragoncoder047 maybe use \uFFFD on platforms that support unicode? + +def ding(qs): + sys.stdout.write('\a') + +def qinput(qs): + prompt = qs.string_from_stack() + qs.string_to_stack(input(prompt)) + +filepath = [] + +def putfile(qs): + filename = qs.string_from_stack() + if len(filepath) > 1: + qs.to_stack(filepath[-1]) + filename = qs.string_from_stack() + filename + filetext = qs.string_from_stack() + try: + with open(filename, 'x'): pass + except FileExistsError: + qs.to_stack(false) + except: + raise + else: + try: + with open(filename, 'w') as f: f.write(filetext) + except: + raise + else: + qs.to_stack(true) + +def releasefile(qs): + nonlocal filepath + filename = qs.string_from_stack() + if len(filepath) > 1: + qs.to_stack(filepath[-1]) + filename = qs.string_from_stack() + filename + try: + os.remove(filename) + except FileNotFoundError: + qs.to_stack(false) + except: + raise + else: + qs.to_stack(true) + +def sharefile(qs): + dup(qs) + filename = qs.string_from_stack() + if len(filepath) > 1: + qs.to_stack(filepath[-1]) + filename = qs.string_from_stack() + filename + try: + with open(filename) as f: filetext = f.read() + except FileNotFoundError: + qsto_stack(false) + except: + raise + else: + drop(qs) + qs.string_to_stack(filetext) + qs.to_stack(true) + +predefined_operators = { + 'python': python, # ( $ --> ) + 'fail': qfail, # ( $ --> ) + 'nand': nand, # ( b b --> b ) + '=': equal, # ( x x --> b ) + '>': greater, # ( n n --> b ) + '1+': inc, # ( n --> n ) + '+': plus, # ( n n --> n ) + 'negate': negate, # ( n --> n ) + '*': multiply, # ( n n --> n ) + '/mod': qdivmod, # ( n n --> n n ) + '**': exponentiate, # ( n n --> n ) + '<<': shift_left, # ( f n --> f ) + '>>': shift_right, # ( f n --> f ) + '&': bitwise_and, # ( f f --> f ) + '|': bitwise_or, # ( f f --> f ) + '^': bitwise_xor, # ( f f --> f ) + '~': bitwise_not, # ( f --> f ) + 'time': qtime, # ( --> n ) + 'stacksize': stack_size, # ( --> n ) + 'nestdepth': nest_depth, # ( --> n ) + 'return': qreturn, # ( --> [ ) + 'dup': dup, # ( x --> x x ) + 'drop': drop, # ( x --> ) + 'swap': swap, # ( x x --> x x ) + 'rot': rot, # ( x x x --> x x x ) + 'over': over, # ( x x --> x x x ) + ']done[': meta_done, # ( --> ) + ']again[': meta_again, # ( --> ) + ']if[': meta_if, # ( b --> ) + ']iff[': meta_iff, # ( b --> ) + ']else[': meta_else, # ( --> ) + "]'[": meta_literal, # ( --> x ) + ']this[': meta_this, # ( --> [ ) + ']do[': meta_do, # ( x --> ) + ']bailby[': meta_bail_by, # ( n --> ) + 'put': qput, # ( x [ --> ) + 'immovable': immovable, # ( --> ) + 'take': take, # ( [ --> x ) + '[]': create_nest, # ( --> n ) + 'split': qsplit, # ( [ n --> [ [ ) + 'join': join, # ( x x --> [ ) + 'find': qfind, # ( x --> b ) + 'peek': peek, # ( [ n --> x ) + 'poke': poke, # ( x [ n --> ) + 'size': qsize, # ( [ --> n ) + 'nest?': qnest, # ( x --> b ) + 'number?': qnumber, # ( x --> b ) + 'operator?': qoperator, # ( x --> b ) + 'quid': quid, # ( x --> n ) + 'emit': qemit, # ( c --> ) + 'ding': ding, # ( --> ) + 'input': qinput, # ( $ --> $ ) + 'filepath': filepath, # ( --> s ) + 'putfile': putfile, # ( $ --> b ) + 'releasefile': releasefile, # ( $ --> b ) + 'sharefile': sharefile # ( $ --> $ b ) +} + +def qis(qs): + qs.check_build() + name = qs.get_name() + qs.operators[name] = qs.current_build.pop() + +def qcomment(qs): + word = '' + while word != ')': + word = qs.next_word() + if word == '': + raise EOFError('Unclosed comment.') + +def endcomment(qs): + raise SyntaxError('Too many end of comments.') + +def unresolved(qs): + raise TypeError('Unresolved forward reference.') + +def forward(qs): + qs.current_build.append([unresolved]) + +def resolves(qs): + name = qs.get_name() + if name in qs.operators: + if qs.operators[name][0] != unresolved: + raise TypeError(name + ' is not a forward reference.') + qs.check_build() + operators[name][0] = current_build.pop() + else: + raise NameError('Unrecognised word: ' + name) + +def char_literal(qs): + char = qs.one_char() + if char == '': + raise SyntaxError('No character found.') + qs.current_build.append(ord(char)) + +def string_literal(qs): + delimiter = '' + result = [] + while delimiter == '': + char = qs.next_char() + if char == '': + raise EOFError('No string found.') + if ord(char) > 32: + delimiter = char + char = '' + while char != delimiter: + char = qs.next_char() + if char == '': + raise EOFError('Endless string discovered.') + if char != delimiter: + result.append(ord(char)) + qs.current_build.append([[meta_literal], result]) + +def hexnum(qs): + word = qs.get_name() + if not ishex(word): + raise SyntaxError(word + " is not hexadecimal.") + qs.current_build.append(int(word, 16)) + +predefined_builders = { + 'is': qis, + '(': qcomment, + ')': endcomment, + 'forward': forward, + 'resolves': resolves, + 'char': char_literal, + '$': string_literal, + 'hex': hexnum +} + + +predefined_qky = r""" [ 0 ] is false ( --> b ) @@ -1650,11 +1638,18 @@ def sub_build(): """ - traverse(build(predefined)) +def quackery(source_string, qs = None): + + if qs is None: + qs = QuackeryState() + qs.bootstrap() + else: + if 'quackery' not in qs.operators.keys(): + raise NameError('QuackeryState must have word `quackery` defined.') + while True: - to_stack([ord(char) for char in source_string]) - try: - traverse(build('quackery')) + qs.to_stack([ord(char) for char in source_string]) + try: qs.run('quackery') except QuackeryError as diagnostics: if __name__ == '__main__' and len(sys.argv) == 1: print(diagnostics) @@ -1664,13 +1659,11 @@ def sub_build(): except Exception as diagnostics: print('Quackery system damage detected.') print('Python error: ' + str(diagnostics)) - sys.exit(1) + raise else: - traverse(build('stacksize pack decimal unbuild')) - result = '' - for ch in qstack[0][2:-2]: - result += chr(ch) - return result + qs.run('stacksize pack decimal unbuild') + the_stack = qs.from_stack() + return ''.join(map(chr, the_stack[2:-2])) if __name__ == '__main__': if len(sys.argv) > 1: From d9a7d872e0ec2ede183ae1a409cfb96d5494fae7 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Thu, 28 Apr 2022 18:02:44 -0400 Subject: [PATCH 002/121] oops, unicode error --- quackery.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/quackery.py b/quackery.py index 85d2018..6276fd9 100644 --- a/quackery.py +++ b/quackery.py @@ -1687,8 +1687,8 @@ def quackery(source_string, qs = None): print('Python error: ' + str(diagnostics)) sys.exit(1) else: - print(‘\nWelcome to Quackery.’) - print(‘\nEnter "leave" to leave the shell.') + print('\nWelcome to Quackery.') + print('\nEnter "leave" to leave the shell.') quackscript = r""" $ 'extensions.qky' dup name? not From 38b17b43f51f534178f67d8d9a2c5f2a7e830e1d Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Thu, 28 Apr 2022 18:04:00 -0400 Subject: [PATCH 003/121] bootstrap only once --- quackery.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/quackery.py b/quackery.py index 6276fd9..b1232bb 100644 --- a/quackery.py +++ b/quackery.py @@ -1638,11 +1638,17 @@ def hexnum(qs): """ +# bootstrap only once +_qs = QuackeryState() +_qs.bootstrap() +predefined_operators = _qs.operators +del _qs + + def quackery(source_string, qs = None): if qs is None: qs = QuackeryState() - qs.bootstrap() else: if 'quackery' not in qs.operators.keys(): raise NameError('QuackeryState must have word `quackery` defined.') From f6f61ce0ba12d68b6165e4b0b07fb96d0ad64b38 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Thu, 28 Apr 2022 18:05:26 -0400 Subject: [PATCH 004/121] rename `qs` to `self` as with Python convention --- quackery.py | 602 ++++++++++++++++++++++++++-------------------------- 1 file changed, 301 insertions(+), 301 deletions(-) diff --git a/quackery.py b/quackery.py index b1232bb..5fbee44 100644 --- a/quackery.py +++ b/quackery.py @@ -232,315 +232,315 @@ def bootstrap(self): -def python(qs): +def python(self): """For backwards compatibility only""" scope = { - "to_stack": qs.to_stack, - "from_stack": qs.from_stack, - "string_to_stack": qs.string_to_stack, - "string_from_stack": qs.string_from_stack, - "qs": qs, + "to_stack": self.to_stack, + "from_stack": self.from_stack, + "string_to_stack": self.string_to_stack, + "string_from_stack": self.string_from_stack, + "self": self, } try: - exec(qs.string_from_stack(), scope, globals()) + exec(self.string_from_stack(), scope, globals()) except QuackeryError: raise except Exception as diagnostics: - qs.failed('Python reported: "' + str(diagnostics) + '"') + self.failed('Python reported: "' + str(diagnostics) + '"') -def qfail(qs): - qs.failed(qs.string_from_stack()) +def qfail(self): + self.failed(self.string_from_stack()) -def stack_size(qs): - qs.to_stack(len(qs.qstack)) +def stack_size(self): + self.to_stack(len(self.qstack)) -def qreturn(qs): - qs.to_stack(qs.rstack) +def qreturn(self): + self.to_stack(self.rstack) -def dup(qs): - a = qs.from_stack() - qs.to_stack(a) - qs.to_stack(a) +def dup(self): + a = self.from_stack() + self.to_stack(a) + self.to_stack(a) -def drop(qs): - qs.from_stack() +def drop(self): + self.from_stack() -def swap(qs): - a = qs.from_stack() - b = qs.from_stack() - qs.to_stack(a) - qs.to_stack(b) +def swap(self): + a = self.from_stack() + b = self.from_stack() + self.to_stack(a) + self.to_stack(b) -def rot(qs): # XXX @dragoncoder047 maybe simplify to [ dip swap swap ] is rot ? There are no cyclic references that would prevent this - a = qs.from_stack() - swap(qs) - qs.to_stack(a) - swap(qs) +def rot(self): # XXX @dragoncoder047 maybe simplify to [ dip swap swap ] is rot ? There are no cyclic references that would prevent this + a = self.from_stack() + swap(self) + self.to_stack(a) + swap(self) -def over(qs): # XXX @dragoncoder047 maybe simplify to [ dip dup swap ] is over ? same reason as above - a = qs.from_stack() - dup(qs) - qs.to_stack(a) - swap(qs) +def over(self): # XXX @dragoncoder047 maybe simplify to [ dip dup swap ] is over ? same reason as above + a = self.from_stack() + dup(self) + self.to_stack(a) + swap(self) -def nest_depth(qs): - qs.to_stack(len(qs.rstack) // 2) +def nest_depth(self): + self.to_stack(len(self.rstack) // 2) true = 1 false = 0 -def nand(qs): - qs.expect_number() - a = qs.from_stack() - qs.expect_number() - qs.bool_to_stack(qs.from_stack() == false or a == false) - -def equal(qs): - qs.expect_something() - a = qs.from_stack() - qs.expect_something() - qs.bool_to_stack(a == qs.from_stack()) - -def greater(qs): - qs.expect_number() - a = qs.from_stack() - qs.expect_number() - qs.bool_to_stack(qs.from_stack() > a) - -def inc(qs): - qs.expect_number() - qs.to_stack(1 + qs.from_stack()) - -def plus(qs): - qs.expect_number() - a = qs.from_stack() - qs.expect_number() - qs.to_stack(a + qs.from_stack()) - -def negate(qs): - qs.expect_number() - qs.to_stack(-qs.from_stack()) - -def multiply(qs): - qs.expect_number() - a = qs.from_stack() - qs.expect_number() - qs.to_stack(a * qs.from_stack()) - -def qdivmod(qs): - qs.expect_number() - a = qs.from_stack() +def nand(self): + self.expect_number() + a = self.from_stack() + self.expect_number() + self.bool_to_stack(self.from_stack() == false or a == false) + +def equal(self): + self.expect_something() + a = self.from_stack() + self.expect_something() + self.bool_to_stack(a == self.from_stack()) + +def greater(self): + self.expect_number() + a = self.from_stack() + self.expect_number() + self.bool_to_stack(self.from_stack() > a) + +def inc(self): + self.expect_number() + self.to_stack(1 + self.from_stack()) + +def plus(self): + self.expect_number() + a = self.from_stack() + self.expect_number() + self.to_stack(a + self.from_stack()) + +def negate(self): + self.expect_number() + self.to_stack(-self.from_stack()) + +def multiply(self): + self.expect_number() + a = self.from_stack() + self.expect_number() + self.to_stack(a * self.from_stack()) + +def qdivmod(self): + self.expect_number() + a = self.from_stack() if a == 0: - qs.failed('Division by zero.') - qs.expect_number() - results = divmod(qs.from_stack(), a) - qs.to_stack(results[0]) - qs.to_stack(results[1]) - -def exponentiate(qs): - qs.expect_number() - a = qs.from_stack() + self.failed('Division by zero.') + self.expect_number() + results = divmod(self.from_stack(), a) + self.to_stack(results[0]) + self.to_stack(results[1]) + +def exponentiate(self): + self.expect_number() + a = self.from_stack() if a < 0: - qs.failed('Tried to raise to a negative power: ' + str(a)) - qs.expect_number() - qs.to_stack(qs.from_stack() ** a) + self.failed('Tried to raise to a negative power: ' + str(a)) + self.expect_number() + self.to_stack(self.from_stack() ** a) -def shift_left(qs): - qs.expect_number() - a = qs.from_stack() +def shift_left(self): + self.expect_number() + a = self.from_stack() if a < 0: - qs.failed('Cannot << by a negative amount: ' + str(a)) - qs.expect_number() - qs.to_stack(qs.from_stack() << a) + self.failed('Cannot << by a negative amount: ' + str(a)) + self.expect_number() + self.to_stack(self.from_stack() << a) -def shift_right(qs): - qs.expect_number() - a = qs.from_stack() +def shift_right(self): + self.expect_number() + a = self.from_stack() if a < 0: - qs.failed('Cannot >> by a negative amount: ' + str(a)) - qs.expect_number() - qs.to_stack(qs.from_stack() >> a) - -def bitwise_and(qs): - qs.expect_number() - a = qs.from_stack() - qs.expect_number() - qs.to_stack(a & qs.from_stack()) - -def bitwise_or(qs): - qs.expect_number() - a = qs.from_stack() - qs.expect_number() - qs.to_stack(a | qs.from_stack()) - -def bitwise_xor(qs): - qs.expect_number() - a = qs.from_stack() - qs.expect_number() - qs.to_stack(a ^ qs.from_stack()) - -def bitwise_not(qs): - qs.expect_number() - qs.to_stack(~qs.from_stack()) - -def qtime(qs): - qs.to_stack(int(time.time()*1000000)) - -def meta_done(qs): - qs.from_return() - qs.from_return() - -def meta_again(qs): - qs.from_return() - qs.to_return(-1) - -def meta_if(qs): - qs.expect_number() - if qs.from_stack() == 0: - qs.to_return(qs.from_return() + 1) - -def meta_iff(qs): - qs.expect_number() - if qs.from_stack() == 0: - qs.to_return(qs.from_return() + 2) - -def meta_else(qs): - qs.to_return(qs.from_return() + 1) - -def meta_literal(qs): - pc = qs.from_return() + 1 - return_nest = qs.from_return() + self.failed('Cannot >> by a negative amount: ' + str(a)) + self.expect_number() + self.to_stack(self.from_stack() >> a) + +def bitwise_and(self): + self.expect_number() + a = self.from_stack() + self.expect_number() + self.to_stack(a & self.from_stack()) + +def bitwise_or(self): + self.expect_number() + a = self.from_stack() + self.expect_number() + self.to_stack(a | self.from_stack()) + +def bitwise_xor(self): + self.expect_number() + a = self.from_stack() + self.expect_number() + self.to_stack(a ^ self.from_stack()) + +def bitwise_not(self): + self.expect_number() + self.to_stack(~self.from_stack()) + +def qtime(self): + self.to_stack(int(time.time()*1000000)) + +def meta_done(self): + self.from_return() + self.from_return() + +def meta_again(self): + self.from_return() + self.to_return(-1) + +def meta_if(self): + self.expect_number() + if self.from_stack() == 0: + self.to_return(self.from_return() + 1) + +def meta_iff(self): + self.expect_number() + if self.from_stack() == 0: + self.to_return(self.from_return() + 2) + +def meta_else(self): + self.to_return(self.from_return() + 1) + +def meta_literal(self): + pc = self.from_return() + 1 + return_nest = self.from_return() if len(return_nest) == pc: - qs.failed('Found a "\'" at the end of a nest.') - qs.to_stack(return_nest[pc]) - qs.to_return(return_nest) - qs.to_return(pc) - -def meta_this(qs): - pc = qs.from_return() - return_nest = qs.from_return() - qs.to_stack(return_nest) - qs.to_return(return_nest) - qs.to_return(pc) - -def meta_do(qs): - qs.expect_something() - the_thing = qs.from_stack() + self.failed('Found a "\'" at the end of a nest.') + self.to_stack(return_nest[pc]) + self.to_return(return_nest) + self.to_return(pc) + +def meta_this(self): + pc = self.from_return() + return_nest = self.from_return() + self.to_stack(return_nest) + self.to_return(return_nest) + self.to_return(pc) + +def meta_do(self): + self.expect_something() + the_thing = self.from_stack() if not isNest(the_thing): the_thing = [the_thing] - qs.to_return(the_thing) - qs.to_return(-1) + self.to_return(the_thing) + self.to_return(-1) -def meta_bail_by(qs): - qs.expect_number() - a = 2 * qs.from_stack() - if a <= len(qs.rstack): +def meta_bail_by(self): + self.expect_number() + a = 2 * self.from_stack() + if a <= len(self.rstack): for _ in range(a): - qs.from_return() + self.from_return() else: - qs.failed('Bailed out of Quackery.') + self.failed('Bailed out of Quackery.') -def qput(qs): - qs.expect_nest() - a = qs.from_stack() - qs.expect_something() - b = qs.from_stack() +def qput(self): + self.expect_nest() + a = self.from_stack() + self.expect_something() + b = self.from_stack() a.append(b) -def immovable(qs): +def immovable(self): pass -def take(qs): - qs.expect_nest() - a = qs.from_stack() +def take(self): + self.expect_nest() + a = self.from_stack() if len(a) == 0: - qs.failed('Unexpectedly empty nest.') + self.failed('Unexpectedly empty nest.') if len(a) == 1: if isNest(a[0]) and len(a[0]) > 0 and a[0][0] == immovable: - qs.failed('Cannot remove an immovable item.') - qs.to_stack(a.pop()) - -def create_nest(qs): - qs.to_stack([]) - -def qsplit(qs): - qs.expect_number() - a = qs.from_stack() - qs.expect_nest() - b = qs.from_stack() - qs.to_stack(b[:a]) - qs.to_stack(b[a:]) - -def join(qs): - qs.expect_something() - b = qs.from_stack() + self.failed('Cannot remove an immovable item.') + self.to_stack(a.pop()) + +def create_nest(self): + self.to_stack([]) + +def qsplit(self): + self.expect_number() + a = self.from_stack() + self.expect_nest() + b = self.from_stack() + self.to_stack(b[:a]) + self.to_stack(b[a:]) + +def join(self): + self.expect_something() + b = self.from_stack() if not isNest(b): b = [b] - qs.expect_something() + self.expect_something() a = from_stack() if not isNest(a): a = [a] - qs.to_stack(a + b) + self.to_stack(a + b) -def qsize(qs): - qs.expect_nest() - qs.to_stack(len(qs.from_stack())) +def qsize(self): + self.expect_nest() + self.to_stack(len(self.from_stack())) -def qfind(qs): - qs.expect_nest() - nest = qs.from_stack() - qs.expect_something() - a = qs.from_stack() +def qfind(self): + self.expect_nest() + nest = self.from_stack() + self.expect_something() + a = self.from_stack() if a in nest: - qs.to_stack(nest.index(a)) + self.to_stack(nest.index(a)) else: - qs.to_stack(len(nest)) + self.to_stack(len(nest)) -def peek(qs): - qs.expect_number() - index = qs.from_stack() - qs.expect_nest() - nest = qs.from_stack() +def peek(self): + self.expect_number() + index = self.from_stack() + self.expect_nest() + nest = self.from_stack() if index >= len(nest) or ( index < 0 and len(nest) < abs(index)): failed('Cannot peek an item outside a nest.') else: to_stack(nest[index]) -def poke(qs): - qs.expect_number() - index = qs.from_stack() - qs.expect_nest() - nest = qs.from_stack().copy() - qs.expect_something() - value = qs.from_stack() +def poke(self): + self.expect_number() + index = self.from_stack() + self.expect_nest() + nest = self.from_stack().copy() + self.expect_something() + value = self.from_stack() if index >= len(nest) or ( index < 0 and len(nest) < abs(index)): - qs.failed('Cannot poke an item outside a nest.') + self.failed('Cannot poke an item outside a nest.') else: nest[index] = value - qs.to_stack(nest) + self.to_stack(nest) -def qnest(qs): - qs.expect_something() - qs.bool_to_stack(isNest(qs.from_stack())) +def qnest(self): + self.expect_something() + self.bool_to_stack(isNest(self.from_stack())) -def qnumber(qs): - qs.expect_something() - qs.bool_to_stack(isNumber(qs.from_stack())) +def qnumber(self): + self.expect_something() + self.bool_to_stack(isNumber(self.from_stack())) -def qoperator(qs): - qs.expect_something() - qs.bool_to_stack(isOperator(qs.from_stack())) +def qoperator(self): + self.expect_something() + self.bool_to_stack(isOperator(self.from_stack())) -def quid(qs): - qs.expect_something() - qs.to_stack(id(qs.from_stack())) +def quid(self): + self.expect_something() + self.to_stack(id(self.from_stack())) -def qemit(qs): - qs.expect_number() - char = qs.from_stack() +def qemit(self): + self.expect_number() + char = self.from_stack() if char == 13: sys.stdout.write('\n') elif 31 < char < 127: @@ -548,25 +548,25 @@ def qemit(qs): else: sys.stdout.write('?') # XXX @dragoncoder047 maybe use \uFFFD on platforms that support unicode? -def ding(qs): +def ding(self): sys.stdout.write('\a') -def qinput(qs): - prompt = qs.string_from_stack() - qs.string_to_stack(input(prompt)) +def qinput(self): + prompt = self.string_from_stack() + self.string_to_stack(input(prompt)) filepath = [] -def putfile(qs): - filename = qs.string_from_stack() +def putfile(self): + filename = self.string_from_stack() if len(filepath) > 1: - qs.to_stack(filepath[-1]) - filename = qs.string_from_stack() + filename - filetext = qs.string_from_stack() + self.to_stack(filepath[-1]) + filename = self.string_from_stack() + filename + filetext = self.string_from_stack() try: with open(filename, 'x'): pass except FileExistsError: - qs.to_stack(false) + self.to_stack(false) except: raise else: @@ -575,29 +575,29 @@ def putfile(qs): except: raise else: - qs.to_stack(true) + self.to_stack(true) -def releasefile(qs): +def releasefile(self): nonlocal filepath - filename = qs.string_from_stack() + filename = self.string_from_stack() if len(filepath) > 1: - qs.to_stack(filepath[-1]) - filename = qs.string_from_stack() + filename + self.to_stack(filepath[-1]) + filename = self.string_from_stack() + filename try: os.remove(filename) except FileNotFoundError: - qs.to_stack(false) + self.to_stack(false) except: raise else: - qs.to_stack(true) + self.to_stack(true) -def sharefile(qs): - dup(qs) - filename = qs.string_from_stack() +def sharefile(self): + dup(self) + filename = self.string_from_stack() if len(filepath) > 1: - qs.to_stack(filepath[-1]) - filename = qs.string_from_stack() + filename + self.to_stack(filepath[-1]) + filename = self.string_from_stack() + filename try: with open(filename) as f: filetext = f.read() except FileNotFoundError: @@ -605,9 +605,9 @@ def sharefile(qs): except: raise else: - drop(qs) - qs.string_to_stack(filetext) - qs.to_stack(true) + drop(self) + self.string_to_stack(filetext) + self.to_stack(true) predefined_operators = { 'python': python, # ( $ --> ) @@ -668,66 +668,66 @@ def sharefile(qs): 'sharefile': sharefile # ( $ --> $ b ) } -def qis(qs): - qs.check_build() - name = qs.get_name() - qs.operators[name] = qs.current_build.pop() +def qis(self): + self.check_build() + name = self.get_name() + self.operators[name] = self.current_build.pop() -def qcomment(qs): +def qcomment(self): word = '' while word != ')': - word = qs.next_word() + word = self.next_word() if word == '': raise EOFError('Unclosed comment.') -def endcomment(qs): +def endcomment(self): raise SyntaxError('Too many end of comments.') -def unresolved(qs): +def unresolved(self): raise TypeError('Unresolved forward reference.') -def forward(qs): - qs.current_build.append([unresolved]) +def forward(self): + self.current_build.append([unresolved]) -def resolves(qs): - name = qs.get_name() - if name in qs.operators: - if qs.operators[name][0] != unresolved: +def resolves(self): + name = self.get_name() + if name in self.operators: + if self.operators[name][0] != unresolved: raise TypeError(name + ' is not a forward reference.') - qs.check_build() + self.check_build() operators[name][0] = current_build.pop() else: raise NameError('Unrecognised word: ' + name) -def char_literal(qs): - char = qs.one_char() +def char_literal(self): + char = self.one_char() if char == '': raise SyntaxError('No character found.') - qs.current_build.append(ord(char)) + self.current_build.append(ord(char)) -def string_literal(qs): +def string_literal(self): delimiter = '' result = [] while delimiter == '': - char = qs.next_char() + char = self.next_char() if char == '': raise EOFError('No string found.') if ord(char) > 32: delimiter = char char = '' while char != delimiter: - char = qs.next_char() + char = self.next_char() if char == '': raise EOFError('Endless string discovered.') if char != delimiter: result.append(ord(char)) - qs.current_build.append([[meta_literal], result]) + self.current_build.append([[meta_literal], result]) -def hexnum(qs): - word = qs.get_name() +def hexnum(self): + word = self.get_name() if not ishex(word): raise SyntaxError(word + " is not hexadecimal.") - qs.current_build.append(int(word, 16)) + self.current_build.append(int(word, 16)) predefined_builders = { 'is': qis, @@ -1645,17 +1645,17 @@ def hexnum(qs): del _qs -def quackery(source_string, qs = None): +def quackery(source_string, self = None): - if qs is None: - qs = QuackeryState() + if self is None: + self = QuackeryState() else: - if 'quackery' not in qs.operators.keys(): + if 'quackery' not in self.operators.keys(): raise NameError('QuackeryState must have word `quackery` defined.') while True: - qs.to_stack([ord(char) for char in source_string]) - try: qs.run('quackery') + self.to_stack([ord(char) for char in source_string]) + try: self.run('quackery') except QuackeryError as diagnostics: if __name__ == '__main__' and len(sys.argv) == 1: print(diagnostics) @@ -1667,8 +1667,8 @@ def quackery(source_string, qs = None): print('Python error: ' + str(diagnostics)) raise else: - qs.run('stacksize pack decimal unbuild') - the_stack = qs.from_stack() + self.run('stacksize pack decimal unbuild') + the_stack = self.from_stack() return ''.join(map(chr, the_stack[2:-2])) if __name__ == '__main__': From 62634fedbd7412dec9e315ed9ab0727d2aae474c Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Thu, 28 Apr 2022 18:06:52 -0400 Subject: [PATCH 005/121] python fix --- quackery.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/quackery.py b/quackery.py index 5fbee44..e3986f9 100644 --- a/quackery.py +++ b/quackery.py @@ -28,10 +28,7 @@ def ishex(string): hexstr = string if len(hexstr) > 1 and hexstr[0] == '-': hexstr = hexstr[1:] - for char in hexstr: - if char.lower() not in '0123456789abcdef': - return False - return True + return all(char.lower() in '0123456789abcdef' for char in hexstr): class QuackeryError(Exception): pass From baf404e7091ab2f5bd385f711636349b19dbde38 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Thu, 28 Apr 2022 18:09:16 -0400 Subject: [PATCH 006/121] rename quackery context, not `self` --- quackery.py | 610 ++++++++++++++++++++++++++-------------------------- 1 file changed, 305 insertions(+), 305 deletions(-) diff --git a/quackery.py b/quackery.py index e3986f9..f0425dc 100644 --- a/quackery.py +++ b/quackery.py @@ -33,7 +33,7 @@ def ishex(string): class QuackeryError(Exception): pass -class QuackeryState: +class QuackeryContext: def __init__(self, qstack = None, operators = None, builders = None): self.qstack = [] if qstack is None else qstack self.rstack = [] @@ -45,7 +45,7 @@ def __init__(self, qstack = None, operators = None, builders = None): self.current_build = [] def copy(self): - new = QuackeryState(self.qstack.copy(), self.operators.copy(), self.builders.copy()) + new = QuackeryContext(self.qstack.copy(), self.operators.copy(), self.builders.copy()) new.rstack = self.rstack.copy() new.program_counter = self.program_counter new.current_nest = self.current_nest.copy() @@ -229,315 +229,315 @@ def bootstrap(self): -def python(self): +def python(ctx): """For backwards compatibility only""" scope = { - "to_stack": self.to_stack, - "from_stack": self.from_stack, - "string_to_stack": self.string_to_stack, - "string_from_stack": self.string_from_stack, - "self": self, + "to_stack": ctx.to_stack, + "from_stack": ctx.from_stack, + "string_to_stack": ctx.string_to_stack, + "string_from_stack": ctx.string_from_stack, + "ctx": ctx, } try: - exec(self.string_from_stack(), scope, globals()) + exec(ctx.string_from_stack(), scope, globals()) except QuackeryError: raise except Exception as diagnostics: - self.failed('Python reported: "' + str(diagnostics) + '"') + ctx.failed('Python reported: "' + str(diagnostics) + '"') -def qfail(self): - self.failed(self.string_from_stack()) +def qfail(ctx): + ctx.failed(ctx.string_from_stack()) -def stack_size(self): - self.to_stack(len(self.qstack)) +def stack_size(ctx): + ctx.to_stack(len(ctx.qstack)) -def qreturn(self): - self.to_stack(self.rstack) +def qreturn(ctx): + ctx.to_stack(ctx.rstack) -def dup(self): - a = self.from_stack() - self.to_stack(a) - self.to_stack(a) +def dup(ctx): + a = ctx.from_stack() + ctx.to_stack(a) + ctx.to_stack(a) -def drop(self): - self.from_stack() +def drop(ctx): + ctx.from_stack() -def swap(self): - a = self.from_stack() - b = self.from_stack() - self.to_stack(a) - self.to_stack(b) +def swap(ctx): + a = ctx.from_stack() + b = ctx.from_stack() + ctx.to_stack(a) + ctx.to_stack(b) -def rot(self): # XXX @dragoncoder047 maybe simplify to [ dip swap swap ] is rot ? There are no cyclic references that would prevent this - a = self.from_stack() - swap(self) - self.to_stack(a) - swap(self) +def rot(ctx): # XXX @dragoncoder047 maybe simplify to [ dip swap swap ] is rot ? There are no cyclic references that would prevent this + a = ctx.from_stack() + swap(ctx) + ctx.to_stack(a) + swap(ctx) -def over(self): # XXX @dragoncoder047 maybe simplify to [ dip dup swap ] is over ? same reason as above - a = self.from_stack() - dup(self) - self.to_stack(a) - swap(self) +def over(ctx): # XXX @dragoncoder047 maybe simplify to [ dip dup swap ] is over ? same reason as above + a = ctx.from_stack() + dup(ctx) + ctx.to_stack(a) + swap(ctx) -def nest_depth(self): - self.to_stack(len(self.rstack) // 2) +def nest_depth(ctx): + ctx.to_stack(len(ctx.rstack) // 2) true = 1 false = 0 -def nand(self): - self.expect_number() - a = self.from_stack() - self.expect_number() - self.bool_to_stack(self.from_stack() == false or a == false) - -def equal(self): - self.expect_something() - a = self.from_stack() - self.expect_something() - self.bool_to_stack(a == self.from_stack()) - -def greater(self): - self.expect_number() - a = self.from_stack() - self.expect_number() - self.bool_to_stack(self.from_stack() > a) - -def inc(self): - self.expect_number() - self.to_stack(1 + self.from_stack()) - -def plus(self): - self.expect_number() - a = self.from_stack() - self.expect_number() - self.to_stack(a + self.from_stack()) - -def negate(self): - self.expect_number() - self.to_stack(-self.from_stack()) - -def multiply(self): - self.expect_number() - a = self.from_stack() - self.expect_number() - self.to_stack(a * self.from_stack()) - -def qdivmod(self): - self.expect_number() - a = self.from_stack() +def nand(ctx): + ctx.expect_number() + a = ctx.from_stack() + ctx.expect_number() + ctx.bool_to_stack(ctx.from_stack() == false or a == false) + +def equal(ctx): + ctx.expect_something() + a = ctx.from_stack() + ctx.expect_something() + ctx.bool_to_stack(a == ctx.from_stack()) + +def greater(ctx): + ctx.expect_number() + a = ctx.from_stack() + ctx.expect_number() + ctx.bool_to_stack(ctx.from_stack() > a) + +def inc(ctx): + ctx.expect_number() + ctx.to_stack(1 + ctx.from_stack()) + +def plus(ctx): + ctx.expect_number() + a = ctx.from_stack() + ctx.expect_number() + ctx.to_stack(a + ctx.from_stack()) + +def negate(ctx): + ctx.expect_number() + ctx.to_stack(-ctx.from_stack()) + +def multiply(ctx): + ctx.expect_number() + a = ctx.from_stack() + ctx.expect_number() + ctx.to_stack(a * ctx.from_stack()) + +def qdivmod(ctx): + ctx.expect_number() + a = ctx.from_stack() if a == 0: - self.failed('Division by zero.') - self.expect_number() - results = divmod(self.from_stack(), a) - self.to_stack(results[0]) - self.to_stack(results[1]) - -def exponentiate(self): - self.expect_number() - a = self.from_stack() + ctx.failed('Division by zero.') + ctx.expect_number() + results = divmod(ctx.from_stack(), a) + ctx.to_stack(results[0]) + ctx.to_stack(results[1]) + +def exponentiate(ctx): + ctx.expect_number() + a = ctx.from_stack() if a < 0: - self.failed('Tried to raise to a negative power: ' + str(a)) - self.expect_number() - self.to_stack(self.from_stack() ** a) + ctx.failed('Tried to raise to a negative power: ' + str(a)) + ctx.expect_number() + ctx.to_stack(ctx.from_stack() ** a) -def shift_left(self): - self.expect_number() - a = self.from_stack() +def shift_left(ctx): + ctx.expect_number() + a = ctx.from_stack() if a < 0: - self.failed('Cannot << by a negative amount: ' + str(a)) - self.expect_number() - self.to_stack(self.from_stack() << a) + ctx.failed('Cannot << by a negative amount: ' + str(a)) + ctx.expect_number() + ctx.to_stack(ctx.from_stack() << a) -def shift_right(self): - self.expect_number() - a = self.from_stack() +def shift_right(ctx): + ctx.expect_number() + a = ctx.from_stack() if a < 0: - self.failed('Cannot >> by a negative amount: ' + str(a)) - self.expect_number() - self.to_stack(self.from_stack() >> a) - -def bitwise_and(self): - self.expect_number() - a = self.from_stack() - self.expect_number() - self.to_stack(a & self.from_stack()) - -def bitwise_or(self): - self.expect_number() - a = self.from_stack() - self.expect_number() - self.to_stack(a | self.from_stack()) - -def bitwise_xor(self): - self.expect_number() - a = self.from_stack() - self.expect_number() - self.to_stack(a ^ self.from_stack()) - -def bitwise_not(self): - self.expect_number() - self.to_stack(~self.from_stack()) - -def qtime(self): - self.to_stack(int(time.time()*1000000)) - -def meta_done(self): - self.from_return() - self.from_return() - -def meta_again(self): - self.from_return() - self.to_return(-1) - -def meta_if(self): - self.expect_number() - if self.from_stack() == 0: - self.to_return(self.from_return() + 1) - -def meta_iff(self): - self.expect_number() - if self.from_stack() == 0: - self.to_return(self.from_return() + 2) - -def meta_else(self): - self.to_return(self.from_return() + 1) - -def meta_literal(self): - pc = self.from_return() + 1 - return_nest = self.from_return() + ctx.failed('Cannot >> by a negative amount: ' + str(a)) + ctx.expect_number() + ctx.to_stack(ctx.from_stack() >> a) + +def bitwise_and(ctx): + ctx.expect_number() + a = ctx.from_stack() + ctx.expect_number() + ctx.to_stack(a & ctx.from_stack()) + +def bitwise_or(ctx): + ctx.expect_number() + a = ctx.from_stack() + ctx.expect_number() + ctx.to_stack(a | ctx.from_stack()) + +def bitwise_xor(ctx): + ctx.expect_number() + a = ctx.from_stack() + ctx.expect_number() + ctx.to_stack(a ^ ctx.from_stack()) + +def bitwise_not(ctx): + ctx.expect_number() + ctx.to_stack(~ctx.from_stack()) + +def qtime(ctx): + ctx.to_stack(int(time.time()*1000000)) + +def meta_done(ctx): + ctx.from_return() + ctx.from_return() + +def meta_again(ctx): + ctx.from_return() + ctx.to_return(-1) + +def meta_if(ctx): + ctx.expect_number() + if ctx.from_stack() == 0: + ctx.to_return(ctx.from_return() + 1) + +def meta_iff(ctx): + ctx.expect_number() + if ctx.from_stack() == 0: + ctx.to_return(ctx.from_return() + 2) + +def meta_else(ctx): + ctx.to_return(ctx.from_return() + 1) + +def meta_literal(ctx): + pc = ctx.from_return() + 1 + return_nest = ctx.from_return() if len(return_nest) == pc: - self.failed('Found a "\'" at the end of a nest.') - self.to_stack(return_nest[pc]) - self.to_return(return_nest) - self.to_return(pc) - -def meta_this(self): - pc = self.from_return() - return_nest = self.from_return() - self.to_stack(return_nest) - self.to_return(return_nest) - self.to_return(pc) - -def meta_do(self): - self.expect_something() - the_thing = self.from_stack() + ctx.failed('Found a "\'" at the end of a nest.') + ctx.to_stack(return_nest[pc]) + ctx.to_return(return_nest) + ctx.to_return(pc) + +def meta_this(ctx): + pc = ctx.from_return() + return_nest = ctx.from_return() + ctx.to_stack(return_nest) + ctx.to_return(return_nest) + ctx.to_return(pc) + +def meta_do(ctx): + ctx.expect_something() + the_thing = ctx.from_stack() if not isNest(the_thing): the_thing = [the_thing] - self.to_return(the_thing) - self.to_return(-1) + ctx.to_return(the_thing) + ctx.to_return(-1) -def meta_bail_by(self): - self.expect_number() - a = 2 * self.from_stack() - if a <= len(self.rstack): +def meta_bail_by(ctx): + ctx.expect_number() + a = 2 * ctx.from_stack() + if a <= len(ctx.rstack): for _ in range(a): - self.from_return() + ctx.from_return() else: - self.failed('Bailed out of Quackery.') + ctx.failed('Bailed out of Quackery.') -def qput(self): - self.expect_nest() - a = self.from_stack() - self.expect_something() - b = self.from_stack() +def qput(ctx): + ctx.expect_nest() + a = ctx.from_stack() + ctx.expect_something() + b = ctx.from_stack() a.append(b) -def immovable(self): +def immovable(ctx): pass -def take(self): - self.expect_nest() - a = self.from_stack() +def take(ctx): + ctx.expect_nest() + a = ctx.from_stack() if len(a) == 0: - self.failed('Unexpectedly empty nest.') + ctx.failed('Unexpectedly empty nest.') if len(a) == 1: if isNest(a[0]) and len(a[0]) > 0 and a[0][0] == immovable: - self.failed('Cannot remove an immovable item.') - self.to_stack(a.pop()) - -def create_nest(self): - self.to_stack([]) - -def qsplit(self): - self.expect_number() - a = self.from_stack() - self.expect_nest() - b = self.from_stack() - self.to_stack(b[:a]) - self.to_stack(b[a:]) - -def join(self): - self.expect_something() - b = self.from_stack() + ctx.failed('Cannot remove an immovable item.') + ctx.to_stack(a.pop()) + +def create_nest(ctx): + ctx.to_stack([]) + +def qsplit(ctx): + ctx.expect_number() + a = ctx.from_stack() + ctx.expect_nest() + b = ctx.from_stack() + ctx.to_stack(b[:a]) + ctx.to_stack(b[a:]) + +def join(ctx): + ctx.expect_something() + b = ctx.from_stack() if not isNest(b): b = [b] - self.expect_something() + ctx.expect_something() a = from_stack() if not isNest(a): a = [a] - self.to_stack(a + b) + ctx.to_stack(a + b) -def qsize(self): - self.expect_nest() - self.to_stack(len(self.from_stack())) +def qsize(ctx): + ctx.expect_nest() + ctx.to_stack(len(ctx.from_stack())) -def qfind(self): - self.expect_nest() - nest = self.from_stack() - self.expect_something() - a = self.from_stack() +def qfind(ctx): + ctx.expect_nest() + nest = ctx.from_stack() + ctx.expect_something() + a = ctx.from_stack() if a in nest: - self.to_stack(nest.index(a)) + ctx.to_stack(nest.index(a)) else: - self.to_stack(len(nest)) + ctx.to_stack(len(nest)) -def peek(self): - self.expect_number() - index = self.from_stack() - self.expect_nest() - nest = self.from_stack() +def peek(ctx): + ctx.expect_number() + index = ctx.from_stack() + ctx.expect_nest() + nest = ctx.from_stack() if index >= len(nest) or ( index < 0 and len(nest) < abs(index)): failed('Cannot peek an item outside a nest.') else: to_stack(nest[index]) -def poke(self): - self.expect_number() - index = self.from_stack() - self.expect_nest() - nest = self.from_stack().copy() - self.expect_something() - value = self.from_stack() +def poke(ctx): + ctx.expect_number() + index = ctx.from_stack() + ctx.expect_nest() + nest = ctx.from_stack().copy() + ctx.expect_something() + value = ctx.from_stack() if index >= len(nest) or ( index < 0 and len(nest) < abs(index)): - self.failed('Cannot poke an item outside a nest.') + ctx.failed('Cannot poke an item outside a nest.') else: nest[index] = value - self.to_stack(nest) + ctx.to_stack(nest) -def qnest(self): - self.expect_something() - self.bool_to_stack(isNest(self.from_stack())) +def qnest(ctx): + ctx.expect_something() + ctx.bool_to_stack(isNest(ctx.from_stack())) -def qnumber(self): - self.expect_something() - self.bool_to_stack(isNumber(self.from_stack())) +def qnumber(ctx): + ctx.expect_something() + ctx.bool_to_stack(isNumber(ctx.from_stack())) -def qoperator(self): - self.expect_something() - self.bool_to_stack(isOperator(self.from_stack())) +def qoperator(ctx): + ctx.expect_something() + ctx.bool_to_stack(isOperator(ctx.from_stack())) -def quid(self): - self.expect_something() - self.to_stack(id(self.from_stack())) +def quid(ctx): + ctx.expect_something() + ctx.to_stack(id(ctx.from_stack())) -def qemit(self): - self.expect_number() - char = self.from_stack() +def qemit(ctx): + ctx.expect_number() + char = ctx.from_stack() if char == 13: sys.stdout.write('\n') elif 31 < char < 127: @@ -545,25 +545,25 @@ def qemit(self): else: sys.stdout.write('?') # XXX @dragoncoder047 maybe use \uFFFD on platforms that support unicode? -def ding(self): +def ding(ctx): sys.stdout.write('\a') -def qinput(self): - prompt = self.string_from_stack() - self.string_to_stack(input(prompt)) +def qinput(ctx): + prompt = ctx.string_from_stack() + ctx.string_to_stack(input(prompt)) filepath = [] -def putfile(self): - filename = self.string_from_stack() +def putfile(ctx): + filename = ctx.string_from_stack() if len(filepath) > 1: - self.to_stack(filepath[-1]) - filename = self.string_from_stack() + filename - filetext = self.string_from_stack() + ctx.to_stack(filepath[-1]) + filename = ctx.string_from_stack() + filename + filetext = ctx.string_from_stack() try: with open(filename, 'x'): pass except FileExistsError: - self.to_stack(false) + ctx.to_stack(false) except: raise else: @@ -572,29 +572,29 @@ def putfile(self): except: raise else: - self.to_stack(true) + ctx.to_stack(true) -def releasefile(self): +def releasefile(ctx): nonlocal filepath - filename = self.string_from_stack() + filename = ctx.string_from_stack() if len(filepath) > 1: - self.to_stack(filepath[-1]) - filename = self.string_from_stack() + filename + ctx.to_stack(filepath[-1]) + filename = ctx.string_from_stack() + filename try: os.remove(filename) except FileNotFoundError: - self.to_stack(false) + ctx.to_stack(false) except: raise else: - self.to_stack(true) + ctx.to_stack(true) -def sharefile(self): - dup(self) - filename = self.string_from_stack() +def sharefile(ctx): + dup(ctx) + filename = ctx.string_from_stack() if len(filepath) > 1: - self.to_stack(filepath[-1]) - filename = self.string_from_stack() + filename + ctx.to_stack(filepath[-1]) + filename = ctx.string_from_stack() + filename try: with open(filename) as f: filetext = f.read() except FileNotFoundError: @@ -602,9 +602,9 @@ def sharefile(self): except: raise else: - drop(self) - self.string_to_stack(filetext) - self.to_stack(true) + drop(ctx) + ctx.string_to_stack(filetext) + ctx.to_stack(true) predefined_operators = { 'python': python, # ( $ --> ) @@ -665,66 +665,66 @@ def sharefile(self): 'sharefile': sharefile # ( $ --> $ b ) } -def qis(self): - self.check_build() - name = self.get_name() - self.operators[name] = self.current_build.pop() +def qis(ctx): + ctx.check_build() + name = ctx.get_name() + ctx.operators[name] = ctx.current_build.pop() -def qcomment(self): +def qcomment(ctx): word = '' while word != ')': - word = self.next_word() + word = ctx.next_word() if word == '': raise EOFError('Unclosed comment.') -def endcomment(self): +def endcomment(ctx): raise SyntaxError('Too many end of comments.') -def unresolved(self): +def unresolved(ctx): raise TypeError('Unresolved forward reference.') -def forward(self): - self.current_build.append([unresolved]) +def forward(ctx): + ctx.current_build.append([unresolved]) -def resolves(self): - name = self.get_name() - if name in self.operators: - if self.operators[name][0] != unresolved: +def resolves(ctx): + name = ctx.get_name() + if name in ctx.operators: + if ctx.operators[name][0] != unresolved: raise TypeError(name + ' is not a forward reference.') - self.check_build() + ctx.check_build() operators[name][0] = current_build.pop() else: raise NameError('Unrecognised word: ' + name) -def char_literal(self): - char = self.one_char() +def char_literal(ctx): + char = ctx.one_char() if char == '': raise SyntaxError('No character found.') - self.current_build.append(ord(char)) + ctx.current_build.append(ord(char)) -def string_literal(self): +def string_literal(ctx): delimiter = '' result = [] while delimiter == '': - char = self.next_char() + char = ctx.next_char() if char == '': raise EOFError('No string found.') if ord(char) > 32: delimiter = char char = '' while char != delimiter: - char = self.next_char() + char = ctx.next_char() if char == '': raise EOFError('Endless string discovered.') if char != delimiter: result.append(ord(char)) - self.current_build.append([[meta_literal], result]) + ctx.current_build.append([[meta_literal], result]) -def hexnum(self): - word = self.get_name() +def hexnum(ctx): + word = ctx.get_name() if not ishex(word): raise SyntaxError(word + " is not hexadecimal.") - self.current_build.append(int(word, 16)) + ctx.current_build.append(int(word, 16)) predefined_builders = { 'is': qis, @@ -1636,23 +1636,23 @@ def hexnum(self): """ # bootstrap only once -_qs = QuackeryState() +_qs = QuackeryContext() _qs.bootstrap() predefined_operators = _qs.operators del _qs -def quackery(source_string, self = None): +def quackery(source_string, ctx = None): - if self is None: - self = QuackeryState() + if ctx is None: + ctx = QuackeryContext() else: - if 'quackery' not in self.operators.keys(): - raise NameError('QuackeryState must have word `quackery` defined.') + if 'quackery' not in ctx.operators.keys(): + raise NameError('QuackeryContext must have word `quackery` defined.') while True: - self.to_stack([ord(char) for char in source_string]) - try: self.run('quackery') + ctx.to_stack([ord(char) for char in source_string]) + try: ctx.run('quackery') except QuackeryError as diagnostics: if __name__ == '__main__' and len(sys.argv) == 1: print(diagnostics) @@ -1664,8 +1664,8 @@ def quackery(source_string, self = None): print('Python error: ' + str(diagnostics)) raise else: - self.run('stacksize pack decimal unbuild') - the_stack = self.from_stack() + ctx.run('stacksize pack decimal unbuild') + the_stack = ctx.from_stack() return ''.join(map(chr, the_stack[2:-2])) if __name__ == '__main__': From 029a297c34b389bc385b03107ba2d3fa2985690a Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Fri, 29 Apr 2022 07:44:56 -0400 Subject: [PATCH 007/121] syntax error --- quackery.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quackery.py b/quackery.py index f0425dc..6bde8a2 100644 --- a/quackery.py +++ b/quackery.py @@ -28,7 +28,7 @@ def ishex(string): hexstr = string if len(hexstr) > 1 and hexstr[0] == '-': hexstr = hexstr[1:] - return all(char.lower() in '0123456789abcdef' for char in hexstr): + return all(char.lower() in '0123456789abcdef' for char in hexstr) class QuackeryError(Exception): pass From ea99454f1179ee5d648949dd30025c2653153188 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Fri, 29 Apr 2022 07:52:05 -0400 Subject: [PATCH 008/121] simplify --- quackery.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/quackery.py b/quackery.py index 6bde8a2..853cbe5 100644 --- a/quackery.py +++ b/quackery.py @@ -18,17 +18,15 @@ def isNumber(item): def isOperator(item): return isinstance(item, types.FunctionType) -def isinteger(string): - numstr = string +def isinteger(numstr): if len(numstr) > 0 and numstr[0] == '-': numstr = numstr[1:] return numstr.isdigit() -def ishex(string): - hexstr = string - if len(hexstr) > 1 and hexstr[0] == '-': - hexstr = hexstr[1:] - return all(char.lower() in '0123456789abcdef' for char in hexstr) +def ishex(hexstr): + if len(hexstr) > 1 and hexstr[0] == '-': + hexstr = hexstr[1:] + return all(char.lower() in '0123456789abcdef' for char in hexstr) class QuackeryError(Exception): pass From b1c950862650316bf4ad9e70fd8e495a5b9a082e Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Fri, 29 Apr 2022 07:55:11 -0400 Subject: [PATCH 009/121] typo --- quackery.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quackery.py b/quackery.py index 853cbe5..4c66280 100644 --- a/quackery.py +++ b/quackery.py @@ -104,7 +104,7 @@ def bool_to_stack(self, qbool): def to_return(self, item): self.rstack.append(item) - def from_return(self.): + def from_return(self): if len(self.rstack) == 0: self.failed('Return stack unexpectedly empty.') return self.rstack.pop() From e1364952970829ccea4ee0224cd7203878d2d222 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Fri, 29 Apr 2022 08:00:29 -0400 Subject: [PATCH 010/121] stray nonlocal --- quackery.py | 1 - 1 file changed, 1 deletion(-) diff --git a/quackery.py b/quackery.py index 4c66280..fd0ce74 100644 --- a/quackery.py +++ b/quackery.py @@ -573,7 +573,6 @@ def putfile(ctx): ctx.to_stack(true) def releasefile(ctx): - nonlocal filepath filename = ctx.string_from_stack() if len(filepath) > 1: ctx.to_stack(filepath[-1]) From 12680aa9d9264118649a700aa1856a63e3fbeeb5 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Fri, 29 Apr 2022 09:27:17 -0400 Subject: [PATCH 011/121] bunch of typos --- quackery.py | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/quackery.py b/quackery.py index fd0ce74..47cdaf3 100644 --- a/quackery.py +++ b/quackery.py @@ -140,9 +140,11 @@ def tick(self): def traverse(self, the_nest): + orig_depth = len(self.rstack) + self.to_return(self.current_nest) + self.to_return(self.program_counter) self.current_nest = the_nest self.program_counter = 0 - orig_depth = len(self.rstack) while len(self.rstack) > orig_depth: self.tick() @@ -176,7 +178,7 @@ def one_char(self): return char def get_name(self): - name = selfnext_word() + name = self.next_word() if name == '': raise EOFError('Unexpected end of program text.') return name @@ -204,7 +206,7 @@ def sub_build(): nesting -= 1 if nesting < 0: raise SyntaxError('Unexpected end of nest.') - return(the_nest) + return the_nest elif word in self.builders.keys(): self.builders[word](self) elif word in self.operators.keys(): @@ -222,9 +224,6 @@ def sub_build(): def run(self, source_string): self.traverse(self.build(source_string)) - def bootstrap(self): - self.run(predefined_qky) - def python(ctx): @@ -689,7 +688,7 @@ def resolves(ctx): if ctx.operators[name][0] != unresolved: raise TypeError(name + ' is not a forward reference.') ctx.check_build() - operators[name][0] = current_build.pop() + ctx.operators[name][0] = current_build.pop() else: raise NameError('Unrecognised word: ' + name) @@ -1634,7 +1633,7 @@ def hexnum(ctx): # bootstrap only once _qs = QuackeryContext() -_qs.bootstrap() +_qs.run(predefined_qky) predefined_operators = _qs.operators del _qs @@ -1644,12 +1643,14 @@ def quackery(source_string, ctx = None): if ctx is None: ctx = QuackeryContext() else: - if 'quackery' not in ctx.operators.keys(): - raise NameError('QuackeryContext must have word `quackery` defined.') + for required_word in ('stacksize', 'pack', 'decimal', 'unbuild', 'quackery'): + if required_word not in ctx.operators.keys(): + raise NameError('QuackeryContext must have word %s defined.' % required_word) while True: ctx.to_stack([ord(char) for char in source_string]) - try: ctx.run('quackery') + try: + ctx.run('quackery') except QuackeryError as diagnostics: if __name__ == '__main__' and len(sys.argv) == 1: print(diagnostics) @@ -1682,6 +1683,7 @@ def quackery(source_string, ctx = None): print('\nQuackery crashed.\n') print(diagnostics) print() + sys.exit(1) except Exception as diagnostics: print('Quackery system damage detected.') print('Python error: ' + str(diagnostics)) @@ -1689,17 +1691,15 @@ def quackery(source_string, ctx = None): else: print('\nWelcome to Quackery.') print('\nEnter "leave" to leave the shell.') - quackscript = r""" + try: + quackery(r""" $ 'extensions.qky' dup name? not dip sharefile and iff [ cr say 'Building extensions.' cr quackery ] else drop - shell """ - - try: - quackery(quackscript) + shell """) except QuackeryError as diagnostics: print('\nQuackery crashed.\n') print(diagnostics) From 89528827c179430d153a4adc7741e4cda08e136e Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Fri, 29 Apr 2022 09:31:04 -0400 Subject: [PATCH 012/121] typo --- quackery.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quackery.py b/quackery.py index 47cdaf3..d34947b 100644 --- a/quackery.py +++ b/quackery.py @@ -688,7 +688,7 @@ def resolves(ctx): if ctx.operators[name][0] != unresolved: raise TypeError(name + ' is not a forward reference.') ctx.check_build() - ctx.operators[name][0] = current_build.pop() + ctx.operators[name][0] = ctx.current_build.pop() else: raise NameError('Unrecognised word: ' + name) From 69b215fab346ecc223f2ef4651a7227c040391eb Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Fri, 29 Apr 2022 09:42:38 -0400 Subject: [PATCH 013/121] 2 typos --- quackery.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/quackery.py b/quackery.py index d34947b..8541861 100644 --- a/quackery.py +++ b/quackery.py @@ -125,8 +125,8 @@ def tick(self): return current_item = self.current_nest[self.program_counter] if isNest(current_item): - self.to_return(current_nest) - self.to_return(program_counter) + self.to_return(self.current_nest) + self.to_return(self.program_counter) self.current_nest = current_item self.program_counter = 0 elif isOperator(current_item): From 2f266ff343e75e5bde7b7799b2564f7874bee57a Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Fri, 29 Apr 2022 09:50:00 -0400 Subject: [PATCH 014/121] typo!!!!!! --- quackery.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quackery.py b/quackery.py index 8541861..b613d2f 100644 --- a/quackery.py +++ b/quackery.py @@ -472,7 +472,7 @@ def join(ctx): if not isNest(b): b = [b] ctx.expect_something() - a = from_stack() + a = ctx.from_stack() if not isNest(a): a = [a] ctx.to_stack(a + b) From 81d5fd0a53ceb9897dff942f8763e6c597a2919a Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Fri, 29 Apr 2022 10:23:37 -0400 Subject: [PATCH 015/121] i think I got everything --- quackery.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/quackery.py b/quackery.py index b613d2f..41e5120 100644 --- a/quackery.py +++ b/quackery.py @@ -498,9 +498,9 @@ def peek(ctx): nest = ctx.from_stack() if index >= len(nest) or ( index < 0 and len(nest) < abs(index)): - failed('Cannot peek an item outside a nest.') + ctx.failed('Cannot peek an item outside a nest.') else: - to_stack(nest[index]) + ctx.to_stack(nest[index]) def poke(ctx): ctx.expect_number() @@ -594,7 +594,7 @@ def sharefile(ctx): try: with open(filename) as f: filetext = f.read() except FileNotFoundError: - qsto_stack(false) + ctx.to_stack(false) except: raise else: From 3c0bb2411849b286aff936208693a744ff7f15cf Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Fri, 29 Apr 2022 13:13:04 -0400 Subject: [PATCH 016/121] add `__all__` --- quackery.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/quackery.py b/quackery.py index 41e5120..e551585 100644 --- a/quackery.py +++ b/quackery.py @@ -9,6 +9,8 @@ except: pass +__all__ = ['QuackeryContext', 'quackery'] + def isNest(item): return isinstance(item, list) From 893dbbe014bd651380e2a0a334281f878f7bb763 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Sat, 30 Apr 2022 12:17:44 -0400 Subject: [PATCH 017/121] add index.html for web console --- index.html | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 index.html diff --git a/index.html b/index.html new file mode 100644 index 0000000..6497216 --- /dev/null +++ b/index.html @@ -0,0 +1,17 @@ + + + + Online Quackery Console + + + +

+ The Book of Quackery +  -----  + Quackery on Github +

+
+ Blah +
+ + \ No newline at end of file From a3d6b977cc2d200b69e9d257aeec8798b168a3fe Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Mon, 2 May 2022 12:09:00 -0400 Subject: [PATCH 018/121] Update index.html --- index.html | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/index.html b/index.html index 6497216..2baa2e6 100644 --- a/index.html +++ b/index.html @@ -3,15 +3,21 @@ Online Quackery Console + + + +

The Book of Quackery  -----  Quackery on Github +  -----  + Quackery on RosettaCode

Blah
- \ No newline at end of file + From 7c2f697426f39f9d870aa9389561a5a499696d0a Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Mon, 2 May 2022 12:51:57 -0400 Subject: [PATCH 019/121] Update index.html --- index.html | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/index.html b/index.html index 2baa2e6..d1e5ff5 100644 --- a/index.html +++ b/index.html @@ -5,7 +5,11 @@ + + + + From cabc7fa0c5d96a28cc7b9f5ad0ef903fed54770c Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Mon, 2 May 2022 13:00:31 -0400 Subject: [PATCH 020/121] Create webapp_main.py --- webapp_main.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 webapp_main.py diff --git a/webapp_main.py b/webapp_main.py new file mode 100644 index 0000000..9183665 --- /dev/null +++ b/webapp_main.py @@ -0,0 +1,20 @@ +from browser import bind, worker, window + +jQuery = window.jQuery +q_worker = worker.Worker('worker') + +def finished_input(text): + worker.postMessage(text) + term.set_prompt('') + term.disable() + +term = jQuery('#terminal').terminal(finished_input, {'greetings': '', 'prompt': ''}) + +@bind(q_worker, "message") +def output(e): + if e.data.type = 'print': + term.echo(e.data.text, {'newline': False}) + else: + term.enable() + term.set_prompt(e.data.text) + From 37595c81469ff4b0ac79018725785aa6a444276f Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Mon, 2 May 2022 13:02:05 -0400 Subject: [PATCH 021/121] typo --- webapp_main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp_main.py b/webapp_main.py index 9183665..1da2a56 100644 --- a/webapp_main.py +++ b/webapp_main.py @@ -4,7 +4,7 @@ q_worker = worker.Worker('worker') def finished_input(text): - worker.postMessage(text) + worker.send(text) term.set_prompt('') term.disable() From cdc6ab0b7c55a476aa0d736c5f3b01045feeaff5 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Mon, 2 May 2022 13:03:04 -0400 Subject: [PATCH 022/121] Update index.html --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index d1e5ff5..f5ddc31 100644 --- a/index.html +++ b/index.html @@ -14,7 +14,7 @@

- The Book of Quackery + The Book of Quackery  -----  Quackery on Github  -----  From 1c4dabd79d13b11c3a3a78d40349254bfcedd31d Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Mon, 2 May 2022 17:40:51 -0400 Subject: [PATCH 023/121] switch to pyodide --- index.html | 9 +++---- webapp_main.js | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++ webapp_main.py | 20 ---------------- 3 files changed, 68 insertions(+), 26 deletions(-) create mode 100644 webapp_main.js delete mode 100644 webapp_main.py diff --git a/index.html b/index.html index f5ddc31..8279798 100644 --- a/index.html +++ b/index.html @@ -3,14 +3,11 @@ Online Quackery Console - - - - - + +

@@ -21,7 +18,7 @@ Quackery on RosettaCode

- Blah + Quackery is loading...
diff --git a/webapp_main.js b/webapp_main.js new file mode 100644 index 0000000..10c2bef --- /dev/null +++ b/webapp_main.js @@ -0,0 +1,65 @@ +// modified from the Pyodide console (https://pyodide.org/en/stable/console.html), since it already uses jQuery.terminal + +const ORIGIN = 'https://dragoncoder047.github.io/QuackeryFork/' + +function sleep(s) { + return new Promise((resolve) => setTimeout(resolve, s)); +} + +window.addEventListener('DOMContentLoaded', async function main() { + + var inputDone; + var term = $("#terminal").terminal(inputDone, { + greetings: '', + prompt: '', + completionEscape: false, + pauseEvents: false, + completion: function (command, callback) { + callback(pyconsole.complete(command).toJs()[0]); + }, + }); + term.pause(); + window.term = term; + + globalThis.pyodide = await loadPyodide({ + homedir: '/home/quackery', + stderr: line => term.error(line), + stdout: line => term.echo(line), + stdin: async prompt => { + term.set_prompt(prompt); + term.resume(); + var input = await new Promise(resolve => (inputDone = resolve)); + term.pause(); + await sleep(10); + return input; + }, + }); + + + pyodide._api.on_fatal = async (e) => { + term.error("AAAAH!! You crashed Python! Please report this error:"); + term.error(e); + term.error("Look in the browser console for more details."); + term.pause(); + await sleep(15); + term.pause(); + }; + + var old_open = pyodide.globals.get('open'); + pyodide.globals.set('open', function fetch_opener(filename, mode) { + throw 'fixme'; + }); + + await pyodide.runPythonAsync(` + from pyodide.http import pyfetch + response = await pyfetch("${ORIGIN}/quackery.py") + with open("quackery.py", "wb") as quackeryfile: + quackeryfile.write(await response.bytes()) + + from quackery import quackery + quackery(''' + say "Welcome to Quackery running on the Pyodide virtual machine." + shell + ''') + `) +}); diff --git a/webapp_main.py b/webapp_main.py deleted file mode 100644 index 1da2a56..0000000 --- a/webapp_main.py +++ /dev/null @@ -1,20 +0,0 @@ -from browser import bind, worker, window - -jQuery = window.jQuery -q_worker = worker.Worker('worker') - -def finished_input(text): - worker.send(text) - term.set_prompt('') - term.disable() - -term = jQuery('#terminal').terminal(finished_input, {'greetings': '', 'prompt': ''}) - -@bind(q_worker, "message") -def output(e): - if e.data.type = 'print': - term.echo(e.data.text, {'newline': False}) - else: - term.enable() - term.set_prompt(e.data.text) - From 8f3ae2bfc16ae9ae8769550c93b47d68fc548f79 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Mon, 2 May 2022 18:08:14 -0400 Subject: [PATCH 024/121] remove `open()` monkey patch --- webapp_main.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/webapp_main.js b/webapp_main.js index 10c2bef..26de114 100644 --- a/webapp_main.js +++ b/webapp_main.js @@ -45,11 +45,6 @@ window.addEventListener('DOMContentLoaded', async function main() { term.pause(); }; - var old_open = pyodide.globals.get('open'); - pyodide.globals.set('open', function fetch_opener(filename, mode) { - throw 'fixme'; - }); - await pyodide.runPythonAsync(` from pyodide.http import pyfetch response = await pyfetch("${ORIGIN}/quackery.py") From 97c9e851122539d82b5b6de54088ab99570ca154 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Mon, 2 May 2022 18:11:59 -0400 Subject: [PATCH 025/121] fix --- README.md | 6 ++++-- webapp_main.js | 9 +++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index f7a1c49..f0d654f 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,12 @@ # Quackery + Quackery is a lightweight, open-source language for recreational and educational programming, inspired by Forth and Lisp. Sample Quackery Shell dialogue; defining and testing an insertion sort. -
/O> [ [] swap witheach
+```qky
+/O> [ [] swap witheach
 ...     [ swap 2dup 
 ...       findwith [ over > ] [ ] 
 ...       nip stuff ] ]           is i-sort ( [ --> [ )
@@ -21,7 +23,7 @@ Stack empty.
 Stack empty.
 
 /O> 
-
+``` The Quackery language is an extensible assembler for a Quackery Engine. The Quackery Engine has a memory model based on dynamic arrays diff --git a/webapp_main.js b/webapp_main.js index 26de114..3ca321b 100644 --- a/webapp_main.js +++ b/webapp_main.js @@ -13,10 +13,7 @@ window.addEventListener('DOMContentLoaded', async function main() { greetings: '', prompt: '', completionEscape: false, - pauseEvents: false, - completion: function (command, callback) { - callback(pyconsole.complete(command).toJs()[0]); - }, + pauseEvents: false }); term.pause(); window.term = term; @@ -24,7 +21,7 @@ window.addEventListener('DOMContentLoaded', async function main() { globalThis.pyodide = await loadPyodide({ homedir: '/home/quackery', stderr: line => term.error(line), - stdout: line => term.echo(line), + stdout: line => term.echo(line, { newline: false }), stdin: async prompt => { term.set_prompt(prompt); term.resume(); @@ -53,7 +50,7 @@ window.addEventListener('DOMContentLoaded', async function main() { from quackery import quackery quackery(''' - say "Welcome to Quackery running on the Pyodide virtual machine." + say "Welcome to Quackery running on the Pyodide virtual machine." cr shell ''') `) From 8c8b454f38727652edfe363f323466e154a6eec3 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Tue, 3 May 2022 07:55:28 -0400 Subject: [PATCH 026/121] add webapp_start.py --- webapp_main.js | 22 ++++++++-------------- webapp_start.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 14 deletions(-) create mode 100644 webapp_start.py diff --git a/webapp_main.js b/webapp_main.js index 3ca321b..d22ca25 100644 --- a/webapp_main.js +++ b/webapp_main.js @@ -9,7 +9,7 @@ function sleep(s) { window.addEventListener('DOMContentLoaded', async function main() { var inputDone; - var term = $("#terminal").terminal(inputDone, { + var term = $("#terminal").terminal(x => inputDone(x), { greetings: '', prompt: '', completionEscape: false, @@ -35,23 +35,17 @@ window.addEventListener('DOMContentLoaded', async function main() { pyodide._api.on_fatal = async (e) => { term.error("AAAAH!! You crashed Python! Please report this error:"); - term.error(e); + term.exception(e); term.error("Look in the browser console for more details."); term.pause(); await sleep(15); term.pause(); }; - await pyodide.runPythonAsync(` - from pyodide.http import pyfetch - response = await pyfetch("${ORIGIN}/quackery.py") - with open("quackery.py", "wb") as quackeryfile: - quackeryfile.write(await response.bytes()) - - from quackery import quackery - quackery(''' - say "Welcome to Quackery running on the Pyodide virtual machine." cr - shell - ''') - `) + var resp = await fetch('webapp_start.py'); + var py = await resp.text(); + + await pyodide.runPythonAsync(py); + + term.error('Reload the page to run Quackery again.'); }); diff --git a/webapp_start.py b/webapp_start.py new file mode 100644 index 0000000..d3cb63a --- /dev/null +++ b/webapp_start.py @@ -0,0 +1,28 @@ +from pyodide.http import pyfetch +from os import mkdir +async def get(file): + response = await pyfetch(f"${ORIGIN}/{file}") + with open(file, "wb") as f: + f.write(await response.bytes()) + +files1 = ['quackery.py', 'bigrat.qky', 'extensions.qky', 'turtleduck.qky'] +for file in files1: + # N. B. top-level await is only allowed in Pyodide + await get(file) + +mkdir('sundry') +files2 = ['cards.qky', 'demo.qky', 'fsm.qky', 'heapsort.qky'] +for file in files2: + await get(f'sundry/{file}') + +from quackery import quackery + +quackery(r''' +say "Welcome to Quackery running on the Pyodide virtual machine." cr + +$ 'extensions.qky' dup name? not +dip sharefile and iff + [ cr say 'Building extensions.' cr quackery ] +else drop + +shell''') \ No newline at end of file From 395bc18d91cd6d96d601be8f8bcb15e97c6a0b99 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Tue, 3 May 2022 07:57:05 -0400 Subject: [PATCH 027/121] added .nojekyll --- .nojekyll | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 .nojekyll diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..e69de29 From 353a83501c8e27024b09dc0a3207c266e7a6382c Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Tue, 3 May 2022 07:59:14 -0400 Subject: [PATCH 028/121] mimetype typos --- index.html | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/index.html b/index.html index 8279798..80ae6a0 100644 --- a/index.html +++ b/index.html @@ -5,7 +5,7 @@ - + @@ -17,8 +17,6 @@  -----  Quackery on RosettaCode

-
- Quackery is loading... -
+
From ce4fe7e57d40ed9b0fe99b935119388fb554d2d8 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Tue, 3 May 2022 08:56:47 -0400 Subject: [PATCH 029/121] add more --- index.html | 2 +- webapp_main.js | 6 ++---- webapp_start.py | 7 +++++++ 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/index.html b/index.html index 80ae6a0..6130fae 100644 --- a/index.html +++ b/index.html @@ -5,7 +5,7 @@ - + diff --git a/webapp_main.js b/webapp_main.js index d22ca25..91e972a 100644 --- a/webapp_main.js +++ b/webapp_main.js @@ -8,8 +8,7 @@ function sleep(s) { window.addEventListener('DOMContentLoaded', async function main() { - var inputDone; - var term = $("#terminal").terminal(x => inputDone(x), { + var term = $("#terminal").terminal(_ => { }, { greetings: '', prompt: '', completionEscape: false, @@ -23,9 +22,8 @@ window.addEventListener('DOMContentLoaded', async function main() { stderr: line => term.error(line), stdout: line => term.echo(line, { newline: false }), stdin: async prompt => { - term.set_prompt(prompt); term.resume(); - var input = await new Promise(resolve => (inputDone = resolve)); + var input = await term.read(prompt); term.pause(); await sleep(10); return input; diff --git a/webapp_start.py b/webapp_start.py index d3cb63a..ddf5c63 100644 --- a/webapp_start.py +++ b/webapp_start.py @@ -18,6 +18,13 @@ async def get(file): from quackery import quackery quackery(r''' +say " + ___ _ ___ _ _ + / _ \ _ _ __ _ ___| | _____ _ __ _ _ / _ \ _ __ | (_)_ __ ___ + | | | | | | |/ _` |/ __| |/ / _ \ '__| | | | | | | | '_ \| | | '_ \ / _ \ + | |_| | |_| | (_| | (__| < __/ | | |_| | | |_| | | | | | | | | | __/ + \__\_\\__,_|\__,_|\___|_|\_\___|_| \__, | \___/|_| |_|_|_|_| |_|\___| + |___/" cr say "Welcome to Quackery running on the Pyodide virtual machine." cr $ 'extensions.qky' dup name? not From a30cd27e1b9b5c10ab6009a39f44a8455c2bf3b8 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Tue, 3 May 2022 10:00:26 -0400 Subject: [PATCH 030/121] add error handler. --- README.md | 43 +++++++++--------------------------- webapp_main.js | 59 ++++++++++++++++++++++++++++---------------------- 2 files changed, 43 insertions(+), 59 deletions(-) diff --git a/README.md b/README.md index f0d654f..bd62497 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ # Quackery -Quackery is a lightweight, open-source language for recreational and -educational programming, inspired by Forth and Lisp. +Quackery is a lightweight, open-source language for recreational and educational programming, inspired by Forth and Lisp. Sample Quackery Shell dialogue; defining and testing an insertion sort. @@ -25,40 +24,18 @@ Stack empty. /O> ``` -The Quackery language is an extensible assembler for a Quackery -Engine. The Quackery Engine has a memory model based on dynamic arrays -and bignums, so presumes comprehensive hardware support for these -features. +The Quackery language is an extensible assembler for a Quackery Engine. The Quackery Engine has a memory model based on dynamic arrays and bignums, so presumes comprehensive hardware support for these features. -Program execution consists of the Quackery Processor traversing -directed tree-like graphs built from dynamic arrays ("Nests" in the -Quackery nomenclature) containing Operators (op-codes), Numbers -(pointers to bignums) and pointers to Nests. The Quackery processor is -stack-based rather than register-based. +Program execution consists of the Quackery Processor traversing directed tree-like graphs built from dynamic arrays ("Nests" in the Quackery nomenclature) containing Operators (op-codes), Numbers (pointers to bignums) and pointers to Nests. The Quackery processor is stack-based rather than register-based. -Programming in Quackery consists of extending the predefined graphs -that constitute the Quackery environment. +Programming in Quackery consists of extending the predefined graphs that constitute the Quackery environment. -This implementation of a virtual Quackery Engine uses Python lists as -Nests, Python functions as Operators and Python ints as Numbers. +This implementation of a virtual Quackery Engine uses Python lists as Nests, Python functions as Operators and Python ints as Numbers. -The Quackery processor and a basic Quackery compiler are coded in -Python 3, and the Python Quackery compiler is used to compile the -Quackery environment, which is written in Quackery and includes a more -fully featured (and extensible) Quackery compiler, which is available -to the Quackery programmer. +The Quackery processor and a basic Quackery compiler are coded in Python 3, and the Python Quackery compiler is used to compile the Quackery environment, which is written in Quackery and includes a more fully featured (and extensible) Quackery compiler, which is available to the Quackery programmer. -That the Quackery language has similarities to Forth (also an -extensible assembler for a stack processor), that it leans on Python -for support for dynamic arrays and bignums, and that the majority of -Quackery is written in Quackery all make for a very compact -implementation, under 50k of source code. The downsides are that it is -rather slow by modern standards, and that it is by no means "fully -featured". +That the Quackery language has similarities to Forth (also an extensible assembler for a stack processor), that it leans on Python for support for dynamic arrays and bignums, and that the majority of Quackery is written in Quackery all make for a very compact implementation, under 50k of source code. The downsides are that it is rather slow by modern standards, and that it is by no means "fully featured". -In its defence it is possible to understand the entirety of Quackery -in short order, and, once the hurdle of Reverse Polish Notation has -been passed, program development with the interactive environment (the -Quackery Shell) is quick and rewarding. Quackery is intended primarily -for recreational and educational programming, and is a relatively -painless introduction to the concatenative programming paradigm. +In its defence it is possible to understand the entirety of Quackery in short order, and, once the hurdle of Reverse Polish Notation has been passed, program development with the interactive environment (the Quackery Shell) is quick and rewarding. Quackery is intended primarily for recreational and educational programming, and is a relatively painless introduction to the concatenative programming paradigm. + +**NEW May 3, 2022**: A beta web console is now available at . diff --git a/webapp_main.js b/webapp_main.js index 91e972a..6f284ab 100644 --- a/webapp_main.js +++ b/webapp_main.js @@ -16,34 +16,41 @@ window.addEventListener('DOMContentLoaded', async function main() { }); term.pause(); window.term = term; - - globalThis.pyodide = await loadPyodide({ - homedir: '/home/quackery', - stderr: line => term.error(line), - stdout: line => term.echo(line, { newline: false }), - stdin: async prompt => { - term.resume(); - var input = await term.read(prompt); + try { + globalThis.pyodide = await loadPyodide({ + homedir: '/home/quackery', + stderr: line => term.error(line), + stdout: line => term.echo(line, { newline: false }), + stdin: async prompt => { + term.resume(); + var input = await term.read(prompt); + term.pause(); + await sleep(10); + return input; + }, + }); + + + pyodide._api.on_fatal = async (e) => { + term.error("AAAAH!! You crashed Python! Please report this error:"); + term.exception(e); + term.error("Look in the browser console for more details."); term.pause(); - await sleep(10); - return input; - }, - }); - - - pyodide._api.on_fatal = async (e) => { - term.error("AAAAH!! You crashed Python! Please report this error:"); - term.exception(e); - term.error("Look in the browser console for more details."); - term.pause(); - await sleep(15); - term.pause(); - }; + await sleep(15); + term.pause(); + }; - var resp = await fetch('webapp_start.py'); - var py = await resp.text(); + var resp = await fetch('webapp_start.py'); + var py = await resp.text(); - await pyodide.runPythonAsync(py); + await pyodide.runPythonAsync(py); - term.error('Reload the page to run Quackery again.'); + term.error('Reload the page to run Quackery again.'); + } + catch (e) { + term.error('An error occurred loading Quackery:') + term.exception(e); + term.error('Please report this error if it continues to occur.'); + term.error('https://github.com/dragoncoder047/QuackeryFork/issues'); + } }); From 6ef7e858f88fab66c707de3e83a1bf3ff5466ef7 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Tue, 3 May 2022 10:03:15 -0400 Subject: [PATCH 031/121] replacement error --- webapp_main.js | 3 ++- webapp_start.py | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/webapp_main.js b/webapp_main.js index 6f284ab..38e066b 100644 --- a/webapp_main.js +++ b/webapp_main.js @@ -43,7 +43,8 @@ window.addEventListener('DOMContentLoaded', async function main() { var resp = await fetch('webapp_start.py'); var py = await resp.text(); - await pyodide.runPythonAsync(py); + + await pyodide.runPythonAsync(py.replaceAll('@@ORIGIN@@', ORIGIN)); term.error('Reload the page to run Quackery again.'); } diff --git a/webapp_start.py b/webapp_start.py index ddf5c63..4bae6c9 100644 --- a/webapp_start.py +++ b/webapp_start.py @@ -1,7 +1,9 @@ +# @@ORIGIN@@ will be replaced in Javascript + from pyodide.http import pyfetch from os import mkdir async def get(file): - response = await pyfetch(f"${ORIGIN}/{file}") + response = await pyfetch(f"@@ORIGIN@@/{file}") with open(file, "wb") as f: f.write(await response.bytes()) @@ -13,7 +15,7 @@ async def get(file): mkdir('sundry') files2 = ['cards.qky', 'demo.qky', 'fsm.qky', 'heapsort.qky'] for file in files2: - await get(f'sundry/{file}') + await get(f'@@ORIGIN@@/sundry/{file}') from quackery import quackery From b187b0e12e80eb64dceeb955ba74bcc07057794e Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Tue, 3 May 2022 10:03:44 -0400 Subject: [PATCH 032/121] fix --- webapp_main.js | 1 + 1 file changed, 1 insertion(+) diff --git a/webapp_main.js b/webapp_main.js index 38e066b..793aef0 100644 --- a/webapp_main.js +++ b/webapp_main.js @@ -49,6 +49,7 @@ window.addEventListener('DOMContentLoaded', async function main() { term.error('Reload the page to run Quackery again.'); } catch (e) { + term.clear(); term.error('An error occurred loading Quackery:') term.exception(e); term.error('Please report this error if it continues to occur.'); From d86d6af56e51e88b90c72f498c825e01ee3e8229 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Tue, 3 May 2022 10:06:58 -0400 Subject: [PATCH 033/121] add css --- index.html | 5 +++-- webapp.css | 11 +++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 webapp.css diff --git a/index.html b/index.html index 6130fae..6b24c8a 100644 --- a/index.html +++ b/index.html @@ -6,17 +6,18 @@ + -

+

The Book of Quackery  -----  Quackery on Github  -----  Quackery on RosettaCode -

+
diff --git a/webapp.css b/webapp.css new file mode 100644 index 0000000..30ba53b --- /dev/null +++ b/webapp.css @@ -0,0 +1,11 @@ +header { + height: 2em; + vertical-align: middle; + margin: none; +} + +#terminal { + height: calc(100vh - 2em); + padding: none; + margin: none; +} \ No newline at end of file From 14d8c96da452534b9cf638f6ad74c7d06106f97f Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Tue, 3 May 2022 10:15:20 -0400 Subject: [PATCH 034/121] more css fix, typo in url --- webapp.css | 9 ++++++++- webapp_main.js | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/webapp.css b/webapp.css index 30ba53b..5da2e12 100644 --- a/webapp.css +++ b/webapp.css @@ -1,3 +1,10 @@ +body { + padding: none; + margin: none; + background: black; + color: white; +} + header { height: 2em; vertical-align: middle; @@ -5,7 +12,7 @@ header { } #terminal { - height: calc(100vh - 2em); + height: calc(90vh - 2em); padding: none; margin: none; } \ No newline at end of file diff --git a/webapp_main.js b/webapp_main.js index 793aef0..ef1349c 100644 --- a/webapp_main.js +++ b/webapp_main.js @@ -1,6 +1,6 @@ // modified from the Pyodide console (https://pyodide.org/en/stable/console.html), since it already uses jQuery.terminal -const ORIGIN = 'https://dragoncoder047.github.io/QuackeryFork/' +const ORIGIN = 'https://dragoncoder047.github.io/QuackeryFork' function sleep(s) { return new Promise((resolve) => setTimeout(resolve, s)); From 9b8335684c9ec87a6c475df5dc27414c57b99416 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Tue, 3 May 2022 10:16:32 -0400 Subject: [PATCH 035/121] change error message --- webapp_main.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/webapp_main.js b/webapp_main.js index ef1349c..f9d8912 100644 --- a/webapp_main.js +++ b/webapp_main.js @@ -50,9 +50,10 @@ window.addEventListener('DOMContentLoaded', async function main() { } catch (e) { term.clear(); - term.error('An error occurred loading Quackery:') - term.exception(e); + term.error('A fatal error occurred while loading Quackery.') term.error('Please report this error if it continues to occur.'); term.error('https://github.com/dragoncoder047/QuackeryFork/issues'); + term.echo(); + term.exception(e); } }); From 5a8f65c0d16e993610208d35d9ecb6948c818116 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Tue, 3 May 2022 10:53:40 -0400 Subject: [PATCH 036/121] update css --- webapp.css | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/webapp.css b/webapp.css index 5da2e12..f0a2bee 100644 --- a/webapp.css +++ b/webapp.css @@ -3,6 +3,15 @@ body { margin: none; background: black; color: white; + font-family: monospace; +} + +a:visited { + color: magenta; +} + +a { + color: white; } header { From ac4a2386428b764e603cb71fd30000352c97d69b Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Tue, 3 May 2022 10:54:50 -0400 Subject: [PATCH 037/121] whoops --- webapp_start.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp_start.py b/webapp_start.py index 4bae6c9..ee156e6 100644 --- a/webapp_start.py +++ b/webapp_start.py @@ -15,7 +15,7 @@ async def get(file): mkdir('sundry') files2 = ['cards.qky', 'demo.qky', 'fsm.qky', 'heapsort.qky'] for file in files2: - await get(f'@@ORIGIN@@/sundry/{file}') + await get(f'sundry/{file}') from quackery import quackery From 9133c961a7ec14b510d59d6adfe67a7676e05cb0 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Tue, 3 May 2022 11:06:22 -0400 Subject: [PATCH 038/121] add loading test --- webapp_main.js | 1 - webapp_start.py | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp_main.js b/webapp_main.js index f9d8912..17cad77 100644 --- a/webapp_main.js +++ b/webapp_main.js @@ -49,7 +49,6 @@ window.addEventListener('DOMContentLoaded', async function main() { term.error('Reload the page to run Quackery again.'); } catch (e) { - term.clear(); term.error('A fatal error occurred while loading Quackery.') term.error('Please report this error if it continues to occur.'); term.error('https://github.com/dragoncoder047/QuackeryFork/issues'); diff --git a/webapp_start.py b/webapp_start.py index ee156e6..ec796d2 100644 --- a/webapp_start.py +++ b/webapp_start.py @@ -5,6 +5,7 @@ async def get(file): response = await pyfetch(f"@@ORIGIN@@/{file}") with open(file, "wb") as f: + print('Loading', file) f.write(await response.bytes()) files1 = ['quackery.py', 'bigrat.qky', 'extensions.qky', 'turtleduck.qky'] From 43742fe93b2e08cbb160b2aba2c79b888e7c0cdb Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Tue, 3 May 2022 11:21:22 -0400 Subject: [PATCH 039/121] newline error --- webapp_main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp_main.js b/webapp_main.js index 17cad77..10037dc 100644 --- a/webapp_main.js +++ b/webapp_main.js @@ -20,7 +20,7 @@ window.addEventListener('DOMContentLoaded', async function main() { globalThis.pyodide = await loadPyodide({ homedir: '/home/quackery', stderr: line => term.error(line), - stdout: line => term.echo(line, { newline: false }), + stdout: line => term.echo(line), stdin: async prompt => { term.resume(); var input = await term.read(prompt); From 10c329fbb4a61f0ebe3302bab9a5ec8a29e0f728 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Tue, 3 May 2022 11:47:45 -0400 Subject: [PATCH 040/121] bump jquery.terminal version --- index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.html b/index.html index 6b24c8a..fe4b72a 100644 --- a/index.html +++ b/index.html @@ -4,8 +4,8 @@ Online Quackery Console - - + + From f51f760e783cb1c1e79a91713885472bd12842c9 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Tue, 3 May 2022 12:03:11 -0400 Subject: [PATCH 041/121] fix jsdelivr url --- index.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/index.html b/index.html index fe4b72a..2d22682 100644 --- a/index.html +++ b/index.html @@ -4,10 +4,10 @@ Online Quackery Console - - + + - + From 8d8fbff3d6573ba696b50a14ce4b003013c18b05 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Tue, 3 May 2022 12:08:01 -0400 Subject: [PATCH 042/121] fix banner --- webapp_start.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/webapp_start.py b/webapp_start.py index ec796d2..ad22379 100644 --- a/webapp_start.py +++ b/webapp_start.py @@ -21,13 +21,12 @@ async def get(file): from quackery import quackery quackery(r''' -say " - ___ _ ___ _ _ - / _ \ _ _ __ _ ___| | _____ _ __ _ _ / _ \ _ __ | (_)_ __ ___ - | | | | | | |/ _` |/ __| |/ / _ \ '__| | | | | | | | '_ \| | | '_ \ / _ \ - | |_| | |_| | (_| | (__| < __/ | | |_| | | |_| | | | | | | | | | __/ - \__\_\\__,_|\__,_|\___|_|\_\___|_| \__, | \___/|_| |_|_|_|_| |_|\___| - |___/" cr +say " ___ _ ___ _ _" cr +say " / _ \ _ _ __ _ ___| | _____ _ __ _ _ / _ \ _ __ | (_)_ __ ___" cr +say " | | | | | | |/ _` |/ __| |/ / _ \ '__| | | | | | | | '_ \| | | '_ \ / _ \" cr +say " | |_| | |_| | (_| | (__| < __/ | | |_| | | |_| | | | | | | | | | __/" cr +say " \__\_\\__,_|\__,_|\___|_|\_\___|_| \__, | \___/|_| |_|_|_|_| |_|\___|" cr +say " |___/" cr say "Welcome to Quackery running on the Pyodide virtual machine." cr $ 'extensions.qky' dup name? not From 485dbd5ff36bb545479d1c62294b75ba36ec2316 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Tue, 3 May 2022 12:12:29 -0400 Subject: [PATCH 043/121] add disable after an error, satisfy \r\n --- quackery.py | 2 +- webapp_main.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/quackery.py b/quackery.py index e551585..bc5a60a 100644 --- a/quackery.py +++ b/quackery.py @@ -538,7 +538,7 @@ def qemit(ctx): ctx.expect_number() char = ctx.from_stack() if char == 13: - sys.stdout.write('\n') + sys.stdout.write('\r\n') elif 31 < char < 127: sys.stdout.write(chr(char)) else: diff --git a/webapp_main.js b/webapp_main.js index 10037dc..9c9c947 100644 --- a/webapp_main.js +++ b/webapp_main.js @@ -54,5 +54,6 @@ window.addEventListener('DOMContentLoaded', async function main() { term.error('https://github.com/dragoncoder047/QuackeryFork/issues'); term.echo(); term.exception(e); + term.disable(); } }); From 4ec473a8033244f121af1e9ad118521720cafcc4 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Tue, 3 May 2022 12:16:14 -0400 Subject: [PATCH 044/121] fix banner once and for all --- webapp_start.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/webapp_start.py b/webapp_start.py index ad22379..d9e5dde 100644 --- a/webapp_start.py +++ b/webapp_start.py @@ -19,19 +19,23 @@ async def get(file): await get(f'sundry/{file}') from quackery import quackery +print(r''' + ___ _ ___ _ _ + / _ \ _ _ __ _ ___| | _____ _ __ _ _ / _ \ _ __ | (_)_ __ ___ + | | | | | | |/ _` |/ __| |/ / _ \ '__| | | | | | | | '_ \| | | '_ \ / _ \ + | |_| | |_| | (_| | (__| < __/ | | |_| | | |_| | | | | | | | | | __/ + \__\_\\__,_|\__,_|\___|_|\_\___|_| \__, | \___/|_| |_|_|_|_| |_|\___| + |___/ + Welcome to Quackery running on the Pyodide virtual machine. +''') quackery(r''' -say " ___ _ ___ _ _" cr -say " / _ \ _ _ __ _ ___| | _____ _ __ _ _ / _ \ _ __ | (_)_ __ ___" cr -say " | | | | | | |/ _` |/ __| |/ / _ \ '__| | | | | | | | '_ \| | | '_ \ / _ \" cr -say " | |_| | |_| | (_| | (__| < __/ | | |_| | | |_| | | | | | | | | | __/" cr -say " \__\_\\__,_|\__,_|\___|_|\_\___|_| \__, | \___/|_| |_|_|_|_| |_|\___|" cr -say " |___/" cr -say "Welcome to Quackery running on the Pyodide virtual machine." cr $ 'extensions.qky' dup name? not dip sharefile and iff [ cr say 'Building extensions.' cr quackery ] else drop -shell''') \ No newline at end of file +shell + +''') \ No newline at end of file From 92b3cae7c342fbef99f41663bffb843a8e898536 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Tue, 3 May 2022 12:41:04 -0400 Subject: [PATCH 045/121] blocking I/O --- webapp_main.js | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/webapp_main.js b/webapp_main.js index 9c9c947..c804e7d 100644 --- a/webapp_main.js +++ b/webapp_main.js @@ -8,7 +8,11 @@ function sleep(s) { window.addEventListener('DOMContentLoaded', async function main() { - var term = $("#terminal").terminal(_ => { }, { + var stdin_queue = []; + var term = $("#terminal").terminal(input => { + var lines = input.split('\n'); + lines.forEach(line => stdin_queue.push(line)); + }, { greetings: '', prompt: '', completionEscape: false, @@ -21,12 +25,12 @@ window.addEventListener('DOMContentLoaded', async function main() { homedir: '/home/quackery', stderr: line => term.error(line), stdout: line => term.echo(line), - stdin: async prompt => { + stdin: prompt => { term.resume(); - var input = await term.read(prompt); - term.pause(); - await sleep(10); - return input; + term.set_prompt(prompt); + var line; + while ((line = stdin_queue.splice(1, 1)[0]) === undefined) /*noop*/; + return line; }, }); @@ -54,6 +58,6 @@ window.addEventListener('DOMContentLoaded', async function main() { term.error('https://github.com/dragoncoder047/QuackeryFork/issues'); term.echo(); term.exception(e); - term.disable(); + term.pause(); } }); From 3e2d67f10292b14cd7efbf6ccb5b3ad21f0cf334 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Tue, 3 May 2022 12:41:22 -0400 Subject: [PATCH 046/121] oops, missed re-pause --- webapp_main.js | 1 + 1 file changed, 1 insertion(+) diff --git a/webapp_main.js b/webapp_main.js index c804e7d..be73e41 100644 --- a/webapp_main.js +++ b/webapp_main.js @@ -30,6 +30,7 @@ window.addEventListener('DOMContentLoaded', async function main() { term.set_prompt(prompt); var line; while ((line = stdin_queue.splice(1, 1)[0]) === undefined) /*noop*/; + term.pause(); return line; }, }); From cb683df34882ec17d5e4a28e7d80ab629039fe24 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Tue, 3 May 2022 12:45:49 -0400 Subject: [PATCH 047/121] sleep in busy loop --- webapp_main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp_main.js b/webapp_main.js index be73e41..e3177a1 100644 --- a/webapp_main.js +++ b/webapp_main.js @@ -29,7 +29,7 @@ window.addEventListener('DOMContentLoaded', async function main() { term.resume(); term.set_prompt(prompt); var line; - while ((line = stdin_queue.splice(1, 1)[0]) === undefined) /*noop*/; + while ((line = stdin_queue.splice(1, 1)[0]) === undefined) await sleep(10); term.pause(); return line; }, From ac45ea8fc1927a1c4f401138da232418b7d5fac6 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Tue, 3 May 2022 12:46:22 -0400 Subject: [PATCH 048/121] oops, can't use await in non-async function --- webapp_main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp_main.js b/webapp_main.js index e3177a1..be73e41 100644 --- a/webapp_main.js +++ b/webapp_main.js @@ -29,7 +29,7 @@ window.addEventListener('DOMContentLoaded', async function main() { term.resume(); term.set_prompt(prompt); var line; - while ((line = stdin_queue.splice(1, 1)[0]) === undefined) await sleep(10); + while ((line = stdin_queue.splice(1, 1)[0]) === undefined) /*noop*/; term.pause(); return line; }, From c4dcf3cec6ca7eaa88b7c18e98c905658211226d Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Tue, 3 May 2022 12:53:36 -0400 Subject: [PATCH 049/121] go back to async --- webapp_main.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/webapp_main.js b/webapp_main.js index be73e41..a65d932 100644 --- a/webapp_main.js +++ b/webapp_main.js @@ -25,13 +25,12 @@ window.addEventListener('DOMContentLoaded', async function main() { homedir: '/home/quackery', stderr: line => term.error(line), stdout: line => term.echo(line), - stdin: prompt => { + stdin: async prompt => { term.resume(); - term.set_prompt(prompt); - var line; - while ((line = stdin_queue.splice(1, 1)[0]) === undefined) /*noop*/; + var input = await term.read(prompt); term.pause(); - return line; + await sleep(10); + return input; }, }); From e7a48ca85998c8dfba0827c2a44af5bde8a58873 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Tue, 3 May 2022 13:22:31 -0400 Subject: [PATCH 050/121] add pythonanywhere paste code --- webapp_main.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/webapp_main.js b/webapp_main.js index a65d932..88b29f9 100644 --- a/webapp_main.js +++ b/webapp_main.js @@ -58,6 +58,15 @@ window.addEventListener('DOMContentLoaded', async function main() { term.error('https://github.com/dragoncoder047/QuackeryFork/issues'); term.echo(); term.exception(e); + term.echo('Until this problem is resolved, to run Quackery you can go to'); + term.echo('https://www.pythonanywhere.com/embedded3/ and paste in this code:') + term.echo(); + term.echo('from requests import get'); + term.echo('def load(url):'); + term.echo(' c = compile(get(url).text, url, \'exec\''); + term.echo(' exec(c, globals(), globals())'); + term.echo('load(\'https://raw.githubusercontent.com/GordonCharlton/Quackery/main/quackery.py\')'); + term.echo(); term.pause(); } }); From 3d816811035d7af77939a33c65269bbfd3179673 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Tue, 3 May 2022 13:26:03 -0400 Subject: [PATCH 051/121] fix error message --- webapp_main.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/webapp_main.js b/webapp_main.js index 88b29f9..5b2d85a 100644 --- a/webapp_main.js +++ b/webapp_main.js @@ -58,8 +58,9 @@ window.addEventListener('DOMContentLoaded', async function main() { term.error('https://github.com/dragoncoder047/QuackeryFork/issues'); term.echo(); term.exception(e); - term.echo('Until this problem is resolved, to run Quackery you can go to'); - term.echo('https://www.pythonanywhere.com/embedded3/ and paste in this code:') + term.echo(); + term.echo(' Until this problem is resolved, to run Quackery you can go to'); + term.echo(' https://www.pythonanywhere.com/embedded3/ and paste in this code:') term.echo(); term.echo('from requests import get'); term.echo('def load(url):'); From 3122b7088969479f007ef2143cea32aba4103920 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Tue, 3 May 2022 13:35:51 -0400 Subject: [PATCH 052/121] change homedir --- webapp_main.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/webapp_main.js b/webapp_main.js index 5b2d85a..cfb8ad6 100644 --- a/webapp_main.js +++ b/webapp_main.js @@ -22,7 +22,7 @@ window.addEventListener('DOMContentLoaded', async function main() { window.term = term; try { globalThis.pyodide = await loadPyodide({ - homedir: '/home/quackery', + homedir: '/Pyodide_VFS/quackery', stderr: line => term.error(line), stdout: line => term.echo(line), stdin: async prompt => { @@ -59,8 +59,8 @@ window.addEventListener('DOMContentLoaded', async function main() { term.echo(); term.exception(e); term.echo(); - term.echo(' Until this problem is resolved, to run Quackery you can go to'); - term.echo(' https://www.pythonanywhere.com/embedded3/ and paste in this code:') + term.echo('Until this problem is resolved, to run Quackery you can go to'); + term.echo('https://www.pythonanywhere.com/embedded3/ and paste in this code:') term.echo(); term.echo('from requests import get'); term.echo('def load(url):'); From d81c1dc77ec1c7347b4c5bb62aeb7af46e1ee77d Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Tue, 3 May 2022 13:36:23 -0400 Subject: [PATCH 053/121] typo --- webapp_main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp_main.js b/webapp_main.js index cfb8ad6..abc2610 100644 --- a/webapp_main.js +++ b/webapp_main.js @@ -64,7 +64,7 @@ window.addEventListener('DOMContentLoaded', async function main() { term.echo(); term.echo('from requests import get'); term.echo('def load(url):'); - term.echo(' c = compile(get(url).text, url, \'exec\''); + term.echo(' c = compile(get(url).text, url, \'exec\')'); term.echo(' exec(c, globals(), globals())'); term.echo('load(\'https://raw.githubusercontent.com/GordonCharlton/Quackery/main/quackery.py\')'); term.echo(); From 00f54a3c526fe22f3f7f4732ef7218e16d1a8b8f Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Tue, 3 May 2022 13:54:41 -0400 Subject: [PATCH 054/121] fix css (render glitch) --- webapp.css | 3 ++- webapp_main.js | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/webapp.css b/webapp.css index f0a2bee..6deb211 100644 --- a/webapp.css +++ b/webapp.css @@ -4,6 +4,7 @@ body { background: black; color: white; font-family: monospace; + font-size: 1.5em; } a:visited { @@ -15,7 +16,7 @@ a { } header { - height: 2em; + height: 3em; vertical-align: middle; margin: none; } diff --git a/webapp_main.js b/webapp_main.js index abc2610..413f6ba 100644 --- a/webapp_main.js +++ b/webapp_main.js @@ -47,7 +47,6 @@ window.addEventListener('DOMContentLoaded', async function main() { var resp = await fetch('webapp_start.py'); var py = await resp.text(); - await pyodide.runPythonAsync(py.replaceAll('@@ORIGIN@@', ORIGIN)); term.error('Reload the page to run Quackery again.'); From 3880d2ed3b4242b76eb90b5cdd86904e5fb43672 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Tue, 3 May 2022 13:59:34 -0400 Subject: [PATCH 055/121] fix css in terminal --- webapp.css | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/webapp.css b/webapp.css index 6deb211..c4c5a7a 100644 --- a/webapp.css +++ b/webapp.css @@ -2,17 +2,25 @@ body { padding: none; margin: none; background: black; - color: white; + color: white; font-family: monospace; font-size: 1.5em; } +.terminal, +.cmd, +.terminal .terminal-output div div, +.cmd .prompt { + font-size: 1.5em; + line-height: 1.6em; +} + a:visited { - color: magenta; + color: white; } a { - color: white; + color: blue; } header { From 18119edb7a19d0155fb848e3303712d825384259 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Tue, 3 May 2022 14:01:58 -0400 Subject: [PATCH 056/121] css fix --- webapp.css | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/webapp.css b/webapp.css index c4c5a7a..91f9a5c 100644 --- a/webapp.css +++ b/webapp.css @@ -7,12 +7,8 @@ body { font-size: 1.5em; } -.terminal, -.cmd, -.terminal .terminal-output div div, -.cmd .prompt { - font-size: 1.5em; - line-height: 1.6em; +.terminal { + --size: 1.5; } a:visited { From 2623e9aff6b4ea524358f965477def82fd58c89e Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Tue, 3 May 2022 14:10:03 -0400 Subject: [PATCH 057/121] fix css again --- webapp.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webapp.css b/webapp.css index 91f9a5c..7687342 100644 --- a/webapp.css +++ b/webapp.css @@ -4,7 +4,6 @@ body { background: black; color: white; font-family: monospace; - font-size: 1.5em; } .terminal { @@ -20,9 +19,10 @@ a { } header { - height: 3em; + height: 2em; vertical-align: middle; margin: none; + font-size: 1.5em; } #terminal { From 61b075f1b45bfdfea43c1b21457efff1ea5f25b6 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Wed, 4 May 2022 09:11:19 -0400 Subject: [PATCH 058/121] try add service worker --- webapp_main.js | 61 +++++++++++++++++++++++++++++++++++--------------- webapp_sw.js | 40 +++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 18 deletions(-) create mode 100644 webapp_sw.js diff --git a/webapp_main.js b/webapp_main.js index 413f6ba..db2d9d0 100644 --- a/webapp_main.js +++ b/webapp_main.js @@ -6,9 +6,28 @@ function sleep(s) { return new Promise((resolve) => setTimeout(resolve, s)); } +var supportsSAB = true; + +function blockUntilResolved(promise) { + var sab; + try { + sab = Int32Array.from(new SharedArrayBuffer(1)); + } catch (e) { + supportsSAB = false; + } + var result; + promise.then(val => { + result = val; + Atomics.notify(sab, 0); + }) + Atomics.wait(sab, 0, 0); + return result; +} + window.addEventListener('DOMContentLoaded', async function main() { - var stdin_queue = []; + navigator.serviceWorker.register(`${ORIGIN}/webapp_sw.js`, { scope: ORIGIN, }); + var term = $("#terminal").terminal(input => { var lines = input.split('\n'); lines.forEach(line => stdin_queue.push(line)); @@ -25,11 +44,10 @@ window.addEventListener('DOMContentLoaded', async function main() { homedir: '/Pyodide_VFS/quackery', stderr: line => term.error(line), stdout: line => term.echo(line), - stdin: async prompt => { + stdin: prompt => { term.resume(); - var input = await term.read(prompt); + var input = blockUntilResolved(term.read(prompt)); term.pause(); - await sleep(10); return input; }, }); @@ -52,20 +70,27 @@ window.addEventListener('DOMContentLoaded', async function main() { term.error('Reload the page to run Quackery again.'); } catch (e) { - term.error('A fatal error occurred while loading Quackery.') - term.error('Please report this error if it continues to occur.'); - term.error('https://github.com/dragoncoder047/QuackeryFork/issues'); - term.echo(); - term.exception(e); - term.echo(); - term.echo('Until this problem is resolved, to run Quackery you can go to'); - term.echo('https://www.pythonanywhere.com/embedded3/ and paste in this code:') - term.echo(); - term.echo('from requests import get'); - term.echo('def load(url):'); - term.echo(' c = compile(get(url).text, url, \'exec\')'); - term.echo(' exec(c, globals(), globals())'); - term.echo('load(\'https://raw.githubusercontent.com/GordonCharlton/Quackery/main/quackery.py\')'); + if (supportsSAB) { + term.error('A fatal error occurred while loading Quackery.') + term.error('Please report this error if it continues to occur.'); + term.error('https://github.com/dragoncoder047/QuackeryFork/issues'); + term.echo(); + term.exception(e); + term.echo(); + term.echo('Until this problem is resolved, to run Quackery you can go to'); + term.echo('https://www.pythonanywhere.com/embedded3/ and paste in this code:') + term.echo(); + term.echo('from requests import get'); + term.echo('def load(url):'); + term.echo(' c = compile(get(url).text, url, \'exec\')'); + term.echo(' exec(c, globals(), globals())'); + term.echo('load(\'https://raw.githubusercontent.com/GordonCharlton/Quackery/main/quackery.py\')'); + } + else { + term.error('Something went wrong loading Quackery. Please reload the page and try again.') + term.error('If the problem persists, it probably means your browser doesn\'t support'); + term.error('the features needed to run Quackery. Sorry about that.'); + } term.echo(); term.pause(); } diff --git a/webapp_sw.js b/webapp_sw.js new file mode 100644 index 0000000..6011a7c --- /dev/null +++ b/webapp_sw.js @@ -0,0 +1,40 @@ +// modified from https://stackoverflow.com/questions/70455869/how-to-enable-sharedarraybuffer-in-microsoft-edge-javascript + +self.addEventListener("install", e => { + self.skipWaiting(); + console.log('[service worker] installed!') +}); + +self.addEventListener("activate", e => { + e.waitUntil(self.clients.claim()); + console.log('[service worker] activated!') +}); + +self.addEventListener("fetch", e => { + if (e.request.cache === "only-if-cached" && e.request.mode !== "same-origin") { + return; + } + console.log(`[service worker] fudging request to ${e.request.url} !`) + + e.respondWith( + fetch(e.request).then(response => { + // It seems like we only need to set the headers for index.html + // If you want to be on the safe side, comment this out + // if (!response.url.includes("index.html")) return response; + + const newHeaders = new Headers(response.headers); + newHeaders.set("Cross-Origin-Embedder-Policy", "require-corp"); + newHeaders.set("Cross-Origin-Opener-Policy", "same-origin"); + + const fudgedResponse = new Response(response.body, { + status: response.status, + statusText: response.statusText, + headers: newHeaders, + }); + console.log(`[service worker] fudging done!`) + return fudgedResponse; + }).catch(function (e) { + console.error(e); + }) + ); +}); \ No newline at end of file From eabbf93e969c7527829352958e132965258c9843 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Wed, 4 May 2022 09:11:55 -0400 Subject: [PATCH 059/121] forgot to rethrow exception --- webapp_main.js | 1 + 1 file changed, 1 insertion(+) diff --git a/webapp_main.js b/webapp_main.js index db2d9d0..8f288b2 100644 --- a/webapp_main.js +++ b/webapp_main.js @@ -14,6 +14,7 @@ function blockUntilResolved(promise) { sab = Int32Array.from(new SharedArrayBuffer(1)); } catch (e) { supportsSAB = false; + throw e; } var result; promise.then(val => { From a8f916a936814e5c91cd279e2200f12acb473066 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Wed, 4 May 2022 09:30:20 -0400 Subject: [PATCH 060/121] add service worker error message --- webapp_main.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/webapp_main.js b/webapp_main.js index 8f288b2..ca3785a 100644 --- a/webapp_main.js +++ b/webapp_main.js @@ -27,7 +27,6 @@ function blockUntilResolved(promise) { window.addEventListener('DOMContentLoaded', async function main() { - navigator.serviceWorker.register(`${ORIGIN}/webapp_sw.js`, { scope: ORIGIN, }); var term = $("#terminal").terminal(input => { var lines = input.split('\n'); @@ -40,6 +39,11 @@ window.addEventListener('DOMContentLoaded', async function main() { }); term.pause(); window.term = term; + try { + navigator.serviceWorker.register(`${ORIGIN}/webapp_sw.js`, { scope: ORIGIN, }); + } catch (e) { + term.error('Could not register service worker.'); + } try { globalThis.pyodide = await loadPyodide({ homedir: '/Pyodide_VFS/quackery', From 776d659af8a6ae20147c70c80314f45ac6397ef7 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Wed, 4 May 2022 09:30:41 -0400 Subject: [PATCH 061/121] stop after service worker error --- webapp_main.js | 1 + 1 file changed, 1 insertion(+) diff --git a/webapp_main.js b/webapp_main.js index ca3785a..1d117e3 100644 --- a/webapp_main.js +++ b/webapp_main.js @@ -43,6 +43,7 @@ window.addEventListener('DOMContentLoaded', async function main() { navigator.serviceWorker.register(`${ORIGIN}/webapp_sw.js`, { scope: ORIGIN, }); } catch (e) { term.error('Could not register service worker.'); + return; } try { globalThis.pyodide = await loadPyodide({ From 885ceea0da2380d7867b2cd39762fc7c901d575b Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Wed, 4 May 2022 09:31:36 -0400 Subject: [PATCH 062/121] rethrow all exceptions --- webapp_main.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/webapp_main.js b/webapp_main.js index 1d117e3..8772120 100644 --- a/webapp_main.js +++ b/webapp_main.js @@ -43,7 +43,8 @@ window.addEventListener('DOMContentLoaded', async function main() { navigator.serviceWorker.register(`${ORIGIN}/webapp_sw.js`, { scope: ORIGIN, }); } catch (e) { term.error('Could not register service worker.'); - return; + term.exception(e); + throw e; } try { globalThis.pyodide = await loadPyodide({ @@ -99,5 +100,6 @@ window.addEventListener('DOMContentLoaded', async function main() { } term.echo(); term.pause(); + throw e; } }); From 8079b8d81d6ad3a60f9bbc6e9fb9bbf301fe9379 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Wed, 4 May 2022 09:43:30 -0400 Subject: [PATCH 063/121] add status message --- webapp_main.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/webapp_main.js b/webapp_main.js index 8772120..bb6a7c6 100644 --- a/webapp_main.js +++ b/webapp_main.js @@ -41,6 +41,7 @@ window.addEventListener('DOMContentLoaded', async function main() { window.term = term; try { navigator.serviceWorker.register(`${ORIGIN}/webapp_sw.js`, { scope: ORIGIN, }); + term.echo('Service worker registered'); } catch (e) { term.error('Could not register service worker.'); term.exception(e); @@ -48,7 +49,7 @@ window.addEventListener('DOMContentLoaded', async function main() { } try { globalThis.pyodide = await loadPyodide({ - homedir: '/Pyodide_VFS/quackery', + homedir: '/quackery', stderr: line => term.error(line), stdout: line => term.echo(line), stdin: prompt => { From 6e240a5c34c7168d1cbbc1ef746736bb15611e39 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Wed, 4 May 2022 09:49:47 -0400 Subject: [PATCH 064/121] await service worker register --- webapp_main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp_main.js b/webapp_main.js index bb6a7c6..843e101 100644 --- a/webapp_main.js +++ b/webapp_main.js @@ -40,7 +40,7 @@ window.addEventListener('DOMContentLoaded', async function main() { term.pause(); window.term = term; try { - navigator.serviceWorker.register(`${ORIGIN}/webapp_sw.js`, { scope: ORIGIN, }); + await navigator.serviceWorker.register(`${ORIGIN}/webapp_sw.js`); term.echo('Service worker registered'); } catch (e) { term.error('Could not register service worker.'); From 39c78eeae6c99127dabea27bd23c0f19646414e7 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Wed, 4 May 2022 10:37:25 -0400 Subject: [PATCH 065/121] testing 123 --- webapp_main.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webapp_main.js b/webapp_main.js index 843e101..c5cef8c 100644 --- a/webapp_main.js +++ b/webapp_main.js @@ -40,7 +40,7 @@ window.addEventListener('DOMContentLoaded', async function main() { term.pause(); window.term = term; try { - await navigator.serviceWorker.register(`${ORIGIN}/webapp_sw.js`); + await navigator.serviceWorker.register('webapp_sw.js'); term.echo('Service worker registered'); } catch (e) { term.error('Could not register service worker.'); @@ -78,7 +78,7 @@ window.addEventListener('DOMContentLoaded', async function main() { term.error('Reload the page to run Quackery again.'); } catch (e) { - if (supportsSAB) { + if (supportsSAB && false) { term.error('A fatal error occurred while loading Quackery.') term.error('Please report this error if it continues to occur.'); term.error('https://github.com/dragoncoder047/QuackeryFork/issues'); From a5f0e8cb9785aaebed70983ed8045c4ed8875416 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Wed, 4 May 2022 10:40:05 -0400 Subject: [PATCH 066/121] oops, wrong branch breaking --- webapp_main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp_main.js b/webapp_main.js index c5cef8c..1715e4c 100644 --- a/webapp_main.js +++ b/webapp_main.js @@ -78,7 +78,7 @@ window.addEventListener('DOMContentLoaded', async function main() { term.error('Reload the page to run Quackery again.'); } catch (e) { - if (supportsSAB && false) { + if (supportsSAB || true) { term.error('A fatal error occurred while loading Quackery.') term.error('Please report this error if it continues to occur.'); term.error('https://github.com/dragoncoder047/QuackeryFork/issues'); From 56cbda3bce1139fd13b8a5537f8b050a96b2dab5 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Wed, 11 May 2022 10:49:43 -0400 Subject: [PATCH 067/121] try to automatically make everything async --- webapp_main.js | 34 +--------------- webapp_start.py | 105 +++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 92 insertions(+), 47 deletions(-) diff --git a/webapp_main.js b/webapp_main.js index 1715e4c..17378e7 100644 --- a/webapp_main.js +++ b/webapp_main.js @@ -6,25 +6,6 @@ function sleep(s) { return new Promise((resolve) => setTimeout(resolve, s)); } -var supportsSAB = true; - -function blockUntilResolved(promise) { - var sab; - try { - sab = Int32Array.from(new SharedArrayBuffer(1)); - } catch (e) { - supportsSAB = false; - throw e; - } - var result; - promise.then(val => { - result = val; - Atomics.notify(sab, 0); - }) - Atomics.wait(sab, 0, 0); - return result; -} - window.addEventListener('DOMContentLoaded', async function main() { @@ -39,25 +20,12 @@ window.addEventListener('DOMContentLoaded', async function main() { }); term.pause(); window.term = term; - try { - await navigator.serviceWorker.register('webapp_sw.js'); - term.echo('Service worker registered'); - } catch (e) { - term.error('Could not register service worker.'); - term.exception(e); - throw e; - } try { globalThis.pyodide = await loadPyodide({ homedir: '/quackery', stderr: line => term.error(line), stdout: line => term.echo(line), - stdin: prompt => { - term.resume(); - var input = blockUntilResolved(term.read(prompt)); - term.pause(); - return input; - }, + stdin: window.prompt, }); diff --git a/webapp_start.py b/webapp_start.py index d9e5dde..d475a78 100644 --- a/webapp_start.py +++ b/webapp_start.py @@ -2,21 +2,98 @@ from pyodide.http import pyfetch from os import mkdir -async def get(file): - response = await pyfetch(f"@@ORIGIN@@/{file}") - with open(file, "wb") as f: - print('Loading', file) - f.write(await response.bytes()) - -files1 = ['quackery.py', 'bigrat.qky', 'extensions.qky', 'turtleduck.qky'] -for file in files1: - # N. B. top-level await is only allowed in Pyodide - await get(file) +import ast +import js mkdir('sundry') -files2 = ['cards.qky', 'demo.qky', 'fsm.qky', 'heapsort.qky'] -for file in files2: - await get(f'sundry/{file}') +files = ['bigrat', 'extensions', 'turtleduck', 'sundty/cards', 'sundry/demo', 'sundry/fsm', 'sundry/heapsort'] + +for file in files: + # N. B. top-level await is only allowed in Pyodide + resp = await pyfetch(f'@@ORIGIN@@/{file}.qky') + print(f'Downloading {file}.qky ...') + text = await resp.bytes() + with open(f'{file}.qky', 'wb') as f: f.write(text) + +resp = await pyfetch('@@ORIGIN@@/quackery.py') +quackerytext = (await resp.bytes()).decode('utf8') + +# PATCH - make functions async + +def has_await(node): + for subnode in ast.walk(node): + if isinstance(subnode, ast.Await): return True + return False + +class FixFirst(ast.NodeTransformer): + def visit_Call(self, node): + name = node.func.id + if name == 'input' or name = 'current_item': + print('\tAwaiting function', name) + if name == 'input': + print('\tRenaming input') + node.func.id = 'async_patched_input' + return ast.Await(node) + +changed = False +asynced_functions = ['async_patched_input', 'current_item'] +class MakeFunctionAsyncValid(ast.NodeTransformer): + def visit_FunctionDef(self, node): + name = node.func.id + if has_await(node): + print('\tFound bad function', name, '> fixing') + return ast.AsyncFunctionDef( + name=node.name, + args=node.args, + body=node.body, + decorator_list=node.decorator_list + ) + asynced_functions.append(name) + changed = True + else: + return node + +class ApplyAwaitsToAsyncedFunctions(ast.NodeTransformer): + def visit_Call(self, node): + if instanceof(node.func, ast.Attribute): + name = node.func.attr + else: + name = node.func.id + if node in asynced_functions: + print('\tNow awaiting call of', name) + return ast.Await(node) + else: + return node + +tree = ast.parse(quackerytext) + +fixed_tree = FixFirst().visit(tree) + +a = MakeFunctionAsyncValid() +b = ApplyAwaitsToAsyncedFunctions() + +while True: + changed = False + fixed_tree = b.visit(a.visit(fixed_tree)) + if changed is False: + break + +fixedquackerytext = f''' + +import js + +async def async_patched_input(prompt): + term = js.term + term.resume() + result = await term.read(prompt) + term.pause() + return result + +{ast.unparse(fixed_tree)}''' + +with open('quackery.py', 'w') as f: f.write(fixedquackerytext) + +js.term.clear() from quackery import quackery print(r''' @@ -29,7 +106,7 @@ async def get(file): Welcome to Quackery running on the Pyodide virtual machine. ''') -quackery(r''' +await quackery(r''' $ 'extensions.qky' dup name? not dip sharefile and iff From d43c40837f7b5bd560d7544acf5ab44b90e6fdf8 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Wed, 11 May 2022 10:51:40 -0400 Subject: [PATCH 068/121] new load methods --- webapp_start.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/webapp_start.py b/webapp_start.py index d475a78..36dda24 100644 --- a/webapp_start.py +++ b/webapp_start.py @@ -12,11 +12,11 @@ # N. B. top-level await is only allowed in Pyodide resp = await pyfetch(f'@@ORIGIN@@/{file}.qky') print(f'Downloading {file}.qky ...') - text = await resp.bytes() - with open(f'{file}.qky', 'wb') as f: f.write(text) + text = await resp.string() + with open(f'{file}.qky', 'w') as f: f.write(text) resp = await pyfetch('@@ORIGIN@@/quackery.py') -quackerytext = (await resp.bytes()).decode('utf8') +quackerytext = await resp.string() # PATCH - make functions async From 1cc61ac81361e3720b0df5aab154818715bdb4d6 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Wed, 11 May 2022 11:03:02 -0400 Subject: [PATCH 069/121] add loading message --- webapp_main.js | 6 ++++-- webapp_start.py | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/webapp_main.js b/webapp_main.js index 17378e7..ca302e1 100644 --- a/webapp_main.js +++ b/webapp_main.js @@ -20,11 +20,13 @@ window.addEventListener('DOMContentLoaded', async function main() { }); term.pause(); window.term = term; + term.echo('Quackery (and Python) are loading...'); + var c = setTimeout(() => term.echo('this may take a while...'), 5000); try { globalThis.pyodide = await loadPyodide({ homedir: '/quackery', - stderr: line => term.error(line), - stdout: line => term.echo(line), + stderr: line => { clearTimeout(c); term.error(line) }, + stdout: line => { clearTimeout(c); term.echo(line) }, stdin: window.prompt, }); diff --git a/webapp_start.py b/webapp_start.py index 36dda24..4265e80 100644 --- a/webapp_start.py +++ b/webapp_start.py @@ -93,7 +93,7 @@ async def async_patched_input(prompt): with open('quackery.py', 'w') as f: f.write(fixedquackerytext) -js.term.clear() +#js.term.clear() from quackery import quackery print(r''' From cf29fdbfb1e7cbc8f2e2d545d8d6e3b04d8bbe60 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Wed, 11 May 2022 11:09:32 -0400 Subject: [PATCH 070/121] fix error message --- webapp_main.js | 37 +++++++++++++++---------------------- webapp_start.py | 2 +- 2 files changed, 16 insertions(+), 23 deletions(-) diff --git a/webapp_main.js b/webapp_main.js index ca302e1..807f57d 100644 --- a/webapp_main.js +++ b/webapp_main.js @@ -21,7 +21,7 @@ window.addEventListener('DOMContentLoaded', async function main() { term.pause(); window.term = term; term.echo('Quackery (and Python) are loading...'); - var c = setTimeout(() => term.echo('this may take a while...'), 5000); + var c = setTimeout(() => term.echo('this may take a while...'), 10000); try { globalThis.pyodide = await loadPyodide({ homedir: '/quackery', @@ -48,27 +48,20 @@ window.addEventListener('DOMContentLoaded', async function main() { term.error('Reload the page to run Quackery again.'); } catch (e) { - if (supportsSAB || true) { - term.error('A fatal error occurred while loading Quackery.') - term.error('Please report this error if it continues to occur.'); - term.error('https://github.com/dragoncoder047/QuackeryFork/issues'); - term.echo(); - term.exception(e); - term.echo(); - term.echo('Until this problem is resolved, to run Quackery you can go to'); - term.echo('https://www.pythonanywhere.com/embedded3/ and paste in this code:') - term.echo(); - term.echo('from requests import get'); - term.echo('def load(url):'); - term.echo(' c = compile(get(url).text, url, \'exec\')'); - term.echo(' exec(c, globals(), globals())'); - term.echo('load(\'https://raw.githubusercontent.com/GordonCharlton/Quackery/main/quackery.py\')'); - } - else { - term.error('Something went wrong loading Quackery. Please reload the page and try again.') - term.error('If the problem persists, it probably means your browser doesn\'t support'); - term.error('the features needed to run Quackery. Sorry about that.'); - } + term.error('A fatal error occurred while loading Quackery.') + term.error('Please report this error if it continues to occur.'); + term.error('https://github.com/dragoncoder047/QuackeryFork/issues'); + term.echo(); + term.exception(e); + term.echo(); + term.echo('Until this problem is resolved, to run Quackery you can go to'); + term.echo('https://www.pythonanywhere.com/embedded3/ and paste in this code:') + term.echo(); + term.echo('from requests import get'); + term.echo('def load(url):'); + term.echo(' c = compile(get(url).text, url, \'exec\')'); + term.echo(' exec(c, globals(), globals())'); + term.echo('load(\'https://raw.githubusercontent.com/GordonCharlton/Quackery/main/quackery.py\')'); term.echo(); term.pause(); throw e; diff --git a/webapp_start.py b/webapp_start.py index 4265e80..fdb0c6a 100644 --- a/webapp_start.py +++ b/webapp_start.py @@ -110,7 +110,7 @@ async def async_patched_input(prompt): $ 'extensions.qky' dup name? not dip sharefile and iff - [ cr say 'Building extensions.' cr quackery ] + [ cr say 'Building extensions...' cr quackery ] else drop shell From e1d3d5fd26265b53695e01d262bc238ea6f6a358 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Wed, 11 May 2022 11:11:13 -0400 Subject: [PATCH 071/121] syntax error --- webapp_start.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp_start.py b/webapp_start.py index fdb0c6a..f035550 100644 --- a/webapp_start.py +++ b/webapp_start.py @@ -28,7 +28,7 @@ def has_await(node): class FixFirst(ast.NodeTransformer): def visit_Call(self, node): name = node.func.id - if name == 'input' or name = 'current_item': + if name == 'input' or name == 'current_item': print('\tAwaiting function', name) if name == 'input': print('\tRenaming input') From a61e361a04a845abb513b54353419ab0db0b9202 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Wed, 11 May 2022 11:15:56 -0400 Subject: [PATCH 072/121] yet another typo --- webapp_start.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp_start.py b/webapp_start.py index f035550..9144ae6 100644 --- a/webapp_start.py +++ b/webapp_start.py @@ -6,7 +6,7 @@ import js mkdir('sundry') -files = ['bigrat', 'extensions', 'turtleduck', 'sundty/cards', 'sundry/demo', 'sundry/fsm', 'sundry/heapsort'] +files = ['bigrat', 'extensions', 'turtleduck', 'sundry/cards', 'sundry/demo', 'sundry/fsm', 'sundry/heapsort'] for file in files: # N. B. top-level await is only allowed in Pyodide From 21aab2a5b57622ae4dbe1191e2a889d2c3270e2b Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Wed, 11 May 2022 11:55:19 -0400 Subject: [PATCH 073/121] fixed `AttributeError` --- webapp_start.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/webapp_start.py b/webapp_start.py index 9144ae6..d8433c0 100644 --- a/webapp_start.py +++ b/webapp_start.py @@ -27,7 +27,10 @@ def has_await(node): class FixFirst(ast.NodeTransformer): def visit_Call(self, node): - name = node.func.id + if isinstance(node.func, ast.Attribute): + name = node.func.attr + else: + name = node.func.id if name == 'input' or name == 'current_item': print('\tAwaiting function', name) if name == 'input': @@ -55,7 +58,7 @@ def visit_FunctionDef(self, node): class ApplyAwaitsToAsyncedFunctions(ast.NodeTransformer): def visit_Call(self, node): - if instanceof(node.func, ast.Attribute): + if isinstance(node.func, ast.Attribute): name = node.func.attr else: name = node.func.id From d2b00ed49c1e04f0c187e84dc2ecfe623e96c3e4 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Wed, 11 May 2022 12:01:02 -0400 Subject: [PATCH 074/121] more fix for attribute error --- webapp_start.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/webapp_start.py b/webapp_start.py index d8433c0..6d12b6e 100644 --- a/webapp_start.py +++ b/webapp_start.py @@ -25,12 +25,20 @@ def has_await(node): if isinstance(subnode, ast.Await): return True return False +def get_name(node): + func = node.func + if isinstance(func, ast.Attribute): + return func.attr + elif isinstance(func, ast.Name): + return func.id + else: + return None + class FixFirst(ast.NodeTransformer): def visit_Call(self, node): - if isinstance(node.func, ast.Attribute): - name = node.func.attr - else: - name = node.func.id + name = get_name(node) + if name is None: + return node if name == 'input' or name == 'current_item': print('\tAwaiting function', name) if name == 'input': @@ -58,10 +66,9 @@ def visit_FunctionDef(self, node): class ApplyAwaitsToAsyncedFunctions(ast.NodeTransformer): def visit_Call(self, node): - if isinstance(node.func, ast.Attribute): - name = node.func.attr - else: - name = node.func.id + name = get_name(node) + if name is None: + return node if node in asynced_functions: print('\tNow awaiting call of', name) return ast.Await(node) From 72c648a2f785261ca7344e701446d81b3ecc7512 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Wed, 11 May 2022 12:16:21 -0400 Subject: [PATCH 075/121] fix name error --- webapp_start.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp_start.py b/webapp_start.py index 6d12b6e..de0441f 100644 --- a/webapp_start.py +++ b/webapp_start.py @@ -50,7 +50,7 @@ def visit_Call(self, node): asynced_functions = ['async_patched_input', 'current_item'] class MakeFunctionAsyncValid(ast.NodeTransformer): def visit_FunctionDef(self, node): - name = node.func.id + name = node.name if has_await(node): print('\tFound bad function', name, '> fixing') return ast.AsyncFunctionDef( From d020bc53b56fb053508361cb87c37ff221121c01 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Wed, 11 May 2022 12:17:19 -0400 Subject: [PATCH 076/121] return the node if nothing is done to it --- webapp_start.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/webapp_start.py b/webapp_start.py index de0441f..730ac81 100644 --- a/webapp_start.py +++ b/webapp_start.py @@ -45,6 +45,8 @@ def visit_Call(self, node): print('\tRenaming input') node.func.id = 'async_patched_input' return ast.Await(node) + else: + return node changed = False asynced_functions = ['async_patched_input', 'current_item'] From bddf30c1e461c741cae6e068cec2b42586a7bf80 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Wed, 11 May 2022 12:28:03 -0400 Subject: [PATCH 077/121] forgot to copy over lineno info on to new node --- webapp_main.js | 5 +---- webapp_start.py | 22 +++++++++++++++++++--- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/webapp_main.js b/webapp_main.js index 807f57d..568e7b9 100644 --- a/webapp_main.js +++ b/webapp_main.js @@ -9,10 +9,7 @@ function sleep(s) { window.addEventListener('DOMContentLoaded', async function main() { - var term = $("#terminal").terminal(input => { - var lines = input.split('\n'); - lines.forEach(line => stdin_queue.push(line)); - }, { + var term = $("#terminal").terminal(() => {}, { greetings: '', prompt: '', completionEscape: false, diff --git a/webapp_start.py b/webapp_start.py index 730ac81..406ba20 100644 --- a/webapp_start.py +++ b/webapp_start.py @@ -44,7 +44,13 @@ def visit_Call(self, node): if name == 'input': print('\tRenaming input') node.func.id = 'async_patched_input' - return ast.Await(node) + return ast.Await( + value=node, + lineno=node.lineno, + col_offset=node.col_offset, + end_lineno=node.end_lineno, + end_col_offset=node.end_col_offset + 6 + ) else: return node @@ -59,7 +65,11 @@ def visit_FunctionDef(self, node): name=node.name, args=node.args, body=node.body, - decorator_list=node.decorator_list + decorator_list=node.decorator_list, + lineno=node.lineno, + col_offset=node.col_offset, + end_lineno=node.end_lineno, + end_col_offset=node.end_col_offset ) asynced_functions.append(name) changed = True @@ -73,7 +83,13 @@ def visit_Call(self, node): return node if node in asynced_functions: print('\tNow awaiting call of', name) - return ast.Await(node) + return return ast.Await( + value=node, + lineno=node.lineno, + col_offset=node.col_offset, + end_lineno=node.end_lineno, + end_col_offset=node.end_col_offset + 6 + ) else: return node From 7a18cb704a9b1bda242cfa9c385debe68c5be280 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Wed, 11 May 2022 12:29:27 -0400 Subject: [PATCH 078/121] add some debugging --- webapp_start.py | 1 + 1 file changed, 1 insertion(+) diff --git a/webapp_start.py b/webapp_start.py index 406ba20..96e020e 100644 --- a/webapp_start.py +++ b/webapp_start.py @@ -26,6 +26,7 @@ def has_await(node): return False def get_name(node): + print(ast.dump(node, indent=2)) func = node.func if isinstance(func, ast.Attribute): return func.attr From ccdcd56aa906200014e61f874cb1e56749638744 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Wed, 11 May 2022 13:26:44 -0400 Subject: [PATCH 079/121] duplicated return --- webapp_start.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/webapp_start.py b/webapp_start.py index 96e020e..b6da5c0 100644 --- a/webapp_start.py +++ b/webapp_start.py @@ -84,7 +84,7 @@ def visit_Call(self, node): return node if node in asynced_functions: print('\tNow awaiting call of', name) - return return ast.Await( + return ast.Await( value=node, lineno=node.lineno, col_offset=node.col_offset, @@ -114,6 +114,7 @@ def visit_Call(self, node): async def async_patched_input(prompt): term = js.term term.resume() + print('\\u200c', end='') # ‌ result = await term.read(prompt) term.pause() return result From 0640325a5856405149459a9b8f8717232f6b65f8 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Wed, 11 May 2022 13:29:12 -0400 Subject: [PATCH 080/121] reduce timeout --- webapp_main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp_main.js b/webapp_main.js index 568e7b9..dd7c300 100644 --- a/webapp_main.js +++ b/webapp_main.js @@ -18,7 +18,7 @@ window.addEventListener('DOMContentLoaded', async function main() { term.pause(); window.term = term; term.echo('Quackery (and Python) are loading...'); - var c = setTimeout(() => term.echo('this may take a while...'), 10000); + var c = setTimeout(() => term.echo('this may take a while...'), 5000); try { globalThis.pyodide = await loadPyodide({ homedir: '/quackery', From 9234c166ec4a031309fde08fa85fe1c3d6350773 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Wed, 11 May 2022 13:51:36 -0400 Subject: [PATCH 081/121] add more verbose loading messages --- webapp_start.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/webapp_start.py b/webapp_start.py index b6da5c0..116c246 100644 --- a/webapp_start.py +++ b/webapp_start.py @@ -4,6 +4,7 @@ from os import mkdir import ast import js +from itertools import count mkdir('sundry') files = ['bigrat', 'extensions', 'turtleduck', 'sundry/cards', 'sundry/demo', 'sundry/fsm', 'sundry/heapsort'] @@ -15,6 +16,7 @@ text = await resp.string() with open(f'{file}.qky', 'w') as f: f.write(text) +print('Downloading quackery.py ...') resp = await pyfetch('@@ORIGIN@@/quackery.py') quackerytext = await resp.string() @@ -94,19 +96,23 @@ def visit_Call(self, node): else: return node +print('Parsing...') tree = ast.parse(quackerytext) +print('Patching') fixed_tree = FixFirst().visit(tree) a = MakeFunctionAsyncValid() b = ApplyAwaitsToAsyncedFunctions() -while True: +for it in count(1): + print('Fixing... iteration', it) changed = False fixed_tree = b.visit(a.visit(fixed_tree)) if changed is False: break +print('Unparsing...') fixedquackerytext = f''' import js @@ -121,6 +127,7 @@ async def async_patched_input(prompt): {ast.unparse(fixed_tree)}''' +print('Loading...') with open('quackery.py', 'w') as f: f.write(fixedquackerytext) #js.term.clear() From 6da46897900dc9c11d6e87625230593efc5481ea Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Wed, 11 May 2022 13:59:41 -0400 Subject: [PATCH 082/121] remove large debug function print --- webapp_start.py | 1 - 1 file changed, 1 deletion(-) diff --git a/webapp_start.py b/webapp_start.py index 116c246..4ae6eab 100644 --- a/webapp_start.py +++ b/webapp_start.py @@ -28,7 +28,6 @@ def has_await(node): return False def get_name(node): - print(ast.dump(node, indent=2)) func = node.func if isinstance(func, ast.Attribute): return func.attr From c02f72f8cf1d761d3818c0da5750f43cae93d290 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Wed, 11 May 2022 14:28:39 -0400 Subject: [PATCH 083/121] ad wait 0.5s for load issue --- webapp_main.js | 1 + 1 file changed, 1 insertion(+) diff --git a/webapp_main.js b/webapp_main.js index dd7c300..10c0f46 100644 --- a/webapp_main.js +++ b/webapp_main.js @@ -17,6 +17,7 @@ window.addEventListener('DOMContentLoaded', async function main() { }); term.pause(); window.term = term; + await sleep(500); term.echo('Quackery (and Python) are loading...'); var c = setTimeout(() => term.echo('this may take a while...'), 5000); try { From 2b16db7aaed87a41f835b445a98651b075b12c58 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Thu, 12 May 2022 07:45:51 -0400 Subject: [PATCH 084/121] missed global statement --- webapp_start.py | 1 + 1 file changed, 1 insertion(+) diff --git a/webapp_start.py b/webapp_start.py index 4ae6eab..4d93e2f 100644 --- a/webapp_start.py +++ b/webapp_start.py @@ -60,6 +60,7 @@ def visit_Call(self, node): asynced_functions = ['async_patched_input', 'current_item'] class MakeFunctionAsyncValid(ast.NodeTransformer): def visit_FunctionDef(self, node): + global changed, asynced_functions name = node.name if has_await(node): print('\tFound bad function', name, '> fixing') From 29d04a86078e16a889c42573307f70135d0b5984 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Thu, 12 May 2022 07:52:47 -0400 Subject: [PATCH 085/121] add `flush=True` to all print statements --- webapp_start.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/webapp_start.py b/webapp_start.py index 4d93e2f..79224b5 100644 --- a/webapp_start.py +++ b/webapp_start.py @@ -12,12 +12,13 @@ for file in files: # N. B. top-level await is only allowed in Pyodide resp = await pyfetch(f'@@ORIGIN@@/{file}.qky') - print(f'Downloading {file}.qky ...') + print(f'Downloading {file}.qky', flush=True) text = await resp.string() with open(f'{file}.qky', 'w') as f: f.write(text) -print('Downloading quackery.py ...') +print('Downloading quackery.py', flush=True) resp = await pyfetch('@@ORIGIN@@/quackery.py') +print('Started download', flush=True) quackerytext = await resp.string() # PATCH - make functions async @@ -96,23 +97,23 @@ def visit_Call(self, node): else: return node -print('Parsing...') +print('Parsing', flush=True) tree = ast.parse(quackerytext) -print('Patching') +print('Patching', flush=True) fixed_tree = FixFirst().visit(tree) a = MakeFunctionAsyncValid() b = ApplyAwaitsToAsyncedFunctions() for it in count(1): - print('Fixing... iteration', it) + print('Fixing, iteration', it, flush=True) changed = False fixed_tree = b.visit(a.visit(fixed_tree)) if changed is False: break -print('Unparsing...') +print('Unparsing', flush=True) fixedquackerytext = f''' import js @@ -120,14 +121,14 @@ def visit_Call(self, node): async def async_patched_input(prompt): term = js.term term.resume() - print('\\u200c', end='') # ‌ + print('\\u200c', end='', flush=True) # ‌ result = await term.read(prompt) term.pause() return result {ast.unparse(fixed_tree)}''' -print('Loading...') +print('Loading', flush=True) with open('quackery.py', 'w') as f: f.write(fixedquackerytext) #js.term.clear() From 00868e13b0fa3f9671e109037b861f72b8759b21 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Thu, 12 May 2022 09:49:35 -0400 Subject: [PATCH 086/121] new transformer every time --- webapp_start.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/webapp_start.py b/webapp_start.py index 79224b5..e016dd1 100644 --- a/webapp_start.py +++ b/webapp_start.py @@ -25,7 +25,8 @@ def has_await(node): for subnode in ast.walk(node): - if isinstance(subnode, ast.Await): return True + if isinstance(subnode, ast.Await): + return True return False def get_name(node): @@ -103,13 +104,10 @@ def visit_Call(self, node): print('Patching', flush=True) fixed_tree = FixFirst().visit(tree) -a = MakeFunctionAsyncValid() -b = ApplyAwaitsToAsyncedFunctions() - for it in count(1): print('Fixing, iteration', it, flush=True) changed = False - fixed_tree = b.visit(a.visit(fixed_tree)) + fixed_tree = ApplyAwaitsToAsyncedFunctions().visit(MakeFunctionAsyncValid().visit(fixed_tree)) if changed is False: break From 26abc36946edb0bc60920a0f0353aca1d3258334 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Wed, 18 May 2022 08:37:47 -0400 Subject: [PATCH 087/121] keep old quackery.py --- quackery.py | 1374 +++++++++++++++++++------------------- quackery_OOP.py | 1708 +++++++++++++++++++++++++++++++++++++++++++++++ webapp_start.py | 16 +- 3 files changed, 2408 insertions(+), 690 deletions(-) create mode 100644 quackery_OOP.py diff --git a/quackery.py b/quackery.py index bc5a60a..765e726 100644 --- a/quackery.py +++ b/quackery.py @@ -9,196 +9,725 @@ except: pass -__all__ = ['QuackeryContext', 'quackery'] -def isNest(item): - return isinstance(item, list) +class QuackeryError(Exception): + pass -def isNumber(item): - return isinstance(item, int) -def isOperator(item): - return isinstance(item, types.FunctionType) +def quackery(source_string): -def isinteger(numstr): - if len(numstr) > 0 and numstr[0] == '-': - numstr = numstr[1:] - return numstr.isdigit() + """ Perform a Quackery program. Return the stack as a string. """ -def ishex(hexstr): - if len(hexstr) > 1 and hexstr[0] == '-': - hexstr = hexstr[1:] - return all(char.lower() in '0123456789abcdef' for char in hexstr) + def failed(message): + traverse(build(""" stacksize pack + decimal unbuild + return$ + nestdepth ]bailby[ """)) + returnstack = string_from_stack() + thestack = string_from_stack() + raise QuackeryError('\n Problem: ' + message + + '\nQuackery Stack: ' + str(thestack)[2:-2] + + '\n Return stack: ' + str(returnstack)) -class QuackeryError(Exception): - pass + def isNest(item): + return isinstance(item, list) + + def isNumber(item): + return isinstance(item, int) + + def isOperator(item): + return isinstance(item, types.FunctionType) + + def expect_something(): + nonlocal qstack + if qstack == []: + failed('Stack unexpectedly empty.') + + def top_of_stack(): + nonlocal qstack + return(qstack[-1]) -class QuackeryContext: - def __init__(self, qstack = None, operators = None, builders = None): - self.qstack = [] if qstack is None else qstack - self.rstack = [] - self.operators = predefined_operators.copy() if operators is None else operators - self.builders = predefined_builders.copy() if builders is None else builders - self.program_counter = 0 - self.current_nest = [] - self.source = '' - self.current_build = [] - - def copy(self): - new = QuackeryContext(self.qstack.copy(), self.operators.copy(), self.builders.copy()) - new.rstack = self.rstack.copy() - new.program_counter = self.program_counter - new.current_nest = self.current_nest.copy() - new.source = self.source - new.current_build = self.current_build.copy() - return new - - def expect_something(self): - if self.qstack == []: - self.failed('Stack unexpectedly empty.') - - def top_of_stack(self): - return self.qstack[-1] - - def expect_nest(self): - self.expect_something() - if not isNest(self.top_of_stack()): - self.failed('Expected nest on stack.') - - def expect_number(self): - self.expect_something() - if not isNumber(self.top_of_stack()): - self.failed('Expected number on stack.') - - def to_stack(self, item): - self.qstack.append(item) - - def from_stack(self): - self.expect_something() - return self.qstack.pop() - - def string_from_stack(self): - self.expect_nest() + def expect_nest(): + expect_something() + if not isNest(top_of_stack()): + failed('Expected nest on stack.') + + def expect_number(): + expect_something() + if not isNumber(top_of_stack()): + failed('Expected number on stack.') + + def to_stack(item): + nonlocal qstack + qstack.append(item) + + def from_stack(): + nonlocal qstack + expect_something() + return qstack.pop() + + def string_from_stack(): + expect_nest() result = '' - for ch in self.from_stack(): + for ch in from_stack(): if ch == 13: # \r result += '\n' elif 31 < ch < 127: result += chr(ch) else: - result += '?' # XXX @dragoncoder047 maybe use \uFFFD on platforms that support unicode? + result += '?' return result - def string_to_stack(self, string): + def string_to_stack(str): result = [] - for ch in string: + for ch in str: if ch == '\n': result.append(13) elif 31 < ord(ch) < 127: result.append(ord(ch)) else: - result.append(ord('?')) # XXX @dragoncoder047 maybe \0 or NULL to signify bad char? - self.to_stack(result) - - def bool_to_stack(self, qbool): - self.to_stack(true if qbool else false) - - def to_return(self, item): - self.rstack.append(item) - - def from_return(self): - if len(self.rstack) == 0: - self.failed('Return stack unexpectedly empty.') - return self.rstack.pop() - - def failed(self, message): - self.traverse(self.build("stacksize pack decimal unbuild return$ nestdepth ]bailby[")) - returnstack = self.string_from_stack() - thestack = self.string_from_stack() - raise QuackeryError('\n Problem: ' + message + - '\nQuackery Stack: ' + str(thestack)[2:-2] + - '\n Return stack: ' + str(returnstack)) - - def tick(self): - if self.program_counter >= len(self.current_nest): - self.program_counter = self.from_return() - self.current_nest = self.from_return() - self.program_counter += 1 - return - current_item = self.current_nest[self.program_counter] - if isNest(current_item): - self.to_return(self.current_nest) - self.to_return(self.program_counter) - self.current_nest = current_item - self.program_counter = 0 - elif isOperator(current_item): - current_item(self) - self.program_counter += 1 - elif isNumber(current_item): - self.to_stack(current_item) - self.program_counter += 1 + result.append(ord('?')) + to_stack(result) + + def python(): + nonlocal to_stack + nonlocal from_stack + nonlocal string_to_stack + nonlocal string_from_stack + try: + exec(string_from_stack()) + except QuackeryError: + raise + except Exception as diagnostics: + failed('Python reported: "' + str(diagnostics) + '"') + + def qfail(): + message = string_from_stack() + failed(message) + + def stack_size(): + nonlocal qstack + to_stack(len(qstack)) + + def qreturn(): + nonlocal rstack + to_stack(rstack) + + def dup(): + a = from_stack() + to_stack(a) + to_stack(a) + + def drop(): + from_stack() + + def swap(): + a = from_stack() + b = from_stack() + to_stack(a) + to_stack(b) + + def rot(): + a = from_stack() + swap() + to_stack(a) + swap() + + def over(): + a = from_stack() + dup() + to_stack(a) + swap() + + def nest_depth(): + nonlocal rstack + to_stack(len(rstack) // 2) + + def to_return(item): + nonlocal rstack + rstack.append(item) + + def from_return(): + nonlocal rstack + if rstack == []: + failed('Return stack unexpectedly empty.') + return rstack.pop() + + true = 1 + + false = 0 + + def bool_to_stack(qbool): + to_stack(true if qbool else false) + + def nand(): + expect_number() + a = from_stack() + expect_number() + bool_to_stack(from_stack() == false or a == false) + + def equal(): + expect_something() + a = from_stack() + expect_something() + bool_to_stack(a == from_stack()) + + def greater(): + expect_number() + a = from_stack() + expect_number() + bool_to_stack(from_stack() > a) + + def inc(): + expect_number() + to_stack(1 + from_stack()) + + def plus(): + expect_number() + a = from_stack() + expect_number() + to_stack(a + from_stack()) + + def negate(): + expect_number() + to_stack(-from_stack()) + + def multiply(): + expect_number() + a = from_stack() + expect_number() + to_stack(a * from_stack()) + + def qdivmod(): + expect_number() + a = from_stack() + if a == 0: + failed('Division by zero.') + expect_number() + results = divmod(from_stack(), a) + to_stack(results[0]) + to_stack(results[1]) + + def exponentiate(): + expect_number() + a = from_stack() + if a < 0: + failed('Tried to raise to a negative power: ' + str(a)) + expect_number() + to_stack(from_stack() ** a) + + def shift_left(): + expect_number() + a = from_stack() + if a < 0: + failed('Cannot << by a negative amount: ' + str(a)) + expect_number() + to_stack(from_stack() << a) + + def shift_right(): + expect_number() + a = from_stack() + if a < 0: + failed('Cannot >> by a negative amount: ' + str(a)) + expect_number() + to_stack(from_stack() >> a) + + def bitwise_and(): + expect_number() + a = from_stack() + expect_number() + to_stack(a & from_stack()) + + def bitwise_or(): + expect_number() + a = from_stack() + expect_number() + to_stack(a | from_stack()) + + def bitwise_xor(): + expect_number() + a = from_stack() + expect_number() + to_stack(a ^ from_stack()) + + def bitwise_not(): + expect_number() + to_stack(~from_stack()) + + def qtime(): + to_stack(int(time.time()*1000000)) + + def meta_done(): + from_return() + from_return() + + def meta_again(): + from_return() + to_return(-1) + + def meta_if(): + expect_number() + if from_stack() == 0: + to_return(from_return() + 1) + + def meta_iff(): + expect_number() + if from_stack() == 0: + to_return(from_return() + 2) + + def meta_else(): + to_return(from_return() + 1) + + def meta_literal(): + pc = from_return() + 1 + return_nest = from_return() + if len(return_nest) == pc: + failed('''Found a "'" at the end of a nest.''') + to_stack(return_nest[pc]) + to_return(return_nest) + to_return(pc) + + def meta_this(): + pc = from_return() + return_nest = from_return() + to_stack(return_nest) + to_return(return_nest) + to_return(pc) + + def meta_do(): + expect_something() + the_thing = from_stack() + if not isNest(the_thing): + the_thing = [the_thing] + to_return(the_thing) + to_return(-1) + + def meta_bail_by(): + expect_number() + a = 2*(from_stack()) + if a <= len(rstack): + for _ in range(a): + from_return() + else: + failed('Bailed out of Quackery.') + + def qput(): + expect_nest() + a = from_stack() + expect_something() + b = from_stack() + a.append(b) + + def immovable(): + pass + + def take(): + expect_nest() + a = from_stack() + if len(a) == 0: + failed('Unexpectedly empty nest.') + if len(a) == 1: + if isNest(a[0]) and len(a[0]) > 0 and a[0][0] == immovable: + failed('Cannot remove an immovable item.') + to_stack(a.pop()) + + def create_nest(): + to_stack([]) + + def qsplit(): + expect_number() + a = from_stack() + expect_nest() + b = from_stack() + to_stack(b[:a]) + to_stack(b[a:]) + + def join(): + expect_something() + b = from_stack() + if not isNest(b): + b = [b] + expect_something() + a = from_stack() + if not isNest(a): + a = [a] + to_stack(a + b) + + def qsize(): + expect_nest() + to_stack(len(from_stack())) + + def qfind(): + expect_nest() + nest = from_stack() + expect_something() + a = from_stack() + if a in nest: + to_stack(nest.index(a)) else: - self.failed('Quackery was worried by a python.') - - - def traverse(self, the_nest): - orig_depth = len(self.rstack) - self.to_return(self.current_nest) - self.to_return(self.program_counter) - self.current_nest = the_nest - self.program_counter = 0 - while len(self.rstack) > orig_depth: - self.tick() - - def next_char(self): - if len(self.source) > 0: - char = self.source[0] - self.source = self.source[1:] + to_stack(len(nest)) + + def peek(): + expect_number() + index = from_stack() + expect_nest() + nest = from_stack() + if index >= len(nest) or ( + index < 0 and len(nest) < abs(index)): + failed('Cannot peek an item outside a nest.') + else: + to_stack(nest[index]) + + def poke(): + expect_number() + index = from_stack() + expect_nest() + nest = from_stack().copy() + expect_something() + value = from_stack() + if index >= len(nest) or ( + index < 0 and len(nest) < abs(index)): + failed('Cannot poke an item outside a nest.') + else: + nest[index] = value + to_stack(nest) + + def qnest(): + expect_something() + bool_to_stack(isNest(from_stack())) + + def qnumber(): + expect_something() + bool_to_stack(isNumber(from_stack())) + + def qoperator(): + expect_something() + bool_to_stack(isOperator(from_stack())) + + def quid(): + expect_something() + to_stack(id(from_stack())) + + def qemit(): + expect_number() + char = from_stack() + if char == 13: + sys.stdout.write('\n') + elif 31 < char < 127: + sys.stdout.write(chr(char)) + else: + sys.stdout.write('?') + def ding(): + sys.stdout.write('\a') + + def qinput(): + prompt = string_from_stack() + string_to_stack(input(prompt)) + + filepath = [] + + def putfile(): + nonlocal filepath + filename = string_from_stack() + if len(filepath) > 1: + to_stack(filepath[-1]) + filename = string_from_stack() + filename + filetext = string_from_stack() + try: + with open(filename, 'x'): pass + except FileExistsError: + to_stack(false) + except: + raise + else: + try: + with open(filename, 'w') as f: f.write(filetext) + except: + raise + else: + to_stack(true) + + def releasefile(): + nonlocal filepath + filename = string_from_stack() + if len(filepath) > 1: + to_stack(filepath[-1]) + filename = string_from_stack() + filename + try: + os.remove(filename) + except FileNotFoundError: + to_stack(false) + except: + raise + else: + to_stack(true) + + def sharefile(): + nonlocal filepath + dup() + filename = string_from_stack() + if len(filepath) > 1: + to_stack(filepath[-1]) + filename = string_from_stack() + filename + try: + with open(filename) as f: filetext = f.read() + except FileNotFoundError: + to_stack(false) + except: + raise + else: + drop() + string_to_stack(filetext) + to_stack(true) + + operators = { + 'python': python, # ( $ --> ) + 'fail': qfail, # ( $ --> ) + 'nand': nand, # ( b b --> b ) + '=': equal, # ( x x --> b ) + '>': greater, # ( n n --> b ) + '1+': inc, # ( n --> n ) + '+': plus, # ( n n --> n ) + 'negate': negate, # ( n --> n ) + '*': multiply, # ( n n --> n ) + '/mod': qdivmod, # ( n n --> n n ) + '**': exponentiate, # ( n n --> n ) + '<<': shift_left, # ( f n --> f ) + '>>': shift_right, # ( f n --> f ) + '&': bitwise_and, # ( f f --> f ) + '|': bitwise_or, # ( f f --> f ) + '^': bitwise_xor, # ( f f --> f ) + '~': bitwise_not, # ( f --> f ) + 'time': qtime, # ( --> n ) + 'stacksize': stack_size, # ( --> n ) + 'nestdepth': nest_depth, # ( --> n ) + 'return': qreturn, # ( --> [ ) + 'dup': dup, # ( x --> x x ) + 'drop': drop, # ( x --> ) + 'swap': swap, # ( x x --> x x ) + 'rot': rot, # ( x x x --> x x x ) + 'over': over, # ( x x --> x x x ) + ']done[': meta_done, # ( --> ) + ']again[': meta_again, # ( --> ) + ']if[': meta_if, # ( b --> ) + ']iff[': meta_iff, # ( b --> ) + ']else[': meta_else, # ( --> ) + "]'[": meta_literal, # ( --> x ) + ']this[': meta_this, # ( --> [ ) + ']do[': meta_do, # ( x --> ) + ']bailby[': meta_bail_by, # ( n --> ) + 'put': qput, # ( x [ --> ) + 'immovable': immovable, # ( --> ) + 'take': take, # ( [ --> x ) + '[]': create_nest, # ( --> n ) + 'split': qsplit, # ( [ n --> [ [ ) + 'join': join, # ( x x --> [ ) + 'find': qfind, # ( x --> b ) + 'peek': peek, # ( [ n --> x ) + 'poke': poke, # ( x [ n --> ) + 'size': qsize, # ( [ --> n ) + 'nest?': qnest, # ( x --> b ) + 'number?': qnumber, # ( x --> b ) + 'operator?': qoperator, # ( x --> b ) + 'quid': quid, # ( x --> n ) + 'emit': qemit, # ( c --> ) + 'ding': ding, # ( --> ) + 'input': qinput, # ( $ --> $ ) + 'filepath': filepath, # ( --> s ) + 'putfile': putfile, # ( $ --> b ) + 'releasefile': releasefile, # ( $ --> b ) + 'sharefile': sharefile} # ( $ --> $ b ) + + qstack = [] + + rstack = [] + + current_nest = [] + + program_counter = 0 + + def traverse(the_nest): + nonlocal current_nest + nonlocal program_counter + nonlocal rstack + current_nest = the_nest + program_counter = 0 + while True: + if program_counter >= len(current_nest): + if len(rstack) == 0: + break + else: + program_counter = from_return() + current_nest = from_return() + program_counter += 1 + continue + current_item = current_nest[program_counter] + if isNest(current_item): + to_return(current_nest) + to_return(program_counter) + current_nest = current_item + program_counter = 0 + elif isOperator(current_item): + current_item() + program_counter += 1 + elif isNumber(current_item): + to_stack(current_item) + program_counter += 1 + else: + failed('Quackery was worried by a python.') + + def isinteger(string): + numstr = string + if len(numstr) > 0 and numstr[0] == '-': + numstr = numstr[1:] + return numstr.isdigit() + + def next_char(): + nonlocal source + if len(source) > 0: + char = source[0] + source = source[1:] return char else: return '' - def next_word(self): + def next_word(): result = '' while True: - char = self.next_char() + char = next_char() if char == '': - return result + return(result) if ord(char) < 33: if result == '': continue return result result += char - def one_char(self): + def one_char(): while True: - char = self.next_char() + char = next_char() if char == '': return char if ord(char) < 33: continue return char - def get_name(self): - name = self.next_word() + def get_name(): + name = next_word() if name == '': raise EOFError('Unexpected end of program text.') return name - def check_build(self): - if len(self.current_build) == 0: + def check_build(): + nonlocal current_build + if len(current_build) == 0: raise IndexError('Nothing to name.') - - def build(self, source_string): - self.source = source_string + + def qis(): + nonlocal operators + nonlocal current_build + check_build() + name = get_name() + operators[name] = current_build.pop() + + def qcomment(): + word = '' + while word != ')': + word = next_word() + if word == '': + raise EOFError('Unclosed comment.') + + def endcomment(): + raise SyntaxError('Too many end of comments.') + + def unresolved(): + raise TypeError('Unresolved forward reference.') + + def forward(): + nonlocal current_build + current_build.append([unresolved]) + + def resolves(): + nonlocal current_build + name = get_name() + if name in operators: + if operators[name][0] != unresolved: + raise TypeError(name + ' is not a forward reference.') + check_build() + operators[name][0] = current_build.pop() + else: + raise NameError('Unrecognised word: ' + name) + + def char_literal(): + nonlocal current_build + char = one_char() + if char == '': + raise SyntaxError('No character found.') + current_build.append(ord(char)) + + def string_literal(): + nonlocal current_build + delimiter = '' + result = [] + while delimiter == '': + char = next_char() + if char == '': + raise EOFError('No string found.') + if ord(char) > 32: + delimiter = char + char = '' + while char != delimiter: + char = next_char() + if char == '': + raise EOFError('Endless string discovered.') + if char != delimiter: + result.append(ord(char)) + current_build.append([[meta_literal], result]) + + def ishex(string): + hexstr = string + if len(hexstr) > 1 and hexstr[0] == '-': + hexstr = hexstr[1:] + for char in hexstr: + if char.lower() not in '0123456789abcdef': + return False + return True + + def hexnum(): + nonlocal current_build + word = get_name() + if not ishex(word): + raise SyntaxError(word + " is not hexadecimal.") + current_build.append(int(word, 16)) + + builders = {'is': qis, + '(': qcomment, + ')': endcomment, + 'forward': forward, + 'resolves': resolves, + 'char': char_literal, + '$': string_literal, + 'hex': hexnum} + + current_build = [] + + source = '' + + the_nest = [] + + def build(source_string): + nonlocal source + nonlocal the_nest + source = source_string nesting = 0 def sub_build(): nonlocal nesting + nonlocal current_build the_nest = [] while True: - self.current_build = the_nest - word = self.next_word() + current_build = the_nest + word = next_word() if word == '': return the_nest elif word == '[': @@ -208,11 +737,11 @@ def sub_build(): nesting -= 1 if nesting < 0: raise SyntaxError('Unexpected end of nest.') - return the_nest - elif word in self.builders.keys(): - self.builders[word](self) - elif word in self.operators.keys(): - the_nest.append(self.operators[word]) + return(the_nest) + elif word in builders.keys(): + builders[word]() + elif word in operators.keys(): + the_nest.append(operators[word]) elif isinteger(word): the_nest.append(int(word, 10)) else: @@ -221,522 +750,9 @@ def sub_build(): the_nest = sub_build() if nesting > 0: raise SyntaxError('Unfinished nest.') - return the_nest - - def run(self, source_string): - self.traverse(self.build(source_string)) - - - -def python(ctx): - """For backwards compatibility only""" - scope = { - "to_stack": ctx.to_stack, - "from_stack": ctx.from_stack, - "string_to_stack": ctx.string_to_stack, - "string_from_stack": ctx.string_from_stack, - "ctx": ctx, - } - try: - exec(ctx.string_from_stack(), scope, globals()) - except QuackeryError: - raise - except Exception as diagnostics: - ctx.failed('Python reported: "' + str(diagnostics) + '"') - -def qfail(ctx): - ctx.failed(ctx.string_from_stack()) - -def stack_size(ctx): - ctx.to_stack(len(ctx.qstack)) - -def qreturn(ctx): - ctx.to_stack(ctx.rstack) - -def dup(ctx): - a = ctx.from_stack() - ctx.to_stack(a) - ctx.to_stack(a) - -def drop(ctx): - ctx.from_stack() - -def swap(ctx): - a = ctx.from_stack() - b = ctx.from_stack() - ctx.to_stack(a) - ctx.to_stack(b) - -def rot(ctx): # XXX @dragoncoder047 maybe simplify to [ dip swap swap ] is rot ? There are no cyclic references that would prevent this - a = ctx.from_stack() - swap(ctx) - ctx.to_stack(a) - swap(ctx) - -def over(ctx): # XXX @dragoncoder047 maybe simplify to [ dip dup swap ] is over ? same reason as above - a = ctx.from_stack() - dup(ctx) - ctx.to_stack(a) - swap(ctx) - -def nest_depth(ctx): - ctx.to_stack(len(ctx.rstack) // 2) - -true = 1 - -false = 0 - -def nand(ctx): - ctx.expect_number() - a = ctx.from_stack() - ctx.expect_number() - ctx.bool_to_stack(ctx.from_stack() == false or a == false) - -def equal(ctx): - ctx.expect_something() - a = ctx.from_stack() - ctx.expect_something() - ctx.bool_to_stack(a == ctx.from_stack()) - -def greater(ctx): - ctx.expect_number() - a = ctx.from_stack() - ctx.expect_number() - ctx.bool_to_stack(ctx.from_stack() > a) - -def inc(ctx): - ctx.expect_number() - ctx.to_stack(1 + ctx.from_stack()) - -def plus(ctx): - ctx.expect_number() - a = ctx.from_stack() - ctx.expect_number() - ctx.to_stack(a + ctx.from_stack()) - -def negate(ctx): - ctx.expect_number() - ctx.to_stack(-ctx.from_stack()) - -def multiply(ctx): - ctx.expect_number() - a = ctx.from_stack() - ctx.expect_number() - ctx.to_stack(a * ctx.from_stack()) - -def qdivmod(ctx): - ctx.expect_number() - a = ctx.from_stack() - if a == 0: - ctx.failed('Division by zero.') - ctx.expect_number() - results = divmod(ctx.from_stack(), a) - ctx.to_stack(results[0]) - ctx.to_stack(results[1]) - -def exponentiate(ctx): - ctx.expect_number() - a = ctx.from_stack() - if a < 0: - ctx.failed('Tried to raise to a negative power: ' + str(a)) - ctx.expect_number() - ctx.to_stack(ctx.from_stack() ** a) - -def shift_left(ctx): - ctx.expect_number() - a = ctx.from_stack() - if a < 0: - ctx.failed('Cannot << by a negative amount: ' + str(a)) - ctx.expect_number() - ctx.to_stack(ctx.from_stack() << a) - -def shift_right(ctx): - ctx.expect_number() - a = ctx.from_stack() - if a < 0: - ctx.failed('Cannot >> by a negative amount: ' + str(a)) - ctx.expect_number() - ctx.to_stack(ctx.from_stack() >> a) - -def bitwise_and(ctx): - ctx.expect_number() - a = ctx.from_stack() - ctx.expect_number() - ctx.to_stack(a & ctx.from_stack()) - -def bitwise_or(ctx): - ctx.expect_number() - a = ctx.from_stack() - ctx.expect_number() - ctx.to_stack(a | ctx.from_stack()) - -def bitwise_xor(ctx): - ctx.expect_number() - a = ctx.from_stack() - ctx.expect_number() - ctx.to_stack(a ^ ctx.from_stack()) - -def bitwise_not(ctx): - ctx.expect_number() - ctx.to_stack(~ctx.from_stack()) - -def qtime(ctx): - ctx.to_stack(int(time.time()*1000000)) - -def meta_done(ctx): - ctx.from_return() - ctx.from_return() - -def meta_again(ctx): - ctx.from_return() - ctx.to_return(-1) - -def meta_if(ctx): - ctx.expect_number() - if ctx.from_stack() == 0: - ctx.to_return(ctx.from_return() + 1) - -def meta_iff(ctx): - ctx.expect_number() - if ctx.from_stack() == 0: - ctx.to_return(ctx.from_return() + 2) - -def meta_else(ctx): - ctx.to_return(ctx.from_return() + 1) - -def meta_literal(ctx): - pc = ctx.from_return() + 1 - return_nest = ctx.from_return() - if len(return_nest) == pc: - ctx.failed('Found a "\'" at the end of a nest.') - ctx.to_stack(return_nest[pc]) - ctx.to_return(return_nest) - ctx.to_return(pc) - -def meta_this(ctx): - pc = ctx.from_return() - return_nest = ctx.from_return() - ctx.to_stack(return_nest) - ctx.to_return(return_nest) - ctx.to_return(pc) - -def meta_do(ctx): - ctx.expect_something() - the_thing = ctx.from_stack() - if not isNest(the_thing): - the_thing = [the_thing] - ctx.to_return(the_thing) - ctx.to_return(-1) - -def meta_bail_by(ctx): - ctx.expect_number() - a = 2 * ctx.from_stack() - if a <= len(ctx.rstack): - for _ in range(a): - ctx.from_return() - else: - ctx.failed('Bailed out of Quackery.') + return(the_nest) -def qput(ctx): - ctx.expect_nest() - a = ctx.from_stack() - ctx.expect_something() - b = ctx.from_stack() - a.append(b) - -def immovable(ctx): - pass - -def take(ctx): - ctx.expect_nest() - a = ctx.from_stack() - if len(a) == 0: - ctx.failed('Unexpectedly empty nest.') - if len(a) == 1: - if isNest(a[0]) and len(a[0]) > 0 and a[0][0] == immovable: - ctx.failed('Cannot remove an immovable item.') - ctx.to_stack(a.pop()) - -def create_nest(ctx): - ctx.to_stack([]) - -def qsplit(ctx): - ctx.expect_number() - a = ctx.from_stack() - ctx.expect_nest() - b = ctx.from_stack() - ctx.to_stack(b[:a]) - ctx.to_stack(b[a:]) - -def join(ctx): - ctx.expect_something() - b = ctx.from_stack() - if not isNest(b): - b = [b] - ctx.expect_something() - a = ctx.from_stack() - if not isNest(a): - a = [a] - ctx.to_stack(a + b) - -def qsize(ctx): - ctx.expect_nest() - ctx.to_stack(len(ctx.from_stack())) - -def qfind(ctx): - ctx.expect_nest() - nest = ctx.from_stack() - ctx.expect_something() - a = ctx.from_stack() - if a in nest: - ctx.to_stack(nest.index(a)) - else: - ctx.to_stack(len(nest)) - -def peek(ctx): - ctx.expect_number() - index = ctx.from_stack() - ctx.expect_nest() - nest = ctx.from_stack() - if index >= len(nest) or ( - index < 0 and len(nest) < abs(index)): - ctx.failed('Cannot peek an item outside a nest.') - else: - ctx.to_stack(nest[index]) - -def poke(ctx): - ctx.expect_number() - index = ctx.from_stack() - ctx.expect_nest() - nest = ctx.from_stack().copy() - ctx.expect_something() - value = ctx.from_stack() - if index >= len(nest) or ( - index < 0 and len(nest) < abs(index)): - ctx.failed('Cannot poke an item outside a nest.') - else: - nest[index] = value - ctx.to_stack(nest) - -def qnest(ctx): - ctx.expect_something() - ctx.bool_to_stack(isNest(ctx.from_stack())) - -def qnumber(ctx): - ctx.expect_something() - ctx.bool_to_stack(isNumber(ctx.from_stack())) - -def qoperator(ctx): - ctx.expect_something() - ctx.bool_to_stack(isOperator(ctx.from_stack())) - -def quid(ctx): - ctx.expect_something() - ctx.to_stack(id(ctx.from_stack())) - -def qemit(ctx): - ctx.expect_number() - char = ctx.from_stack() - if char == 13: - sys.stdout.write('\r\n') - elif 31 < char < 127: - sys.stdout.write(chr(char)) - else: - sys.stdout.write('?') # XXX @dragoncoder047 maybe use \uFFFD on platforms that support unicode? - -def ding(ctx): - sys.stdout.write('\a') - -def qinput(ctx): - prompt = ctx.string_from_stack() - ctx.string_to_stack(input(prompt)) - -filepath = [] - -def putfile(ctx): - filename = ctx.string_from_stack() - if len(filepath) > 1: - ctx.to_stack(filepath[-1]) - filename = ctx.string_from_stack() + filename - filetext = ctx.string_from_stack() - try: - with open(filename, 'x'): pass - except FileExistsError: - ctx.to_stack(false) - except: - raise - else: - try: - with open(filename, 'w') as f: f.write(filetext) - except: - raise - else: - ctx.to_stack(true) - -def releasefile(ctx): - filename = ctx.string_from_stack() - if len(filepath) > 1: - ctx.to_stack(filepath[-1]) - filename = ctx.string_from_stack() + filename - try: - os.remove(filename) - except FileNotFoundError: - ctx.to_stack(false) - except: - raise - else: - ctx.to_stack(true) - -def sharefile(ctx): - dup(ctx) - filename = ctx.string_from_stack() - if len(filepath) > 1: - ctx.to_stack(filepath[-1]) - filename = ctx.string_from_stack() + filename - try: - with open(filename) as f: filetext = f.read() - except FileNotFoundError: - ctx.to_stack(false) - except: - raise - else: - drop(ctx) - ctx.string_to_stack(filetext) - ctx.to_stack(true) - -predefined_operators = { - 'python': python, # ( $ --> ) - 'fail': qfail, # ( $ --> ) - 'nand': nand, # ( b b --> b ) - '=': equal, # ( x x --> b ) - '>': greater, # ( n n --> b ) - '1+': inc, # ( n --> n ) - '+': plus, # ( n n --> n ) - 'negate': negate, # ( n --> n ) - '*': multiply, # ( n n --> n ) - '/mod': qdivmod, # ( n n --> n n ) - '**': exponentiate, # ( n n --> n ) - '<<': shift_left, # ( f n --> f ) - '>>': shift_right, # ( f n --> f ) - '&': bitwise_and, # ( f f --> f ) - '|': bitwise_or, # ( f f --> f ) - '^': bitwise_xor, # ( f f --> f ) - '~': bitwise_not, # ( f --> f ) - 'time': qtime, # ( --> n ) - 'stacksize': stack_size, # ( --> n ) - 'nestdepth': nest_depth, # ( --> n ) - 'return': qreturn, # ( --> [ ) - 'dup': dup, # ( x --> x x ) - 'drop': drop, # ( x --> ) - 'swap': swap, # ( x x --> x x ) - 'rot': rot, # ( x x x --> x x x ) - 'over': over, # ( x x --> x x x ) - ']done[': meta_done, # ( --> ) - ']again[': meta_again, # ( --> ) - ']if[': meta_if, # ( b --> ) - ']iff[': meta_iff, # ( b --> ) - ']else[': meta_else, # ( --> ) - "]'[": meta_literal, # ( --> x ) - ']this[': meta_this, # ( --> [ ) - ']do[': meta_do, # ( x --> ) - ']bailby[': meta_bail_by, # ( n --> ) - 'put': qput, # ( x [ --> ) - 'immovable': immovable, # ( --> ) - 'take': take, # ( [ --> x ) - '[]': create_nest, # ( --> n ) - 'split': qsplit, # ( [ n --> [ [ ) - 'join': join, # ( x x --> [ ) - 'find': qfind, # ( x --> b ) - 'peek': peek, # ( [ n --> x ) - 'poke': poke, # ( x [ n --> ) - 'size': qsize, # ( [ --> n ) - 'nest?': qnest, # ( x --> b ) - 'number?': qnumber, # ( x --> b ) - 'operator?': qoperator, # ( x --> b ) - 'quid': quid, # ( x --> n ) - 'emit': qemit, # ( c --> ) - 'ding': ding, # ( --> ) - 'input': qinput, # ( $ --> $ ) - 'filepath': filepath, # ( --> s ) - 'putfile': putfile, # ( $ --> b ) - 'releasefile': releasefile, # ( $ --> b ) - 'sharefile': sharefile # ( $ --> $ b ) -} - -def qis(ctx): - ctx.check_build() - name = ctx.get_name() - ctx.operators[name] = ctx.current_build.pop() - -def qcomment(ctx): - word = '' - while word != ')': - word = ctx.next_word() - if word == '': - raise EOFError('Unclosed comment.') - -def endcomment(ctx): - raise SyntaxError('Too many end of comments.') - -def unresolved(ctx): - raise TypeError('Unresolved forward reference.') - -def forward(ctx): - ctx.current_build.append([unresolved]) - -def resolves(ctx): - name = ctx.get_name() - if name in ctx.operators: - if ctx.operators[name][0] != unresolved: - raise TypeError(name + ' is not a forward reference.') - ctx.check_build() - ctx.operators[name][0] = ctx.current_build.pop() - else: - raise NameError('Unrecognised word: ' + name) - -def char_literal(ctx): - char = ctx.one_char() - if char == '': - raise SyntaxError('No character found.') - ctx.current_build.append(ord(char)) - -def string_literal(ctx): - delimiter = '' - result = [] - while delimiter == '': - char = ctx.next_char() - if char == '': - raise EOFError('No string found.') - if ord(char) > 32: - delimiter = char - char = '' - while char != delimiter: - char = ctx.next_char() - if char == '': - raise EOFError('Endless string discovered.') - if char != delimiter: - result.append(ord(char)) - ctx.current_build.append([[meta_literal], result]) - -def hexnum(ctx): - word = ctx.get_name() - if not ishex(word): - raise SyntaxError(word + " is not hexadecimal.") - ctx.current_build.append(int(word, 16)) - -predefined_builders = { - 'is': qis, - '(': qcomment, - ')': endcomment, - 'forward': forward, - 'resolves': resolves, - 'char': char_literal, - '$': string_literal, - 'hex': hexnum -} - - -predefined_qky = r""" + predefined = r""" [ 0 ] is false ( --> b ) @@ -1633,26 +1649,11 @@ def hexnum(ctx): """ -# bootstrap only once -_qs = QuackeryContext() -_qs.run(predefined_qky) -predefined_operators = _qs.operators -del _qs - - -def quackery(source_string, ctx = None): - - if ctx is None: - ctx = QuackeryContext() - else: - for required_word in ('stacksize', 'pack', 'decimal', 'unbuild', 'quackery'): - if required_word not in ctx.operators.keys(): - raise NameError('QuackeryContext must have word %s defined.' % required_word) - + traverse(build(predefined)) while True: - ctx.to_stack([ord(char) for char in source_string]) + to_stack([ord(char) for char in source_string]) try: - ctx.run('quackery') + traverse(build('quackery')) except QuackeryError as diagnostics: if __name__ == '__main__' and len(sys.argv) == 1: print(diagnostics) @@ -1662,11 +1663,13 @@ def quackery(source_string, ctx = None): except Exception as diagnostics: print('Quackery system damage detected.') print('Python error: ' + str(diagnostics)) - raise + sys.exit(1) else: - ctx.run('stacksize pack decimal unbuild') - the_stack = ctx.from_stack() - return ''.join(map(chr, the_stack[2:-2])) + traverse(build('stacksize pack decimal unbuild')) + result = '' + for ch in qstack[0][2:-2]: + result += chr(ch) + return result if __name__ == '__main__': if len(sys.argv) > 1: @@ -1685,24 +1688,25 @@ def quackery(source_string, ctx = None): print('\nQuackery crashed.\n') print(diagnostics) print() - sys.exit(1) except Exception as diagnostics: print('Quackery system damage detected.') print('Python error: ' + str(diagnostics)) sys.exit(1) else: - print('\nWelcome to Quackery.') + print('\nWelcome to Quackery') print('\nEnter "leave" to leave the shell.') - try: - quackery(r""" + quackscript = r""" $ 'extensions.qky' dup name? not dip sharefile and iff [ cr say 'Building extensions.' cr quackery ] else drop - shell """) + shell """ + + try: + quackery(quackscript) except QuackeryError as diagnostics: print('\nQuackery crashed.\n') print(diagnostics) - print() + print() \ No newline at end of file diff --git a/quackery_OOP.py b/quackery_OOP.py new file mode 100644 index 0000000..bc5a60a --- /dev/null +++ b/quackery_OOP.py @@ -0,0 +1,1708 @@ +# [ quackery ] + +import time +import sys +import os +import types +try: + import readline +except: + pass + +__all__ = ['QuackeryContext', 'quackery'] + +def isNest(item): + return isinstance(item, list) + +def isNumber(item): + return isinstance(item, int) + +def isOperator(item): + return isinstance(item, types.FunctionType) + +def isinteger(numstr): + if len(numstr) > 0 and numstr[0] == '-': + numstr = numstr[1:] + return numstr.isdigit() + +def ishex(hexstr): + if len(hexstr) > 1 and hexstr[0] == '-': + hexstr = hexstr[1:] + return all(char.lower() in '0123456789abcdef' for char in hexstr) + +class QuackeryError(Exception): + pass + +class QuackeryContext: + def __init__(self, qstack = None, operators = None, builders = None): + self.qstack = [] if qstack is None else qstack + self.rstack = [] + self.operators = predefined_operators.copy() if operators is None else operators + self.builders = predefined_builders.copy() if builders is None else builders + self.program_counter = 0 + self.current_nest = [] + self.source = '' + self.current_build = [] + + def copy(self): + new = QuackeryContext(self.qstack.copy(), self.operators.copy(), self.builders.copy()) + new.rstack = self.rstack.copy() + new.program_counter = self.program_counter + new.current_nest = self.current_nest.copy() + new.source = self.source + new.current_build = self.current_build.copy() + return new + + def expect_something(self): + if self.qstack == []: + self.failed('Stack unexpectedly empty.') + + def top_of_stack(self): + return self.qstack[-1] + + def expect_nest(self): + self.expect_something() + if not isNest(self.top_of_stack()): + self.failed('Expected nest on stack.') + + def expect_number(self): + self.expect_something() + if not isNumber(self.top_of_stack()): + self.failed('Expected number on stack.') + + def to_stack(self, item): + self.qstack.append(item) + + def from_stack(self): + self.expect_something() + return self.qstack.pop() + + def string_from_stack(self): + self.expect_nest() + result = '' + for ch in self.from_stack(): + if ch == 13: # \r + result += '\n' + elif 31 < ch < 127: + result += chr(ch) + else: + result += '?' # XXX @dragoncoder047 maybe use \uFFFD on platforms that support unicode? + return result + + def string_to_stack(self, string): + result = [] + for ch in string: + if ch == '\n': + result.append(13) + elif 31 < ord(ch) < 127: + result.append(ord(ch)) + else: + result.append(ord('?')) # XXX @dragoncoder047 maybe \0 or NULL to signify bad char? + self.to_stack(result) + + def bool_to_stack(self, qbool): + self.to_stack(true if qbool else false) + + def to_return(self, item): + self.rstack.append(item) + + def from_return(self): + if len(self.rstack) == 0: + self.failed('Return stack unexpectedly empty.') + return self.rstack.pop() + + def failed(self, message): + self.traverse(self.build("stacksize pack decimal unbuild return$ nestdepth ]bailby[")) + returnstack = self.string_from_stack() + thestack = self.string_from_stack() + raise QuackeryError('\n Problem: ' + message + + '\nQuackery Stack: ' + str(thestack)[2:-2] + + '\n Return stack: ' + str(returnstack)) + + def tick(self): + if self.program_counter >= len(self.current_nest): + self.program_counter = self.from_return() + self.current_nest = self.from_return() + self.program_counter += 1 + return + current_item = self.current_nest[self.program_counter] + if isNest(current_item): + self.to_return(self.current_nest) + self.to_return(self.program_counter) + self.current_nest = current_item + self.program_counter = 0 + elif isOperator(current_item): + current_item(self) + self.program_counter += 1 + elif isNumber(current_item): + self.to_stack(current_item) + self.program_counter += 1 + else: + self.failed('Quackery was worried by a python.') + + + def traverse(self, the_nest): + orig_depth = len(self.rstack) + self.to_return(self.current_nest) + self.to_return(self.program_counter) + self.current_nest = the_nest + self.program_counter = 0 + while len(self.rstack) > orig_depth: + self.tick() + + def next_char(self): + if len(self.source) > 0: + char = self.source[0] + self.source = self.source[1:] + return char + else: + return '' + + def next_word(self): + result = '' + while True: + char = self.next_char() + if char == '': + return result + if ord(char) < 33: + if result == '': + continue + return result + result += char + + def one_char(self): + while True: + char = self.next_char() + if char == '': + return char + if ord(char) < 33: + continue + return char + + def get_name(self): + name = self.next_word() + if name == '': + raise EOFError('Unexpected end of program text.') + return name + + def check_build(self): + if len(self.current_build) == 0: + raise IndexError('Nothing to name.') + + def build(self, source_string): + self.source = source_string + nesting = 0 + + def sub_build(): + nonlocal nesting + the_nest = [] + while True: + self.current_build = the_nest + word = self.next_word() + if word == '': + return the_nest + elif word == '[': + nesting += 1 + the_nest.append(sub_build()) + elif word == ']': + nesting -= 1 + if nesting < 0: + raise SyntaxError('Unexpected end of nest.') + return the_nest + elif word in self.builders.keys(): + self.builders[word](self) + elif word in self.operators.keys(): + the_nest.append(self.operators[word]) + elif isinteger(word): + the_nest.append(int(word, 10)) + else: + raise NameError('Unrecognised word: ' + word) + + the_nest = sub_build() + if nesting > 0: + raise SyntaxError('Unfinished nest.') + return the_nest + + def run(self, source_string): + self.traverse(self.build(source_string)) + + + +def python(ctx): + """For backwards compatibility only""" + scope = { + "to_stack": ctx.to_stack, + "from_stack": ctx.from_stack, + "string_to_stack": ctx.string_to_stack, + "string_from_stack": ctx.string_from_stack, + "ctx": ctx, + } + try: + exec(ctx.string_from_stack(), scope, globals()) + except QuackeryError: + raise + except Exception as diagnostics: + ctx.failed('Python reported: "' + str(diagnostics) + '"') + +def qfail(ctx): + ctx.failed(ctx.string_from_stack()) + +def stack_size(ctx): + ctx.to_stack(len(ctx.qstack)) + +def qreturn(ctx): + ctx.to_stack(ctx.rstack) + +def dup(ctx): + a = ctx.from_stack() + ctx.to_stack(a) + ctx.to_stack(a) + +def drop(ctx): + ctx.from_stack() + +def swap(ctx): + a = ctx.from_stack() + b = ctx.from_stack() + ctx.to_stack(a) + ctx.to_stack(b) + +def rot(ctx): # XXX @dragoncoder047 maybe simplify to [ dip swap swap ] is rot ? There are no cyclic references that would prevent this + a = ctx.from_stack() + swap(ctx) + ctx.to_stack(a) + swap(ctx) + +def over(ctx): # XXX @dragoncoder047 maybe simplify to [ dip dup swap ] is over ? same reason as above + a = ctx.from_stack() + dup(ctx) + ctx.to_stack(a) + swap(ctx) + +def nest_depth(ctx): + ctx.to_stack(len(ctx.rstack) // 2) + +true = 1 + +false = 0 + +def nand(ctx): + ctx.expect_number() + a = ctx.from_stack() + ctx.expect_number() + ctx.bool_to_stack(ctx.from_stack() == false or a == false) + +def equal(ctx): + ctx.expect_something() + a = ctx.from_stack() + ctx.expect_something() + ctx.bool_to_stack(a == ctx.from_stack()) + +def greater(ctx): + ctx.expect_number() + a = ctx.from_stack() + ctx.expect_number() + ctx.bool_to_stack(ctx.from_stack() > a) + +def inc(ctx): + ctx.expect_number() + ctx.to_stack(1 + ctx.from_stack()) + +def plus(ctx): + ctx.expect_number() + a = ctx.from_stack() + ctx.expect_number() + ctx.to_stack(a + ctx.from_stack()) + +def negate(ctx): + ctx.expect_number() + ctx.to_stack(-ctx.from_stack()) + +def multiply(ctx): + ctx.expect_number() + a = ctx.from_stack() + ctx.expect_number() + ctx.to_stack(a * ctx.from_stack()) + +def qdivmod(ctx): + ctx.expect_number() + a = ctx.from_stack() + if a == 0: + ctx.failed('Division by zero.') + ctx.expect_number() + results = divmod(ctx.from_stack(), a) + ctx.to_stack(results[0]) + ctx.to_stack(results[1]) + +def exponentiate(ctx): + ctx.expect_number() + a = ctx.from_stack() + if a < 0: + ctx.failed('Tried to raise to a negative power: ' + str(a)) + ctx.expect_number() + ctx.to_stack(ctx.from_stack() ** a) + +def shift_left(ctx): + ctx.expect_number() + a = ctx.from_stack() + if a < 0: + ctx.failed('Cannot << by a negative amount: ' + str(a)) + ctx.expect_number() + ctx.to_stack(ctx.from_stack() << a) + +def shift_right(ctx): + ctx.expect_number() + a = ctx.from_stack() + if a < 0: + ctx.failed('Cannot >> by a negative amount: ' + str(a)) + ctx.expect_number() + ctx.to_stack(ctx.from_stack() >> a) + +def bitwise_and(ctx): + ctx.expect_number() + a = ctx.from_stack() + ctx.expect_number() + ctx.to_stack(a & ctx.from_stack()) + +def bitwise_or(ctx): + ctx.expect_number() + a = ctx.from_stack() + ctx.expect_number() + ctx.to_stack(a | ctx.from_stack()) + +def bitwise_xor(ctx): + ctx.expect_number() + a = ctx.from_stack() + ctx.expect_number() + ctx.to_stack(a ^ ctx.from_stack()) + +def bitwise_not(ctx): + ctx.expect_number() + ctx.to_stack(~ctx.from_stack()) + +def qtime(ctx): + ctx.to_stack(int(time.time()*1000000)) + +def meta_done(ctx): + ctx.from_return() + ctx.from_return() + +def meta_again(ctx): + ctx.from_return() + ctx.to_return(-1) + +def meta_if(ctx): + ctx.expect_number() + if ctx.from_stack() == 0: + ctx.to_return(ctx.from_return() + 1) + +def meta_iff(ctx): + ctx.expect_number() + if ctx.from_stack() == 0: + ctx.to_return(ctx.from_return() + 2) + +def meta_else(ctx): + ctx.to_return(ctx.from_return() + 1) + +def meta_literal(ctx): + pc = ctx.from_return() + 1 + return_nest = ctx.from_return() + if len(return_nest) == pc: + ctx.failed('Found a "\'" at the end of a nest.') + ctx.to_stack(return_nest[pc]) + ctx.to_return(return_nest) + ctx.to_return(pc) + +def meta_this(ctx): + pc = ctx.from_return() + return_nest = ctx.from_return() + ctx.to_stack(return_nest) + ctx.to_return(return_nest) + ctx.to_return(pc) + +def meta_do(ctx): + ctx.expect_something() + the_thing = ctx.from_stack() + if not isNest(the_thing): + the_thing = [the_thing] + ctx.to_return(the_thing) + ctx.to_return(-1) + +def meta_bail_by(ctx): + ctx.expect_number() + a = 2 * ctx.from_stack() + if a <= len(ctx.rstack): + for _ in range(a): + ctx.from_return() + else: + ctx.failed('Bailed out of Quackery.') + +def qput(ctx): + ctx.expect_nest() + a = ctx.from_stack() + ctx.expect_something() + b = ctx.from_stack() + a.append(b) + +def immovable(ctx): + pass + +def take(ctx): + ctx.expect_nest() + a = ctx.from_stack() + if len(a) == 0: + ctx.failed('Unexpectedly empty nest.') + if len(a) == 1: + if isNest(a[0]) and len(a[0]) > 0 and a[0][0] == immovable: + ctx.failed('Cannot remove an immovable item.') + ctx.to_stack(a.pop()) + +def create_nest(ctx): + ctx.to_stack([]) + +def qsplit(ctx): + ctx.expect_number() + a = ctx.from_stack() + ctx.expect_nest() + b = ctx.from_stack() + ctx.to_stack(b[:a]) + ctx.to_stack(b[a:]) + +def join(ctx): + ctx.expect_something() + b = ctx.from_stack() + if not isNest(b): + b = [b] + ctx.expect_something() + a = ctx.from_stack() + if not isNest(a): + a = [a] + ctx.to_stack(a + b) + +def qsize(ctx): + ctx.expect_nest() + ctx.to_stack(len(ctx.from_stack())) + +def qfind(ctx): + ctx.expect_nest() + nest = ctx.from_stack() + ctx.expect_something() + a = ctx.from_stack() + if a in nest: + ctx.to_stack(nest.index(a)) + else: + ctx.to_stack(len(nest)) + +def peek(ctx): + ctx.expect_number() + index = ctx.from_stack() + ctx.expect_nest() + nest = ctx.from_stack() + if index >= len(nest) or ( + index < 0 and len(nest) < abs(index)): + ctx.failed('Cannot peek an item outside a nest.') + else: + ctx.to_stack(nest[index]) + +def poke(ctx): + ctx.expect_number() + index = ctx.from_stack() + ctx.expect_nest() + nest = ctx.from_stack().copy() + ctx.expect_something() + value = ctx.from_stack() + if index >= len(nest) or ( + index < 0 and len(nest) < abs(index)): + ctx.failed('Cannot poke an item outside a nest.') + else: + nest[index] = value + ctx.to_stack(nest) + +def qnest(ctx): + ctx.expect_something() + ctx.bool_to_stack(isNest(ctx.from_stack())) + +def qnumber(ctx): + ctx.expect_something() + ctx.bool_to_stack(isNumber(ctx.from_stack())) + +def qoperator(ctx): + ctx.expect_something() + ctx.bool_to_stack(isOperator(ctx.from_stack())) + +def quid(ctx): + ctx.expect_something() + ctx.to_stack(id(ctx.from_stack())) + +def qemit(ctx): + ctx.expect_number() + char = ctx.from_stack() + if char == 13: + sys.stdout.write('\r\n') + elif 31 < char < 127: + sys.stdout.write(chr(char)) + else: + sys.stdout.write('?') # XXX @dragoncoder047 maybe use \uFFFD on platforms that support unicode? + +def ding(ctx): + sys.stdout.write('\a') + +def qinput(ctx): + prompt = ctx.string_from_stack() + ctx.string_to_stack(input(prompt)) + +filepath = [] + +def putfile(ctx): + filename = ctx.string_from_stack() + if len(filepath) > 1: + ctx.to_stack(filepath[-1]) + filename = ctx.string_from_stack() + filename + filetext = ctx.string_from_stack() + try: + with open(filename, 'x'): pass + except FileExistsError: + ctx.to_stack(false) + except: + raise + else: + try: + with open(filename, 'w') as f: f.write(filetext) + except: + raise + else: + ctx.to_stack(true) + +def releasefile(ctx): + filename = ctx.string_from_stack() + if len(filepath) > 1: + ctx.to_stack(filepath[-1]) + filename = ctx.string_from_stack() + filename + try: + os.remove(filename) + except FileNotFoundError: + ctx.to_stack(false) + except: + raise + else: + ctx.to_stack(true) + +def sharefile(ctx): + dup(ctx) + filename = ctx.string_from_stack() + if len(filepath) > 1: + ctx.to_stack(filepath[-1]) + filename = ctx.string_from_stack() + filename + try: + with open(filename) as f: filetext = f.read() + except FileNotFoundError: + ctx.to_stack(false) + except: + raise + else: + drop(ctx) + ctx.string_to_stack(filetext) + ctx.to_stack(true) + +predefined_operators = { + 'python': python, # ( $ --> ) + 'fail': qfail, # ( $ --> ) + 'nand': nand, # ( b b --> b ) + '=': equal, # ( x x --> b ) + '>': greater, # ( n n --> b ) + '1+': inc, # ( n --> n ) + '+': plus, # ( n n --> n ) + 'negate': negate, # ( n --> n ) + '*': multiply, # ( n n --> n ) + '/mod': qdivmod, # ( n n --> n n ) + '**': exponentiate, # ( n n --> n ) + '<<': shift_left, # ( f n --> f ) + '>>': shift_right, # ( f n --> f ) + '&': bitwise_and, # ( f f --> f ) + '|': bitwise_or, # ( f f --> f ) + '^': bitwise_xor, # ( f f --> f ) + '~': bitwise_not, # ( f --> f ) + 'time': qtime, # ( --> n ) + 'stacksize': stack_size, # ( --> n ) + 'nestdepth': nest_depth, # ( --> n ) + 'return': qreturn, # ( --> [ ) + 'dup': dup, # ( x --> x x ) + 'drop': drop, # ( x --> ) + 'swap': swap, # ( x x --> x x ) + 'rot': rot, # ( x x x --> x x x ) + 'over': over, # ( x x --> x x x ) + ']done[': meta_done, # ( --> ) + ']again[': meta_again, # ( --> ) + ']if[': meta_if, # ( b --> ) + ']iff[': meta_iff, # ( b --> ) + ']else[': meta_else, # ( --> ) + "]'[": meta_literal, # ( --> x ) + ']this[': meta_this, # ( --> [ ) + ']do[': meta_do, # ( x --> ) + ']bailby[': meta_bail_by, # ( n --> ) + 'put': qput, # ( x [ --> ) + 'immovable': immovable, # ( --> ) + 'take': take, # ( [ --> x ) + '[]': create_nest, # ( --> n ) + 'split': qsplit, # ( [ n --> [ [ ) + 'join': join, # ( x x --> [ ) + 'find': qfind, # ( x --> b ) + 'peek': peek, # ( [ n --> x ) + 'poke': poke, # ( x [ n --> ) + 'size': qsize, # ( [ --> n ) + 'nest?': qnest, # ( x --> b ) + 'number?': qnumber, # ( x --> b ) + 'operator?': qoperator, # ( x --> b ) + 'quid': quid, # ( x --> n ) + 'emit': qemit, # ( c --> ) + 'ding': ding, # ( --> ) + 'input': qinput, # ( $ --> $ ) + 'filepath': filepath, # ( --> s ) + 'putfile': putfile, # ( $ --> b ) + 'releasefile': releasefile, # ( $ --> b ) + 'sharefile': sharefile # ( $ --> $ b ) +} + +def qis(ctx): + ctx.check_build() + name = ctx.get_name() + ctx.operators[name] = ctx.current_build.pop() + +def qcomment(ctx): + word = '' + while word != ')': + word = ctx.next_word() + if word == '': + raise EOFError('Unclosed comment.') + +def endcomment(ctx): + raise SyntaxError('Too many end of comments.') + +def unresolved(ctx): + raise TypeError('Unresolved forward reference.') + +def forward(ctx): + ctx.current_build.append([unresolved]) + +def resolves(ctx): + name = ctx.get_name() + if name in ctx.operators: + if ctx.operators[name][0] != unresolved: + raise TypeError(name + ' is not a forward reference.') + ctx.check_build() + ctx.operators[name][0] = ctx.current_build.pop() + else: + raise NameError('Unrecognised word: ' + name) + +def char_literal(ctx): + char = ctx.one_char() + if char == '': + raise SyntaxError('No character found.') + ctx.current_build.append(ord(char)) + +def string_literal(ctx): + delimiter = '' + result = [] + while delimiter == '': + char = ctx.next_char() + if char == '': + raise EOFError('No string found.') + if ord(char) > 32: + delimiter = char + char = '' + while char != delimiter: + char = ctx.next_char() + if char == '': + raise EOFError('Endless string discovered.') + if char != delimiter: + result.append(ord(char)) + ctx.current_build.append([[meta_literal], result]) + +def hexnum(ctx): + word = ctx.get_name() + if not ishex(word): + raise SyntaxError(word + " is not hexadecimal.") + ctx.current_build.append(int(word, 16)) + +predefined_builders = { + 'is': qis, + '(': qcomment, + ')': endcomment, + 'forward': forward, + 'resolves': resolves, + 'char': char_literal, + '$': string_literal, + 'hex': hexnum +} + + +predefined_qky = r""" + + [ 0 ] is false ( --> b ) + + [ 1 ] is true ( --> b ) + + [ dup nand ] is not ( b --> b ) + + [ nand not ] is and ( b b --> b ) + + [ not swap not nand ] is or ( b b --> b ) + + [ = not ] is != ( x x --> b ) + + [ not swap not != ] is xor ( b b --> b ) + + [ swap > ] is < ( n n --> b ) + + [ negate + ] is - ( n --> n ) + + [ /mod drop ] is / ( n n --> n ) + + [ swap drop ] is nip ( x x --> x ) + + [ /mod nip ] is mod ( n n --> n ) + + [ 1 swap << ] is bit ( n --> n ) + + [ swap over ] is tuck ( x x --> x x x ) + + [ rot rot ] is unrot ( x x x --> x x x ) + + [ rot tuck > + unrot > not and ] is within ( n n n --> b ) + + [ over over ] is 2dup ( x x --> x x x x ) + + [ drop drop ] is 2drop ( x x --> ) + + [ ]again[ ] is again ( --> ) + + [ ]done[ ] is done ( --> ) + + [ ]if[ ] is if ( b --> ) + + [ ]iff[ ] is iff ( b --> ) + + [ ]else[ ] is else ( --> ) + + [ 2dup > if swap drop ] is min ( n n n --> n ) + + [ 2dup < if swap drop ] is max ( n n n --> n ) + + [ rot min max ] is clamp ( n n n --> n ) + + [ dup nest? iff [] join ] is copy ( [ --> [ ) + + [ ]'[ ] is ' ( --> x ) + + [ ]this[ ] is this ( --> [ ) + + [ ]do[ ] is do ( x --> ) + + [ ]this[ do ] is recurse ( --> ) + + [ not if ]again[ ] is until ( b --> ) + + [ not if ]done[ ] is while ( b --> ) + + [ immovable ]this[ ]done[ ] is stack ( --> s ) + + [ dup take dup rot put ] is share ( s --> x ) + + [ take drop ] is release ( s --> ) + + [ dup release put ] is replace ( x s --> ) + + [ dup take rot + swap put ] is tally ( n s --> ) + + [ swap take swap put ] is move ( s s --> ) + + [ [] tuck put ] is nested ( x --> [ ) + + [ stack [ ] ] is protected ( --> s ) + + [ protected take + ]'[ nested join + protected put ] is protect ( --> ) + + ' stack ' filepath put + protect filepath + + [ stack ] is dip.hold ( --> s ) + protect dip.hold + + [ dip.hold put + ]'[ do dip.hold take ] is dip ( x --> x ) + + [ rot dip rot ] is 2swap ( x x x x --> x x x x ) + + [ dip [ dip 2dup ] 2swap ] is 2over ( x x x x --> x x x x x x ) + + [ stack ] is depth ( --> s ) + protect depth + + [ depth share + 0 != while + -1 depth tally + ]this[ do + 1 depth tally ] is decurse ( --> ) + + [ dup 0 < if negate ] is abs ( n --> n ) + + [ stack ] is times.start ( --> s ) + protect times.start + + [ stack ] is times.count ( --> s ) + protect times.count + + [ stack ] is times.action ( --> s ) + protect times.action + + [ ]'[ times.action put + dup times.start put + [ 1 - dup -1 > while + times.count put + times.action share do + times.count take again ] + drop + times.action release + times.start release ] is times ( n --> ) + + [ times.count share ] is i ( --> n ) + + [ times.start share i 1+ - ] is i^ ( --> n ) + + [ 0 times.count replace ] is conclude ( --> ) + + [ times.start share + times.count replace ] is refresh ( --> ) + + [ times.count take 1+ + swap - times.count put ] is step ( --> s ) + + [ stack ] is temp ( --> s ) + protect temp + + [ immovable + dup -1 > + + ]this[ swap peek + ]done[ ] is table ( n --> x ) + + [ [] unrot + dup 1 < iff 2drop done + [ 2 /mod over while + if [ dip [ tuck join swap ] ] + dip [ dup join ] + again ] 2drop join ] is of ( x n --> [ ) + + [ split 1 split + swap dip join + 0 peek ] is pluck ( [ n --> [ x ) + + [ split + rot nested + swap join join ] is stuff ( x [ n --> [ ) + + [ 0 pluck ] is behead ( [ --> [ x ) + + [ over size over size + dup temp put + swap - 1+ times + [ 2dup over size split + drop = if + [ i^ temp replace + conclude ] + behead drop ] + 2drop temp take ] is findseq ( [ [ --> n ) + + [ 13 ] is carriage ( --> c ) + + [ carriage emit ] is cr ( --> ) + + [ 32 ] is space ( --> c ) + + [ space emit ] is sp ( --> ) + + [ dup char a char { within + if [ 32 - ] ] is upper ( c --> c ) + + [ dup char A char [ within + if [ 32 + ] ] is lower ( c --> c ) + + [ dup 10 < + iff 48 else 55 + ] is digit ( n --> c ) + + [ stack 10 ] is base ( --> s ) + protect base + + [ 10 base put ] is decimal ( --> ) + + [ $ '' over abs + [ base share /mod digit + rot join swap + dup 0 = until ] + drop + swap 0 < if + [ $ '-' swap join ] ] is number$ ( n --> $ ) + + [ stack ] is with.hold ( --> s ) + protect with.hold + + [ nested + ' [ dup with.hold put + size times ] + ' [ with.hold share + i ~ peek ] + rot join + nested join + ' [ with.hold release ] + join ] is makewith ( x --> [ ) + + [ ]'[ makewith do ] is witheach ( [ --> ) + + [ witheach emit ] is echo$ ( $ --> ) + + [ stack ] is mi.tidyup ( --> s ) + protect mi.tidyup + + [ stack ] is mi.result ( --> s ) + protect mi.result + + [ mi.tidyup put + over size mi.result put + nested + ' [ if + [ i^ mi.result replace + conclude ] ] + join makewith do + mi.tidyup take do + mi.result take ] is matchitem ( [ x x --> n ) + + [ ]'[ ]'[ matchitem ] is findwith ( [ --> n ) + + [ size < ] is found ( n [ --> b ) + + [ space > ] is printable ( c --> b ) + + [ dup findwith + printable [ ] + split nip ] is trim ( $ --> $ ) + + [ dup findwith + [ printable not ] [ ] + split swap ] is nextword ( $ --> $ $ ) + + [ dup nest? if + [ dup size 2 < if done + dup size 2 / split + recurse swap + recurse join ] ] is reverse ( x --> x ) + + [ [] swap times + [ swap nested join ] + reverse ] is pack ( * n --> [ ) + + [ witheach [ ] ] is unpack ( [ --> * ) + + [ stack ] is to-do ( --> s ) + protect to-do + + [ ' done swap put ] is new-do ( s --> ) + + [ dip [ 1+ pack ] put ] is add-to ( * x n s --> ) + + [ [ dup take + unpack do again ] drop ] is now-do ( s --> ) + + [ 1 split reverse join + now-do ] is do-now ( s --> ) + + [ [ dup take ' done = until ] + drop ] is not-do ( s --> ) + + [ stack ] is sort.test ( --> s ) + protect sort.test + + [ ]'[ sort.test put + [] swap witheach + [ swap 2dup findwith + [ over sort.test share + do ] [ ] + nip stuff ] + sort.test release ] is sortwith ( [ --> [ ) + + [ sortwith > ] is sort ( [ --> [ ) + + [ 32 127 clamp 32 - + [ table + 0 86 88 93 94 90 92 87 63 64 75 73 82 74 81 76 + 1 2 3 4 5 6 7 8 9 10 83 84 69 72 70 85 + 91 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 + 41 43 45 47 49 51 53 55 57 59 61 65 78 66 77 80 + 89 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 + 42 44 46 48 50 52 54 56 58 60 62 67 79 68 71 0 ] + ] is qacsfot ( c --> n ) + + [ [ dup $ '' = iff false done + over $ '' = iff true done + behead rot behead rot + 2dup = iff [ 2drop swap ] again + qacsfot swap qacsfot > ] + unrot 2drop ] is $< ( $ $ --> b ) + + [ swap $< ] is $> ( $ $ --> b ) + + [ sortwith $> ] is sort$ ( [ --> [ ) + + [ upper 47 - 0 44 clamp + [ table + -1 0 1 2 3 4 5 6 7 8 9 -1 -1 -1 -1 + -1 -1 -1 10 11 12 13 14 15 16 17 18 19 20 21 + 22 23 24 25 26 27 28 29 30 31 32 33 34 35 -1 ] + dup 0 base share + within not if [ drop -1 ] ] is char->n ( c --> n ) + + [ dup $ '' = iff [ drop 0 false ] done + dup 0 peek char - = + tuck if [ behead drop ] + dup $ '' = iff [ 2drop 0 false ] done + true 0 rot witheach + [ char->n + dup 0 < iff [ drop nip false swap ] + else [ swap base share * + ] ] + rot if negate + swap ] is $->n ( $ --> n b ) + + ( adapted from 'A small noncryptographic PRNG' by Bob Jenkins ) + ( https://burtleburtle.net/bob/rand/smallprng.html ) + + [ hex FFFFFFFFFFFFFFFF ] is 64bitmask ( --> f ) + + [ 64bitmask & ] is 64bits ( f --> f ) + + [ dip 64bits 2dup << 64bits + unrot 64 swap - >> | ] is rot64 ( f n --> f ) + + [ stack 0 ] is prng.a ( --> s ) + [ stack 0 ] is prng.b ( --> s ) + [ stack 0 ] is prng.c ( --> s ) + [ stack 0 ] is prng.d ( --> s ) + + [ prng.a share + prng.b share tuck + 7 rot64 - 64bits swap + prng.c share tuck + 13 rot64 ^ prng.a replace + prng.d share tuck + 37 rot64 + 64bits prng.b replace + over + 64bits prng.c replace + prng.a share + 64bits + dup prng.d replace ] is prng ( --> n ) + + [ hex F1EA5EAD prng.a replace + dup prng.b replace + dup prng.c replace + prng.d replace + 20 times [ prng drop ] ] is initrandom ( n --> ) + + hex DEFACEABADFACADE initrandom + + [ time initrandom ] is randomise ( --> ) + + [ 64bitmask 1+ + over / over * + [ prng 2dup > not while + drop again ] + nip swap mod ] is random ( n --> n ) + + [ [] swap dup size times + [ dup size random pluck + nested rot join swap ] + drop ] is shuffle ( [ --> [ ) + + [ stack ] is history ( --> s ) + + [ protected share history put + protected share 0 + [ over size over + > while + 2dup peek + size unrot + 1+ again ] + 2drop + protected share size pack + history put + pack dup history put unpack + stacksize history put + nestdepth history put + false history put ] is backup ( n --> ) + + [ history release + nestdepth + history take + - ]bailby[ + true history put ] is bail ( --> ) + + [ history take iff + [ stacksize + history take + history share + size - - times drop + history take unpack + history take unpack + history share size + [ dup 0 > while + 1 - + history share + over peek + rot over size + swap - + [ dup 0 > while + over release + 1 - again ] + 2drop again ] + drop + history take + protected replace + true ] + else + [ 5 times + [ history release ] + false ] ] is bailed ( --> b ) + + [ quid swap quid = ] is oats ( x x --> b ) + + [ [] swap + [ trim + dup size while + nextword nested + swap dip join again ] + drop ] is nest$ ( $ --> [ ) + + [ stack ] is namenest ( --> s ) + + [ namenest share ] is names ( --> [ ) + + [ names find names found ] is name? ( $ --> b ) + + forward is actions ( n --> x ) + + [ ' actions ] is actiontable ( --> x ) + + [ actiontable share tuck + findwith [ over oats ] drop + swap found ] is named? ( x --> b ) + + forward is reflect ( x --> x ) + [ dup nest? if + [ dup [] = if done + dup size 1 = iff + [ 0 peek + dup named? iff + nested done + reflect nested ] + done + dup size 2 / split + recurse swap + recurse join ] ] resolves reflect ( x --> x ) + + [ stack ] is buildernest ( --> s ) + + [ buildernest share ] is builders ( --> s ) + + [ builders find + builders found ] is builder? ( $ --> b ) + + forward is jobs ( n --> x ) + + [ ' jobs ] is jobtable ( --> [ ) + + [ stack ] is message ( --> s ) + + [ stack ] is b.nesting ( --> s ) + protect b.nesting + + [ stack ] is b.to-do ( --> s ) + + [ $ '[' b.nesting put + [] swap ] is b.[ ( [ $ --> [ [ $ ) + + [ b.nesting take dup + $ '' = if + [ $ 'Unexpected "]".' + message put + bail ] + dup $ '[' = iff drop + else + [ $ 'Nest mismatch: ' + swap join $ ' ]' join + message put + bail ] + dip [ nested join ] ] is b.] ( [ [ $ --> [ $ ) + + [ over [] = if + [ $ '"is" needs something to name before it.' + message put + bail ] + dup $ '' = if + [ $ '"is" needs a name after it.' + message put + bail ] + nextword nested + namenest take + join + namenest put + dip + [ -1 pluck + actiontable take + 1 stuff + actiontable put ] ] is b.is ( [ $ --> [ $ ) + + [ over [] = if + [ $ '"builds" needs something to name before it.' + message put + bail ] + dup $ '' = if + [ $ '"builds" needs a name after it.' + message put + bail ] + nextword nested + buildernest take + join + buildernest put + dip + [ -1 pluck + jobtable take + 1 stuff + jobtable put ] ] is b.builds ( [ $ --> [ $ ) + + [ trim nextword + dup $ '' = if + [ $ 'Unfinished comment.' + message put + bail ] + $ ')' = until ] is b.( ( [ $ --> $ [ ) + + [ $ 'Unexpected ")".' + message put + bail ] is b.) ( [ $ --> $ [ ) + + [ $ 'Unresolved reference.' + fail ] is unresolved ( --> ) + + [ dip + [ ' [ unresolved ] + copy nested join ] ] is b.forward ( [ $ --> [ $ ) + + [ over [] = if + [ $ '"resolves" needs something to resolve.' + message put + bail ] + dup $ '' = if + [ $ '"resolves" needs a name to resolve into.' + message put + bail ] + dip [ -1 split ] + nextword dup temp put + names find + dup names found not if + [ $ 'Unknown word after "resolves": ' + temp take join + message put + bail ] + actions + dup ' [ unresolved ] = not if + [ char " temp take join + $ '" is not an unresolved forward reference.' + join + message put + bail ] + rot 0 peek over + replace + ' unresolved swap + ' replace 2 b.to-do add-to + temp release ] is b.resolves ( [ $ --> [ $ ) + + [ 1 split + over $ '' = if + [ $ '"char" needs a character after it.' + message put + bail ] + dip join ] is b.char ( [ $ --> [ $ ) + + [ dup $ '' = if + [ $ '"$" needs to be followed by a string.' + message put + bail ] + behead over find + 2dup swap found not if + [ $ 'Endless string discovered.' + message put + bail ] + split behead drop + ' ' nested + rot nested join + nested swap dip join ] is b.$ ( [ $ --> [ $ ) + + [ dup $ '' = if + [ $ '"say" needs to be followed by a string.' + message put + bail ] + $ '$' builders find jobs do + dip + [ -1 pluck + ' echo$ nested join + nested join ] ] is b.say ( [ $ --> [ $ ) + + [ 16 base put + nextword dup + $ '' = if + [ $ '"hex" needs a number after it.' + message put + bail ] + dup $->n iff + [ nip swap dip join ] + else + [ drop + char " swap join + $ '" is not hexadecimal.' + join message put + bail ] + base release ] is b.hex ( [ $ --> [ $ ) + + [ dip [ -1 split ] swap do ] is b.now! ( [ $ --> [ $ ) + + [ over [] = if + [ $ '"constant" needs something before it.' + message put + bail ] + dip + [ -1 pluck do + dup number? not if + [ ' ' nested swap + nested join + nested ] + join ] ] is b.constant ( [ $ --> [ $ ) + + [ ' [ namenest actiontable + buildernest jobtable ] + witheach + [ do share copy + history put ] ] is backupwords ( --> ) + + [ ' [ jobtable buildernest + actiontable namenest ] + witheach + [ do dup release + history swap move ] ] is restorewords ( --> ) + + [ 4 times + [ history release ] ] is releasewords ( --> ) + + [ backupwords + b.to-do new-do + 1 backup + [ $ '' b.nesting put + decimal + [] swap + [ trim + dup $ '' = iff drop done + nextword + dup builders find + dup builders found iff + [ dip [ drop trim ] + jobs do ] again + drop + dup names find + dup names found iff + [ actions nested + nip swap dip join ] again + drop + dup $->n iff + [ nip swap dip join ] again + drop + $ 'Unknown word: ' + swap join message put + bail ] + base release + b.nesting take dup + $ '' = iff drop + else + [ $ 'Unfinished nest: ' + swap join message put + bail ] ] + bailed iff + [ drop b.to-do now-do + restorewords + ' ' nested + message take nested join + ' echo$ nested join ] + else + [ b.to-do not-do + releasewords ] ] is build ( $ --> [ ) + + [ build do ] is quackery ( $ --> ) + + [ stack -1 ] is nesting ( --> [ ) + + forward is unbuild ( x --> $ ) + + [ nesting share + 0 = iff [ drop $ '...' ] done + $ '' swap + dup number? iff + [ number$ join ] done + actiontable share + behead drop + [ dup [] = iff + [ drop false ] done + behead + rot tuck oats iff + [ drop size 2 + + actiontable share + size swap - + names swap peek join + true ] done + swap again ] + if done + dup nest? iff + [ $ '[ ' rot join swap + [ dup [] = iff drop done + behead + -1 nesting tally + unbuild + 1 nesting tally + space join + swap dip join again ] + $ ']' join ] + else + [ drop + $ "Quackery was worried by a python." + fail ] ] resolves unbuild ( x --> $ ) + + [ unbuild echo$ ] is echo ( x --> ) + + [ $ '' + return -2 split drop + witheach + [ dup number? iff + [ number$ join + $ '} ' join ] + else + [ $ '{' swap dip join + actiontable share + findwith + [ over oats ] drop + dup actiontable share + found iff + [ 1 - names swap + peek join + space join ] + else + [ drop $ '[...] ' + join ] ] ] + -1 split drop ] is return$ ( --> $ ) + + [ return$ echo$ ] is echoreturn ( --> ) + + [ stacksize dup 0 = iff + [ $ 'Stack empty.' echo$ drop ] + else + [ $ 'Stack: ' echo$ + pack dup + witheach [ echo sp ] + unpack ] + cr ] is echostack ( --> ) + + [ cr $ '' $ '/O> ' + [ input + dup $ '' != while + carriage join join + $ '... ' again ] + drop + quackery + 5 nesting put + cr echostack + nesting release again ] is shell ( --> ) + + [ cr randomise 12 random + [ table + $ 'Goodbye.' $ 'Adieu.' $ 'So long.' + $ 'Cheerio.' $ 'Aloha.' $ 'Ciao.' + $ 'Farewell.' $ 'Be seeing you.' + $ 'Sayonara.' $ 'Auf wiedersehen.' + $ 'Toodles.' $ 'Hasta la vista.' ] + do echo$ cr cr + 3 ]bailby[ ] is leave ( --> ) + + [ stacksize times drop ] is empty ( all --> ) + + [ tuck temp put + witheach + [ dup size + rot + dup + temp share > iff + [ cr drop dup size ] + else sp 1+ swap echo$ ] + drop temp release ] is wrap$ ( [ n --> ) + + [ names reverse 70 wrap$ cr + builders reverse + 70 wrap$ cr ] is words ( --> ) + + [ dup name? iff drop + else + [ dup sharefile not if + [ $ |$ 'file not found: "| + swap join + $ |"' echo$| join ] + nip quackery ] ] is loadfile ( $ --> ) + + [ dup sharefile iff + [ swap releasefile ] + else [ drop false ] ] is takefile ( $ --> $ b ) + + [ dup releasefile iff + putfile + else [ 2drop false ] ] is replacefile ( $ $ --> b ) + + [ nested ' [ ' ] + swap join + decimal unbuild + base release ] is quackify ( x --> $ ) + + $ "quackify replacefile takefile loadfile words empty wrap$ leave + shell echostack echoreturn return$ echo unbuild nesting quackery + build releasewords restorewords backupwords unresolved b.to-do + b.nesting message jobtable jobs builder? builders buildernest + reflect named? actiontable actions name? names namenest nest$ oats + bailed bail backup history shuffle random randomise initrandom + prng prng.d prng.c prng.b prng.a rot64 64bits 64bitmask $->n + char->n sort$ $> $< qacsfot sort sortwith sort.test not-do do-now + now-do add-to new-do to-do unpack pack reverse nextword trim + printable found findwith matchitem mi.result mi.tidyup echo$ + witheach makewith with.hold number$ decimal base digit lower upper + sp space cr carriage findseq behead stuff pluck of table temp step + refresh conclude i^ i times times.action times.count times.start + abs decurse depth 2over 2swap dip dip.hold protect protected + nested move tally replace release share stack while until recurse + do this ' copy clamp max min else iff if done again 2drop 2dup + within unrot tuck bit mod nip / - < xor != or and not true false + sharefile releasefile putfile filepath input ding emit quid + operator? number? nest? size poke peek find join split [] take + immovable put ]bailby[ ]do[ ]this[ ]'[ ]else[ ]iff[ ]if[ ]again[ + ]done[ over rot swap drop dup return nestdepth stacksize time ~ ^ + | & >> << ** /mod * negate + 1+ > = nand fail python" + + nest$ namenest put + + [ table + quackify replacefile takefile loadfile words empty wrap$ leave + shell echostack echoreturn return$ echo unbuild nesting quackery + build releasewords restorewords backupwords unresolved b.to-do + b.nesting message jobtable jobs builder? builders buildernest + reflect named? actiontable actions name? names namenest nest$ oats + bailed bail backup history shuffle random randomise initrandom + prng prng.d prng.c prng.b prng.a rot64 64bits 64bitmask $->n + char->n sort$ $> $< qacsfot sort sortwith sort.test not-do do-now + now-do add-to new-do to-do unpack pack reverse nextword trim + printable found findwith matchitem mi.result mi.tidyup echo$ + witheach makewith with.hold number$ decimal base digit lower upper + sp space cr carriage findseq behead stuff pluck of table temp step + refresh conclude i^ i times times.action times.count times.start + abs decurse depth 2over 2swap dip dip.hold protect protected + nested move tally replace release share stack while until recurse + do this ' copy clamp max min else iff if done again 2drop 2dup + within unrot tuck bit mod nip / - < xor != or and not true false + sharefile releasefile putfile filepath input ding emit quid + operator? number? nest? size poke peek find join split [] take + immovable put ]bailby[ ]do[ ]this[ ]'[ ]else[ ]iff[ ]if[ ]again[ + ]done[ over rot swap drop dup return nestdepth stacksize time ~ ^ + | & >> << ** /mod * negate + 1+ > = nand fail python ] + + resolves actions ( n --> x ) + + $ "constant now! hex say $ char resolves forward ) ( builds is ] [" + nest$ buildernest put + + [ table + b.constant b.now! b.hex b.say b.$ b.char b.resolves + b.forward b.) b.( b.builds b.is b.] b.[ ] + + resolves jobs ( n --> x ) + + """ + +# bootstrap only once +_qs = QuackeryContext() +_qs.run(predefined_qky) +predefined_operators = _qs.operators +del _qs + + +def quackery(source_string, ctx = None): + + if ctx is None: + ctx = QuackeryContext() + else: + for required_word in ('stacksize', 'pack', 'decimal', 'unbuild', 'quackery'): + if required_word not in ctx.operators.keys(): + raise NameError('QuackeryContext must have word %s defined.' % required_word) + + while True: + ctx.to_stack([ord(char) for char in source_string]) + try: + ctx.run('quackery') + except QuackeryError as diagnostics: + if __name__ == '__main__' and len(sys.argv) == 1: + print(diagnostics) + continue + else: + raise + except Exception as diagnostics: + print('Quackery system damage detected.') + print('Python error: ' + str(diagnostics)) + raise + else: + ctx.run('stacksize pack decimal unbuild') + the_stack = ctx.from_stack() + return ''.join(map(chr, the_stack[2:-2])) + +if __name__ == '__main__': + if len(sys.argv) > 1: + filename = sys.argv[1] + try: + with open(filename) as f: + filetext = f.read() + except FileNotFoundError: + print('file not found: "' + filename + '"') + sys.exit(1) + else: + try: + print(quackery(filetext)) + print() + except QuackeryError as diagnostics: + print('\nQuackery crashed.\n') + print(diagnostics) + print() + sys.exit(1) + except Exception as diagnostics: + print('Quackery system damage detected.') + print('Python error: ' + str(diagnostics)) + sys.exit(1) + else: + print('\nWelcome to Quackery.') + print('\nEnter "leave" to leave the shell.') + try: + quackery(r""" + + $ 'extensions.qky' dup name? not + dip sharefile and iff + [ cr say 'Building extensions.' cr quackery ] + else drop + + shell """) + except QuackeryError as diagnostics: + print('\nQuackery crashed.\n') + print(diagnostics) + print() diff --git a/webapp_start.py b/webapp_start.py index e016dd1..421a0b3 100644 --- a/webapp_start.py +++ b/webapp_start.py @@ -2,7 +2,7 @@ from pyodide.http import pyfetch from os import mkdir -import ast +import re import js from itertools import count @@ -17,12 +17,11 @@ with open(f'{file}.qky', 'w') as f: f.write(text) print('Downloading quackery.py', flush=True) -resp = await pyfetch('@@ORIGIN@@/quackery.py') -print('Started download', flush=True) +resp = await pyfetch('@@ORIGIN@@/quackery_OOP.py') quackerytext = await resp.string() # PATCH - make functions async - +''' def has_await(node): for subnode in ast.walk(node): if isinstance(subnode, ast.Await): @@ -112,6 +111,13 @@ def visit_Call(self, node): break print('Unparsing', flush=True) +''' + +NO_INDENT_DEF_RE = re.compile(r'(?[\w_][\w\d_]*)\(.*\):(?:\n+ {4}.*)+', re.M) +ONE_INDENT_DEF_RE = re.compile(r' {4}(?[\w_][\w\d_]*)\(.*\):(?:\n+ {8}.*)+', re.M) + +#TODO - patch using RE + fixedquackerytext = f''' import js @@ -124,7 +130,7 @@ async def async_patched_input(prompt): term.pause() return result -{ast.unparse(fixed_tree)}''' +{quackerytext}''' print('Loading', flush=True) with open('quackery.py', 'w') as f: f.write(fixedquackerytext) From 7907596c784539911cbcc19ba003c4a0d16c8d2b Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Wed, 18 May 2022 09:03:04 -0400 Subject: [PATCH 088/121] change to RE patching --- webapp_start.py | 149 ++++++++++++------------------------------------ 1 file changed, 38 insertions(+), 111 deletions(-) diff --git a/webapp_start.py b/webapp_start.py index 421a0b3..f958303 100644 --- a/webapp_start.py +++ b/webapp_start.py @@ -4,7 +4,7 @@ from os import mkdir import re import js -from itertools import count +from itertools import count, chain mkdir('sundry') files = ['bigrat', 'extensions', 'turtleduck', 'sundry/cards', 'sundry/demo', 'sundry/fsm', 'sundry/heapsort'] @@ -21,111 +21,43 @@ quackerytext = await resp.string() # PATCH - make functions async -''' -def has_await(node): - for subnode in ast.walk(node): - if isinstance(subnode, ast.Await): - return True - return False - -def get_name(node): - func = node.func - if isinstance(func, ast.Attribute): - return func.attr - elif isinstance(func, ast.Name): - return func.id - else: - return None - -class FixFirst(ast.NodeTransformer): - def visit_Call(self, node): - name = get_name(node) - if name is None: - return node - if name == 'input' or name == 'current_item': - print('\tAwaiting function', name) - if name == 'input': - print('\tRenaming input') - node.func.id = 'async_patched_input' - return ast.Await( - value=node, - lineno=node.lineno, - col_offset=node.col_offset, - end_lineno=node.end_lineno, - end_col_offset=node.end_col_offset + 6 - ) - else: - return node - -changed = False -asynced_functions = ['async_patched_input', 'current_item'] -class MakeFunctionAsyncValid(ast.NodeTransformer): - def visit_FunctionDef(self, node): - global changed, asynced_functions - name = node.name - if has_await(node): - print('\tFound bad function', name, '> fixing') - return ast.AsyncFunctionDef( - name=node.name, - args=node.args, - body=node.body, - decorator_list=node.decorator_list, - lineno=node.lineno, - col_offset=node.col_offset, - end_lineno=node.end_lineno, - end_col_offset=node.end_col_offset - ) - asynced_functions.append(name) - changed = True - else: - return node - -class ApplyAwaitsToAsyncedFunctions(ast.NodeTransformer): - def visit_Call(self, node): - name = get_name(node) - if name is None: - return node - if node in asynced_functions: - print('\tNow awaiting call of', name) - return ast.Await( - value=node, - lineno=node.lineno, - col_offset=node.col_offset, - end_lineno=node.end_lineno, - end_col_offset=node.end_col_offset + 6 - ) - else: - return node - -print('Parsing', flush=True) -tree = ast.parse(quackerytext) - -print('Patching', flush=True) -fixed_tree = FixFirst().visit(tree) - -for it in count(1): - print('Fixing, iteration', it, flush=True) - changed = False - fixed_tree = ApplyAwaitsToAsyncedFunctions().visit(MakeFunctionAsyncValid().visit(fixed_tree)) - if changed is False: - break - -print('Unparsing', flush=True) -''' NO_INDENT_DEF_RE = re.compile(r'(?[\w_][\w\d_]*)\(.*\):(?:\n+ {4}.*)+', re.M) ONE_INDENT_DEF_RE = re.compile(r' {4}(?[\w_][\w\d_]*)\(.*\):(?:\n+ {8}.*)+', re.M) - -#TODO - patch using RE - -fixedquackerytext = f''' +CALL_RE = r'(? 0: + done = False + print('Doing await of', name, flush=True) + +for w in ('async', 'await'): + while f'{w} {w}' in quackerytext: + quackerytext = quackerytext.replace(f'{w} {w}', w) + + +quackerytext = rf''' import js -async def async_patched_input(prompt): +async def ainput(prompt): term = js.term term.resume() - print('\\u200c', end='', flush=True) # ‌ + print('\u200c', end='', flush=True) # ‌ result = await term.read(prompt) term.pause() return result @@ -145,16 +77,11 @@ async def async_patched_input(prompt): | |_| | |_| | (_| | (__| < __/ | | |_| | | |_| | | | | | | | | | __/ \__\_\\__,_|\__,_|\___|_|\_\___|_| \__, | \___/|_| |_|_|_|_| |_|\___| |___/ - Welcome to Quackery running on the Pyodide virtual machine. - -''') -await quackery(r''' - -$ 'extensions.qky' dup name? not -dip sharefile and iff - [ cr say 'Building extensions...' cr quackery ] -else drop - -shell - -''') \ No newline at end of file + Welcome to Quackery running on the Pyodide virtual machine.''') +await quackery(r'''$ 'extensions.qky' dup name? not +dip sharefile and iff [ + cr say 'Building extensions...' cr + quackery +] else + drop +shell''') \ No newline at end of file From 441c6c1b98e7a5145b37f7b46cd84516919e2732 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Wed, 18 May 2022 09:14:07 -0400 Subject: [PATCH 089/121] typos --- webapp_start.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webapp_start.py b/webapp_start.py index f958303..cd22a0c 100644 --- a/webapp_start.py +++ b/webapp_start.py @@ -29,13 +29,13 @@ quackerytext = quackerytext.replace('input(', 'await ainput(').replace('current_item(', 'await current_item(') done = False -async_functions = [] +asynced_functions = [] while not done: done = True for m in chain(NO_INDENT_DEF_RE.finditer(quackerytext), ONE_INDENT_DEF_RE.finditer(quackerytext)): name, body = m.group('name', 0) if 'await' in body: - async_functions.append(name) + asynced_functions.append(name) print('Doing asyncing of', name, flush=True) quackerytext = quackerytext.replace(body, 'async ' + body) done = False From d7f249e1871eaa8f0de5d5ed06e0346ee25c36a3 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Wed, 18 May 2022 09:29:54 -0400 Subject: [PATCH 090/121] missed replacement argument --- webapp_start.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/webapp_start.py b/webapp_start.py index cd22a0c..1d1ed1c 100644 --- a/webapp_start.py +++ b/webapp_start.py @@ -24,7 +24,7 @@ NO_INDENT_DEF_RE = re.compile(r'(?[\w_][\w\d_]*)\(.*\):(?:\n+ {4}.*)+', re.M) ONE_INDENT_DEF_RE = re.compile(r' {4}(?[\w_][\w\d_]*)\(.*\):(?:\n+ {8}.*)+', re.M) -CALL_RE = r'(? 0: done = False print('Doing await of', name, flush=True) for w in ('async', 'await'): while f'{w} {w}' in quackerytext: + print('Doubled', w, flush=True) quackerytext = quackerytext.replace(f'{w} {w}', w) From 2333bd5b642b6f5d6033cb69bc3360efce31c6f3 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Wed, 18 May 2022 10:08:26 -0400 Subject: [PATCH 091/121] fix typos, slower to see output --- webapp_start.py | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/webapp_start.py b/webapp_start.py index 1d1ed1c..afef8c6 100644 --- a/webapp_start.py +++ b/webapp_start.py @@ -6,48 +6,55 @@ import js from itertools import count, chain +async def delay(time): + await js.Promise.new(lambda resolve, reject: js.setTimeout(resolve, time)) + mkdir('sundry') files = ['bigrat', 'extensions', 'turtleduck', 'sundry/cards', 'sundry/demo', 'sundry/fsm', 'sundry/heapsort'] for file in files: # N. B. top-level await is only allowed in Pyodide resp = await pyfetch(f'@@ORIGIN@@/{file}.qky') - print(f'Downloading {file}.qky', flush=True) + print(f'Downloading {file}.qky') text = await resp.string() with open(f'{file}.qky', 'w') as f: f.write(text) -print('Downloading quackery.py', flush=True) +print('Downloading quackery.py') resp = await pyfetch('@@ORIGIN@@/quackery_OOP.py') quackerytext = await resp.string() # PATCH - make functions async -NO_INDENT_DEF_RE = re.compile(r'(?[\w_][\w\d_]*)\(.*\):(?:\n+ {4}.*)+', re.M) -ONE_INDENT_DEF_RE = re.compile(r' {4}(?[\w_][\w\d_]*)\(.*\):(?:\n+ {8}.*)+', re.M) +NO_INDENT_DEF_RE = re.compile(r'(?(?!__)[\w_][\w\d_]*)\(.*\):(?:\n+ {4}.*)+', re.M) +ONE_INDENT_DEF_RE = re.compile(r' {4}(?(?!__)[\w_][\w\d_]*)\(.*\):(?:\n+ {8}.*)+', re.M) CALL_RE = r'(? 0: done = False - print('Doing await of', name, flush=True) + print('Doing await of', name) + delay(100) + if done: + break for w in ('async', 'await'): while f'{w} {w}' in quackerytext: - print('Doubled', w, flush=True) + print('XX>>', w) + delay(100) quackerytext = quackerytext.replace(f'{w} {w}', w) @@ -58,15 +65,15 @@ async def ainput(prompt): term = js.term term.resume() - print('\u200c', end='', flush=True) # ‌ + print('\u200c', end='') # ‌ result = await term.read(prompt) term.pause() return result {quackerytext}''' -print('Loading', flush=True) -with open('quackery.py', 'w') as f: f.write(fixedquackerytext) +print('Loading') +with open('quackery.py', 'w') as f: f.write(quackerytext) #js.term.clear() From 0b92b3006f5ed4b6ed53b95406ee15e36ff44527 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Wed, 18 May 2022 10:12:48 -0400 Subject: [PATCH 092/121] typo --- webapp_start.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp_start.py b/webapp_start.py index afef8c6..0fd42f6 100644 --- a/webapp_start.py +++ b/webapp_start.py @@ -34,7 +34,7 @@ async def delay(time): asynced_functions = [] for it in count(1): done = True - print('Iteration' it) + print('Iteration', it) for m in chain(NO_INDENT_DEF_RE.finditer(quackerytext), ONE_INDENT_DEF_RE.finditer(quackerytext)): name, body = m.group('name', 0) if 'await' in body: From e42da801490e3ffeda65e2ec7904e97b71b6b34a Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Thu, 19 May 2022 07:49:28 -0400 Subject: [PATCH 093/121] regex and await issues --- webapp_start.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/webapp_start.py b/webapp_start.py index 0fd42f6..3bb4af8 100644 --- a/webapp_start.py +++ b/webapp_start.py @@ -19,7 +19,7 @@ async def delay(time): text = await resp.string() with open(f'{file}.qky', 'w') as f: f.write(text) -print('Downloading quackery.py') +print('Downloading quackery_OOP.py') resp = await pyfetch('@@ORIGIN@@/quackery_OOP.py') quackerytext = await resp.string() @@ -27,7 +27,7 @@ async def delay(time): NO_INDENT_DEF_RE = re.compile(r'(?(?!__)[\w_][\w\d_]*)\(.*\):(?:\n+ {4}.*)+', re.M) ONE_INDENT_DEF_RE = re.compile(r' {4}(?(?!__)[\w_][\w\d_]*)\(.*\):(?:\n+ {8}.*)+', re.M) -CALL_RE = r'(? 0: done = False print('Doing await of', name) - delay(100) + await delay(100) if done: break From d29e7d886c480a7b1e68b99c7672a0f97ce90f7a Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Thu, 19 May 2022 07:51:36 -0400 Subject: [PATCH 094/121] more syntax issues --- webapp_start.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp_start.py b/webapp_start.py index 3bb4af8..a1c2959 100644 --- a/webapp_start.py +++ b/webapp_start.py @@ -27,7 +27,7 @@ async def delay(time): NO_INDENT_DEF_RE = re.compile(r'(?(?!__)[\w_][\w\d_]*)\(.*\):(?:\n+ {4}.*)+', re.M) ONE_INDENT_DEF_RE = re.compile(r' {4}(?(?!__)[\w_][\w\d_]*)\(.*\):(?:\n+ {8}.*)+', re.M) -CALL_RE = r'(? Date: Thu, 19 May 2022 08:11:27 -0400 Subject: [PATCH 095/121] print out text after fix --- webapp_start.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/webapp_start.py b/webapp_start.py index a1c2959..7254cfe 100644 --- a/webapp_start.py +++ b/webapp_start.py @@ -25,9 +25,9 @@ async def delay(time): # PATCH - make functions async -NO_INDENT_DEF_RE = re.compile(r'(?(?!__)[\w_][\w\d_]*)\(.*\):(?:\n+ {4}.*)+', re.M) -ONE_INDENT_DEF_RE = re.compile(r' {4}(?(?!__)[\w_][\w\d_]*)\(.*\):(?:\n+ {8}.*)+', re.M) -CALL_RE = r'(?(?!__)[\w_][\w\d_]*)\(.*\):(?:\n+ {4}.*)+', re.M) +ONE_INDENT_DEF_RE = re.compile(r' {4}(?(?!__)[\w_][\w\d_]*)\(.*\):(?:\n+ {8}.*)+', re.M) +CALL_RE = r'(?{quackerytext}', + {'raw': True} +) + #js.term.clear() from quackery import quackery From a21709138de87326603f2686ffa44b166d113b65 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Thu, 19 May 2022 09:57:28 -0400 Subject: [PATCH 096/121] fix syntax errors --- webapp_start.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/webapp_start.py b/webapp_start.py index 7254cfe..1ccd920 100644 --- a/webapp_start.py +++ b/webapp_start.py @@ -25,9 +25,9 @@ async def delay(time): # PATCH - make functions async -NO_INDENT_DEF_RE = re.compile(r'(?(?!__)[\w_][\w\d_]*)\(.*\):(?:\n+ {4}.*)+', re.M) -ONE_INDENT_DEF_RE = re.compile(r' {4}(?(?!__)[\w_][\w\d_]*)\(.*\):(?:\n+ {8}.*)+', re.M) -CALL_RE = r'(?(?!__)[\w_][\w\d_]*)\(.*\):(?:\n+ {4}.*)+', re.M) +ONE_INDENT_DEF_RE = re.compile(r' {4}(?(?!__)[\w_][\w\d_]*)\(.*\):(?:\n+ {8}.*)+', re.M) +CALL_RE = r'(? 0: done = False print('Doing await of', name) + while 'await ' in quackerytext: + quackerytext = quackerytext.replace('await ', 'await ') await delay(100) if done: break From 943c06a1a9c743647612f2c8a4b56bd76d73120d Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Thu, 19 May 2022 10:57:23 -0400 Subject: [PATCH 097/121] add manual asynced file --- quackery_OOP_ASYNC.py | 1672 +++++++++++++++++++++++++++++++++++++++++ webapp_start.py | 66 +- 2 files changed, 1675 insertions(+), 63 deletions(-) create mode 100644 quackery_OOP_ASYNC.py diff --git a/quackery_OOP_ASYNC.py b/quackery_OOP_ASYNC.py new file mode 100644 index 0000000..54dbe4e --- /dev/null +++ b/quackery_OOP_ASYNC.py @@ -0,0 +1,1672 @@ +# [ quackery ] + +import time +import sys +import os +import types +import js +from asyncio import Future + +async def ainput(prompt): + term = js.term + term.resume() + print('\u200c', end='') # ‌ + result = await term.read(prompt) + term.pause() + return result + +async def maybe_await(item): + if isinstance(item, Future): + await item + +def isNest(item): + return isinstance(item, list) + +def isNumber(item): + return isinstance(item, int) + +def isOperator(item): + return isinstance(item, types.FunctionType) + +def isinteger(numstr): + if len(numstr) > 0 and numstr[0] == '-': + numstr = numstr[1:] + return numstr.isdigit() + +def ishex(hexstr): + if len(hexstr) > 1 and hexstr[0] == '-': + hexstr = hexstr[1:] + return all(char.lower() in '0123456789abcdef' for char in hexstr) + +class QuackeryError(Exception): + pass + +class QuackeryContext: + def __init__(self, qstack = None, operators = None, builders = None): + self.qstack = [] if qstack is None else qstack + self.rstack = [] + self.operators = predefined_operators.copy() if operators is None else operators + self.builders = predefined_builders.copy() if builders is None else builders + self.program_counter = 0 + self.current_nest = [] + self.source = '' + self.current_build = [] + + def copy(self): + new = QuackeryContext(self.qstack.copy(), self.operators.copy(), self.builders.copy()) + new.rstack = self.rstack.copy() + new.program_counter = self.program_counter + new.current_nest = self.current_nest.copy() + new.source = self.source + new.current_build = self.current_build.copy() + return new + + async def expect_something(self): + if self.qstack == []: + await self.failed('Stack unexpectedly empty.') + + def top_of_stack(self): + return self.qstack[-1] + + async def expect_nest(self): + await self.expect_something() + if not isNest(self.top_of_stack()): + await self.failed('Expected nest on stack.') + + async def expect_number(self): + await self.expect_something() + if not isNumber(self.top_of_stack()): + await self.failed('Expected number on stack.') + + def to_stack(self, item): + self.qstack.append(item) + + async def from_stack(self): + await self.expect_something() + return self.qstack.pop() + + async def string_from_stack(self): + await self.expect_nest() + result = '' + for ch in await self.from_stack(): + if ch == 13: # \r + result += '\n' + elif 31 < ch < 127: + result += chr(ch) + else: + result += '?' # XXX @dragoncoder047 maybe use \uFFFD on platforms that support unicode? + return result + + def string_to_stack(self, string): + result = [] + for ch in string: + if ch == '\n': + result.append(13) + elif 31 < ord(ch) < 127: + result.append(ord(ch)) + else: + result.append(ord('?')) # XXX @dragoncoder047 maybe \0 or NULL to signify bad char? + self.to_stack(result) + + def bool_to_stack(self, qbool): + self.to_stack(true if qbool else false) + + def to_return(self, item): + self.rstack.append(item) + + async def from_return(self): + if len(self.rstack) == 0: + await self.failed('Return stack unexpectedly empty.') + return self.rstack.pop() + + async def failed(self, message): + await self.traverse(await self.build("stacksize pack decimal unbuild return$ nestdepth ]bailby[")) + returnstack = await self.string_from_stack() + thestack = await self.string_from_stack() + raise QuackeryError('\n Problem: ' + message + + '\nQuackery Stack: ' + str(thestack)[2:-2] + + '\n Return stack: ' + str(returnstack)) + + async def tick(self): + if self.program_counter >= len(self.current_nest): + self.program_counter = await self.from_return() + self.current_nest = await self.from_return() + self.program_counter += 1 + return + current_item = self.current_nest[self.program_counter] + if isNest(current_item): + self.to_return(self.current_nest) + self.to_return(self.program_counter) + self.current_nest = current_item + self.program_counter = 0 + elif isOperator(current_item): + await maybe_await(current_item(self)) + self.program_counter += 1 + elif isNumber(current_item): + self.to_stack(current_item) + self.program_counter += 1 + else: + await self.failed('Quackery was worried by a python.') + + + async def traverse(self, the_nest): + orig_depth = len(self.rstack) + self.to_return(self.current_nest) + self.to_return(self.program_counter) + self.current_nest = the_nest + self.program_counter = 0 + while len(self.rstack) > orig_depth: + await self.tick() + + def next_char(self): + if len(self.source) > 0: + char = self.source[0] + self.source = self.source[1:] + return char + else: + return '' + + def next_word(self): + result = '' + while True: + char = self.next_char() + if char == '': + return result + if ord(char) < 33: + if result == '': + continue + return result + result += char + + def one_char(self): + while True: + char = self.next_char() + if char == '': + return char + if ord(char) < 33: + continue + return char + + def get_name(self): + name = self.next_word() + if name == '': + raise EOFError('Unexpected end of program text.') + return name + + def check_build(self): + if len(self.current_build) == 0: + raise IndexError('Nothing to name.') + + async def build(self, source_string): + self.source = source_string + nesting = 0 + + async def sub_build(): + nonlocal nesting + the_nest = [] + while True: + self.current_build = the_nest + word = self.next_word() + if word == '': + return the_nest + elif word == '[': + nesting += 1 + the_nest.append(await sub_build()) + elif word == ']': + nesting -= 1 + if nesting < 0: + raise SyntaxError('Unexpected end of nest.') + return the_nest + elif word in self.builders.keys(): + await maybe_await(self.builders[word](self)) + elif word in self.operators.keys(): + the_nest.append(self.operators[word]) + elif isinteger(word): + the_nest.append(int(word, 10)) + else: + raise NameError('Unrecognised word: ' + word) + + the_nest = await sub_build() + if nesting > 0: + raise SyntaxError('Unfinished nest.') + return the_nest + + async def run(self, source_string): + await self.traverse(await self.build(source_string)) + + + +async def python(ctx): + """For backwards compatibility only""" + scope = { + "to_stack": ctx.to_stack, + "from_stack": ctx.from_stack, + "string_to_stack": ctx.string_to_stack, + "string_from_stack": ctx.string_from_stack, + "ctx": ctx, + } + try: + exec(await ctx.string_from_stack(), scope, globals()) + except QuackeryError: + raise + except Exception as diagnostics: + await ctx.failed('Python reported: "' + str(diagnostics) + '"') + +async def qfail(ctx): + await ctx.failed(await ctx.string_from_stack()) + +def stack_size(ctx): + ctx.to_stack(len(ctx.qstack)) + +def qreturn(ctx): + ctx.to_stack(ctx.rstack) + +async def dup(ctx): + a = await ctx.from_stack() + ctx.to_stack(a) + ctx.to_stack(a) + +async def drop(ctx): + await ctx.from_stack() + +async def swap(ctx): + a = await ctx.from_stack() + b = await ctx.from_stack() + ctx.to_stack(a) + ctx.to_stack(b) + +async def rot(ctx): + a = await ctx.from_stack() + await swap(ctx) + ctx.to_stack(a) + await swap(ctx) + +async def over(ctx): + a = await ctx.from_stack() + await dup(ctx) + ctx.to_stack(a) + await swap(ctx) + +def nest_depth(ctx): + ctx.to_stack(len(ctx.rstack) // 2) + +true = 1 + +false = 0 + +async def nand(ctx): + await ctx.expect_number() + a = await ctx.from_stack() + await ctx.expect_number() + ctx.bool_to_stack(await ctx.from_stack() == false or a == false) + +async def equal(ctx): + a = await ctx.from_stack() + ctx.bool_to_stack(a == await ctx.from_stack()) + +async def greater(ctx): + await ctx.expect_number() + a = await ctx.from_stack() + await ctx.expect_number() + ctx.bool_to_stack(await ctx.from_stack() > a) + +async def inc(ctx): + await ctx.expect_number() + ctx.to_stack(1 + await ctx.from_stack()) + +async def plus(ctx): + await ctx.expect_number() + a = await ctx.from_stack() + await ctx.expect_number() + ctx.to_stack(a + await ctx.from_stack()) + +async def negate(ctx): + await ctx.expect_number() + ctx.to_stack(-await ctx.from_stack()) + +async def multiply(ctx): + await ctx.expect_number() + a = await ctx.from_stack() + await ctx.expect_number() + ctx.to_stack(a * await ctx.from_stack()) + +async def qdivmod(ctx): + await ctx.expect_number() + a = await tx.from_stack() + if a == 0: + await ctx.failed('Division by zero.') + await ctx.expect_number() + results = divmod(await ctx.from_stack(), a) + ctx.to_stack(results[0]) + ctx.to_stack(results[1]) + +async def exponentiate(ctx): + await ctx.expect_number() + a = await ctx.from_stack() + if a < 0: + await ctx.failed('Tried to raise to a negative power: ' + str(a)) + await ctx.expect_number() + ctx.to_stack(await ctx.from_stack() ** a) + +async def shift_left(ctx): + await ctx.expect_number() + a = await ctx.from_stack() + if a < 0: + await ctx.failed('Cannot << by a negative amount: ' + str(a)) + await ctx.expect_number() + ctx.to_stack(await ctx.from_stack() << a) + +async def shift_right(ctx): + await ctx.expect_number() + a = await ctx.from_stack() + if a < 0: + await ctx.failed('Cannot >> by a negative amount: ' + str(a)) + await ctx.expect_number() + await ctx.to_stack(await ctx.from_stack() >> a) + +async def bitwise_and(ctx): + await ctx.expect_number() + a = await ctx.from_stack() + await ctx.expect_number() + ctx.to_stack(a & await ctx.from_stack()) + +async def bitwise_or(ctx): + await ctx.expect_number() + a = await ctx.from_stack() + await ctx.expect_number() + ctx.to_stack(a | await ctx.from_stack()) + +async def bitwise_xor(ctx): + await ctx.expect_number() + a = await ctx.from_stack() + await ctx.expect_number() + ctx.to_stack(a ^ await ctx.from_stack()) + +async def bitwise_not(ctx): + await ctx.expect_number() + ctx.to_stack(~await ctx.from_stack()) + +def qtime(ctx): + ctx.to_stack(int(time.time()*1000000)) + +async def meta_done(ctx): + await ctx.from_return() + await ctx.from_return() + +async def meta_again(ctx): + await ctx.from_return() + ctx.to_return(-1) + +async def meta_if(ctx): + await ctx.expect_number() + if await ctx.from_stack() == 0: + ctx.to_return(await ctx.from_return() + 1) + +async def meta_iff(ctx): + await ctx.expect_number() + if await ctx.from_stack() == 0: + ctx.to_return(await ctx.from_return() + 2) + +async def meta_else(ctx): + ctx.to_return(await ctx.from_return() + 1) + +async def meta_literal(ctx): + pc = await ctx.from_return() + 1 + return_nest = await ctx.from_return() + if len(return_nest) == pc: + await ctx.failed('Found a "\'" at the end of a nest.') + ctx.to_stack(return_nest[pc]) + ctx.to_return(return_nest) + ctx.to_return(pc) + +async def meta_this(ctx): + pc = await ctx.from_return() + return_nest = await ctx.from_return() + ctx.to_stack(return_nest) + ctx.to_return(return_nest) + ctx.to_return(pc) + +async def meta_do(ctx): + await ctx.expect_something() + the_thing = await ctx.from_stack() + if not isNest(the_thing): + the_thing = [the_thing] + ctx.to_return(the_thing) + ctx.to_return(-1) + +async def meta_bail_by(ctx): + await ctx.expect_number() + a = 2 * await ctx.from_stack() + if a <= len(ctx.rstack): + for _ in range(a): + await ctx.from_return() + else: + await ctx.failed('Bailed out of Quackery.') + +async def qput(ctx): + await ctx.expect_nest() + a = await ctx.from_stack() + await ctx.expect_something() + b = await ctx.from_stack() + a.append(b) + +def immovable(ctx): + pass + +async def take(ctx): + await ctx.expect_nest() + a = await ctx.from_stack() + if len(a) == 0: + await ctx.failed('Unexpectedly empty nest.') + if len(a) == 1: + if isNest(a[0]) and len(a[0]) > 0 and a[0][0] == immovable: + await ctx.failed('Cannot remove an immovable item.') + await ctx.to_stack(a.pop()) + +def create_nest(ctx): + ctx.to_stack([]) + +async def qsplit(ctx): + await ctx.expect_number() + a = await ctx.from_stack() + await ctx.expect_nest() + b = await ctx.from_stack() + await ctx.to_stack(b[:a]) + await ctx.to_stack(b[a:]) + +async def join(ctx): + await ctx.expect_something() + b = await ctx.from_stack() + if not isNest(b): + b = [b] + await ctx.expect_something() + a = await ctx.from_stack() + if not isNest(a): + a = [a] + await ctx.to_stack(a + b) + +async def qsize(ctx): + await ctx.expect_nest() + ctx.to_stack(len(await ctx.from_stack())) + +async def qfind(ctx): + await ctx.expect_nest() + nest = await ctx.from_stack() + await ctx.expect_something() + a = await ctx.from_stack() + if a in nest: + ctx.to_stack(nest.index(a)) + else: + ctx.to_stack(len(nest)) + +async def peek(ctx): + await ctx.expect_number() + index = await ctx.from_stack() + await ctx.expect_nest() + nest = await ctx.from_stack() + if index >= len(nest) or ( + index < 0 and len(nest) < abs(index)): + await ctx.failed('Cannot peek an item outside a nest.') + else: + ctx.to_stack(nest[index]) + +async def poke(ctx): + await ctx.expect_number() + index = await ctx.from_stack() + await ctx.expect_nest() + nest = await ctx.from_stack().copy() + value = await ctx.from_stack() + if index >= len(nest) or ( + index < 0 and len(nest) < abs(index)): + await ctx.failed('Cannot poke an item outside a nest.') + else: + nest[index] = value + ctx.to_stack(nest) + +async def qnest(ctx): + ctx.bool_to_stack(isNest(await ctx.from_stack())) + +async def qnumber(ctx): + ctx.bool_to_stack(isNumber(await ctx.from_stack())) + +async def qoperator(ctx): + ctx.bool_to_stack(isOperator(await ctx.from_stack())) + +async def quid(ctx): + ctx.to_stack(id(await ctx.from_stack())) + +async def qemit(ctx): + await ctx.expect_number() + char = await ctx.from_stack() + if char == 13: + sys.stdout.write('\r\n') + elif 31 < char < 127: + sys.stdout.write(chr(char)) + else: + sys.stdout.write('\uFFFD') + +def ding(ctx): + sys.stdout.write('\a') + +async def qinput(ctx): + prompt = await ctx.string_from_stack() + ctx.string_to_stack(await ainput(prompt)) + +filepath = [] + +async def putfile(ctx): + filename = await ctx.string_from_stack() + if len(filepath) > 1: + ctx.to_stack(filepath[-1]) + filename = await ctx.string_from_stack() + filename + filetext = await ctx.string_from_stack() + try: + with open(filename, 'x'): pass + except FileExistsError: + ctx.to_stack(false) + except: + raise + else: + try: + with open(filename, 'w') as f: f.write(filetext) + except: + raise + else: + ctx.to_stack(true) + +async def releasefile(ctx): + filename = await ctx.string_from_stack() + if len(filepath) > 1: + ctx.to_stack(filepath[-1]) + filename = await ctx.string_from_stack() + filename + try: + os.remove(filename) + except FileNotFoundError: + ctx.to_stack(false) + except: + raise + else: + ctx.to_stack(true) + +async def sharefile(ctx): + await dup(ctx) + filename = await ctx.string_from_stack() + if len(filepath) > 1: + ctx.to_stack(filepath[-1]) + filename = await ctx.string_from_stack() + filename + try: + with open(filename) as f: filetext = f.read() + except FileNotFoundError: + ctx.to_stack(false) + except: + raise + else: + await drop(ctx) + ctx.string_to_stack(filetext) + ctx.to_stack(true) + +predefined_operators = { + 'python': python, # ( $ --> ) + 'fail': qfail, # ( $ --> ) + 'nand': nand, # ( b b --> b ) + '=': equal, # ( x x --> b ) + '>': greater, # ( n n --> b ) + '1+': inc, # ( n --> n ) + '+': plus, # ( n n --> n ) + 'negate': negate, # ( n --> n ) + '*': multiply, # ( n n --> n ) + '/mod': qdivmod, # ( n n --> n n ) + '**': exponentiate, # ( n n --> n ) + '<<': shift_left, # ( f n --> f ) + '>>': shift_right, # ( f n --> f ) + '&': bitwise_and, # ( f f --> f ) + '|': bitwise_or, # ( f f --> f ) + '^': bitwise_xor, # ( f f --> f ) + '~': bitwise_not, # ( f --> f ) + 'time': qtime, # ( --> n ) + 'stacksize': stack_size, # ( --> n ) + 'nestdepth': nest_depth, # ( --> n ) + 'return': qreturn, # ( --> [ ) + 'dup': dup, # ( x --> x x ) + 'drop': drop, # ( x --> ) + 'swap': swap, # ( x x --> x x ) + 'rot': rot, # ( x x x --> x x x ) + 'over': over, # ( x x --> x x x ) + ']done[': meta_done, # ( --> ) + ']again[': meta_again, # ( --> ) + ']if[': meta_if, # ( b --> ) + ']iff[': meta_iff, # ( b --> ) + ']else[': meta_else, # ( --> ) + "]'[": meta_literal, # ( --> x ) + ']this[': meta_this, # ( --> [ ) + ']do[': meta_do, # ( x --> ) + ']bailby[': meta_bail_by, # ( n --> ) + 'put': qput, # ( x [ --> ) + 'immovable': immovable, # ( --> ) + 'take': take, # ( [ --> x ) + '[]': create_nest, # ( --> n ) + 'split': qsplit, # ( [ n --> [ [ ) + 'join': join, # ( x x --> [ ) + 'find': qfind, # ( x --> b ) + 'peek': peek, # ( [ n --> x ) + 'poke': poke, # ( x [ n --> ) + 'size': qsize, # ( [ --> n ) + 'nest?': qnest, # ( x --> b ) + 'number?': qnumber, # ( x --> b ) + 'operator?': qoperator, # ( x --> b ) + 'quid': quid, # ( x --> n ) + 'emit': qemit, # ( c --> ) + 'ding': ding, # ( --> ) + 'input': qinput, # ( $ --> $ ) + 'filepath': filepath, # ( --> s ) + 'putfile': putfile, # ( $ --> b ) + 'releasefile': releasefile, # ( $ --> b ) + 'sharefile': sharefile # ( $ --> $ b ) +} + +async def qis(ctx): + await ctx.check_build() + name = ctx.get_name() + ctx.operators[name] = ctx.current_build.pop() + +def qcomment(ctx): + word = '' + while word != ')': + word = ctx.next_word() + if word == '': + raise EOFError('Unclosed comment.') + +def endcomment(ctx): + raise SyntaxError('Too many end of comments.') + +def unresolved(ctx): + raise TypeError('Unresolved forward reference.') + +def forward(ctx): + ctx.current_build.append([unresolved]) + +async def resolves(ctx): + name = ctx.get_name() + if name in ctx.operators: + if ctx.operators[name][0] != unresolved: + raise TypeError(name + ' is not a forward reference.') + await ctx.check_build() + ctx.operators[name][0] = ctx.current_build.pop() + else: + raise NameError('Unrecognised word: ' + name) + +def char_literal(ctx): + char = ctx.one_char() + if char == '': + raise SyntaxError('No character found.') + ctx.current_build.append(ord(char)) + +def string_literal(ctx): + delimiter = '' + result = [] + while delimiter == '': + char = ctx.next_char() + if char == '': + raise EOFError('No string found.') + if ord(char) > 32: + delimiter = char + char = '' + while char != delimiter: + char = ctx.next_char() + if char == '': + raise EOFError('Endless string discovered.') + if char != delimiter: + result.append(ord(char)) + ctx.current_build.append([[meta_literal], result]) + +def hexnum(ctx): + word = ctx.get_name() + if not ishex(word): + raise SyntaxError(word + " is not hexadecimal.") + ctx.current_build.append(int(word, 16)) + +predefined_builders = { + 'is': qis, + '(': qcomment, + ')': endcomment, + 'forward': forward, + 'resolves': resolves, + 'char': char_literal, + '$': string_literal, + 'hex': hexnum +} + + +predefined_qky = r""" + [ 0 ] is false ( --> b ) + + [ 1 ] is true ( --> b ) + + [ dup nand ] is not ( b --> b ) + + [ nand not ] is and ( b b --> b ) + + [ not swap not nand ] is or ( b b --> b ) + + [ = not ] is != ( x x --> b ) + + [ not swap not != ] is xor ( b b --> b ) + + [ swap > ] is < ( n n --> b ) + + [ negate + ] is - ( n --> n ) + + [ /mod drop ] is / ( n n --> n ) + + [ swap drop ] is nip ( x x --> x ) + + [ /mod nip ] is mod ( n n --> n ) + + [ 1 swap << ] is bit ( n --> n ) + + [ swap over ] is tuck ( x x --> x x x ) + + [ rot rot ] is unrot ( x x x --> x x x ) + + [ rot tuck > + unrot > not and ] is within ( n n n --> b ) + + [ over over ] is 2dup ( x x --> x x x x ) + + [ drop drop ] is 2drop ( x x --> ) + + [ ]again[ ] is again ( --> ) + + [ ]done[ ] is done ( --> ) + + [ ]if[ ] is if ( b --> ) + + [ ]iff[ ] is iff ( b --> ) + + [ ]else[ ] is else ( --> ) + + [ 2dup > if swap drop ] is min ( n n n --> n ) + + [ 2dup < if swap drop ] is max ( n n n --> n ) + + [ rot min max ] is clamp ( n n n --> n ) + + [ dup nest? iff [] join ] is copy ( [ --> [ ) + + [ ]'[ ] is ' ( --> x ) + + [ ]this[ ] is this ( --> [ ) + + [ ]do[ ] is do ( x --> ) + + [ ]this[ do ] is recurse ( --> ) + + [ not if ]again[ ] is until ( b --> ) + + [ not if ]done[ ] is while ( b --> ) + + [ immovable ]this[ ]done[ ] is stack ( --> s ) + + [ dup take dup rot put ] is share ( s --> x ) + + [ take drop ] is release ( s --> ) + + [ dup release put ] is replace ( x s --> ) + + [ dup take rot + swap put ] is tally ( n s --> ) + + [ swap take swap put ] is move ( s s --> ) + + [ [] tuck put ] is nested ( x --> [ ) + + [ stack [ ] ] is protected ( --> s ) + + [ protected take + ]'[ nested join + protected put ] is protect ( --> ) + + ' stack ' filepath put + protect filepath + + [ stack ] is dip.hold ( --> s ) + protect dip.hold + + [ dip.hold put + ]'[ do dip.hold take ] is dip ( x --> x ) + + [ rot dip rot ] is 2swap ( x x x x --> x x x x ) + + [ dip [ dip 2dup ] 2swap ] is 2over ( x x x x --> x x x x x x ) + + [ stack ] is depth ( --> s ) + protect depth + + [ depth share + 0 != while + -1 depth tally + ]this[ do + 1 depth tally ] is decurse ( --> ) + + [ dup 0 < if negate ] is abs ( n --> n ) + + [ stack ] is times.start ( --> s ) + protect times.start + + [ stack ] is times.count ( --> s ) + protect times.count + + [ stack ] is times.action ( --> s ) + protect times.action + + [ ]'[ times.action put + dup times.start put + [ 1 - dup -1 > while + times.count put + times.action share do + times.count take again ] + drop + times.action release + times.start release ] is times ( n --> ) + + [ times.count share ] is i ( --> n ) + + [ times.start share i 1+ - ] is i^ ( --> n ) + + [ 0 times.count replace ] is conclude ( --> ) + + [ times.start share + times.count replace ] is refresh ( --> ) + + [ times.count take 1+ + swap - times.count put ] is step ( --> s ) + + [ stack ] is temp ( --> s ) + protect temp + + [ immovable + dup -1 > + + ]this[ swap peek + ]done[ ] is table ( n --> x ) + + [ [] unrot + dup 1 < iff 2drop done + [ 2 /mod over while + if [ dip [ tuck join swap ] ] + dip [ dup join ] + again ] 2drop join ] is of ( x n --> [ ) + + [ split 1 split + swap dip join + 0 peek ] is pluck ( [ n --> [ x ) + + [ split + rot nested + swap join join ] is stuff ( x [ n --> [ ) + + [ 0 pluck ] is behead ( [ --> [ x ) + + [ over size over size + dup temp put + swap - 1+ times + [ 2dup over size split + drop = if + [ i^ temp replace + conclude ] + behead drop ] + 2drop temp take ] is findseq ( [ [ --> n ) + + [ 13 ] is carriage ( --> c ) + + [ carriage emit ] is cr ( --> ) + + [ 32 ] is space ( --> c ) + + [ space emit ] is sp ( --> ) + + [ dup char a char { within + if [ 32 - ] ] is upper ( c --> c ) + + [ dup char A char [ within + if [ 32 + ] ] is lower ( c --> c ) + + [ dup 10 < + iff 48 else 55 + ] is digit ( n --> c ) + + [ stack 10 ] is base ( --> s ) + protect base + + [ 10 base put ] is decimal ( --> ) + + [ $ '' over abs + [ base share /mod digit + rot join swap + dup 0 = until ] + drop + swap 0 < if + [ $ '-' swap join ] ] is number$ ( n --> $ ) + + [ stack ] is with.hold ( --> s ) + protect with.hold + + [ nested + ' [ dup with.hold put + size times ] + ' [ with.hold share + i ~ peek ] + rot join + nested join + ' [ with.hold release ] + join ] is makewith ( x --> [ ) + + [ ]'[ makewith do ] is witheach ( [ --> ) + + [ witheach emit ] is echo$ ( $ --> ) + + [ stack ] is mi.tidyup ( --> s ) + protect mi.tidyup + + [ stack ] is mi.result ( --> s ) + protect mi.result + + [ mi.tidyup put + over size mi.result put + nested + ' [ if + [ i^ mi.result replace + conclude ] ] + join makewith do + mi.tidyup take do + mi.result take ] is matchitem ( [ x x --> n ) + + [ ]'[ ]'[ matchitem ] is findwith ( [ --> n ) + + [ size < ] is found ( n [ --> b ) + + [ space > ] is printable ( c --> b ) + + [ dup findwith + printable [ ] + split nip ] is trim ( $ --> $ ) + + [ dup findwith + [ printable not ] [ ] + split swap ] is nextword ( $ --> $ $ ) + + [ dup nest? if + [ dup size 2 < if done + dup size 2 / split + recurse swap + recurse join ] ] is reverse ( x --> x ) + + [ [] swap times + [ swap nested join ] + reverse ] is pack ( * n --> [ ) + + [ witheach [ ] ] is unpack ( [ --> * ) + + [ stack ] is to-do ( --> s ) + protect to-do + + [ ' done swap put ] is new-do ( s --> ) + + [ dip [ 1+ pack ] put ] is add-to ( * x n s --> ) + + [ [ dup take + unpack do again ] drop ] is now-do ( s --> ) + + [ 1 split reverse join + now-do ] is do-now ( s --> ) + + [ [ dup take ' done = until ] + drop ] is not-do ( s --> ) + + [ stack ] is sort.test ( --> s ) + protect sort.test + + [ ]'[ sort.test put + [] swap witheach + [ swap 2dup findwith + [ over sort.test share + do ] [ ] + nip stuff ] + sort.test release ] is sortwith ( [ --> [ ) + + [ sortwith > ] is sort ( [ --> [ ) + + [ 32 127 clamp 32 - + [ table + 0 86 88 93 94 90 92 87 63 64 75 73 82 74 81 76 + 1 2 3 4 5 6 7 8 9 10 83 84 69 72 70 85 + 91 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 + 41 43 45 47 49 51 53 55 57 59 61 65 78 66 77 80 + 89 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 + 42 44 46 48 50 52 54 56 58 60 62 67 79 68 71 0 ] + ] is qacsfot ( c --> n ) + + [ [ dup $ '' = iff false done + over $ '' = iff true done + behead rot behead rot + 2dup = iff [ 2drop swap ] again + qacsfot swap qacsfot > ] + unrot 2drop ] is $< ( $ $ --> b ) + + [ swap $< ] is $> ( $ $ --> b ) + + [ sortwith $> ] is sort$ ( [ --> [ ) + + [ upper 47 - 0 44 clamp + [ table + -1 0 1 2 3 4 5 6 7 8 9 -1 -1 -1 -1 + -1 -1 -1 10 11 12 13 14 15 16 17 18 19 20 21 + 22 23 24 25 26 27 28 29 30 31 32 33 34 35 -1 ] + dup 0 base share + within not if [ drop -1 ] ] is char->n ( c --> n ) + + [ dup $ '' = iff [ drop 0 false ] done + dup 0 peek char - = + tuck if [ behead drop ] + dup $ '' = iff [ 2drop 0 false ] done + true 0 rot witheach + [ char->n + dup 0 < iff [ drop nip false swap ] + else [ swap base share * + ] ] + rot if negate + swap ] is $->n ( $ --> n b ) + + ( adapted from 'A small noncryptographic PRNG' by Bob Jenkins ) + ( https://burtleburtle.net/bob/rand/smallprng.html ) + + [ hex FFFFFFFFFFFFFFFF ] is 64bitmask ( --> f ) + + [ 64bitmask & ] is 64bits ( f --> f ) + + [ dip 64bits 2dup << 64bits + unrot 64 swap - >> | ] is rot64 ( f n --> f ) + + [ stack 0 ] is prng.a ( --> s ) + [ stack 0 ] is prng.b ( --> s ) + [ stack 0 ] is prng.c ( --> s ) + [ stack 0 ] is prng.d ( --> s ) + + [ prng.a share + prng.b share tuck + 7 rot64 - 64bits swap + prng.c share tuck + 13 rot64 ^ prng.a replace + prng.d share tuck + 37 rot64 + 64bits prng.b replace + over + 64bits prng.c replace + prng.a share + 64bits + dup prng.d replace ] is prng ( --> n ) + + [ hex F1EA5EAD prng.a replace + dup prng.b replace + dup prng.c replace + prng.d replace + 20 times [ prng drop ] ] is initrandom ( n --> ) + + hex DEFACEABADFACADE initrandom + + [ time initrandom ] is randomise ( --> ) + + [ 64bitmask 1+ + over / over * + [ prng 2dup > not while + drop again ] + nip swap mod ] is random ( n --> n ) + + [ [] swap dup size times + [ dup size random pluck + nested rot join swap ] + drop ] is shuffle ( [ --> [ ) + + [ stack ] is history ( --> s ) + + [ protected share history put + protected share 0 + [ over size over + > while + 2dup peek + size unrot + 1+ again ] + 2drop + protected share size pack + history put + pack dup history put unpack + stacksize history put + nestdepth history put + false history put ] is backup ( n --> ) + + [ history release + nestdepth + history take + - ]bailby[ + true history put ] is bail ( --> ) + + [ history take iff + [ stacksize + history take + history share + size - - times drop + history take unpack + history take unpack + history share size + [ dup 0 > while + 1 - + history share + over peek + rot over size + swap - + [ dup 0 > while + over release + 1 - again ] + 2drop again ] + drop + history take + protected replace + true ] + else + [ 5 times + [ history release ] + false ] ] is bailed ( --> b ) + + [ quid swap quid = ] is oats ( x x --> b ) + + [ [] swap + [ trim + dup size while + nextword nested + swap dip join again ] + drop ] is nest$ ( $ --> [ ) + + [ stack ] is namenest ( --> s ) + + [ namenest share ] is names ( --> [ ) + + [ names find names found ] is name? ( $ --> b ) + + forward is actions ( n --> x ) + + [ ' actions ] is actiontable ( --> x ) + + [ actiontable share tuck + findwith [ over oats ] drop + swap found ] is named? ( x --> b ) + + forward is reflect ( x --> x ) + [ dup nest? if + [ dup [] = if done + dup size 1 = iff + [ 0 peek + dup named? iff + nested done + reflect nested ] + done + dup size 2 / split + recurse swap + recurse join ] ] resolves reflect ( x --> x ) + + [ stack ] is buildernest ( --> s ) + + [ buildernest share ] is builders ( --> s ) + + [ builders find + builders found ] is builder? ( $ --> b ) + + forward is jobs ( n --> x ) + + [ ' jobs ] is jobtable ( --> [ ) + + [ stack ] is message ( --> s ) + + [ stack ] is b.nesting ( --> s ) + protect b.nesting + + [ stack ] is b.to-do ( --> s ) + + [ $ '[' b.nesting put + [] swap ] is b.[ ( [ $ --> [ [ $ ) + + [ b.nesting take dup + $ '' = if + [ $ 'Unexpected "]".' + message put + bail ] + dup $ '[' = iff drop + else + [ $ 'Nest mismatch: ' + swap join $ ' ]' join + message put + bail ] + dip [ nested join ] ] is b.] ( [ [ $ --> [ $ ) + + [ over [] = if + [ $ '"is" needs something to name before it.' + message put + bail ] + dup $ '' = if + [ $ '"is" needs a name after it.' + message put + bail ] + nextword nested + namenest take + join + namenest put + dip + [ -1 pluck + actiontable take + 1 stuff + actiontable put ] ] is b.is ( [ $ --> [ $ ) + + [ over [] = if + [ $ '"builds" needs something to name before it.' + message put + bail ] + dup $ '' = if + [ $ '"builds" needs a name after it.' + message put + bail ] + nextword nested + buildernest take + join + buildernest put + dip + [ -1 pluck + jobtable take + 1 stuff + jobtable put ] ] is b.builds ( [ $ --> [ $ ) + + [ trim nextword + dup $ '' = if + [ $ 'Unfinished comment.' + message put + bail ] + $ ')' = until ] is b.( ( [ $ --> $ [ ) + + [ $ 'Unexpected ")".' + message put + bail ] is b.) ( [ $ --> $ [ ) + + [ $ 'Unresolved reference.' + fail ] is unresolved ( --> ) + + [ dip + [ ' [ unresolved ] + copy nested join ] ] is b.forward ( [ $ --> [ $ ) + + [ over [] = if + [ $ '"resolves" needs something to resolve.' + message put + bail ] + dup $ '' = if + [ $ '"resolves" needs a name to resolve into.' + message put + bail ] + dip [ -1 split ] + nextword dup temp put + names find + dup names found not if + [ $ 'Unknown word after "resolves": ' + temp take join + message put + bail ] + actions + dup ' [ unresolved ] = not if + [ char " temp take join + $ '" is not an unresolved forward reference.' + join + message put + bail ] + rot 0 peek over + replace + ' unresolved swap + ' replace 2 b.to-do add-to + temp release ] is b.resolves ( [ $ --> [ $ ) + + [ 1 split + over $ '' = if + [ $ '"char" needs a character after it.' + message put + bail ] + dip join ] is b.char ( [ $ --> [ $ ) + + [ dup $ '' = if + [ $ '"$" needs to be followed by a string.' + message put + bail ] + behead over find + 2dup swap found not if + [ $ 'Endless string discovered.' + message put + bail ] + split behead drop + ' ' nested + rot nested join + nested swap dip join ] is b.$ ( [ $ --> [ $ ) + + [ dup $ '' = if + [ $ '"say" needs to be followed by a string.' + message put + bail ] + $ '$' builders find jobs do + dip + [ -1 pluck + ' echo$ nested join + nested join ] ] is b.say ( [ $ --> [ $ ) + + [ 16 base put + nextword dup + $ '' = if + [ $ '"hex" needs a number after it.' + message put + bail ] + dup $->n iff + [ nip swap dip join ] + else + [ drop + char " swap join + $ '" is not hexadecimal.' + join message put + bail ] + base release ] is b.hex ( [ $ --> [ $ ) + + [ dip [ -1 split ] swap do ] is b.now! ( [ $ --> [ $ ) + + [ over [] = if + [ $ '"constant" needs something before it.' + message put + bail ] + dip + [ -1 pluck do + dup number? not if + [ ' ' nested swap + nested join + nested ] + join ] ] is b.constant ( [ $ --> [ $ ) + + [ ' [ namenest actiontable + buildernest jobtable ] + witheach + [ do share copy + history put ] ] is backupwords ( --> ) + + [ ' [ jobtable buildernest + actiontable namenest ] + witheach + [ do dup release + history swap move ] ] is restorewords ( --> ) + + [ 4 times + [ history release ] ] is releasewords ( --> ) + + [ backupwords + b.to-do new-do + 1 backup + [ $ '' b.nesting put + decimal + [] swap + [ trim + dup $ '' = iff drop done + nextword + dup builders find + dup builders found iff + [ dip [ drop trim ] + jobs do ] again + drop + dup names find + dup names found iff + [ actions nested + nip swap dip join ] again + drop + dup $->n iff + [ nip swap dip join ] again + drop + $ 'Unknown word: ' + swap join message put + bail ] + base release + b.nesting take dup + $ '' = iff drop + else + [ $ 'Unfinished nest: ' + swap join message put + bail ] ] + bailed iff + [ drop b.to-do now-do + restorewords + ' ' nested + message take nested join + ' echo$ nested join ] + else + [ b.to-do not-do + releasewords ] ] is build ( $ --> [ ) + + [ build do ] is quackery ( $ --> ) + + [ stack -1 ] is nesting ( --> [ ) + + forward is unbuild ( x --> $ ) + + [ nesting share + 0 = iff [ drop $ '...' ] done + $ '' swap + dup number? iff + [ number$ join ] done + actiontable share + behead drop + [ dup [] = iff + [ drop false ] done + behead + rot tuck oats iff + [ drop size 2 + + actiontable share + size swap - + names swap peek join + true ] done + swap again ] + if done + dup nest? iff + [ $ '[ ' rot join swap + [ dup [] = iff drop done + behead + -1 nesting tally + unbuild + 1 nesting tally + space join + swap dip join again ] + $ ']' join ] + else + [ drop + $ "Quackery was worried by a python." + fail ] ] resolves unbuild ( x --> $ ) + + [ unbuild echo$ ] is echo ( x --> ) + + [ $ '' + return -2 split drop + witheach + [ dup number? iff + [ number$ join + $ '} ' join ] + else + [ $ '{' swap dip join + actiontable share + findwith + [ over oats ] drop + dup actiontable share + found iff + [ 1 - names swap + peek join + space join ] + else + [ drop $ '[...] ' + join ] ] ] + -1 split drop ] is return$ ( --> $ ) + + [ return$ echo$ ] is echoreturn ( --> ) + + [ stacksize dup 0 = iff + [ $ 'Stack empty.' echo$ drop ] + else + [ $ 'Stack: ' echo$ + pack dup + witheach [ echo sp ] + unpack ] + cr ] is echostack ( --> ) + + [ cr $ '' $ '/O> ' + [ input + dup $ '' != while + carriage join join + $ '... ' again ] + drop + quackery + 5 nesting put + cr echostack + nesting release again ] is shell ( --> ) + + [ cr randomise 12 random + [ table + $ 'Goodbye.' $ 'Adieu.' $ 'So long.' + $ 'Cheerio.' $ 'Aloha.' $ 'Ciao.' + $ 'Farewell.' $ 'Be seeing you.' + $ 'Sayonara.' $ 'Auf wiedersehen.' + $ 'Toodles.' $ 'Hasta la vista.' ] + do echo$ cr cr + 3 ]bailby[ ] is leave ( --> ) + + [ stacksize times drop ] is empty ( all --> ) + + [ tuck temp put + witheach + [ dup size + rot + dup + temp share > iff + [ cr drop dup size ] + else sp 1+ swap echo$ ] + drop temp release ] is wrap$ ( [ n --> ) + + [ names reverse 70 wrap$ cr + builders reverse + 70 wrap$ cr ] is words ( --> ) + + [ dup name? iff drop + else + [ dup sharefile not if + [ $ |$ 'file not found: "| + swap join + $ |"' echo$| join ] + nip quackery ] ] is loadfile ( $ --> ) + + [ dup sharefile iff + [ swap releasefile ] + else [ drop false ] ] is takefile ( $ --> $ b ) + + [ dup releasefile iff + putfile + else [ 2drop false ] ] is replacefile ( $ $ --> b ) + + [ nested ' [ ' ] + swap join + decimal unbuild + base release ] is quackify ( x --> $ ) + + $ "quackify replacefile takefile loadfile words empty wrap$ leave + shell echostack echoreturn return$ echo unbuild nesting quackery + build releasewords restorewords backupwords unresolved b.to-do + b.nesting message jobtable jobs builder? builders buildernest + reflect named? actiontable actions name? names namenest nest$ oats + bailed bail backup history shuffle random randomise initrandom + prng prng.d prng.c prng.b prng.a rot64 64bits 64bitmask $->n + char->n sort$ $> $< qacsfot sort sortwith sort.test not-do do-now + now-do add-to new-do to-do unpack pack reverse nextword trim + printable found findwith matchitem mi.result mi.tidyup echo$ + witheach makewith with.hold number$ decimal base digit lower upper + sp space cr carriage findseq behead stuff pluck of table temp step + refresh conclude i^ i times times.action times.count times.start + abs decurse depth 2over 2swap dip dip.hold protect protected + nested move tally replace release share stack while until recurse + do this ' copy clamp max min else iff if done again 2drop 2dup + within unrot tuck bit mod nip / - < xor != or and not true false + sharefile releasefile putfile filepath input ding emit quid + operator? number? nest? size poke peek find join split [] take + immovable put ]bailby[ ]do[ ]this[ ]'[ ]else[ ]iff[ ]if[ ]again[ + ]done[ over rot swap drop dup return nestdepth stacksize time ~ ^ + | & >> << ** /mod * negate + 1+ > = nand fail python" + + nest$ namenest put + + [ table + quackify replacefile takefile loadfile words empty wrap$ leave + shell echostack echoreturn return$ echo unbuild nesting quackery + build releasewords restorewords backupwords unresolved b.to-do + b.nesting message jobtable jobs builder? builders buildernest + reflect named? actiontable actions name? names namenest nest$ oats + bailed bail backup history shuffle random randomise initrandom + prng prng.d prng.c prng.b prng.a rot64 64bits 64bitmask $->n + char->n sort$ $> $< qacsfot sort sortwith sort.test not-do do-now + now-do add-to new-do to-do unpack pack reverse nextword trim + printable found findwith matchitem mi.result mi.tidyup echo$ + witheach makewith with.hold number$ decimal base digit lower upper + sp space cr carriage findseq behead stuff pluck of table temp step + refresh conclude i^ i times times.action times.count times.start + abs decurse depth 2over 2swap dip dip.hold protect protected + nested move tally replace release share stack while until recurse + do this ' copy clamp max min else iff if done again 2drop 2dup + within unrot tuck bit mod nip / - < xor != or and not true false + sharefile releasefile putfile filepath input ding emit quid + operator? number? nest? size poke peek find join split [] take + immovable put ]bailby[ ]do[ ]this[ ]'[ ]else[ ]iff[ ]if[ ]again[ + ]done[ over rot swap drop dup return nestdepth stacksize time ~ ^ + | & >> << ** /mod * negate + 1+ > = nand fail python ] + + resolves actions ( n --> x ) + + $ "constant now! hex say $ char resolves forward ) ( builds is ] [" + nest$ buildernest put + + [ table + b.constant b.now! b.hex b.say b.$ b.char b.resolves + b.forward b.) b.( b.builds b.is b.] b.[ ] + + resolves jobs ( n --> x )""" + +async def bootstrap(): + global predefined_operators + _qs = QuackeryContext() + await _qs.run(predefined_qky) + predefined_operators = _qs.operators + del _qs + + +async def quackery(source_string, ctx = None): + global bootstrap + if bootstrap is not None: + await bootstrap() + bootstrap = None + + if ctx is None: + ctx = QuackeryContext() + else: + for required_word in ('stacksize', 'pack', 'decimal', 'unbuild', 'quackery'): + if required_word not in ctx.operators.keys(): + raise NameError('QuackeryContext must have word %s defined.' % required_word) + + while True: + ctx.to_stack([ord(char) for char in source_string]) + try: + ctx.run('quackery') + except QuackeryError as diagnostics: + if __name__ == '__main__' and len(sys.argv) == 1: + print(diagnostics) + continue + else: + raise + except Exception as diagnostics: + print('Quackery system damage detected.') + print('Python error: ' + str(diagnostics)) + raise + else: + ctx.run('stacksize pack decimal unbuild') + the_stack = ctx.from_stack() + return ''.join(map(chr, the_stack[2:-2])) diff --git a/webapp_start.py b/webapp_start.py index 1ccd920..6df480d 100644 --- a/webapp_start.py +++ b/webapp_start.py @@ -19,72 +19,12 @@ async def delay(time): text = await resp.string() with open(f'{file}.qky', 'w') as f: f.write(text) -print('Downloading quackery_OOP.py') -resp = await pyfetch('@@ORIGIN@@/quackery_OOP.py') +print('Downloading quackery_OOP_ASYNC.py') +resp = await pyfetch('@@ORIGIN@@/quackery_OOP_ASYNC.py') quackerytext = await resp.string() - -# PATCH - make functions async - -NO_INDENT_DEF_RE = re.compile(r'(?(?!__)[\w_][\w\d_]*)\(.*\):(?:\n+ {4}.*)+', re.M) -ONE_INDENT_DEF_RE = re.compile(r' {4}(?(?!__)[\w_][\w\d_]*)\(.*\):(?:\n+ {8}.*)+', re.M) -CALL_RE = r'(? 0: - done = False - print('Doing await of', name) - while 'await ' in quackerytext: - quackerytext = quackerytext.replace('await ', 'await ') - await delay(100) - if done: - break - -for w in ('async', 'await'): - while f'{w} {w}' in quackerytext: - print('XX>>', w) - delay(100) - quackerytext = quackerytext.replace(f'{w} {w}', w) - - -quackerytext = rf''' - -import js - -async def ainput(prompt): - term = js.term - term.resume() - print('\u200c', end='') # ‌ - result = await term.read(prompt) - term.pause() - return result - -{quackerytext}''' - -print('Loading') with open('quackery.py', 'w') as f: f.write(quackerytext) -js.term.echo( - f'{quackerytext}', - {'raw': True} -) - -#js.term.clear() +js.term.clear() from quackery import quackery print(r''' From 1bf8beec41d33b47c2388c32875d1c08971f475f Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Thu, 19 May 2022 11:03:24 -0400 Subject: [PATCH 098/121] actually try to await it --- quackery_OOP_ASYNC.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/quackery_OOP_ASYNC.py b/quackery_OOP_ASYNC.py index 54dbe4e..bf4476d 100644 --- a/quackery_OOP_ASYNC.py +++ b/quackery_OOP_ASYNC.py @@ -16,8 +16,10 @@ async def ainput(prompt): return result async def maybe_await(item): - if isinstance(item, Future): + try: await item + except TypeError: + pass def isNest(item): return isinstance(item, list) From 95db8ac76ee1206ebf688ce27d7b9c9c00115aec Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Thu, 19 May 2022 11:07:15 -0400 Subject: [PATCH 099/121] only need it twice --- quackery_OOP_ASYNC.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/quackery_OOP_ASYNC.py b/quackery_OOP_ASYNC.py index bf4476d..3a2ed88 100644 --- a/quackery_OOP_ASYNC.py +++ b/quackery_OOP_ASYNC.py @@ -15,12 +15,6 @@ async def ainput(prompt): term.pause() return result -async def maybe_await(item): - try: - await item - except TypeError: - pass - def isNest(item): return isinstance(item, list) @@ -142,7 +136,10 @@ async def tick(self): self.current_nest = current_item self.program_counter = 0 elif isOperator(current_item): - await maybe_await(current_item(self)) + try: + await current_item(self) + except TypeError: + pass self.program_counter += 1 elif isNumber(current_item): self.to_stack(current_item) @@ -220,7 +217,10 @@ async def sub_build(): raise SyntaxError('Unexpected end of nest.') return the_nest elif word in self.builders.keys(): - await maybe_await(self.builders[word](self)) + try: + await self.builders[word](self) + except TypeError: + pass elif word in self.operators.keys(): the_nest.append(self.operators[word]) elif isinteger(word): From 9bb91407bddebfa69297a7f9315ad8350e06189c Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Thu, 19 May 2022 11:12:20 -0400 Subject: [PATCH 100/121] maybe parenthesis fix it? --- quackery_OOP_ASYNC.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quackery_OOP_ASYNC.py b/quackery_OOP_ASYNC.py index 3a2ed88..5551e8f 100644 --- a/quackery_OOP_ASYNC.py +++ b/quackery_OOP_ASYNC.py @@ -218,7 +218,7 @@ async def sub_build(): return the_nest elif word in self.builders.keys(): try: - await self.builders[word](self) + await (self.builders[word](self)) except TypeError: pass elif word in self.operators.keys(): From 7a7fbe91d4fb2a7e1355c0b66ac6522c0d9c1072 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Thu, 16 Jun 2022 21:03:01 -0400 Subject: [PATCH 101/121] noticed a typo --- quackery_OOP_ASYNC.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quackery_OOP_ASYNC.py b/quackery_OOP_ASYNC.py index 5551e8f..516ade4 100644 --- a/quackery_OOP_ASYNC.py +++ b/quackery_OOP_ASYNC.py @@ -334,7 +334,7 @@ async def multiply(ctx): async def qdivmod(ctx): await ctx.expect_number() - a = await tx.from_stack() + a = await ctx.from_stack() if a == 0: await ctx.failed('Division by zero.') await ctx.expect_number() From 1624a5d01bd5f374aef087bfe32688b05a0f2102 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Thu, 21 Jul 2022 13:52:29 -0400 Subject: [PATCH 102/121] Update webapp_main.js --- webapp_main.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/webapp_main.js b/webapp_main.js index 10c0f46..3f2511a 100644 --- a/webapp_main.js +++ b/webapp_main.js @@ -13,7 +13,10 @@ window.addEventListener('DOMContentLoaded', async function main() { greetings: '', prompt: '', completionEscape: false, - pauseEvents: false + pauseEvents: false, + exit: false, + scrollOnEcho: true, + mousewheel: () => true }); term.pause(); window.term = term; From 03b483597499a2e75d9e8de2d293eac2c2c3a496 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Thu, 21 Jul 2022 13:58:30 -0400 Subject: [PATCH 103/121] async everything --- quackery_OOP_ASYNC.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/quackery_OOP_ASYNC.py b/quackery_OOP_ASYNC.py index 516ade4..42f6f8c 100644 --- a/quackery_OOP_ASYNC.py +++ b/quackery_OOP_ASYNC.py @@ -257,10 +257,10 @@ async def python(ctx): async def qfail(ctx): await ctx.failed(await ctx.string_from_stack()) -def stack_size(ctx): +async def stack_size(ctx): ctx.to_stack(len(ctx.qstack)) -def qreturn(ctx): +async def qreturn(ctx): ctx.to_stack(ctx.rstack) async def dup(ctx): @@ -289,7 +289,7 @@ async def over(ctx): ctx.to_stack(a) await swap(ctx) -def nest_depth(ctx): +async def nest_depth(ctx): ctx.to_stack(len(ctx.rstack) // 2) true = 1 @@ -388,7 +388,7 @@ async def bitwise_not(ctx): await ctx.expect_number() ctx.to_stack(~await ctx.from_stack()) -def qtime(ctx): +async def qtime(ctx): ctx.to_stack(int(time.time()*1000000)) async def meta_done(ctx): @@ -452,7 +452,7 @@ async def qput(ctx): b = await ctx.from_stack() a.append(b) -def immovable(ctx): +async def immovable(ctx): pass async def take(ctx): @@ -465,7 +465,7 @@ async def take(ctx): await ctx.failed('Cannot remove an immovable item.') await ctx.to_stack(a.pop()) -def create_nest(ctx): +async def create_nest(ctx): ctx.to_stack([]) async def qsplit(ctx): @@ -547,7 +547,7 @@ async def qemit(ctx): else: sys.stdout.write('\uFFFD') -def ding(ctx): +async def ding(ctx): sys.stdout.write('\a') async def qinput(ctx): @@ -671,23 +671,23 @@ async def qis(ctx): name = ctx.get_name() ctx.operators[name] = ctx.current_build.pop() -def qcomment(ctx): +async def qcomment(ctx): word = '' while word != ')': word = ctx.next_word() if word == '': raise EOFError('Unclosed comment.') -def endcomment(ctx): +async def endcomment(ctx): raise SyntaxError('Too many end of comments.') -def unresolved(ctx): +async def unresolved(ctx): raise TypeError('Unresolved forward reference.') -def forward(ctx): +async def forward(ctx): ctx.current_build.append([unresolved]) -async def resolves(ctx): +async async def resolves(ctx): name = ctx.get_name() if name in ctx.operators: if ctx.operators[name][0] != unresolved: @@ -697,13 +697,13 @@ async def resolves(ctx): else: raise NameError('Unrecognised word: ' + name) -def char_literal(ctx): +async def char_literal(ctx): char = ctx.one_char() if char == '': raise SyntaxError('No character found.') ctx.current_build.append(ord(char)) -def string_literal(ctx): +async def string_literal(ctx): delimiter = '' result = [] while delimiter == '': @@ -721,7 +721,7 @@ def string_literal(ctx): result.append(ord(char)) ctx.current_build.append([[meta_literal], result]) -def hexnum(ctx): +async def hexnum(ctx): word = ctx.get_name() if not ishex(word): raise SyntaxError(word + " is not hexadecimal.") From 5dc22e6d6ea609a083817d04d97b7e7011f4ab2c Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Thu, 21 Jul 2022 14:05:18 -0400 Subject: [PATCH 104/121] oops --- quackery_OOP_ASYNC.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quackery_OOP_ASYNC.py b/quackery_OOP_ASYNC.py index 42f6f8c..69082b5 100644 --- a/quackery_OOP_ASYNC.py +++ b/quackery_OOP_ASYNC.py @@ -687,7 +687,7 @@ async def unresolved(ctx): async def forward(ctx): ctx.current_build.append([unresolved]) -async async def resolves(ctx): +async def resolves(ctx): name = ctx.get_name() if name in ctx.operators: if ctx.operators[name][0] != unresolved: From 5a3ad6f4f3b213fea2d3b3fbe2720f2728fe6c8e Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Fri, 2 Sep 2022 21:30:03 -0400 Subject: [PATCH 105/121] Update quackery_OOP.py --- quackery_OOP.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/quackery_OOP.py b/quackery_OOP.py index bc5a60a..2a7edad 100644 --- a/quackery_OOP.py +++ b/quackery_OOP.py @@ -209,9 +209,9 @@ def sub_build(): if nesting < 0: raise SyntaxError('Unexpected end of nest.') return the_nest - elif word in self.builders.keys(): + elif word in self.builders: self.builders[word](self) - elif word in self.operators.keys(): + elif word in self.operators: the_nest.append(self.operators[word]) elif isinteger(word): the_nest.append(int(word, 10)) @@ -1646,7 +1646,7 @@ def quackery(source_string, ctx = None): ctx = QuackeryContext() else: for required_word in ('stacksize', 'pack', 'decimal', 'unbuild', 'quackery'): - if required_word not in ctx.operators.keys(): + if required_word not in ctx.operators: raise NameError('QuackeryContext must have word %s defined.' % required_word) while True: From 07396c737b7d6cb71537aba4f199e9341a37fd33 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Fri, 2 Sep 2022 21:31:48 -0400 Subject: [PATCH 106/121] Update quackery_OOP_ASYNC.py --- quackery_OOP_ASYNC.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/quackery_OOP_ASYNC.py b/quackery_OOP_ASYNC.py index 69082b5..3fd53f9 100644 --- a/quackery_OOP_ASYNC.py +++ b/quackery_OOP_ASYNC.py @@ -216,12 +216,12 @@ async def sub_build(): if nesting < 0: raise SyntaxError('Unexpected end of nest.') return the_nest - elif word in self.builders.keys(): + elif word in self.builders: try: await (self.builders[word](self)) except TypeError: pass - elif word in self.operators.keys(): + elif word in self.operators: the_nest.append(self.operators[word]) elif isinteger(word): the_nest.append(int(word, 10)) @@ -1651,7 +1651,7 @@ async def quackery(source_string, ctx = None): ctx = QuackeryContext() else: for required_word in ('stacksize', 'pack', 'decimal', 'unbuild', 'quackery'): - if required_word not in ctx.operators.keys(): + if required_word not in ctx.operators: raise NameError('QuackeryContext must have word %s defined.' % required_word) while True: From d72cf7aa807e52c543afc4bb7582a3276b4cc878 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Fri, 21 Oct 2022 22:52:48 -0400 Subject: [PATCH 107/121] add kludge function --- webapp_start.py | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/webapp_start.py b/webapp_start.py index 6df480d..681a6cf 100644 --- a/webapp_start.py +++ b/webapp_start.py @@ -20,13 +20,22 @@ async def delay(time): with open(f'{file}.qky', 'w') as f: f.write(text) print('Downloading quackery_OOP_ASYNC.py') -resp = await pyfetch('@@ORIGIN@@/quackery_OOP_ASYNC.py') +resp = await pyfetch('@@ORIGIN@@/quackery_OOP.py') quackerytext = await resp.string() with open('quackery.py', 'w') as f: f.write(quackerytext) js.term.clear() -from quackery import quackery +async def ainput(prompt): + term = js.term + term.resume() + print('\u200c', end='') # ‌ + result = await term.read(prompt) + term.pause() + return result + +from quackery import quackery, QuackeryContext +qc = QuackeryContext() print(r''' ___ _ ___ _ _ / _ \ _ _ __ _ ___| | _____ _ __ _ _ / _ \ _ __ | (_)_ __ ___ @@ -34,11 +43,21 @@ async def delay(time): | |_| | |_| | (_| | (__| < __/ | | |_| | | |_| | | | | | | | | | __/ \__\_\\__,_|\__,_|\___|_|\_\___|_| \__, | \___/|_| |_|_|_|_| |_|\___| |___/ - Welcome to Quackery running on the Pyodide virtual machine.''') -await quackery(r'''$ 'extensions.qky' dup name? not -dip sharefile and iff [ - cr say 'Building extensions...' cr - quackery -] else - drop -shell''') \ No newline at end of file + Welcome to Quackery running on the Pyodide virtual machine. + Don't type 'leave' or you'll break something.''') + +quackery(r'''$ 'extensions.qky' dup name? not dip sharefile and iff [ cr say 'Building extensions...' cr quackery ] else drop''', qc) + +async def shell_loop(): + while True: + prompt = '/O> ' + input = '' + while True: + i = await ainput(prompt) + input += i + '\n' + prompt = '... ' + if not i: + break + ctx.to_stack([ord(char) for char in input]) + quackery('quackery 5 nesting put cr echostack nesting release') +await shell_loop() From c81e86dfddea074548d8fe4090931484ce82a99c Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Fri, 21 Oct 2022 22:55:00 -0400 Subject: [PATCH 108/121] typos --- webapp_start.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webapp_start.py b/webapp_start.py index 681a6cf..e1fd1d8 100644 --- a/webapp_start.py +++ b/webapp_start.py @@ -58,6 +58,6 @@ async def shell_loop(): prompt = '... ' if not i: break - ctx.to_stack([ord(char) for char in input]) - quackery('quackery 5 nesting put cr echostack nesting release') + qc.to_stack([ord(char) for char in input]) + quackery('quackery 5 nesting put cr echostack nesting release', qc) await shell_loop() From e5a59dcbd45d9ec019b7a6e969bcb7f99d419581 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Fri, 21 Oct 2022 22:56:10 -0400 Subject: [PATCH 109/121] make qky files show up as forth --- .gitattributes | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1655562 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.qky linguist-language=Forth From 8a5432759558649ec810cbdc3a2222cbc2987c20 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Fri, 21 Oct 2022 23:07:50 -0400 Subject: [PATCH 110/121] better startup sequence --- webapp_start.py | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/webapp_start.py b/webapp_start.py index e1fd1d8..6e5093f 100644 --- a/webapp_start.py +++ b/webapp_start.py @@ -13,29 +13,41 @@ async def delay(time): files = ['bigrat', 'extensions', 'turtleduck', 'sundry/cards', 'sundry/demo', 'sundry/fsm', 'sundry/heapsort'] for file in files: + print(f'Downloading {file}.qky... ', end='') # N. B. top-level await is only allowed in Pyodide resp = await pyfetch(f'@@ORIGIN@@/{file}.qky') - print(f'Downloading {file}.qky') text = await resp.string() with open(f'{file}.qky', 'w') as f: f.write(text) + delay(10) + print('done') -print('Downloading quackery_OOP_ASYNC.py') +print('Downloading quackery_OOP.py... ', end='') resp = await pyfetch('@@ORIGIN@@/quackery_OOP.py') quackerytext = await resp.string() with open('quackery.py', 'w') as f: f.write(quackerytext) - -js.term.clear() +print('done') async def ainput(prompt): term = js.term term.resume() print('\u200c', end='') # ‌ - result = await term.read(prompt) + promise = term.read(prompt) + term.history().enable() + result = await promise term.pause() return result -from quackery import quackery, QuackeryContext +print('Compiling builtins... ', end='') +from quackery import * qc = QuackeryContext() +print('done') + +quackery(r'''$ 'extensions.qky' dup name? not dip sharefile and iff [ say 'Compiling extensions... ' cr quackery say 'done' cr ] else drop''', qc) + +print('Starting...') +delay(1000) +js.term.clear() + print(r''' ___ _ ___ _ _ / _ \ _ _ __ _ ___| | _____ _ __ _ _ / _ \ _ __ | (_)_ __ ___ @@ -46,8 +58,6 @@ async def ainput(prompt): Welcome to Quackery running on the Pyodide virtual machine. Don't type 'leave' or you'll break something.''') -quackery(r'''$ 'extensions.qky' dup name? not dip sharefile and iff [ cr say 'Building extensions...' cr quackery ] else drop''', qc) - async def shell_loop(): while True: prompt = '/O> ' From b9c26b3ae5ac108d81ad1dac3fa2d3795849b32c Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Fri, 21 Oct 2022 23:10:11 -0400 Subject: [PATCH 111/121] missed awaits --- webapp_start.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webapp_start.py b/webapp_start.py index 6e5093f..eea6956 100644 --- a/webapp_start.py +++ b/webapp_start.py @@ -18,7 +18,7 @@ async def delay(time): resp = await pyfetch(f'@@ORIGIN@@/{file}.qky') text = await resp.string() with open(f'{file}.qky', 'w') as f: f.write(text) - delay(10) + await delay(100) print('done') print('Downloading quackery_OOP.py... ', end='') @@ -45,7 +45,7 @@ async def ainput(prompt): quackery(r'''$ 'extensions.qky' dup name? not dip sharefile and iff [ say 'Compiling extensions... ' cr quackery say 'done' cr ] else drop''', qc) print('Starting...') -delay(1000) +await delay(1000) js.term.clear() print(r''' From 4e9d17e4c9990db1484f5b86b0d9fbfff2df87d5 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Fri, 21 Oct 2022 23:18:42 -0400 Subject: [PATCH 112/121] fix some bugs --- webapp_start.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/webapp_start.py b/webapp_start.py index eea6956..d338128 100644 --- a/webapp_start.py +++ b/webapp_start.py @@ -2,9 +2,7 @@ from pyodide.http import pyfetch from os import mkdir -import re import js -from itertools import count, chain async def delay(time): await js.Promise.new(lambda resolve, reject: js.setTimeout(resolve, time)) @@ -18,7 +16,7 @@ async def delay(time): resp = await pyfetch(f'@@ORIGIN@@/{file}.qky') text = await resp.string() with open(f'{file}.qky', 'w') as f: f.write(text) - await delay(100) + await delay(300) print('done') print('Downloading quackery_OOP.py... ', end='') @@ -38,7 +36,7 @@ async def ainput(prompt): return result print('Compiling builtins... ', end='') -from quackery import * +from quackery import quackery, QuackeryContext qc = QuackeryContext() print('done') From d912040ec89fcdc201bcaeb5f9ca4651722cce1e Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Fri, 21 Oct 2022 23:32:21 -0400 Subject: [PATCH 113/121] error handling --- webapp_start.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp_start.py b/webapp_start.py index d338128..4d5c4f1 100644 --- a/webapp_start.py +++ b/webapp_start.py @@ -67,5 +67,5 @@ async def shell_loop(): if not i: break qc.to_stack([ord(char) for char in input]) - quackery('quackery 5 nesting put cr echostack nesting release', qc) + quackery('0 backup [ quackery ] bailed not iff [ 5 nesting put cr echostack nesting release ] else [ message take echo$ ]', qc) await shell_loop() From 661c5b88fbaf300e99747f10121c7eea13c69341 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Sat, 22 Oct 2022 10:35:05 -0400 Subject: [PATCH 114/121] add rAF on stdout and stderr --- webapp_main.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webapp_main.js b/webapp_main.js index 3f2511a..9d7bdbd 100644 --- a/webapp_main.js +++ b/webapp_main.js @@ -26,8 +26,8 @@ window.addEventListener('DOMContentLoaded', async function main() { try { globalThis.pyodide = await loadPyodide({ homedir: '/quackery', - stderr: line => { clearTimeout(c); term.error(line) }, - stdout: line => { clearTimeout(c); term.echo(line) }, + stderr: line => { clearTimeout(c); requestAnimationFrame(() => term.error(line)); }, + stdout: line => { clearTimeout(c); requestAnimationFrame(() => term.echo(line)); }, stdin: window.prompt, }); From 0c1e17c79c49720c935f3c6ae692076ad52558fc Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Fri, 9 Dec 2022 07:52:44 -0500 Subject: [PATCH 115/121] Update quackery.py --- quackery.py | 1573 ++++++++++++++++++++++++++------------------------- 1 file changed, 787 insertions(+), 786 deletions(-) diff --git a/quackery.py b/quackery.py index 8a48d3c..c055117 100644 --- a/quackery.py +++ b/quackery.py @@ -21,6 +21,7 @@ def quackery(source_string): def failed(message): traverse(build(""" stacksize pack decimal unbuild + base release return$ nestdepth ]bailby[ """)) returnstack = string_from_stack() @@ -749,898 +750,898 @@ def sub_build(): predefined = r""" - [ 0 ] is false ( --> b ) +[ 0 ] is false ( --> b ) - [ 1 ] is true ( --> b ) +[ 1 ] is true ( --> b ) - [ dup nand ] is not ( b --> b ) +[ dup nand ] is not ( b --> b ) - [ nand not ] is and ( b b --> b ) +[ nand not ] is and ( b b --> b ) - [ not swap not nand ] is or ( b b --> b ) +[ not swap not nand ] is or ( b b --> b ) - [ = not ] is != ( x x --> b ) +[ = not ] is != ( x x --> b ) - [ not swap not != ] is xor ( b b --> b ) +[ not swap not != ] is xor ( b b --> b ) - [ swap > ] is < ( n n --> b ) +[ swap > ] is < ( n n --> b ) - [ negate + ] is - ( n --> n ) +[ negate + ] is - ( n --> n ) - [ /mod drop ] is / ( n n --> n ) +[ /mod drop ] is / ( n n --> n ) - [ swap drop ] is nip ( x x --> x ) +[ swap drop ] is nip ( x x --> x ) - [ /mod nip ] is mod ( n n --> n ) +[ /mod nip ] is mod ( n n --> n ) - [ 1 swap << ] is bit ( n --> n ) +[ 1 swap << ] is bit ( n --> n ) - [ swap over ] is tuck ( x x --> x x x ) +[ swap over ] is tuck ( x x --> x x x ) - [ rot rot ] is unrot ( x x x --> x x x ) +[ rot rot ] is unrot ( x x x --> x x x ) - [ rot tuck > - unrot > not and ] is within ( n n n --> b ) +[ rot tuck > + unrot > not and ] is within ( n n n --> b ) - [ over over ] is 2dup ( x x --> x x x x ) +[ over over ] is 2dup ( x x --> x x x x ) - [ drop drop ] is 2drop ( x x --> ) +[ drop drop ] is 2drop ( x x --> ) - [ ]again[ ] is again ( --> ) +[ ]again[ ] is again ( --> ) - [ ]done[ ] is done ( --> ) +[ ]done[ ] is done ( --> ) - [ ]if[ ] is if ( b --> ) +[ ]if[ ] is if ( b --> ) - [ ]iff[ ] is iff ( b --> ) +[ ]iff[ ] is iff ( b --> ) - [ ]else[ ] is else ( --> ) +[ ]else[ ] is else ( --> ) - [ 2dup > if swap drop ] is min ( n n n --> n ) +[ 2dup > if swap drop ] is min ( n n n --> n ) - [ 2dup < if swap drop ] is max ( n n n --> n ) +[ 2dup < if swap drop ] is max ( n n n --> n ) - [ rot min max ] is clamp ( n n n --> n ) +[ rot min max ] is clamp ( n n n --> n ) - [ dup nest? iff [] join ] is copy ( [ --> [ ) +[ dup nest? iff [] join ] is copy ( [ --> [ ) - [ ]'[ ] is ' ( --> x ) +[ ]'[ ] is ' ( --> x ) - [ ]this[ ] is this ( --> [ ) +[ ]this[ ] is this ( --> [ ) - [ ]do[ ] is do ( x --> ) +[ ]do[ ] is do ( x --> ) - [ ]this[ do ] is recurse ( --> ) +[ ]this[ do ] is recurse ( --> ) - [ not if ]again[ ] is until ( b --> ) +[ not if ]again[ ] is until ( b --> ) - [ not if ]done[ ] is while ( b --> ) +[ not if ]done[ ] is while ( b --> ) - [ immovable ]this[ ]done[ ] is stack ( --> s ) +[ immovable ]this[ ]done[ ] is stack ( --> s ) - [ dup take dup rot put ] is share ( s --> x ) +[ dup take dup rot put ] is share ( s --> x ) - [ take drop ] is release ( s --> ) +[ take drop ] is release ( s --> ) - [ dup release put ] is replace ( x s --> ) +[ dup release put ] is replace ( x s --> ) - [ dup take rot + swap put ] is tally ( n s --> ) +[ dup take rot + swap put ] is tally ( n s --> ) - [ swap take swap put ] is move ( s s --> ) +[ swap take swap put ] is move ( s s --> ) - [ [] tuck put ] is nested ( x --> [ ) +[ [] tuck put ] is nested ( x --> [ ) - [ stack [ ] ] is protected ( --> s ) +[ stack [ ] ] is protected ( --> s ) - [ protected take - ]'[ nested join - protected put ] is protect ( --> ) +[ protected take + ]'[ nested join + protected put ] is protect ( --> ) - ' stack ' filepath put - protect filepath +' stack ' filepath put +protect filepath - [ stack ] is dip.hold ( --> s ) - protect dip.hold +[ stack ] is dip.hold ( --> s ) +protect dip.hold - [ dip.hold put - ]'[ do dip.hold take ] is dip ( x --> x ) +[ dip.hold put + ]'[ do dip.hold take ] is dip ( x --> x ) - [ rot dip rot ] is 2swap ( x x x x --> x x x x ) +[ rot dip rot ] is 2swap ( x x x x --> x x x x ) - [ dip [ dip 2dup ] 2swap ] is 2over ( x x x x --> x x x x x x ) +[ dip [ dip 2dup ] 2swap ] is 2over ( x x x x --> x x x x x x ) - [ stack ] is depth ( --> s ) - protect depth +[ stack ] is depth ( --> s ) +protect depth - [ depth share - 0 != while - -1 depth tally - ]this[ do - 1 depth tally ] is decurse ( --> ) +[ depth share + 0 != while + -1 depth tally + ]this[ do + 1 depth tally ] is decurse ( --> ) - [ dup 0 < if negate ] is abs ( n --> n ) +[ dup 0 < if negate ] is abs ( n --> n ) - [ stack ] is times.start ( --> s ) - protect times.start +[ stack ] is times.start ( --> s ) +protect times.start - [ stack ] is times.count ( --> s ) - protect times.count +[ stack ] is times.count ( --> s ) +protect times.count - [ stack ] is times.action ( --> s ) - protect times.action +[ stack ] is times.action ( --> s ) +protect times.action - [ ]'[ times.action put - dup times.start put - [ 1 - dup -1 > while - times.count put - times.action share do - times.count take again ] - drop - times.action release - times.start release ] is times ( n --> ) - - [ times.count share ] is i ( --> n ) - - [ times.start share i 1+ - ] is i^ ( --> n ) - - [ 0 times.count replace ] is conclude ( --> ) - - [ times.start share - times.count replace ] is refresh ( --> ) - - [ times.count take 1+ - swap - times.count put ] is step ( --> s ) - - [ stack ] is temp ( --> s ) - protect temp +[ ]'[ times.action put + dup times.start put + [ 1 - dup -1 > while + times.count put + times.action share do + times.count take again ] + drop + times.action release + times.start release ] is times ( n --> ) - [ immovable - dup -1 > + - ]this[ swap peek - ]done[ ] is table ( n --> x ) +[ times.count share ] is i ( --> n ) - [ [] unrot - dup 1 < iff 2drop done - [ 2 /mod over while - if [ dip [ tuck join swap ] ] - dip [ dup join ] - again ] 2drop join ] is of ( x n --> [ ) +[ times.start share i 1+ - ] is i^ ( --> n ) - [ split 1 split - swap dip join - 0 peek ] is pluck ( [ n --> [ x ) +[ 0 times.count replace ] is conclude ( --> ) - [ split - rot nested - swap join join ] is stuff ( x [ n --> [ ) +[ times.start share + times.count replace ] is refresh ( --> ) - [ 0 pluck ] is behead ( [ --> [ x ) +[ times.count take 1+ + swap - times.count put ] is step ( --> s ) - [ over size over size - dup temp put - swap - 1+ times - [ 2dup over size split - drop = if - [ i^ temp replace - conclude ] - behead drop ] - 2drop temp take ] is findseq ( [ [ --> n ) +[ stack ] is temp ( --> s ) +protect temp - [ 13 ] is carriage ( --> c ) +[ immovable + dup -1 > + + ]this[ swap peek + ]done[ ] is table ( n --> x ) - [ carriage emit ] is cr ( --> ) +[ [] unrot + dup 1 < iff 2drop done + [ 2 /mod over while + if [ dip [ tuck join swap ] ] + dip [ dup join ] + again ] 2drop join ] is of ( x n --> [ ) - [ 32 ] is space ( --> c ) +[ split 1 split + swap dip join + 0 peek ] is pluck ( [ n --> [ x ) - [ space emit ] is sp ( --> ) +[ split + rot nested + swap join join ] is stuff ( x [ n --> [ ) - [ dup char a char { within - if [ 32 - ] ] is upper ( c --> c ) +[ 0 pluck ] is behead ( [ --> [ x ) - [ dup char A char [ within - if [ 32 + ] ] is lower ( c --> c ) +[ over size over size + dup temp put + swap - 1+ times + [ 2dup over size split + drop = if + [ i^ temp replace + conclude ] + behead drop ] + 2drop temp take ] is findseq ( [ [ --> n ) - [ dup 10 < - iff 48 else 55 + ] is digit ( n --> c ) +[ 13 ] is carriage ( --> c ) - [ stack 10 ] is base ( --> s ) - protect base +[ carriage emit ] is cr ( --> ) - [ 10 base put ] is decimal ( --> ) +[ 32 ] is space ( --> c ) - [ $ '' over abs - [ base share /mod digit - rot join swap - dup 0 = until ] - drop - swap 0 < if - [ $ '-' swap join ] ] is number$ ( n --> $ ) - - [ stack ] is with.hold ( --> s ) - protect with.hold +[ space emit ] is sp ( --> ) - [ nested - ' [ dup with.hold put - size times ] - ' [ with.hold share - i ~ peek ] - rot join - nested join - ' [ with.hold release ] - join ] is makewith ( x --> [ ) +[ dup char a char { within + if [ 32 - ] ] is upper ( c --> c ) - [ ]'[ makewith do ] is witheach ( [ --> ) +[ dup char A char [ within + if [ 32 + ] ] is lower ( c --> c ) - [ witheach emit ] is echo$ ( $ --> ) +[ dup 10 < + iff 48 else 55 + ] is digit ( n --> c ) - [ stack ] is mi.tidyup ( --> s ) - protect mi.tidyup +[ stack 10 ] is base ( --> s ) +protect base - [ stack ] is mi.result ( --> s ) - protect mi.result - - [ mi.tidyup put - over size mi.result put - nested - ' [ if - [ i^ mi.result replace - conclude ] ] - join makewith do - mi.tidyup take do - mi.result take ] is matchitem ( [ x x --> n ) - - [ ]'[ ]'[ matchitem ] is findwith ( [ --> n ) +[ 10 base put ] is decimal ( --> ) - [ size < ] is found ( n [ --> b ) - - [ space > ] is printable ( c --> b ) - - [ dup findwith - printable [ ] - split nip ] is trim ( $ --> $ ) +[ $ '' over abs + [ base share /mod digit + rot join swap + dup 0 = until ] + drop + swap 0 < if + [ $ '-' swap join ] ] is number$ ( n --> $ ) - [ dup findwith - [ printable not ] [ ] - split swap ] is nextword ( $ --> $ $ ) - - [ dup nest? if - [ dup size 2 < if done - dup size 2 / split - recurse swap - recurse join ] ] is reverse ( x --> x ) - - [ [] swap times - [ swap nested join ] - reverse ] is pack ( * n --> [ ) - - [ witheach [ ] ] is unpack ( [ --> * ) - - [ stack ] is to-do ( --> s ) - protect to-do - - [ ' done swap put ] is new-do ( s --> ) - - [ dip [ 1+ pack ] put ] is add-to ( * x n s --> ) - - [ [ dup take - unpack do again ] drop ] is now-do ( s --> ) - - [ 1 split reverse join - now-do ] is do-now ( s --> ) - - [ [ dup take ' done = until ] - drop ] is not-do ( s --> ) - - [ stack ] is sort.test ( --> s ) - protect sort.test - - [ ]'[ sort.test put - [] swap witheach - [ swap 2dup findwith - [ over sort.test share - do ] [ ] - nip stuff ] - sort.test release ] is sortwith ( [ --> [ ) - - [ sortwith > ] is sort ( [ --> [ ) - - [ 32 127 clamp 32 - - [ table - 0 86 88 93 94 90 92 87 63 64 75 73 82 74 81 76 - 1 2 3 4 5 6 7 8 9 10 83 84 69 72 70 85 - 91 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 - 41 43 45 47 49 51 53 55 57 59 61 65 78 66 77 80 - 89 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 - 42 44 46 48 50 52 54 56 58 60 62 67 79 68 71 0 ] - ] is qacsfot ( c --> n ) - - [ [ dup $ '' = iff false done - over $ '' = iff true done - behead rot behead rot - 2dup = iff [ 2drop swap ] again - qacsfot swap qacsfot > ] - unrot 2drop ] is $< ( $ $ --> b ) - - [ swap $< ] is $> ( $ $ --> b ) - - [ sortwith $> ] is sort$ ( [ --> [ ) - - [ upper 47 - 0 44 clamp - [ table - -1 0 1 2 3 4 5 6 7 8 9 -1 -1 -1 -1 - -1 -1 -1 10 11 12 13 14 15 16 17 18 19 20 21 - 22 23 24 25 26 27 28 29 30 31 32 33 34 35 -1 ] - dup 0 base share - within not if [ drop -1 ] ] is char->n ( c --> n ) - - [ dup $ '' = iff [ drop 0 false ] done - dup 0 peek char - = - tuck if [ behead drop ] - dup $ '' = iff [ 2drop 0 false ] done - true 0 rot witheach - [ char->n - dup 0 < iff [ drop nip false swap ] - else [ swap base share * + ] ] - rot if negate - swap ] is $->n ( $ --> n b ) - - ( adapted from 'A small noncryptographic PRNG' by Bob Jenkins ) - ( https://burtleburtle.net/bob/rand/smallprng.html ) - - [ hex FFFFFFFFFFFFFFFF ] is 64bitmask ( --> f ) - - [ 64bitmask & ] is 64bits ( f --> f ) - - [ dip 64bits 2dup << 64bits - unrot 64 swap - >> | ] is rot64 ( f n --> f ) - - [ stack 0 ] is prng.a ( --> s ) - [ stack 0 ] is prng.b ( --> s ) - [ stack 0 ] is prng.c ( --> s ) - [ stack 0 ] is prng.d ( --> s ) - - [ prng.a share - prng.b share tuck - 7 rot64 - 64bits swap - prng.c share tuck - 13 rot64 ^ prng.a replace - prng.d share tuck - 37 rot64 + 64bits prng.b replace - over + 64bits prng.c replace - prng.a share + 64bits - dup prng.d replace ] is prng ( --> n ) - - [ hex F1EA5EAD prng.a replace - dup prng.b replace - dup prng.c replace - prng.d replace - 20 times [ prng drop ] ] is initrandom ( n --> ) - - hex DEFACEABADFACADE initrandom - - [ time initrandom ] is randomise ( --> ) - - [ 64bitmask 1+ - over / over * - [ prng 2dup > not while - drop again ] - nip swap mod ] is random ( n --> n ) - - [ [] swap dup size times - [ dup size random pluck - nested rot join swap ] - drop ] is shuffle ( [ --> [ ) - - [ stack ] is history ( --> s ) - - [ protected share history put - protected share 0 - [ over size over - > while - 2dup peek - size unrot - 1+ again ] - 2drop - protected share size pack - history put - pack dup history put unpack - stacksize history put - nestdepth history put - false history put ] is backup ( n --> ) - - [ history release - nestdepth - history take - - ]bailby[ - true history put ] is bail ( --> ) - - [ history take iff - [ stacksize - history take - history share - size - - times drop - history take unpack - history take unpack - history share size - [ dup 0 > while - 1 - - history share - over peek - rot over size - swap - - [ dup 0 > while - over release - 1 - again ] - 2drop again ] - drop - history take - protected replace - true ] - else - [ 5 times - [ history release ] - false ] ] is bailed ( --> b ) +[ stack ] is with.hold ( --> s ) +protect with.hold - [ quid swap quid = ] is oats ( x x --> b ) +[ nested + ' [ dup with.hold put + size times ] + ' [ with.hold share + i ~ peek ] + rot join + nested join + ' [ with.hold release ] + join ] is makewith ( x --> [ ) - [ [] swap - [ trim - dup size while - nextword nested - swap dip join again ] - drop ] is nest$ ( $ --> [ ) +[ ]'[ makewith do ] is witheach ( [ --> ) - [ stack ] is namenest ( --> s ) +[ witheach emit ] is echo$ ( $ --> ) - [ namenest share ] is names ( --> [ ) +[ stack ] is mi.tidyup ( --> s ) +protect mi.tidyup - [ names find names found ] is name? ( $ --> b ) +[ stack ] is mi.result ( --> s ) +protect mi.result - forward is actions ( n --> x ) +[ mi.tidyup put + over size mi.result put + nested + ' [ if + [ i^ mi.result replace + conclude ] ] + join makewith do + mi.tidyup take do + mi.result take ] is matchitem ( [ x x --> n ) - [ ' actions ] is actiontable ( --> x ) +[ ]'[ ]'[ matchitem ] is findwith ( [ --> n ) - [ actiontable share tuck - findwith [ over oats ] drop - swap found ] is named? ( x --> b ) +[ size < ] is found ( n [ --> b ) - forward is reflect ( x --> x ) - [ dup nest? if - [ dup [] = if done - dup size 1 = iff - [ 0 peek - dup named? iff - nested done - reflect nested ] - done - dup size 2 / split - recurse swap - recurse join ] ] resolves reflect ( x --> x ) +[ space > ] is printable ( c --> b ) - [ stack ] is buildernest ( --> s ) +[ dup findwith + printable [ ] + split nip ] is trim ( $ --> $ ) - [ buildernest share ] is builders ( --> s ) +[ dup findwith + [ printable not ] [ ] + split swap ] is nextword ( $ --> $ $ ) - [ builders find - builders found ] is builder? ( $ --> b ) +[ dup nest? if + [ dup size 2 < if done + dup size 2 / split + recurse swap + recurse join ] ] is reverse ( x --> x ) - forward is jobs ( n --> x ) +[ [] swap times + [ swap nested join ] + reverse ] is pack ( * n --> [ ) - [ ' jobs ] is jobtable ( --> [ ) +[ witheach [ ] ] is unpack ( [ --> * ) - [ stack ] is message ( --> s ) +[ stack ] is to-do ( --> s ) +protect to-do - [ stack ] is b.nesting ( --> s ) - protect b.nesting +[ ' done swap put ] is new-do ( s --> ) - [ stack ] is b.to-do ( --> s ) +[ dip [ 1+ pack ] put ] is add-to ( * x n s --> ) - [ $ '[' b.nesting put - [] swap ] is b.[ ( [ $ --> [ [ $ ) +[ [ dup take + unpack do again ] drop ] is now-do ( s --> ) - [ b.nesting take dup - $ '' = if - [ $ 'Unexpected "]".' - message put - bail ] - dup $ '[' = iff drop - else - [ $ 'Nest mismatch: ' - swap join $ ' ]' join - message put - bail ] - dip [ nested join ] ] is b.] ( [ [ $ --> [ $ ) +[ 1 split reverse join + now-do ] is do-now ( s --> ) - [ over [] = if - [ $ '"is" needs something to name before it.' - message put - bail ] - dup $ '' = if - [ $ '"is" needs a name after it.' - message put - bail ] - nextword nested - namenest take - join - namenest put - dip - [ -1 pluck - actiontable take - 1 stuff - actiontable put ] ] is b.is ( [ $ --> [ $ ) - - [ over [] = if - [ $ '"builds" needs something to name before it.' - message put - bail ] - dup $ '' = if - [ $ '"builds" needs a name after it.' - message put - bail ] - nextword nested - buildernest take - join - buildernest put - dip - [ -1 pluck - jobtable take - 1 stuff - jobtable put ] ] is b.builds ( [ $ --> [ $ ) - - [ trim nextword - dup $ '' = if - [ $ 'Unfinished comment.' - message put - bail ] - $ ')' = until ] is b.( ( [ $ --> $ [ ) +[ [ dup take ' done = until ] + drop ] is not-do ( s --> ) - [ $ 'Unexpected ")".' - message put - bail ] is b.) ( [ $ --> $ [ ) +[ stack ] is sort.test ( --> s ) +protect sort.test - [ $ 'Unresolved reference.' - fail ] is unresolved ( --> ) +[ ]'[ sort.test put + [] swap witheach + [ swap 2dup findwith + [ over sort.test share + do ] [ ] + nip stuff ] + sort.test release ] is sortwith ( [ --> [ ) - [ dip - [ ' [ unresolved ] - copy nested join ] ] is b.forward ( [ $ --> [ $ ) +[ sortwith > ] is sort ( [ --> [ ) - [ over [] = if - [ $ '"resolves" needs something to resolve.' - message put - bail ] - dup $ '' = if - [ $ '"resolves" needs a name to resolve into.' - message put - bail ] - dip [ -1 split ] - nextword dup temp put - names find - dup names found not if - [ $ 'Unknown word after "resolves": ' - temp take join - message put - bail ] - actions - dup ' [ unresolved ] = not if - [ char " temp take join - $ '" is not an unresolved forward reference.' - join - message put - bail ] - rot 0 peek over - replace - ' unresolved swap - ' replace 2 b.to-do add-to - temp release ] is b.resolves ( [ $ --> [ $ ) - - [ 1 split - over $ '' = if - [ $ '"char" needs a character after it.' - message put - bail ] - dip join ] is b.char ( [ $ --> [ $ ) - - [ dup $ '' = if - [ $ '"$" needs to be followed by a string.' - message put - bail ] - behead over find - 2dup swap found not if - [ $ 'Endless string discovered.' - message put - bail ] - split behead drop - ' ' nested - rot nested join - nested swap dip join ] is b.$ ( [ $ --> [ $ ) - - [ dup $ '' = if - [ $ '"say" needs to be followed by a string.' - message put - bail ] - $ '$' builders find jobs do - dip - [ -1 pluck - ' echo$ nested join - nested join ] ] is b.say ( [ $ --> [ $ ) - - [ 16 base put - nextword dup - $ '' = if - [ $ '"hex" needs a number after it.' - message put - bail ] - dup $->n iff - [ nip swap dip join ] +[ 32 127 clamp 32 - + [ table + 0 86 88 93 94 90 92 87 63 64 75 73 82 74 81 76 + 1 2 3 4 5 6 7 8 9 10 83 84 69 72 70 85 + 91 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 + 41 43 45 47 49 51 53 55 57 59 61 65 78 66 77 80 + 89 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 + 42 44 46 48 50 52 54 56 58 60 62 67 79 68 71 0 ] +] is qacsfot ( c --> n ) + +[ [ dup $ '' = iff false done + over $ '' = iff true done + behead rot behead rot + 2dup = iff [ 2drop swap ] again + qacsfot swap qacsfot > ] + unrot 2drop ] is $< ( $ $ --> b ) + +[ swap $< ] is $> ( $ $ --> b ) + +[ sortwith $> ] is sort$ ( [ --> [ ) + +[ upper 47 - 0 44 clamp + [ table + -1 0 1 2 3 4 5 6 7 8 9 -1 -1 -1 -1 + -1 -1 -1 10 11 12 13 14 15 16 17 18 19 20 21 + 22 23 24 25 26 27 28 29 30 31 32 33 34 35 -1 ] + dup 0 base share + within not if [ drop -1 ] ] is char->n ( c --> n ) + +[ dup $ '' = iff [ drop 0 false ] done + dup 0 peek char - = + tuck if [ behead drop ] + dup $ '' = iff [ 2drop 0 false ] done + true 0 rot witheach + [ char->n + dup 0 < iff [ drop nip false swap ] + else [ swap base share * + ] ] + rot if negate + swap ] is $->n ( $ --> n b ) + +( adapted from 'A small noncryptographic PRNG' by Bob Jenkins ) +( https://burtleburtle.net/bob/rand/smallprng.html ) + +[ hex FFFFFFFFFFFFFFFF ] is 64bitmask ( --> f ) + +[ 64bitmask & ] is 64bits ( f --> f ) + +[ dip 64bits 2dup << 64bits + unrot 64 swap - >> | ] is rot64 ( f n --> f ) + +[ stack 0 ] is prng.a ( --> s ) +[ stack 0 ] is prng.b ( --> s ) +[ stack 0 ] is prng.c ( --> s ) +[ stack 0 ] is prng.d ( --> s ) + +[ prng.a share + prng.b share tuck + 7 rot64 - 64bits swap + prng.c share tuck + 13 rot64 ^ prng.a replace + prng.d share tuck + 37 rot64 + 64bits prng.b replace + over + 64bits prng.c replace + prng.a share + 64bits + dup prng.d replace ] is prng ( --> n ) + +[ hex F1EA5EAD prng.a replace + dup prng.b replace + dup prng.c replace + prng.d replace + 20 times [ prng drop ] ] is initrandom ( n --> ) + +hex DEFACEABADFACADE initrandom + +[ time initrandom ] is randomise ( --> ) + +[ 64bitmask 1+ + over / over * + [ prng 2dup > not while + drop again ] + nip swap mod ] is random ( n --> n ) + +[ [] swap dup size times + [ dup size random pluck + nested rot join swap ] + drop ] is shuffle ( [ --> [ ) + +[ stack ] is history ( --> s ) + +[ protected share history put + protected share 0 + [ over size over + > while + 2dup peek + size unrot + 1+ again ] + 2drop + protected share size pack + history put + pack dup history put unpack + stacksize history put + nestdepth history put + false history put ] is backup ( n --> ) + +[ history release + nestdepth + history take + - ]bailby[ + true history put ] is bail ( --> ) + +[ history take iff + [ stacksize + history take + history share + size - - times drop + history take unpack + history take unpack + history share size + [ dup 0 > while + 1 - + history share + over peek + rot over size + swap - + [ dup 0 > while + over release + 1 - again ] + 2drop again ] + drop + history take + protected replace + true ] else - [ drop - char " swap join - $ '" is not hexadecimal.' - join message put - bail ] - base release ] is b.hex ( [ $ --> [ $ ) + [ 5 times + [ history release ] + false ] ] is bailed ( --> b ) - [ dip [ -1 split ] swap do ] is b.now! ( [ $ --> [ $ ) +[ quid swap quid = ] is oats ( x x --> b ) - [ over [] = if - [ $ '"constant" needs something before it.' - message put +[ [] swap + [ trim + dup size while + nextword nested + swap dip join again ] + drop ] is nest$ ( $ --> [ ) + +[ stack ] is namenest ( --> s ) + +[ namenest share ] is names ( --> [ ) + +[ names find names found ] is name? ( $ --> b ) + + forward is actions ( n --> x ) + +[ ' actions ] is actiontable ( --> x ) + +[ actiontable share tuck + findwith [ over oats ] drop + swap found ] is named? ( x --> b ) + + forward is reflect ( x --> x ) +[ dup nest? if + [ dup [] = if done + dup size 1 = iff + [ 0 peek + dup named? iff + nested done + reflect nested ] + done + dup size 2 / split + recurse swap + recurse join ] ] resolves reflect ( x --> x ) + +[ stack ] is buildernest ( --> s ) + +[ buildernest share ] is builders ( --> s ) + +[ builders find + builders found ] is builder? ( $ --> b ) + + forward is jobs ( n --> x ) + +[ ' jobs ] is jobtable ( --> [ ) + +[ stack ] is message ( --> s ) + +[ stack ] is b.nesting ( --> s ) +protect b.nesting + +[ stack ] is b.to-do ( --> s ) + +[ $ '[' b.nesting put + [] swap ] is b.[ ( [ $ --> [ [ $ ) + +[ b.nesting take dup + $ '' = if + [ $ 'Unexpected "]".' + message put + bail ] + dup $ '[' = iff drop + else + [ $ 'Nest mismatch: ' + swap join $ ' ]' join + message put + bail ] + dip [ nested join ] ] is b.] ( [ [ $ --> [ $ ) + +[ over [] = if + [ $ '"is" needs something to name before it.' + message put + bail ] + dup $ '' = if + [ $ '"is" needs a name after it.' + message put + bail ] + nextword nested + namenest take + join + namenest put + dip + [ -1 pluck + actiontable take + 1 stuff + actiontable put ] ] is b.is ( [ $ --> [ $ ) + +[ over [] = if + [ $ '"builds" needs something to name before it.' + message put + bail ] + dup $ '' = if + [ $ '"builds" needs a name after it.' + message put + bail ] + nextword nested + buildernest take + join + buildernest put + dip + [ -1 pluck + jobtable take + 1 stuff + jobtable put ] ] is b.builds ( [ $ --> [ $ ) + +[ trim nextword + dup $ '' = if + [ $ 'Unfinished comment.' + message put + bail ] + $ ')' = until ] is b.( ( [ $ --> $ [ ) + +[ $ 'Unexpected ")".' + message put + bail ] is b.) ( [ $ --> $ [ ) + +[ $ 'Unresolved reference.' + fail ] is unresolved ( --> ) + +[ dip + [ ' [ unresolved ] + copy nested join ] ] is b.forward ( [ $ --> [ $ ) + + [ over [] = if + [ $ '"resolves" needs something to resolve.' + message put + bail ] + dup $ '' = if + [ $ '"resolves" needs a name to resolve into.' + message put + bail ] + dip [ -1 split ] + nextword dup temp put + names find + dup names found not if + [ $ 'Unknown word after "resolves": ' + temp take join + message put + bail ] + actions + dup ' [ unresolved ] = not if + [ char " temp take join + $ '" is not an unresolved forward reference.' + join + message put + bail ] + rot 0 peek over + replace + ' unresolved swap + ' replace 2 b.to-do add-to + temp release ] is b.resolves ( [ $ --> [ $ ) + +[ 1 split + over $ '' = if + [ $ '"char" needs a character after it.' + message put + bail ] + dip join ] is b.char ( [ $ --> [ $ ) + +[ dup $ '' = if + [ $ '"$" needs to be followed by a string.' + message put + bail ] + behead over find + 2dup swap found not if + [ $ 'Endless string discovered.' + message put + bail ] + split behead drop + ' ' nested + rot nested join + nested swap dip join ] is b.$ ( [ $ --> [ $ ) + +[ dup $ '' = if + [ $ '"say" needs to be followed by a string.' + message put + bail ] + $ '$' builders find jobs do + dip + [ -1 pluck + ' echo$ nested join + nested join ] ] is b.say ( [ $ --> [ $ ) + +[ 16 base put + nextword dup + $ '' = if + [ $ '"hex" needs a number after it.' + message put + bail ] + dup $->n iff + [ nip swap dip join ] + else + [ drop + char " swap join + $ '" is not hexadecimal.' + join message put + bail ] + base release ] is b.hex ( [ $ --> [ $ ) + +[ dip [ -1 split ] swap do ] is b.now! ( [ $ --> [ $ ) + +[ over [] = if + [ $ '"constant" needs something before it.' + message put + bail ] + dip + [ -1 pluck do + dup number? not if + [ ' ' nested swap + nested join + nested ] + join ] ] is b.constant ( [ $ --> [ $ ) + +[ ' [ namenest actiontable + buildernest jobtable ] + witheach + [ do share copy + history put ] ] is backupwords ( --> ) + +[ ' [ jobtable buildernest + actiontable namenest ] + witheach + [ do dup release + history swap move ] ] is restorewords ( --> ) + +[ 4 times + [ history release ] ] is releasewords ( --> ) + +[ backupwords + b.to-do new-do + 1 backup + [ $ '' b.nesting put + decimal + [] swap + [ trim + dup $ '' = iff drop done + nextword + dup builders find + dup builders found iff + [ dip [ drop trim ] + jobs do ] again + drop + dup names find + dup names found iff + [ actions nested + nip swap dip join ] again + drop + dup $->n iff + [ nip swap dip join ] again + drop + $ 'Unknown word: ' + swap join message put bail ] - dip - [ -1 pluck do - dup number? not if - [ ' ' nested swap - nested join - nested ] - join ] ] is b.constant ( [ $ --> [ $ ) - - [ ' [ namenest actiontable - buildernest jobtable ] - witheach - [ do share copy - history put ] ] is backupwords ( --> ) - - [ ' [ jobtable buildernest - actiontable namenest ] - witheach - [ do dup release - history swap move ] ] is restorewords ( --> ) - - [ 4 times - [ history release ] ] is releasewords ( --> ) - - [ backupwords - b.to-do new-do - 1 backup - [ $ '' b.nesting put - decimal - [] swap - [ trim - dup $ '' = iff drop done - nextword - dup builders find - dup builders found iff - [ dip [ drop trim ] - jobs do ] again - drop - dup names find - dup names found iff - [ actions nested - nip swap dip join ] again - drop - dup $->n iff - [ nip swap dip join ] again - drop - $ 'Unknown word: ' - swap join message put - bail ] - base release - b.nesting take dup - $ '' = iff drop - else - [ $ 'Unfinished nest: ' - swap join message put - bail ] ] - bailed iff - [ drop b.to-do now-do - restorewords - ' ' nested - message take nested join - ' echo$ nested join ] - else - [ b.to-do not-do - releasewords ] ] is build ( $ --> [ ) - - [ build do ] is quackery ( $ --> ) - - [ stack -1 ] is nesting ( --> [ ) - - forward is unbuild ( x --> $ ) - - [ nesting share - 0 = iff [ drop $ '...' ] done - $ '' swap - dup number? iff - [ number$ join ] done - actiontable share - behead drop - [ dup [] = iff - [ drop false ] done - behead - rot tuck oats iff - [ drop size 2 + - actiontable share - size swap - - names swap peek join - true ] done - swap again ] - if done - dup nest? iff - [ $ '[ ' rot join swap - [ dup [] = iff drop done - behead - -1 nesting tally - unbuild - 1 nesting tally - space join - swap dip join again ] - $ ']' join ] - else - [ drop - $ "Quackery was worried by a python." - fail ] ] resolves unbuild ( x --> $ ) - - [ unbuild echo$ ] is echo ( x --> ) - - [ $ '' - return -2 split drop - witheach - [ dup number? iff - [ number$ join - $ '} ' join ] + base release + b.nesting take dup + $ '' = iff drop else - [ $ '{' swap dip join - actiontable share - findwith - [ over oats ] drop - dup actiontable share - found iff - [ 1 - names swap - peek join - space join ] - else - [ drop $ '[...] ' - join ] ] ] - -1 split drop ] is return$ ( --> $ ) - - [ return$ echo$ ] is echoreturn ( --> ) - - [ stacksize dup 0 = iff - [ $ 'Stack empty.' echo$ drop ] - else - [ $ 'Stack: ' echo$ - pack dup - witheach [ echo sp ] - unpack ] - cr ] is echostack ( --> ) - - [ cr $ '' $ '/O> ' - [ input - dup $ '' != while - carriage join join - $ '... ' again ] - drop - quackery - 5 nesting put - cr echostack - nesting release again ] is shell ( --> ) - - [ cr randomise 12 random - [ table - $ 'Goodbye.' $ 'Adieu.' $ 'So long.' - $ 'Cheerio.' $ 'Aloha.' $ 'Ciao.' - $ 'Farewell.' $ 'Be seeing you.' - $ 'Sayonara.' $ 'Auf wiedersehen.' - $ 'Toodles.' $ 'Hasta la vista.' ] - do echo$ cr cr - 3 ]bailby[ ] is leave ( --> ) - - [ stacksize times drop ] is empty ( all --> ) - - [ tuck temp put - witheach - [ dup size - rot + dup - temp share > iff - [ cr drop dup size ] - else sp 1+ swap echo$ ] - drop temp release ] is wrap$ ( [ n --> ) - - [ names reverse 70 wrap$ cr - builders reverse - 70 wrap$ cr ] is words ( --> ) - - [ dup name? iff drop + [ $ 'Unfinished nest: ' + swap join message put + bail ] ] + bailed iff + [ drop b.to-do now-do + restorewords + ' ' nested + message take nested join + ' echo$ nested join ] + else + [ b.to-do not-do + releasewords ] ] is build ( $ --> [ ) + +[ build do ] is quackery ( $ --> ) + +[ stack -1 ] is nesting ( --> [ ) + + forward is unbuild ( x --> $ ) + +[ nesting share + 0 = iff [ drop $ '...' ] done + $ '' swap + dup number? iff + [ number$ join ] done + actiontable share + behead drop + [ dup [] = iff + [ drop false ] done + behead + rot tuck oats iff + [ drop size 2 + + actiontable share + size swap - + names swap peek join + true ] done + swap again ] + if done + dup nest? iff + [ $ '[ ' rot join swap + [ dup [] = iff drop done + behead + -1 nesting tally + unbuild + 1 nesting tally + space join + swap dip join again ] + $ ']' join ] + else + [ drop + $ "Quackery was worried by a python." + fail ] ] resolves unbuild ( x --> $ ) + +[ unbuild echo$ ] is echo ( x --> ) + +[ $ '' + return -2 split drop + witheach + [ dup number? iff + [ number$ join + $ '} ' join ] else - [ dup sharefile not if - [ $ |$ 'file not found: "| - swap join - $ |"' echo$| join ] - nip quackery ] ] is loadfile ( $ --> ) - - [ dup sharefile iff - [ swap releasefile ] - else [ drop false ] ] is takefile ( $ --> $ b ) - - [ dup releasefile iff - putfile - else [ 2drop false ] ] is replacefile ( $ $ --> b ) - - [ nested ' [ ' ] - swap join - decimal unbuild - base release ] is quackify ( x --> $ ) - - $ "quackify replacefile takefile loadfile words empty wrap$ leave - shell echostack echoreturn return$ echo unbuild nesting quackery - build releasewords restorewords backupwords unresolved b.to-do - b.nesting message jobtable jobs builder? builders buildernest - reflect named? actiontable actions name? names namenest nest$ oats - bailed bail backup history shuffle random randomise initrandom - prng prng.d prng.c prng.b prng.a rot64 64bits 64bitmask $->n - char->n sort$ $> $< qacsfot sort sortwith sort.test not-do do-now - now-do add-to new-do to-do unpack pack reverse nextword trim - printable found findwith matchitem mi.result mi.tidyup echo$ - witheach makewith with.hold number$ decimal base digit lower upper - sp space cr carriage findseq behead stuff pluck of table temp step - refresh conclude i^ i times times.action times.count times.start - abs decurse depth 2over 2swap dip dip.hold protect protected - nested move tally replace release share stack while until recurse - do this ' copy clamp max min else iff if done again 2drop 2dup - within unrot tuck bit mod nip / - < xor != or and not true false - sharefile releasefile putfile filepath input ding emit quid - operator? number? nest? size poke peek find join split [] take - immovable put ]bailby[ ]do[ ]this[ ]'[ ]else[ ]iff[ ]if[ ]again[ - ]done[ over rot swap drop dup return nestdepth stacksize time ~ ^ - | & >> << ** /mod * negate + 1+ > = nand fail python" - - nest$ namenest put - - [ table - quackify replacefile takefile loadfile words empty wrap$ leave - shell echostack echoreturn return$ echo unbuild nesting quackery - build releasewords restorewords backupwords unresolved b.to-do - b.nesting message jobtable jobs builder? builders buildernest - reflect named? actiontable actions name? names namenest nest$ oats - bailed bail backup history shuffle random randomise initrandom - prng prng.d prng.c prng.b prng.a rot64 64bits 64bitmask $->n - char->n sort$ $> $< qacsfot sort sortwith sort.test not-do do-now - now-do add-to new-do to-do unpack pack reverse nextword trim - printable found findwith matchitem mi.result mi.tidyup echo$ - witheach makewith with.hold number$ decimal base digit lower upper - sp space cr carriage findseq behead stuff pluck of table temp step - refresh conclude i^ i times times.action times.count times.start - abs decurse depth 2over 2swap dip dip.hold protect protected - nested move tally replace release share stack while until recurse - do this ' copy clamp max min else iff if done again 2drop 2dup - within unrot tuck bit mod nip / - < xor != or and not true false - sharefile releasefile putfile filepath input ding emit quid - operator? number? nest? size poke peek find join split [] take - immovable put ]bailby[ ]do[ ]this[ ]'[ ]else[ ]iff[ ]if[ ]again[ - ]done[ over rot swap drop dup return nestdepth stacksize time ~ ^ - | & >> << ** /mod * negate + 1+ > = nand fail python ] - - resolves actions ( n --> x ) - - $ "constant now! hex say $ char resolves forward ) ( builds is ] [" - nest$ buildernest put - + [ $ '{' swap dip join + actiontable share + findwith + [ over oats ] drop + dup actiontable share + found iff + [ 1 - names swap + peek join + space join ] + else + [ drop $ '[...] ' + join ] ] ] + -1 split drop ] is return$ ( --> $ ) + +[ return$ echo$ ] is echoreturn ( --> ) + +[ stacksize dup 0 = iff + [ $ 'Stack empty.' echo$ drop ] + else + [ $ 'Stack: ' echo$ + pack dup + witheach [ echo sp ] + unpack ] + cr ] is echostack ( --> ) + +[ cr $ '' $ '/O> ' + [ input + dup $ '' != while + carriage join join + $ '... ' again ] + drop + quackery + 5 nesting put + cr echostack + nesting release again ] is shell ( --> ) + +[ cr randomise 12 random [ table - b.constant b.now! b.hex b.say b.$ b.char b.resolves - b.forward b.) b.( b.builds b.is b.] b.[ ] - - resolves jobs ( n --> x ) + $ 'Goodbye.' $ 'Adieu.' $ 'So long.' + $ 'Cheerio.' $ 'Aloha.' $ 'Ciao.' + $ 'Farewell.' $ 'Be seeing you.' + $ 'Sayonara.' $ 'Auf wiedersehen.' + $ 'Toodles.' $ 'Hasta la vista.' ] + do echo$ cr cr + 3 ]bailby[ ] is leave ( --> ) + +[ stacksize times drop ] is empty ( all --> ) + +[ tuck temp put + witheach + [ dup size + rot + dup + temp share > iff + [ cr drop dup size ] + else sp 1+ swap echo$ ] + drop temp release ] is wrap$ ( [ n --> ) + +[ names reverse 70 wrap$ cr + builders reverse + 70 wrap$ cr ] is words ( --> ) + +[ dup name? iff drop + else + [ dup sharefile not if + [ $ |$ 'file not found: "| + swap join + $ |"' echo$| join ] + nip quackery ] ] is loadfile ( $ --> ) + +[ dup sharefile iff + [ swap releasefile ] + else [ drop false ] ] is takefile ( $ --> $ b ) + +[ dup releasefile iff + putfile + else [ 2drop false ] ] is replacefile ( $ $ --> b ) + +[ nested ' [ ' ] + swap join + decimal unbuild + base release ] is quackify ( x --> $ ) + +$ "quackify replacefile takefile loadfile words empty wrap$ leave + shell echostack echoreturn return$ echo unbuild nesting quackery + build releasewords restorewords backupwords unresolved b.to-do + b.nesting message jobtable jobs builder? builders buildernest + reflect named? actiontable actions name? names namenest nest$ oats + bailed bail backup history shuffle random randomise initrandom + prng prng.d prng.c prng.b prng.a rot64 64bits 64bitmask $->n + char->n sort$ $> $< qacsfot sort sortwith sort.test not-do do-now + now-do add-to new-do to-do unpack pack reverse nextword trim + printable found findwith matchitem mi.result mi.tidyup echo$ + witheach makewith with.hold number$ decimal base digit lower upper + sp space cr carriage findseq behead stuff pluck of table temp step + refresh conclude i^ i times times.action times.count times.start + abs decurse depth 2over 2swap dip dip.hold protect protected + nested move tally replace release share stack while until recurse + do this ' copy clamp max min else iff if done again 2drop 2dup + within unrot tuck bit mod nip / - < xor != or and not true false + sharefile releasefile putfile filepath input ding emit quid + operator? number? nest? size poke peek find join split [] take + immovable put ]bailby[ ]do[ ]this[ ]'[ ]else[ ]iff[ ]if[ ]again[ + ]done[ over rot swap drop dup return nestdepth stacksize time ~ ^ + | & >> << ** /mod * negate + 1+ > = nand fail python" + +nest$ namenest put + +[ table + quackify replacefile takefile loadfile words empty wrap$ leave + shell echostack echoreturn return$ echo unbuild nesting quackery + build releasewords restorewords backupwords unresolved b.to-do + b.nesting message jobtable jobs builder? builders buildernest + reflect named? actiontable actions name? names namenest nest$ oats + bailed bail backup history shuffle random randomise initrandom + prng prng.d prng.c prng.b prng.a rot64 64bits 64bitmask $->n + char->n sort$ $> $< qacsfot sort sortwith sort.test not-do do-now + now-do add-to new-do to-do unpack pack reverse nextword trim + printable found findwith matchitem mi.result mi.tidyup echo$ + witheach makewith with.hold number$ decimal base digit lower upper + sp space cr carriage findseq behead stuff pluck of table temp step + refresh conclude i^ i times times.action times.count times.start + abs decurse depth 2over 2swap dip dip.hold protect protected + nested move tally replace release share stack while until recurse + do this ' copy clamp max min else iff if done again 2drop 2dup + within unrot tuck bit mod nip / - < xor != or and not true false + sharefile releasefile putfile filepath input ding emit quid + operator? number? nest? size poke peek find join split [] take + immovable put ]bailby[ ]do[ ]this[ ]'[ ]else[ ]iff[ ]if[ ]again[ + ]done[ over rot swap drop dup return nestdepth stacksize time ~ ^ + | & >> << ** /mod * negate + 1+ > = nand fail python ] + + resolves actions ( n --> x ) + +$ "constant now! hex say $ char resolves forward ) ( builds is ] [" +nest$ buildernest put + +[ table + b.constant b.now! b.hex b.say b.$ b.char b.resolves + b.forward b.) b.( b.builds b.is b.] b.[ ] + + resolves jobs ( n --> x ) """ @@ -1704,4 +1705,4 @@ def sub_build(): except QuackeryError as diagnostics: print('\nQuackery crashed.\n') print(diagnostics) - print() \ No newline at end of file + print() From 241ba0b3e038d5140751230666bbbeaced6a3707 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Fri, 9 Dec 2022 07:52:46 -0500 Subject: [PATCH 116/121] Update quackery_OOP.py --- quackery_OOP.py | 1572 +++++++++++++++++++++++------------------------ 1 file changed, 786 insertions(+), 786 deletions(-) diff --git a/quackery_OOP.py b/quackery_OOP.py index 2a7edad..2a07551 100644 --- a/quackery_OOP.py +++ b/quackery_OOP.py @@ -112,7 +112,7 @@ def from_return(self): return self.rstack.pop() def failed(self, message): - self.traverse(self.build("stacksize pack decimal unbuild return$ nestdepth ]bailby[")) + self.traverse(self.build("stacksize pack decimal unbuild base release return$ nestdepth ]bailby[")) returnstack = self.string_from_stack() thestack = self.string_from_stack() raise QuackeryError('\n Problem: ' + message + @@ -738,898 +738,898 @@ def hexnum(ctx): predefined_qky = r""" - [ 0 ] is false ( --> b ) +[ 0 ] is false ( --> b ) - [ 1 ] is true ( --> b ) +[ 1 ] is true ( --> b ) - [ dup nand ] is not ( b --> b ) +[ dup nand ] is not ( b --> b ) - [ nand not ] is and ( b b --> b ) +[ nand not ] is and ( b b --> b ) - [ not swap not nand ] is or ( b b --> b ) +[ not swap not nand ] is or ( b b --> b ) - [ = not ] is != ( x x --> b ) +[ = not ] is != ( x x --> b ) - [ not swap not != ] is xor ( b b --> b ) +[ not swap not != ] is xor ( b b --> b ) - [ swap > ] is < ( n n --> b ) +[ swap > ] is < ( n n --> b ) - [ negate + ] is - ( n --> n ) +[ negate + ] is - ( n --> n ) - [ /mod drop ] is / ( n n --> n ) +[ /mod drop ] is / ( n n --> n ) - [ swap drop ] is nip ( x x --> x ) +[ swap drop ] is nip ( x x --> x ) - [ /mod nip ] is mod ( n n --> n ) +[ /mod nip ] is mod ( n n --> n ) - [ 1 swap << ] is bit ( n --> n ) +[ 1 swap << ] is bit ( n --> n ) - [ swap over ] is tuck ( x x --> x x x ) +[ swap over ] is tuck ( x x --> x x x ) - [ rot rot ] is unrot ( x x x --> x x x ) +[ rot rot ] is unrot ( x x x --> x x x ) - [ rot tuck > - unrot > not and ] is within ( n n n --> b ) +[ rot tuck > + unrot > not and ] is within ( n n n --> b ) - [ over over ] is 2dup ( x x --> x x x x ) +[ over over ] is 2dup ( x x --> x x x x ) - [ drop drop ] is 2drop ( x x --> ) +[ drop drop ] is 2drop ( x x --> ) - [ ]again[ ] is again ( --> ) +[ ]again[ ] is again ( --> ) - [ ]done[ ] is done ( --> ) +[ ]done[ ] is done ( --> ) - [ ]if[ ] is if ( b --> ) +[ ]if[ ] is if ( b --> ) - [ ]iff[ ] is iff ( b --> ) +[ ]iff[ ] is iff ( b --> ) - [ ]else[ ] is else ( --> ) +[ ]else[ ] is else ( --> ) - [ 2dup > if swap drop ] is min ( n n n --> n ) +[ 2dup > if swap drop ] is min ( n n n --> n ) - [ 2dup < if swap drop ] is max ( n n n --> n ) +[ 2dup < if swap drop ] is max ( n n n --> n ) - [ rot min max ] is clamp ( n n n --> n ) +[ rot min max ] is clamp ( n n n --> n ) - [ dup nest? iff [] join ] is copy ( [ --> [ ) +[ dup nest? iff [] join ] is copy ( [ --> [ ) - [ ]'[ ] is ' ( --> x ) +[ ]'[ ] is ' ( --> x ) - [ ]this[ ] is this ( --> [ ) +[ ]this[ ] is this ( --> [ ) - [ ]do[ ] is do ( x --> ) +[ ]do[ ] is do ( x --> ) - [ ]this[ do ] is recurse ( --> ) +[ ]this[ do ] is recurse ( --> ) - [ not if ]again[ ] is until ( b --> ) +[ not if ]again[ ] is until ( b --> ) - [ not if ]done[ ] is while ( b --> ) +[ not if ]done[ ] is while ( b --> ) - [ immovable ]this[ ]done[ ] is stack ( --> s ) +[ immovable ]this[ ]done[ ] is stack ( --> s ) - [ dup take dup rot put ] is share ( s --> x ) +[ dup take dup rot put ] is share ( s --> x ) - [ take drop ] is release ( s --> ) +[ take drop ] is release ( s --> ) - [ dup release put ] is replace ( x s --> ) +[ dup release put ] is replace ( x s --> ) - [ dup take rot + swap put ] is tally ( n s --> ) +[ dup take rot + swap put ] is tally ( n s --> ) - [ swap take swap put ] is move ( s s --> ) +[ swap take swap put ] is move ( s s --> ) - [ [] tuck put ] is nested ( x --> [ ) +[ [] tuck put ] is nested ( x --> [ ) - [ stack [ ] ] is protected ( --> s ) +[ stack [ ] ] is protected ( --> s ) - [ protected take - ]'[ nested join - protected put ] is protect ( --> ) +[ protected take + ]'[ nested join + protected put ] is protect ( --> ) - ' stack ' filepath put - protect filepath +' stack ' filepath put +protect filepath - [ stack ] is dip.hold ( --> s ) - protect dip.hold +[ stack ] is dip.hold ( --> s ) +protect dip.hold - [ dip.hold put - ]'[ do dip.hold take ] is dip ( x --> x ) +[ dip.hold put + ]'[ do dip.hold take ] is dip ( x --> x ) - [ rot dip rot ] is 2swap ( x x x x --> x x x x ) +[ rot dip rot ] is 2swap ( x x x x --> x x x x ) - [ dip [ dip 2dup ] 2swap ] is 2over ( x x x x --> x x x x x x ) +[ dip [ dip 2dup ] 2swap ] is 2over ( x x x x --> x x x x x x ) - [ stack ] is depth ( --> s ) - protect depth +[ stack ] is depth ( --> s ) +protect depth - [ depth share - 0 != while - -1 depth tally - ]this[ do - 1 depth tally ] is decurse ( --> ) +[ depth share + 0 != while + -1 depth tally + ]this[ do + 1 depth tally ] is decurse ( --> ) - [ dup 0 < if negate ] is abs ( n --> n ) +[ dup 0 < if negate ] is abs ( n --> n ) - [ stack ] is times.start ( --> s ) - protect times.start +[ stack ] is times.start ( --> s ) +protect times.start - [ stack ] is times.count ( --> s ) - protect times.count +[ stack ] is times.count ( --> s ) +protect times.count - [ stack ] is times.action ( --> s ) - protect times.action +[ stack ] is times.action ( --> s ) +protect times.action - [ ]'[ times.action put - dup times.start put - [ 1 - dup -1 > while - times.count put - times.action share do - times.count take again ] - drop - times.action release - times.start release ] is times ( n --> ) - - [ times.count share ] is i ( --> n ) - - [ times.start share i 1+ - ] is i^ ( --> n ) - - [ 0 times.count replace ] is conclude ( --> ) - - [ times.start share - times.count replace ] is refresh ( --> ) - - [ times.count take 1+ - swap - times.count put ] is step ( --> s ) - - [ stack ] is temp ( --> s ) - protect temp +[ ]'[ times.action put + dup times.start put + [ 1 - dup -1 > while + times.count put + times.action share do + times.count take again ] + drop + times.action release + times.start release ] is times ( n --> ) - [ immovable - dup -1 > + - ]this[ swap peek - ]done[ ] is table ( n --> x ) +[ times.count share ] is i ( --> n ) - [ [] unrot - dup 1 < iff 2drop done - [ 2 /mod over while - if [ dip [ tuck join swap ] ] - dip [ dup join ] - again ] 2drop join ] is of ( x n --> [ ) +[ times.start share i 1+ - ] is i^ ( --> n ) - [ split 1 split - swap dip join - 0 peek ] is pluck ( [ n --> [ x ) +[ 0 times.count replace ] is conclude ( --> ) - [ split - rot nested - swap join join ] is stuff ( x [ n --> [ ) +[ times.start share + times.count replace ] is refresh ( --> ) - [ 0 pluck ] is behead ( [ --> [ x ) +[ times.count take 1+ + swap - times.count put ] is step ( --> s ) - [ over size over size - dup temp put - swap - 1+ times - [ 2dup over size split - drop = if - [ i^ temp replace - conclude ] - behead drop ] - 2drop temp take ] is findseq ( [ [ --> n ) +[ stack ] is temp ( --> s ) +protect temp - [ 13 ] is carriage ( --> c ) +[ immovable + dup -1 > + + ]this[ swap peek + ]done[ ] is table ( n --> x ) - [ carriage emit ] is cr ( --> ) +[ [] unrot + dup 1 < iff 2drop done + [ 2 /mod over while + if [ dip [ tuck join swap ] ] + dip [ dup join ] + again ] 2drop join ] is of ( x n --> [ ) - [ 32 ] is space ( --> c ) +[ split 1 split + swap dip join + 0 peek ] is pluck ( [ n --> [ x ) - [ space emit ] is sp ( --> ) +[ split + rot nested + swap join join ] is stuff ( x [ n --> [ ) - [ dup char a char { within - if [ 32 - ] ] is upper ( c --> c ) +[ 0 pluck ] is behead ( [ --> [ x ) - [ dup char A char [ within - if [ 32 + ] ] is lower ( c --> c ) +[ over size over size + dup temp put + swap - 1+ times + [ 2dup over size split + drop = if + [ i^ temp replace + conclude ] + behead drop ] + 2drop temp take ] is findseq ( [ [ --> n ) - [ dup 10 < - iff 48 else 55 + ] is digit ( n --> c ) +[ 13 ] is carriage ( --> c ) - [ stack 10 ] is base ( --> s ) - protect base +[ carriage emit ] is cr ( --> ) - [ 10 base put ] is decimal ( --> ) +[ 32 ] is space ( --> c ) - [ $ '' over abs - [ base share /mod digit - rot join swap - dup 0 = until ] - drop - swap 0 < if - [ $ '-' swap join ] ] is number$ ( n --> $ ) - - [ stack ] is with.hold ( --> s ) - protect with.hold +[ space emit ] is sp ( --> ) - [ nested - ' [ dup with.hold put - size times ] - ' [ with.hold share - i ~ peek ] - rot join - nested join - ' [ with.hold release ] - join ] is makewith ( x --> [ ) +[ dup char a char { within + if [ 32 - ] ] is upper ( c --> c ) - [ ]'[ makewith do ] is witheach ( [ --> ) +[ dup char A char [ within + if [ 32 + ] ] is lower ( c --> c ) - [ witheach emit ] is echo$ ( $ --> ) +[ dup 10 < + iff 48 else 55 + ] is digit ( n --> c ) - [ stack ] is mi.tidyup ( --> s ) - protect mi.tidyup +[ stack 10 ] is base ( --> s ) +protect base - [ stack ] is mi.result ( --> s ) - protect mi.result - - [ mi.tidyup put - over size mi.result put - nested - ' [ if - [ i^ mi.result replace - conclude ] ] - join makewith do - mi.tidyup take do - mi.result take ] is matchitem ( [ x x --> n ) - - [ ]'[ ]'[ matchitem ] is findwith ( [ --> n ) +[ 10 base put ] is decimal ( --> ) - [ size < ] is found ( n [ --> b ) - - [ space > ] is printable ( c --> b ) - - [ dup findwith - printable [ ] - split nip ] is trim ( $ --> $ ) +[ $ '' over abs + [ base share /mod digit + rot join swap + dup 0 = until ] + drop + swap 0 < if + [ $ '-' swap join ] ] is number$ ( n --> $ ) - [ dup findwith - [ printable not ] [ ] - split swap ] is nextword ( $ --> $ $ ) - - [ dup nest? if - [ dup size 2 < if done - dup size 2 / split - recurse swap - recurse join ] ] is reverse ( x --> x ) - - [ [] swap times - [ swap nested join ] - reverse ] is pack ( * n --> [ ) - - [ witheach [ ] ] is unpack ( [ --> * ) - - [ stack ] is to-do ( --> s ) - protect to-do - - [ ' done swap put ] is new-do ( s --> ) - - [ dip [ 1+ pack ] put ] is add-to ( * x n s --> ) - - [ [ dup take - unpack do again ] drop ] is now-do ( s --> ) - - [ 1 split reverse join - now-do ] is do-now ( s --> ) - - [ [ dup take ' done = until ] - drop ] is not-do ( s --> ) - - [ stack ] is sort.test ( --> s ) - protect sort.test - - [ ]'[ sort.test put - [] swap witheach - [ swap 2dup findwith - [ over sort.test share - do ] [ ] - nip stuff ] - sort.test release ] is sortwith ( [ --> [ ) - - [ sortwith > ] is sort ( [ --> [ ) - - [ 32 127 clamp 32 - - [ table - 0 86 88 93 94 90 92 87 63 64 75 73 82 74 81 76 - 1 2 3 4 5 6 7 8 9 10 83 84 69 72 70 85 - 91 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 - 41 43 45 47 49 51 53 55 57 59 61 65 78 66 77 80 - 89 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 - 42 44 46 48 50 52 54 56 58 60 62 67 79 68 71 0 ] - ] is qacsfot ( c --> n ) - - [ [ dup $ '' = iff false done - over $ '' = iff true done - behead rot behead rot - 2dup = iff [ 2drop swap ] again - qacsfot swap qacsfot > ] - unrot 2drop ] is $< ( $ $ --> b ) - - [ swap $< ] is $> ( $ $ --> b ) - - [ sortwith $> ] is sort$ ( [ --> [ ) - - [ upper 47 - 0 44 clamp - [ table - -1 0 1 2 3 4 5 6 7 8 9 -1 -1 -1 -1 - -1 -1 -1 10 11 12 13 14 15 16 17 18 19 20 21 - 22 23 24 25 26 27 28 29 30 31 32 33 34 35 -1 ] - dup 0 base share - within not if [ drop -1 ] ] is char->n ( c --> n ) - - [ dup $ '' = iff [ drop 0 false ] done - dup 0 peek char - = - tuck if [ behead drop ] - dup $ '' = iff [ 2drop 0 false ] done - true 0 rot witheach - [ char->n - dup 0 < iff [ drop nip false swap ] - else [ swap base share * + ] ] - rot if negate - swap ] is $->n ( $ --> n b ) - - ( adapted from 'A small noncryptographic PRNG' by Bob Jenkins ) - ( https://burtleburtle.net/bob/rand/smallprng.html ) - - [ hex FFFFFFFFFFFFFFFF ] is 64bitmask ( --> f ) - - [ 64bitmask & ] is 64bits ( f --> f ) - - [ dip 64bits 2dup << 64bits - unrot 64 swap - >> | ] is rot64 ( f n --> f ) - - [ stack 0 ] is prng.a ( --> s ) - [ stack 0 ] is prng.b ( --> s ) - [ stack 0 ] is prng.c ( --> s ) - [ stack 0 ] is prng.d ( --> s ) - - [ prng.a share - prng.b share tuck - 7 rot64 - 64bits swap - prng.c share tuck - 13 rot64 ^ prng.a replace - prng.d share tuck - 37 rot64 + 64bits prng.b replace - over + 64bits prng.c replace - prng.a share + 64bits - dup prng.d replace ] is prng ( --> n ) - - [ hex F1EA5EAD prng.a replace - dup prng.b replace - dup prng.c replace - prng.d replace - 20 times [ prng drop ] ] is initrandom ( n --> ) - - hex DEFACEABADFACADE initrandom - - [ time initrandom ] is randomise ( --> ) - - [ 64bitmask 1+ - over / over * - [ prng 2dup > not while - drop again ] - nip swap mod ] is random ( n --> n ) - - [ [] swap dup size times - [ dup size random pluck - nested rot join swap ] - drop ] is shuffle ( [ --> [ ) - - [ stack ] is history ( --> s ) - - [ protected share history put - protected share 0 - [ over size over - > while - 2dup peek - size unrot - 1+ again ] - 2drop - protected share size pack - history put - pack dup history put unpack - stacksize history put - nestdepth history put - false history put ] is backup ( n --> ) - - [ history release - nestdepth - history take - - ]bailby[ - true history put ] is bail ( --> ) - - [ history take iff - [ stacksize - history take - history share - size - - times drop - history take unpack - history take unpack - history share size - [ dup 0 > while - 1 - - history share - over peek - rot over size - swap - - [ dup 0 > while - over release - 1 - again ] - 2drop again ] - drop - history take - protected replace - true ] - else - [ 5 times - [ history release ] - false ] ] is bailed ( --> b ) +[ stack ] is with.hold ( --> s ) +protect with.hold - [ quid swap quid = ] is oats ( x x --> b ) +[ nested + ' [ dup with.hold put + size times ] + ' [ with.hold share + i ~ peek ] + rot join + nested join + ' [ with.hold release ] + join ] is makewith ( x --> [ ) - [ [] swap - [ trim - dup size while - nextword nested - swap dip join again ] - drop ] is nest$ ( $ --> [ ) +[ ]'[ makewith do ] is witheach ( [ --> ) - [ stack ] is namenest ( --> s ) +[ witheach emit ] is echo$ ( $ --> ) - [ namenest share ] is names ( --> [ ) +[ stack ] is mi.tidyup ( --> s ) +protect mi.tidyup - [ names find names found ] is name? ( $ --> b ) +[ stack ] is mi.result ( --> s ) +protect mi.result - forward is actions ( n --> x ) +[ mi.tidyup put + over size mi.result put + nested + ' [ if + [ i^ mi.result replace + conclude ] ] + join makewith do + mi.tidyup take do + mi.result take ] is matchitem ( [ x x --> n ) - [ ' actions ] is actiontable ( --> x ) +[ ]'[ ]'[ matchitem ] is findwith ( [ --> n ) - [ actiontable share tuck - findwith [ over oats ] drop - swap found ] is named? ( x --> b ) +[ size < ] is found ( n [ --> b ) - forward is reflect ( x --> x ) - [ dup nest? if - [ dup [] = if done - dup size 1 = iff - [ 0 peek - dup named? iff - nested done - reflect nested ] - done - dup size 2 / split - recurse swap - recurse join ] ] resolves reflect ( x --> x ) +[ space > ] is printable ( c --> b ) - [ stack ] is buildernest ( --> s ) +[ dup findwith + printable [ ] + split nip ] is trim ( $ --> $ ) - [ buildernest share ] is builders ( --> s ) +[ dup findwith + [ printable not ] [ ] + split swap ] is nextword ( $ --> $ $ ) - [ builders find - builders found ] is builder? ( $ --> b ) +[ dup nest? if + [ dup size 2 < if done + dup size 2 / split + recurse swap + recurse join ] ] is reverse ( x --> x ) - forward is jobs ( n --> x ) +[ [] swap times + [ swap nested join ] + reverse ] is pack ( * n --> [ ) - [ ' jobs ] is jobtable ( --> [ ) +[ witheach [ ] ] is unpack ( [ --> * ) - [ stack ] is message ( --> s ) +[ stack ] is to-do ( --> s ) +protect to-do - [ stack ] is b.nesting ( --> s ) - protect b.nesting +[ ' done swap put ] is new-do ( s --> ) - [ stack ] is b.to-do ( --> s ) +[ dip [ 1+ pack ] put ] is add-to ( * x n s --> ) - [ $ '[' b.nesting put - [] swap ] is b.[ ( [ $ --> [ [ $ ) +[ [ dup take + unpack do again ] drop ] is now-do ( s --> ) - [ b.nesting take dup - $ '' = if - [ $ 'Unexpected "]".' - message put - bail ] - dup $ '[' = iff drop - else - [ $ 'Nest mismatch: ' - swap join $ ' ]' join - message put - bail ] - dip [ nested join ] ] is b.] ( [ [ $ --> [ $ ) +[ 1 split reverse join + now-do ] is do-now ( s --> ) - [ over [] = if - [ $ '"is" needs something to name before it.' - message put - bail ] - dup $ '' = if - [ $ '"is" needs a name after it.' - message put - bail ] - nextword nested - namenest take - join - namenest put - dip - [ -1 pluck - actiontable take - 1 stuff - actiontable put ] ] is b.is ( [ $ --> [ $ ) - - [ over [] = if - [ $ '"builds" needs something to name before it.' - message put - bail ] - dup $ '' = if - [ $ '"builds" needs a name after it.' - message put - bail ] - nextword nested - buildernest take - join - buildernest put - dip - [ -1 pluck - jobtable take - 1 stuff - jobtable put ] ] is b.builds ( [ $ --> [ $ ) - - [ trim nextword - dup $ '' = if - [ $ 'Unfinished comment.' - message put - bail ] - $ ')' = until ] is b.( ( [ $ --> $ [ ) +[ [ dup take ' done = until ] + drop ] is not-do ( s --> ) - [ $ 'Unexpected ")".' - message put - bail ] is b.) ( [ $ --> $ [ ) +[ stack ] is sort.test ( --> s ) +protect sort.test - [ $ 'Unresolved reference.' - fail ] is unresolved ( --> ) +[ ]'[ sort.test put + [] swap witheach + [ swap 2dup findwith + [ over sort.test share + do ] [ ] + nip stuff ] + sort.test release ] is sortwith ( [ --> [ ) - [ dip - [ ' [ unresolved ] - copy nested join ] ] is b.forward ( [ $ --> [ $ ) +[ sortwith > ] is sort ( [ --> [ ) - [ over [] = if - [ $ '"resolves" needs something to resolve.' - message put - bail ] - dup $ '' = if - [ $ '"resolves" needs a name to resolve into.' - message put - bail ] - dip [ -1 split ] - nextword dup temp put - names find - dup names found not if - [ $ 'Unknown word after "resolves": ' - temp take join - message put - bail ] - actions - dup ' [ unresolved ] = not if - [ char " temp take join - $ '" is not an unresolved forward reference.' - join - message put - bail ] - rot 0 peek over - replace - ' unresolved swap - ' replace 2 b.to-do add-to - temp release ] is b.resolves ( [ $ --> [ $ ) - - [ 1 split - over $ '' = if - [ $ '"char" needs a character after it.' - message put - bail ] - dip join ] is b.char ( [ $ --> [ $ ) - - [ dup $ '' = if - [ $ '"$" needs to be followed by a string.' - message put - bail ] - behead over find - 2dup swap found not if - [ $ 'Endless string discovered.' - message put - bail ] - split behead drop - ' ' nested - rot nested join - nested swap dip join ] is b.$ ( [ $ --> [ $ ) - - [ dup $ '' = if - [ $ '"say" needs to be followed by a string.' - message put - bail ] - $ '$' builders find jobs do - dip - [ -1 pluck - ' echo$ nested join - nested join ] ] is b.say ( [ $ --> [ $ ) - - [ 16 base put - nextword dup - $ '' = if - [ $ '"hex" needs a number after it.' - message put - bail ] - dup $->n iff - [ nip swap dip join ] +[ 32 127 clamp 32 - + [ table + 0 86 88 93 94 90 92 87 63 64 75 73 82 74 81 76 + 1 2 3 4 5 6 7 8 9 10 83 84 69 72 70 85 + 91 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 + 41 43 45 47 49 51 53 55 57 59 61 65 78 66 77 80 + 89 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 + 42 44 46 48 50 52 54 56 58 60 62 67 79 68 71 0 ] +] is qacsfot ( c --> n ) + +[ [ dup $ '' = iff false done + over $ '' = iff true done + behead rot behead rot + 2dup = iff [ 2drop swap ] again + qacsfot swap qacsfot > ] + unrot 2drop ] is $< ( $ $ --> b ) + +[ swap $< ] is $> ( $ $ --> b ) + +[ sortwith $> ] is sort$ ( [ --> [ ) + +[ upper 47 - 0 44 clamp + [ table + -1 0 1 2 3 4 5 6 7 8 9 -1 -1 -1 -1 + -1 -1 -1 10 11 12 13 14 15 16 17 18 19 20 21 + 22 23 24 25 26 27 28 29 30 31 32 33 34 35 -1 ] + dup 0 base share + within not if [ drop -1 ] ] is char->n ( c --> n ) + +[ dup $ '' = iff [ drop 0 false ] done + dup 0 peek char - = + tuck if [ behead drop ] + dup $ '' = iff [ 2drop 0 false ] done + true 0 rot witheach + [ char->n + dup 0 < iff [ drop nip false swap ] + else [ swap base share * + ] ] + rot if negate + swap ] is $->n ( $ --> n b ) + +( adapted from 'A small noncryptographic PRNG' by Bob Jenkins ) +( https://burtleburtle.net/bob/rand/smallprng.html ) + +[ hex FFFFFFFFFFFFFFFF ] is 64bitmask ( --> f ) + +[ 64bitmask & ] is 64bits ( f --> f ) + +[ dip 64bits 2dup << 64bits + unrot 64 swap - >> | ] is rot64 ( f n --> f ) + +[ stack 0 ] is prng.a ( --> s ) +[ stack 0 ] is prng.b ( --> s ) +[ stack 0 ] is prng.c ( --> s ) +[ stack 0 ] is prng.d ( --> s ) + +[ prng.a share + prng.b share tuck + 7 rot64 - 64bits swap + prng.c share tuck + 13 rot64 ^ prng.a replace + prng.d share tuck + 37 rot64 + 64bits prng.b replace + over + 64bits prng.c replace + prng.a share + 64bits + dup prng.d replace ] is prng ( --> n ) + +[ hex F1EA5EAD prng.a replace + dup prng.b replace + dup prng.c replace + prng.d replace + 20 times [ prng drop ] ] is initrandom ( n --> ) + +hex DEFACEABADFACADE initrandom + +[ time initrandom ] is randomise ( --> ) + +[ 64bitmask 1+ + over / over * + [ prng 2dup > not while + drop again ] + nip swap mod ] is random ( n --> n ) + +[ [] swap dup size times + [ dup size random pluck + nested rot join swap ] + drop ] is shuffle ( [ --> [ ) + +[ stack ] is history ( --> s ) + +[ protected share history put + protected share 0 + [ over size over + > while + 2dup peek + size unrot + 1+ again ] + 2drop + protected share size pack + history put + pack dup history put unpack + stacksize history put + nestdepth history put + false history put ] is backup ( n --> ) + +[ history release + nestdepth + history take + - ]bailby[ + true history put ] is bail ( --> ) + +[ history take iff + [ stacksize + history take + history share + size - - times drop + history take unpack + history take unpack + history share size + [ dup 0 > while + 1 - + history share + over peek + rot over size + swap - + [ dup 0 > while + over release + 1 - again ] + 2drop again ] + drop + history take + protected replace + true ] else - [ drop - char " swap join - $ '" is not hexadecimal.' - join message put - bail ] - base release ] is b.hex ( [ $ --> [ $ ) + [ 5 times + [ history release ] + false ] ] is bailed ( --> b ) - [ dip [ -1 split ] swap do ] is b.now! ( [ $ --> [ $ ) +[ quid swap quid = ] is oats ( x x --> b ) - [ over [] = if - [ $ '"constant" needs something before it.' - message put +[ [] swap + [ trim + dup size while + nextword nested + swap dip join again ] + drop ] is nest$ ( $ --> [ ) + +[ stack ] is namenest ( --> s ) + +[ namenest share ] is names ( --> [ ) + +[ names find names found ] is name? ( $ --> b ) + + forward is actions ( n --> x ) + +[ ' actions ] is actiontable ( --> x ) + +[ actiontable share tuck + findwith [ over oats ] drop + swap found ] is named? ( x --> b ) + + forward is reflect ( x --> x ) +[ dup nest? if + [ dup [] = if done + dup size 1 = iff + [ 0 peek + dup named? iff + nested done + reflect nested ] + done + dup size 2 / split + recurse swap + recurse join ] ] resolves reflect ( x --> x ) + +[ stack ] is buildernest ( --> s ) + +[ buildernest share ] is builders ( --> s ) + +[ builders find + builders found ] is builder? ( $ --> b ) + + forward is jobs ( n --> x ) + +[ ' jobs ] is jobtable ( --> [ ) + +[ stack ] is message ( --> s ) + +[ stack ] is b.nesting ( --> s ) +protect b.nesting + +[ stack ] is b.to-do ( --> s ) + +[ $ '[' b.nesting put + [] swap ] is b.[ ( [ $ --> [ [ $ ) + +[ b.nesting take dup + $ '' = if + [ $ 'Unexpected "]".' + message put + bail ] + dup $ '[' = iff drop + else + [ $ 'Nest mismatch: ' + swap join $ ' ]' join + message put + bail ] + dip [ nested join ] ] is b.] ( [ [ $ --> [ $ ) + +[ over [] = if + [ $ '"is" needs something to name before it.' + message put + bail ] + dup $ '' = if + [ $ '"is" needs a name after it.' + message put + bail ] + nextword nested + namenest take + join + namenest put + dip + [ -1 pluck + actiontable take + 1 stuff + actiontable put ] ] is b.is ( [ $ --> [ $ ) + +[ over [] = if + [ $ '"builds" needs something to name before it.' + message put + bail ] + dup $ '' = if + [ $ '"builds" needs a name after it.' + message put + bail ] + nextword nested + buildernest take + join + buildernest put + dip + [ -1 pluck + jobtable take + 1 stuff + jobtable put ] ] is b.builds ( [ $ --> [ $ ) + +[ trim nextword + dup $ '' = if + [ $ 'Unfinished comment.' + message put + bail ] + $ ')' = until ] is b.( ( [ $ --> $ [ ) + +[ $ 'Unexpected ")".' + message put + bail ] is b.) ( [ $ --> $ [ ) + +[ $ 'Unresolved reference.' + fail ] is unresolved ( --> ) + +[ dip + [ ' [ unresolved ] + copy nested join ] ] is b.forward ( [ $ --> [ $ ) + + [ over [] = if + [ $ '"resolves" needs something to resolve.' + message put + bail ] + dup $ '' = if + [ $ '"resolves" needs a name to resolve into.' + message put + bail ] + dip [ -1 split ] + nextword dup temp put + names find + dup names found not if + [ $ 'Unknown word after "resolves": ' + temp take join + message put + bail ] + actions + dup ' [ unresolved ] = not if + [ char " temp take join + $ '" is not an unresolved forward reference.' + join + message put + bail ] + rot 0 peek over + replace + ' unresolved swap + ' replace 2 b.to-do add-to + temp release ] is b.resolves ( [ $ --> [ $ ) + +[ 1 split + over $ '' = if + [ $ '"char" needs a character after it.' + message put + bail ] + dip join ] is b.char ( [ $ --> [ $ ) + +[ dup $ '' = if + [ $ '"$" needs to be followed by a string.' + message put + bail ] + behead over find + 2dup swap found not if + [ $ 'Endless string discovered.' + message put + bail ] + split behead drop + ' ' nested + rot nested join + nested swap dip join ] is b.$ ( [ $ --> [ $ ) + +[ dup $ '' = if + [ $ '"say" needs to be followed by a string.' + message put + bail ] + $ '$' builders find jobs do + dip + [ -1 pluck + ' echo$ nested join + nested join ] ] is b.say ( [ $ --> [ $ ) + +[ 16 base put + nextword dup + $ '' = if + [ $ '"hex" needs a number after it.' + message put + bail ] + dup $->n iff + [ nip swap dip join ] + else + [ drop + char " swap join + $ '" is not hexadecimal.' + join message put + bail ] + base release ] is b.hex ( [ $ --> [ $ ) + +[ dip [ -1 split ] swap do ] is b.now! ( [ $ --> [ $ ) + +[ over [] = if + [ $ '"constant" needs something before it.' + message put + bail ] + dip + [ -1 pluck do + dup number? not if + [ ' ' nested swap + nested join + nested ] + join ] ] is b.constant ( [ $ --> [ $ ) + +[ ' [ namenest actiontable + buildernest jobtable ] + witheach + [ do share copy + history put ] ] is backupwords ( --> ) + +[ ' [ jobtable buildernest + actiontable namenest ] + witheach + [ do dup release + history swap move ] ] is restorewords ( --> ) + +[ 4 times + [ history release ] ] is releasewords ( --> ) + +[ backupwords + b.to-do new-do + 1 backup + [ $ '' b.nesting put + decimal + [] swap + [ trim + dup $ '' = iff drop done + nextword + dup builders find + dup builders found iff + [ dip [ drop trim ] + jobs do ] again + drop + dup names find + dup names found iff + [ actions nested + nip swap dip join ] again + drop + dup $->n iff + [ nip swap dip join ] again + drop + $ 'Unknown word: ' + swap join message put bail ] - dip - [ -1 pluck do - dup number? not if - [ ' ' nested swap - nested join - nested ] - join ] ] is b.constant ( [ $ --> [ $ ) - - [ ' [ namenest actiontable - buildernest jobtable ] - witheach - [ do share copy - history put ] ] is backupwords ( --> ) - - [ ' [ jobtable buildernest - actiontable namenest ] - witheach - [ do dup release - history swap move ] ] is restorewords ( --> ) - - [ 4 times - [ history release ] ] is releasewords ( --> ) - - [ backupwords - b.to-do new-do - 1 backup - [ $ '' b.nesting put - decimal - [] swap - [ trim - dup $ '' = iff drop done - nextword - dup builders find - dup builders found iff - [ dip [ drop trim ] - jobs do ] again - drop - dup names find - dup names found iff - [ actions nested - nip swap dip join ] again - drop - dup $->n iff - [ nip swap dip join ] again - drop - $ 'Unknown word: ' - swap join message put - bail ] - base release - b.nesting take dup - $ '' = iff drop - else - [ $ 'Unfinished nest: ' - swap join message put - bail ] ] - bailed iff - [ drop b.to-do now-do - restorewords - ' ' nested - message take nested join - ' echo$ nested join ] - else - [ b.to-do not-do - releasewords ] ] is build ( $ --> [ ) - - [ build do ] is quackery ( $ --> ) - - [ stack -1 ] is nesting ( --> [ ) - - forward is unbuild ( x --> $ ) - - [ nesting share - 0 = iff [ drop $ '...' ] done - $ '' swap - dup number? iff - [ number$ join ] done - actiontable share - behead drop - [ dup [] = iff - [ drop false ] done - behead - rot tuck oats iff - [ drop size 2 + - actiontable share - size swap - - names swap peek join - true ] done - swap again ] - if done - dup nest? iff - [ $ '[ ' rot join swap - [ dup [] = iff drop done - behead - -1 nesting tally - unbuild - 1 nesting tally - space join - swap dip join again ] - $ ']' join ] - else - [ drop - $ "Quackery was worried by a python." - fail ] ] resolves unbuild ( x --> $ ) - - [ unbuild echo$ ] is echo ( x --> ) - - [ $ '' - return -2 split drop - witheach - [ dup number? iff - [ number$ join - $ '} ' join ] + base release + b.nesting take dup + $ '' = iff drop else - [ $ '{' swap dip join - actiontable share - findwith - [ over oats ] drop - dup actiontable share - found iff - [ 1 - names swap - peek join - space join ] - else - [ drop $ '[...] ' - join ] ] ] - -1 split drop ] is return$ ( --> $ ) - - [ return$ echo$ ] is echoreturn ( --> ) - - [ stacksize dup 0 = iff - [ $ 'Stack empty.' echo$ drop ] - else - [ $ 'Stack: ' echo$ - pack dup - witheach [ echo sp ] - unpack ] - cr ] is echostack ( --> ) - - [ cr $ '' $ '/O> ' - [ input - dup $ '' != while - carriage join join - $ '... ' again ] - drop - quackery - 5 nesting put - cr echostack - nesting release again ] is shell ( --> ) - - [ cr randomise 12 random - [ table - $ 'Goodbye.' $ 'Adieu.' $ 'So long.' - $ 'Cheerio.' $ 'Aloha.' $ 'Ciao.' - $ 'Farewell.' $ 'Be seeing you.' - $ 'Sayonara.' $ 'Auf wiedersehen.' - $ 'Toodles.' $ 'Hasta la vista.' ] - do echo$ cr cr - 3 ]bailby[ ] is leave ( --> ) - - [ stacksize times drop ] is empty ( all --> ) - - [ tuck temp put - witheach - [ dup size - rot + dup - temp share > iff - [ cr drop dup size ] - else sp 1+ swap echo$ ] - drop temp release ] is wrap$ ( [ n --> ) - - [ names reverse 70 wrap$ cr - builders reverse - 70 wrap$ cr ] is words ( --> ) - - [ dup name? iff drop + [ $ 'Unfinished nest: ' + swap join message put + bail ] ] + bailed iff + [ drop b.to-do now-do + restorewords + ' ' nested + message take nested join + ' echo$ nested join ] + else + [ b.to-do not-do + releasewords ] ] is build ( $ --> [ ) + +[ build do ] is quackery ( $ --> ) + +[ stack -1 ] is nesting ( --> [ ) + + forward is unbuild ( x --> $ ) + +[ nesting share + 0 = iff [ drop $ '...' ] done + $ '' swap + dup number? iff + [ number$ join ] done + actiontable share + behead drop + [ dup [] = iff + [ drop false ] done + behead + rot tuck oats iff + [ drop size 2 + + actiontable share + size swap - + names swap peek join + true ] done + swap again ] + if done + dup nest? iff + [ $ '[ ' rot join swap + [ dup [] = iff drop done + behead + -1 nesting tally + unbuild + 1 nesting tally + space join + swap dip join again ] + $ ']' join ] + else + [ drop + $ "Quackery was worried by a python." + fail ] ] resolves unbuild ( x --> $ ) + +[ unbuild echo$ ] is echo ( x --> ) + +[ $ '' + return -2 split drop + witheach + [ dup number? iff + [ number$ join + $ '} ' join ] else - [ dup sharefile not if - [ $ |$ 'file not found: "| - swap join - $ |"' echo$| join ] - nip quackery ] ] is loadfile ( $ --> ) - - [ dup sharefile iff - [ swap releasefile ] - else [ drop false ] ] is takefile ( $ --> $ b ) - - [ dup releasefile iff - putfile - else [ 2drop false ] ] is replacefile ( $ $ --> b ) - - [ nested ' [ ' ] - swap join - decimal unbuild - base release ] is quackify ( x --> $ ) - - $ "quackify replacefile takefile loadfile words empty wrap$ leave - shell echostack echoreturn return$ echo unbuild nesting quackery - build releasewords restorewords backupwords unresolved b.to-do - b.nesting message jobtable jobs builder? builders buildernest - reflect named? actiontable actions name? names namenest nest$ oats - bailed bail backup history shuffle random randomise initrandom - prng prng.d prng.c prng.b prng.a rot64 64bits 64bitmask $->n - char->n sort$ $> $< qacsfot sort sortwith sort.test not-do do-now - now-do add-to new-do to-do unpack pack reverse nextword trim - printable found findwith matchitem mi.result mi.tidyup echo$ - witheach makewith with.hold number$ decimal base digit lower upper - sp space cr carriage findseq behead stuff pluck of table temp step - refresh conclude i^ i times times.action times.count times.start - abs decurse depth 2over 2swap dip dip.hold protect protected - nested move tally replace release share stack while until recurse - do this ' copy clamp max min else iff if done again 2drop 2dup - within unrot tuck bit mod nip / - < xor != or and not true false - sharefile releasefile putfile filepath input ding emit quid - operator? number? nest? size poke peek find join split [] take - immovable put ]bailby[ ]do[ ]this[ ]'[ ]else[ ]iff[ ]if[ ]again[ - ]done[ over rot swap drop dup return nestdepth stacksize time ~ ^ - | & >> << ** /mod * negate + 1+ > = nand fail python" - - nest$ namenest put - - [ table - quackify replacefile takefile loadfile words empty wrap$ leave - shell echostack echoreturn return$ echo unbuild nesting quackery - build releasewords restorewords backupwords unresolved b.to-do - b.nesting message jobtable jobs builder? builders buildernest - reflect named? actiontable actions name? names namenest nest$ oats - bailed bail backup history shuffle random randomise initrandom - prng prng.d prng.c prng.b prng.a rot64 64bits 64bitmask $->n - char->n sort$ $> $< qacsfot sort sortwith sort.test not-do do-now - now-do add-to new-do to-do unpack pack reverse nextword trim - printable found findwith matchitem mi.result mi.tidyup echo$ - witheach makewith with.hold number$ decimal base digit lower upper - sp space cr carriage findseq behead stuff pluck of table temp step - refresh conclude i^ i times times.action times.count times.start - abs decurse depth 2over 2swap dip dip.hold protect protected - nested move tally replace release share stack while until recurse - do this ' copy clamp max min else iff if done again 2drop 2dup - within unrot tuck bit mod nip / - < xor != or and not true false - sharefile releasefile putfile filepath input ding emit quid - operator? number? nest? size poke peek find join split [] take - immovable put ]bailby[ ]do[ ]this[ ]'[ ]else[ ]iff[ ]if[ ]again[ - ]done[ over rot swap drop dup return nestdepth stacksize time ~ ^ - | & >> << ** /mod * negate + 1+ > = nand fail python ] - - resolves actions ( n --> x ) - - $ "constant now! hex say $ char resolves forward ) ( builds is ] [" - nest$ buildernest put - + [ $ '{' swap dip join + actiontable share + findwith + [ over oats ] drop + dup actiontable share + found iff + [ 1 - names swap + peek join + space join ] + else + [ drop $ '[...] ' + join ] ] ] + -1 split drop ] is return$ ( --> $ ) + +[ return$ echo$ ] is echoreturn ( --> ) + +[ stacksize dup 0 = iff + [ $ 'Stack empty.' echo$ drop ] + else + [ $ 'Stack: ' echo$ + pack dup + witheach [ echo sp ] + unpack ] + cr ] is echostack ( --> ) + +[ cr $ '' $ '/O> ' + [ input + dup $ '' != while + carriage join join + $ '... ' again ] + drop + quackery + 5 nesting put + cr echostack + nesting release again ] is shell ( --> ) + +[ cr randomise 12 random [ table - b.constant b.now! b.hex b.say b.$ b.char b.resolves - b.forward b.) b.( b.builds b.is b.] b.[ ] - - resolves jobs ( n --> x ) + $ 'Goodbye.' $ 'Adieu.' $ 'So long.' + $ 'Cheerio.' $ 'Aloha.' $ 'Ciao.' + $ 'Farewell.' $ 'Be seeing you.' + $ 'Sayonara.' $ 'Auf wiedersehen.' + $ 'Toodles.' $ 'Hasta la vista.' ] + do echo$ cr cr + 3 ]bailby[ ] is leave ( --> ) + +[ stacksize times drop ] is empty ( all --> ) + +[ tuck temp put + witheach + [ dup size + rot + dup + temp share > iff + [ cr drop dup size ] + else sp 1+ swap echo$ ] + drop temp release ] is wrap$ ( [ n --> ) + +[ names reverse 70 wrap$ cr + builders reverse + 70 wrap$ cr ] is words ( --> ) + +[ dup name? iff drop + else + [ dup sharefile not if + [ $ |$ 'file not found: "| + swap join + $ |"' echo$| join ] + nip quackery ] ] is loadfile ( $ --> ) + +[ dup sharefile iff + [ swap releasefile ] + else [ drop false ] ] is takefile ( $ --> $ b ) + +[ dup releasefile iff + putfile + else [ 2drop false ] ] is replacefile ( $ $ --> b ) + +[ nested ' [ ' ] + swap join + decimal unbuild + base release ] is quackify ( x --> $ ) + +$ "quackify replacefile takefile loadfile words empty wrap$ leave + shell echostack echoreturn return$ echo unbuild nesting quackery + build releasewords restorewords backupwords unresolved b.to-do + b.nesting message jobtable jobs builder? builders buildernest + reflect named? actiontable actions name? names namenest nest$ oats + bailed bail backup history shuffle random randomise initrandom + prng prng.d prng.c prng.b prng.a rot64 64bits 64bitmask $->n + char->n sort$ $> $< qacsfot sort sortwith sort.test not-do do-now + now-do add-to new-do to-do unpack pack reverse nextword trim + printable found findwith matchitem mi.result mi.tidyup echo$ + witheach makewith with.hold number$ decimal base digit lower upper + sp space cr carriage findseq behead stuff pluck of table temp step + refresh conclude i^ i times times.action times.count times.start + abs decurse depth 2over 2swap dip dip.hold protect protected + nested move tally replace release share stack while until recurse + do this ' copy clamp max min else iff if done again 2drop 2dup + within unrot tuck bit mod nip / - < xor != or and not true false + sharefile releasefile putfile filepath input ding emit quid + operator? number? nest? size poke peek find join split [] take + immovable put ]bailby[ ]do[ ]this[ ]'[ ]else[ ]iff[ ]if[ ]again[ + ]done[ over rot swap drop dup return nestdepth stacksize time ~ ^ + | & >> << ** /mod * negate + 1+ > = nand fail python" + +nest$ namenest put + +[ table + quackify replacefile takefile loadfile words empty wrap$ leave + shell echostack echoreturn return$ echo unbuild nesting quackery + build releasewords restorewords backupwords unresolved b.to-do + b.nesting message jobtable jobs builder? builders buildernest + reflect named? actiontable actions name? names namenest nest$ oats + bailed bail backup history shuffle random randomise initrandom + prng prng.d prng.c prng.b prng.a rot64 64bits 64bitmask $->n + char->n sort$ $> $< qacsfot sort sortwith sort.test not-do do-now + now-do add-to new-do to-do unpack pack reverse nextword trim + printable found findwith matchitem mi.result mi.tidyup echo$ + witheach makewith with.hold number$ decimal base digit lower upper + sp space cr carriage findseq behead stuff pluck of table temp step + refresh conclude i^ i times times.action times.count times.start + abs decurse depth 2over 2swap dip dip.hold protect protected + nested move tally replace release share stack while until recurse + do this ' copy clamp max min else iff if done again 2drop 2dup + within unrot tuck bit mod nip / - < xor != or and not true false + sharefile releasefile putfile filepath input ding emit quid + operator? number? nest? size poke peek find join split [] take + immovable put ]bailby[ ]do[ ]this[ ]'[ ]else[ ]iff[ ]if[ ]again[ + ]done[ over rot swap drop dup return nestdepth stacksize time ~ ^ + | & >> << ** /mod * negate + 1+ > = nand fail python ] + + resolves actions ( n --> x ) + +$ "constant now! hex say $ char resolves forward ) ( builds is ] [" +nest$ buildernest put + +[ table + b.constant b.now! b.hex b.say b.$ b.char b.resolves + b.forward b.) b.( b.builds b.is b.] b.[ ] + + resolves jobs ( n --> x ) """ From 77f64f79a19b0d57b4309a74c81c1906080a7fd7 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Fri, 9 Dec 2022 07:53:22 -0500 Subject: [PATCH 117/121] Delete old file --- quackery_OOP_ASYNC.py | 1674 ----------------------------------------- 1 file changed, 1674 deletions(-) delete mode 100644 quackery_OOP_ASYNC.py diff --git a/quackery_OOP_ASYNC.py b/quackery_OOP_ASYNC.py deleted file mode 100644 index 3fd53f9..0000000 --- a/quackery_OOP_ASYNC.py +++ /dev/null @@ -1,1674 +0,0 @@ -# [ quackery ] - -import time -import sys -import os -import types -import js -from asyncio import Future - -async def ainput(prompt): - term = js.term - term.resume() - print('\u200c', end='') # ‌ - result = await term.read(prompt) - term.pause() - return result - -def isNest(item): - return isinstance(item, list) - -def isNumber(item): - return isinstance(item, int) - -def isOperator(item): - return isinstance(item, types.FunctionType) - -def isinteger(numstr): - if len(numstr) > 0 and numstr[0] == '-': - numstr = numstr[1:] - return numstr.isdigit() - -def ishex(hexstr): - if len(hexstr) > 1 and hexstr[0] == '-': - hexstr = hexstr[1:] - return all(char.lower() in '0123456789abcdef' for char in hexstr) - -class QuackeryError(Exception): - pass - -class QuackeryContext: - def __init__(self, qstack = None, operators = None, builders = None): - self.qstack = [] if qstack is None else qstack - self.rstack = [] - self.operators = predefined_operators.copy() if operators is None else operators - self.builders = predefined_builders.copy() if builders is None else builders - self.program_counter = 0 - self.current_nest = [] - self.source = '' - self.current_build = [] - - def copy(self): - new = QuackeryContext(self.qstack.copy(), self.operators.copy(), self.builders.copy()) - new.rstack = self.rstack.copy() - new.program_counter = self.program_counter - new.current_nest = self.current_nest.copy() - new.source = self.source - new.current_build = self.current_build.copy() - return new - - async def expect_something(self): - if self.qstack == []: - await self.failed('Stack unexpectedly empty.') - - def top_of_stack(self): - return self.qstack[-1] - - async def expect_nest(self): - await self.expect_something() - if not isNest(self.top_of_stack()): - await self.failed('Expected nest on stack.') - - async def expect_number(self): - await self.expect_something() - if not isNumber(self.top_of_stack()): - await self.failed('Expected number on stack.') - - def to_stack(self, item): - self.qstack.append(item) - - async def from_stack(self): - await self.expect_something() - return self.qstack.pop() - - async def string_from_stack(self): - await self.expect_nest() - result = '' - for ch in await self.from_stack(): - if ch == 13: # \r - result += '\n' - elif 31 < ch < 127: - result += chr(ch) - else: - result += '?' # XXX @dragoncoder047 maybe use \uFFFD on platforms that support unicode? - return result - - def string_to_stack(self, string): - result = [] - for ch in string: - if ch == '\n': - result.append(13) - elif 31 < ord(ch) < 127: - result.append(ord(ch)) - else: - result.append(ord('?')) # XXX @dragoncoder047 maybe \0 or NULL to signify bad char? - self.to_stack(result) - - def bool_to_stack(self, qbool): - self.to_stack(true if qbool else false) - - def to_return(self, item): - self.rstack.append(item) - - async def from_return(self): - if len(self.rstack) == 0: - await self.failed('Return stack unexpectedly empty.') - return self.rstack.pop() - - async def failed(self, message): - await self.traverse(await self.build("stacksize pack decimal unbuild return$ nestdepth ]bailby[")) - returnstack = await self.string_from_stack() - thestack = await self.string_from_stack() - raise QuackeryError('\n Problem: ' + message + - '\nQuackery Stack: ' + str(thestack)[2:-2] + - '\n Return stack: ' + str(returnstack)) - - async def tick(self): - if self.program_counter >= len(self.current_nest): - self.program_counter = await self.from_return() - self.current_nest = await self.from_return() - self.program_counter += 1 - return - current_item = self.current_nest[self.program_counter] - if isNest(current_item): - self.to_return(self.current_nest) - self.to_return(self.program_counter) - self.current_nest = current_item - self.program_counter = 0 - elif isOperator(current_item): - try: - await current_item(self) - except TypeError: - pass - self.program_counter += 1 - elif isNumber(current_item): - self.to_stack(current_item) - self.program_counter += 1 - else: - await self.failed('Quackery was worried by a python.') - - - async def traverse(self, the_nest): - orig_depth = len(self.rstack) - self.to_return(self.current_nest) - self.to_return(self.program_counter) - self.current_nest = the_nest - self.program_counter = 0 - while len(self.rstack) > orig_depth: - await self.tick() - - def next_char(self): - if len(self.source) > 0: - char = self.source[0] - self.source = self.source[1:] - return char - else: - return '' - - def next_word(self): - result = '' - while True: - char = self.next_char() - if char == '': - return result - if ord(char) < 33: - if result == '': - continue - return result - result += char - - def one_char(self): - while True: - char = self.next_char() - if char == '': - return char - if ord(char) < 33: - continue - return char - - def get_name(self): - name = self.next_word() - if name == '': - raise EOFError('Unexpected end of program text.') - return name - - def check_build(self): - if len(self.current_build) == 0: - raise IndexError('Nothing to name.') - - async def build(self, source_string): - self.source = source_string - nesting = 0 - - async def sub_build(): - nonlocal nesting - the_nest = [] - while True: - self.current_build = the_nest - word = self.next_word() - if word == '': - return the_nest - elif word == '[': - nesting += 1 - the_nest.append(await sub_build()) - elif word == ']': - nesting -= 1 - if nesting < 0: - raise SyntaxError('Unexpected end of nest.') - return the_nest - elif word in self.builders: - try: - await (self.builders[word](self)) - except TypeError: - pass - elif word in self.operators: - the_nest.append(self.operators[word]) - elif isinteger(word): - the_nest.append(int(word, 10)) - else: - raise NameError('Unrecognised word: ' + word) - - the_nest = await sub_build() - if nesting > 0: - raise SyntaxError('Unfinished nest.') - return the_nest - - async def run(self, source_string): - await self.traverse(await self.build(source_string)) - - - -async def python(ctx): - """For backwards compatibility only""" - scope = { - "to_stack": ctx.to_stack, - "from_stack": ctx.from_stack, - "string_to_stack": ctx.string_to_stack, - "string_from_stack": ctx.string_from_stack, - "ctx": ctx, - } - try: - exec(await ctx.string_from_stack(), scope, globals()) - except QuackeryError: - raise - except Exception as diagnostics: - await ctx.failed('Python reported: "' + str(diagnostics) + '"') - -async def qfail(ctx): - await ctx.failed(await ctx.string_from_stack()) - -async def stack_size(ctx): - ctx.to_stack(len(ctx.qstack)) - -async def qreturn(ctx): - ctx.to_stack(ctx.rstack) - -async def dup(ctx): - a = await ctx.from_stack() - ctx.to_stack(a) - ctx.to_stack(a) - -async def drop(ctx): - await ctx.from_stack() - -async def swap(ctx): - a = await ctx.from_stack() - b = await ctx.from_stack() - ctx.to_stack(a) - ctx.to_stack(b) - -async def rot(ctx): - a = await ctx.from_stack() - await swap(ctx) - ctx.to_stack(a) - await swap(ctx) - -async def over(ctx): - a = await ctx.from_stack() - await dup(ctx) - ctx.to_stack(a) - await swap(ctx) - -async def nest_depth(ctx): - ctx.to_stack(len(ctx.rstack) // 2) - -true = 1 - -false = 0 - -async def nand(ctx): - await ctx.expect_number() - a = await ctx.from_stack() - await ctx.expect_number() - ctx.bool_to_stack(await ctx.from_stack() == false or a == false) - -async def equal(ctx): - a = await ctx.from_stack() - ctx.bool_to_stack(a == await ctx.from_stack()) - -async def greater(ctx): - await ctx.expect_number() - a = await ctx.from_stack() - await ctx.expect_number() - ctx.bool_to_stack(await ctx.from_stack() > a) - -async def inc(ctx): - await ctx.expect_number() - ctx.to_stack(1 + await ctx.from_stack()) - -async def plus(ctx): - await ctx.expect_number() - a = await ctx.from_stack() - await ctx.expect_number() - ctx.to_stack(a + await ctx.from_stack()) - -async def negate(ctx): - await ctx.expect_number() - ctx.to_stack(-await ctx.from_stack()) - -async def multiply(ctx): - await ctx.expect_number() - a = await ctx.from_stack() - await ctx.expect_number() - ctx.to_stack(a * await ctx.from_stack()) - -async def qdivmod(ctx): - await ctx.expect_number() - a = await ctx.from_stack() - if a == 0: - await ctx.failed('Division by zero.') - await ctx.expect_number() - results = divmod(await ctx.from_stack(), a) - ctx.to_stack(results[0]) - ctx.to_stack(results[1]) - -async def exponentiate(ctx): - await ctx.expect_number() - a = await ctx.from_stack() - if a < 0: - await ctx.failed('Tried to raise to a negative power: ' + str(a)) - await ctx.expect_number() - ctx.to_stack(await ctx.from_stack() ** a) - -async def shift_left(ctx): - await ctx.expect_number() - a = await ctx.from_stack() - if a < 0: - await ctx.failed('Cannot << by a negative amount: ' + str(a)) - await ctx.expect_number() - ctx.to_stack(await ctx.from_stack() << a) - -async def shift_right(ctx): - await ctx.expect_number() - a = await ctx.from_stack() - if a < 0: - await ctx.failed('Cannot >> by a negative amount: ' + str(a)) - await ctx.expect_number() - await ctx.to_stack(await ctx.from_stack() >> a) - -async def bitwise_and(ctx): - await ctx.expect_number() - a = await ctx.from_stack() - await ctx.expect_number() - ctx.to_stack(a & await ctx.from_stack()) - -async def bitwise_or(ctx): - await ctx.expect_number() - a = await ctx.from_stack() - await ctx.expect_number() - ctx.to_stack(a | await ctx.from_stack()) - -async def bitwise_xor(ctx): - await ctx.expect_number() - a = await ctx.from_stack() - await ctx.expect_number() - ctx.to_stack(a ^ await ctx.from_stack()) - -async def bitwise_not(ctx): - await ctx.expect_number() - ctx.to_stack(~await ctx.from_stack()) - -async def qtime(ctx): - ctx.to_stack(int(time.time()*1000000)) - -async def meta_done(ctx): - await ctx.from_return() - await ctx.from_return() - -async def meta_again(ctx): - await ctx.from_return() - ctx.to_return(-1) - -async def meta_if(ctx): - await ctx.expect_number() - if await ctx.from_stack() == 0: - ctx.to_return(await ctx.from_return() + 1) - -async def meta_iff(ctx): - await ctx.expect_number() - if await ctx.from_stack() == 0: - ctx.to_return(await ctx.from_return() + 2) - -async def meta_else(ctx): - ctx.to_return(await ctx.from_return() + 1) - -async def meta_literal(ctx): - pc = await ctx.from_return() + 1 - return_nest = await ctx.from_return() - if len(return_nest) == pc: - await ctx.failed('Found a "\'" at the end of a nest.') - ctx.to_stack(return_nest[pc]) - ctx.to_return(return_nest) - ctx.to_return(pc) - -async def meta_this(ctx): - pc = await ctx.from_return() - return_nest = await ctx.from_return() - ctx.to_stack(return_nest) - ctx.to_return(return_nest) - ctx.to_return(pc) - -async def meta_do(ctx): - await ctx.expect_something() - the_thing = await ctx.from_stack() - if not isNest(the_thing): - the_thing = [the_thing] - ctx.to_return(the_thing) - ctx.to_return(-1) - -async def meta_bail_by(ctx): - await ctx.expect_number() - a = 2 * await ctx.from_stack() - if a <= len(ctx.rstack): - for _ in range(a): - await ctx.from_return() - else: - await ctx.failed('Bailed out of Quackery.') - -async def qput(ctx): - await ctx.expect_nest() - a = await ctx.from_stack() - await ctx.expect_something() - b = await ctx.from_stack() - a.append(b) - -async def immovable(ctx): - pass - -async def take(ctx): - await ctx.expect_nest() - a = await ctx.from_stack() - if len(a) == 0: - await ctx.failed('Unexpectedly empty nest.') - if len(a) == 1: - if isNest(a[0]) and len(a[0]) > 0 and a[0][0] == immovable: - await ctx.failed('Cannot remove an immovable item.') - await ctx.to_stack(a.pop()) - -async def create_nest(ctx): - ctx.to_stack([]) - -async def qsplit(ctx): - await ctx.expect_number() - a = await ctx.from_stack() - await ctx.expect_nest() - b = await ctx.from_stack() - await ctx.to_stack(b[:a]) - await ctx.to_stack(b[a:]) - -async def join(ctx): - await ctx.expect_something() - b = await ctx.from_stack() - if not isNest(b): - b = [b] - await ctx.expect_something() - a = await ctx.from_stack() - if not isNest(a): - a = [a] - await ctx.to_stack(a + b) - -async def qsize(ctx): - await ctx.expect_nest() - ctx.to_stack(len(await ctx.from_stack())) - -async def qfind(ctx): - await ctx.expect_nest() - nest = await ctx.from_stack() - await ctx.expect_something() - a = await ctx.from_stack() - if a in nest: - ctx.to_stack(nest.index(a)) - else: - ctx.to_stack(len(nest)) - -async def peek(ctx): - await ctx.expect_number() - index = await ctx.from_stack() - await ctx.expect_nest() - nest = await ctx.from_stack() - if index >= len(nest) or ( - index < 0 and len(nest) < abs(index)): - await ctx.failed('Cannot peek an item outside a nest.') - else: - ctx.to_stack(nest[index]) - -async def poke(ctx): - await ctx.expect_number() - index = await ctx.from_stack() - await ctx.expect_nest() - nest = await ctx.from_stack().copy() - value = await ctx.from_stack() - if index >= len(nest) or ( - index < 0 and len(nest) < abs(index)): - await ctx.failed('Cannot poke an item outside a nest.') - else: - nest[index] = value - ctx.to_stack(nest) - -async def qnest(ctx): - ctx.bool_to_stack(isNest(await ctx.from_stack())) - -async def qnumber(ctx): - ctx.bool_to_stack(isNumber(await ctx.from_stack())) - -async def qoperator(ctx): - ctx.bool_to_stack(isOperator(await ctx.from_stack())) - -async def quid(ctx): - ctx.to_stack(id(await ctx.from_stack())) - -async def qemit(ctx): - await ctx.expect_number() - char = await ctx.from_stack() - if char == 13: - sys.stdout.write('\r\n') - elif 31 < char < 127: - sys.stdout.write(chr(char)) - else: - sys.stdout.write('\uFFFD') - -async def ding(ctx): - sys.stdout.write('\a') - -async def qinput(ctx): - prompt = await ctx.string_from_stack() - ctx.string_to_stack(await ainput(prompt)) - -filepath = [] - -async def putfile(ctx): - filename = await ctx.string_from_stack() - if len(filepath) > 1: - ctx.to_stack(filepath[-1]) - filename = await ctx.string_from_stack() + filename - filetext = await ctx.string_from_stack() - try: - with open(filename, 'x'): pass - except FileExistsError: - ctx.to_stack(false) - except: - raise - else: - try: - with open(filename, 'w') as f: f.write(filetext) - except: - raise - else: - ctx.to_stack(true) - -async def releasefile(ctx): - filename = await ctx.string_from_stack() - if len(filepath) > 1: - ctx.to_stack(filepath[-1]) - filename = await ctx.string_from_stack() + filename - try: - os.remove(filename) - except FileNotFoundError: - ctx.to_stack(false) - except: - raise - else: - ctx.to_stack(true) - -async def sharefile(ctx): - await dup(ctx) - filename = await ctx.string_from_stack() - if len(filepath) > 1: - ctx.to_stack(filepath[-1]) - filename = await ctx.string_from_stack() + filename - try: - with open(filename) as f: filetext = f.read() - except FileNotFoundError: - ctx.to_stack(false) - except: - raise - else: - await drop(ctx) - ctx.string_to_stack(filetext) - ctx.to_stack(true) - -predefined_operators = { - 'python': python, # ( $ --> ) - 'fail': qfail, # ( $ --> ) - 'nand': nand, # ( b b --> b ) - '=': equal, # ( x x --> b ) - '>': greater, # ( n n --> b ) - '1+': inc, # ( n --> n ) - '+': plus, # ( n n --> n ) - 'negate': negate, # ( n --> n ) - '*': multiply, # ( n n --> n ) - '/mod': qdivmod, # ( n n --> n n ) - '**': exponentiate, # ( n n --> n ) - '<<': shift_left, # ( f n --> f ) - '>>': shift_right, # ( f n --> f ) - '&': bitwise_and, # ( f f --> f ) - '|': bitwise_or, # ( f f --> f ) - '^': bitwise_xor, # ( f f --> f ) - '~': bitwise_not, # ( f --> f ) - 'time': qtime, # ( --> n ) - 'stacksize': stack_size, # ( --> n ) - 'nestdepth': nest_depth, # ( --> n ) - 'return': qreturn, # ( --> [ ) - 'dup': dup, # ( x --> x x ) - 'drop': drop, # ( x --> ) - 'swap': swap, # ( x x --> x x ) - 'rot': rot, # ( x x x --> x x x ) - 'over': over, # ( x x --> x x x ) - ']done[': meta_done, # ( --> ) - ']again[': meta_again, # ( --> ) - ']if[': meta_if, # ( b --> ) - ']iff[': meta_iff, # ( b --> ) - ']else[': meta_else, # ( --> ) - "]'[": meta_literal, # ( --> x ) - ']this[': meta_this, # ( --> [ ) - ']do[': meta_do, # ( x --> ) - ']bailby[': meta_bail_by, # ( n --> ) - 'put': qput, # ( x [ --> ) - 'immovable': immovable, # ( --> ) - 'take': take, # ( [ --> x ) - '[]': create_nest, # ( --> n ) - 'split': qsplit, # ( [ n --> [ [ ) - 'join': join, # ( x x --> [ ) - 'find': qfind, # ( x --> b ) - 'peek': peek, # ( [ n --> x ) - 'poke': poke, # ( x [ n --> ) - 'size': qsize, # ( [ --> n ) - 'nest?': qnest, # ( x --> b ) - 'number?': qnumber, # ( x --> b ) - 'operator?': qoperator, # ( x --> b ) - 'quid': quid, # ( x --> n ) - 'emit': qemit, # ( c --> ) - 'ding': ding, # ( --> ) - 'input': qinput, # ( $ --> $ ) - 'filepath': filepath, # ( --> s ) - 'putfile': putfile, # ( $ --> b ) - 'releasefile': releasefile, # ( $ --> b ) - 'sharefile': sharefile # ( $ --> $ b ) -} - -async def qis(ctx): - await ctx.check_build() - name = ctx.get_name() - ctx.operators[name] = ctx.current_build.pop() - -async def qcomment(ctx): - word = '' - while word != ')': - word = ctx.next_word() - if word == '': - raise EOFError('Unclosed comment.') - -async def endcomment(ctx): - raise SyntaxError('Too many end of comments.') - -async def unresolved(ctx): - raise TypeError('Unresolved forward reference.') - -async def forward(ctx): - ctx.current_build.append([unresolved]) - -async def resolves(ctx): - name = ctx.get_name() - if name in ctx.operators: - if ctx.operators[name][0] != unresolved: - raise TypeError(name + ' is not a forward reference.') - await ctx.check_build() - ctx.operators[name][0] = ctx.current_build.pop() - else: - raise NameError('Unrecognised word: ' + name) - -async def char_literal(ctx): - char = ctx.one_char() - if char == '': - raise SyntaxError('No character found.') - ctx.current_build.append(ord(char)) - -async def string_literal(ctx): - delimiter = '' - result = [] - while delimiter == '': - char = ctx.next_char() - if char == '': - raise EOFError('No string found.') - if ord(char) > 32: - delimiter = char - char = '' - while char != delimiter: - char = ctx.next_char() - if char == '': - raise EOFError('Endless string discovered.') - if char != delimiter: - result.append(ord(char)) - ctx.current_build.append([[meta_literal], result]) - -async def hexnum(ctx): - word = ctx.get_name() - if not ishex(word): - raise SyntaxError(word + " is not hexadecimal.") - ctx.current_build.append(int(word, 16)) - -predefined_builders = { - 'is': qis, - '(': qcomment, - ')': endcomment, - 'forward': forward, - 'resolves': resolves, - 'char': char_literal, - '$': string_literal, - 'hex': hexnum -} - - -predefined_qky = r""" - [ 0 ] is false ( --> b ) - - [ 1 ] is true ( --> b ) - - [ dup nand ] is not ( b --> b ) - - [ nand not ] is and ( b b --> b ) - - [ not swap not nand ] is or ( b b --> b ) - - [ = not ] is != ( x x --> b ) - - [ not swap not != ] is xor ( b b --> b ) - - [ swap > ] is < ( n n --> b ) - - [ negate + ] is - ( n --> n ) - - [ /mod drop ] is / ( n n --> n ) - - [ swap drop ] is nip ( x x --> x ) - - [ /mod nip ] is mod ( n n --> n ) - - [ 1 swap << ] is bit ( n --> n ) - - [ swap over ] is tuck ( x x --> x x x ) - - [ rot rot ] is unrot ( x x x --> x x x ) - - [ rot tuck > - unrot > not and ] is within ( n n n --> b ) - - [ over over ] is 2dup ( x x --> x x x x ) - - [ drop drop ] is 2drop ( x x --> ) - - [ ]again[ ] is again ( --> ) - - [ ]done[ ] is done ( --> ) - - [ ]if[ ] is if ( b --> ) - - [ ]iff[ ] is iff ( b --> ) - - [ ]else[ ] is else ( --> ) - - [ 2dup > if swap drop ] is min ( n n n --> n ) - - [ 2dup < if swap drop ] is max ( n n n --> n ) - - [ rot min max ] is clamp ( n n n --> n ) - - [ dup nest? iff [] join ] is copy ( [ --> [ ) - - [ ]'[ ] is ' ( --> x ) - - [ ]this[ ] is this ( --> [ ) - - [ ]do[ ] is do ( x --> ) - - [ ]this[ do ] is recurse ( --> ) - - [ not if ]again[ ] is until ( b --> ) - - [ not if ]done[ ] is while ( b --> ) - - [ immovable ]this[ ]done[ ] is stack ( --> s ) - - [ dup take dup rot put ] is share ( s --> x ) - - [ take drop ] is release ( s --> ) - - [ dup release put ] is replace ( x s --> ) - - [ dup take rot + swap put ] is tally ( n s --> ) - - [ swap take swap put ] is move ( s s --> ) - - [ [] tuck put ] is nested ( x --> [ ) - - [ stack [ ] ] is protected ( --> s ) - - [ protected take - ]'[ nested join - protected put ] is protect ( --> ) - - ' stack ' filepath put - protect filepath - - [ stack ] is dip.hold ( --> s ) - protect dip.hold - - [ dip.hold put - ]'[ do dip.hold take ] is dip ( x --> x ) - - [ rot dip rot ] is 2swap ( x x x x --> x x x x ) - - [ dip [ dip 2dup ] 2swap ] is 2over ( x x x x --> x x x x x x ) - - [ stack ] is depth ( --> s ) - protect depth - - [ depth share - 0 != while - -1 depth tally - ]this[ do - 1 depth tally ] is decurse ( --> ) - - [ dup 0 < if negate ] is abs ( n --> n ) - - [ stack ] is times.start ( --> s ) - protect times.start - - [ stack ] is times.count ( --> s ) - protect times.count - - [ stack ] is times.action ( --> s ) - protect times.action - - [ ]'[ times.action put - dup times.start put - [ 1 - dup -1 > while - times.count put - times.action share do - times.count take again ] - drop - times.action release - times.start release ] is times ( n --> ) - - [ times.count share ] is i ( --> n ) - - [ times.start share i 1+ - ] is i^ ( --> n ) - - [ 0 times.count replace ] is conclude ( --> ) - - [ times.start share - times.count replace ] is refresh ( --> ) - - [ times.count take 1+ - swap - times.count put ] is step ( --> s ) - - [ stack ] is temp ( --> s ) - protect temp - - [ immovable - dup -1 > + - ]this[ swap peek - ]done[ ] is table ( n --> x ) - - [ [] unrot - dup 1 < iff 2drop done - [ 2 /mod over while - if [ dip [ tuck join swap ] ] - dip [ dup join ] - again ] 2drop join ] is of ( x n --> [ ) - - [ split 1 split - swap dip join - 0 peek ] is pluck ( [ n --> [ x ) - - [ split - rot nested - swap join join ] is stuff ( x [ n --> [ ) - - [ 0 pluck ] is behead ( [ --> [ x ) - - [ over size over size - dup temp put - swap - 1+ times - [ 2dup over size split - drop = if - [ i^ temp replace - conclude ] - behead drop ] - 2drop temp take ] is findseq ( [ [ --> n ) - - [ 13 ] is carriage ( --> c ) - - [ carriage emit ] is cr ( --> ) - - [ 32 ] is space ( --> c ) - - [ space emit ] is sp ( --> ) - - [ dup char a char { within - if [ 32 - ] ] is upper ( c --> c ) - - [ dup char A char [ within - if [ 32 + ] ] is lower ( c --> c ) - - [ dup 10 < - iff 48 else 55 + ] is digit ( n --> c ) - - [ stack 10 ] is base ( --> s ) - protect base - - [ 10 base put ] is decimal ( --> ) - - [ $ '' over abs - [ base share /mod digit - rot join swap - dup 0 = until ] - drop - swap 0 < if - [ $ '-' swap join ] ] is number$ ( n --> $ ) - - [ stack ] is with.hold ( --> s ) - protect with.hold - - [ nested - ' [ dup with.hold put - size times ] - ' [ with.hold share - i ~ peek ] - rot join - nested join - ' [ with.hold release ] - join ] is makewith ( x --> [ ) - - [ ]'[ makewith do ] is witheach ( [ --> ) - - [ witheach emit ] is echo$ ( $ --> ) - - [ stack ] is mi.tidyup ( --> s ) - protect mi.tidyup - - [ stack ] is mi.result ( --> s ) - protect mi.result - - [ mi.tidyup put - over size mi.result put - nested - ' [ if - [ i^ mi.result replace - conclude ] ] - join makewith do - mi.tidyup take do - mi.result take ] is matchitem ( [ x x --> n ) - - [ ]'[ ]'[ matchitem ] is findwith ( [ --> n ) - - [ size < ] is found ( n [ --> b ) - - [ space > ] is printable ( c --> b ) - - [ dup findwith - printable [ ] - split nip ] is trim ( $ --> $ ) - - [ dup findwith - [ printable not ] [ ] - split swap ] is nextword ( $ --> $ $ ) - - [ dup nest? if - [ dup size 2 < if done - dup size 2 / split - recurse swap - recurse join ] ] is reverse ( x --> x ) - - [ [] swap times - [ swap nested join ] - reverse ] is pack ( * n --> [ ) - - [ witheach [ ] ] is unpack ( [ --> * ) - - [ stack ] is to-do ( --> s ) - protect to-do - - [ ' done swap put ] is new-do ( s --> ) - - [ dip [ 1+ pack ] put ] is add-to ( * x n s --> ) - - [ [ dup take - unpack do again ] drop ] is now-do ( s --> ) - - [ 1 split reverse join - now-do ] is do-now ( s --> ) - - [ [ dup take ' done = until ] - drop ] is not-do ( s --> ) - - [ stack ] is sort.test ( --> s ) - protect sort.test - - [ ]'[ sort.test put - [] swap witheach - [ swap 2dup findwith - [ over sort.test share - do ] [ ] - nip stuff ] - sort.test release ] is sortwith ( [ --> [ ) - - [ sortwith > ] is sort ( [ --> [ ) - - [ 32 127 clamp 32 - - [ table - 0 86 88 93 94 90 92 87 63 64 75 73 82 74 81 76 - 1 2 3 4 5 6 7 8 9 10 83 84 69 72 70 85 - 91 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 - 41 43 45 47 49 51 53 55 57 59 61 65 78 66 77 80 - 89 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 - 42 44 46 48 50 52 54 56 58 60 62 67 79 68 71 0 ] - ] is qacsfot ( c --> n ) - - [ [ dup $ '' = iff false done - over $ '' = iff true done - behead rot behead rot - 2dup = iff [ 2drop swap ] again - qacsfot swap qacsfot > ] - unrot 2drop ] is $< ( $ $ --> b ) - - [ swap $< ] is $> ( $ $ --> b ) - - [ sortwith $> ] is sort$ ( [ --> [ ) - - [ upper 47 - 0 44 clamp - [ table - -1 0 1 2 3 4 5 6 7 8 9 -1 -1 -1 -1 - -1 -1 -1 10 11 12 13 14 15 16 17 18 19 20 21 - 22 23 24 25 26 27 28 29 30 31 32 33 34 35 -1 ] - dup 0 base share - within not if [ drop -1 ] ] is char->n ( c --> n ) - - [ dup $ '' = iff [ drop 0 false ] done - dup 0 peek char - = - tuck if [ behead drop ] - dup $ '' = iff [ 2drop 0 false ] done - true 0 rot witheach - [ char->n - dup 0 < iff [ drop nip false swap ] - else [ swap base share * + ] ] - rot if negate - swap ] is $->n ( $ --> n b ) - - ( adapted from 'A small noncryptographic PRNG' by Bob Jenkins ) - ( https://burtleburtle.net/bob/rand/smallprng.html ) - - [ hex FFFFFFFFFFFFFFFF ] is 64bitmask ( --> f ) - - [ 64bitmask & ] is 64bits ( f --> f ) - - [ dip 64bits 2dup << 64bits - unrot 64 swap - >> | ] is rot64 ( f n --> f ) - - [ stack 0 ] is prng.a ( --> s ) - [ stack 0 ] is prng.b ( --> s ) - [ stack 0 ] is prng.c ( --> s ) - [ stack 0 ] is prng.d ( --> s ) - - [ prng.a share - prng.b share tuck - 7 rot64 - 64bits swap - prng.c share tuck - 13 rot64 ^ prng.a replace - prng.d share tuck - 37 rot64 + 64bits prng.b replace - over + 64bits prng.c replace - prng.a share + 64bits - dup prng.d replace ] is prng ( --> n ) - - [ hex F1EA5EAD prng.a replace - dup prng.b replace - dup prng.c replace - prng.d replace - 20 times [ prng drop ] ] is initrandom ( n --> ) - - hex DEFACEABADFACADE initrandom - - [ time initrandom ] is randomise ( --> ) - - [ 64bitmask 1+ - over / over * - [ prng 2dup > not while - drop again ] - nip swap mod ] is random ( n --> n ) - - [ [] swap dup size times - [ dup size random pluck - nested rot join swap ] - drop ] is shuffle ( [ --> [ ) - - [ stack ] is history ( --> s ) - - [ protected share history put - protected share 0 - [ over size over - > while - 2dup peek - size unrot - 1+ again ] - 2drop - protected share size pack - history put - pack dup history put unpack - stacksize history put - nestdepth history put - false history put ] is backup ( n --> ) - - [ history release - nestdepth - history take - - ]bailby[ - true history put ] is bail ( --> ) - - [ history take iff - [ stacksize - history take - history share - size - - times drop - history take unpack - history take unpack - history share size - [ dup 0 > while - 1 - - history share - over peek - rot over size - swap - - [ dup 0 > while - over release - 1 - again ] - 2drop again ] - drop - history take - protected replace - true ] - else - [ 5 times - [ history release ] - false ] ] is bailed ( --> b ) - - [ quid swap quid = ] is oats ( x x --> b ) - - [ [] swap - [ trim - dup size while - nextword nested - swap dip join again ] - drop ] is nest$ ( $ --> [ ) - - [ stack ] is namenest ( --> s ) - - [ namenest share ] is names ( --> [ ) - - [ names find names found ] is name? ( $ --> b ) - - forward is actions ( n --> x ) - - [ ' actions ] is actiontable ( --> x ) - - [ actiontable share tuck - findwith [ over oats ] drop - swap found ] is named? ( x --> b ) - - forward is reflect ( x --> x ) - [ dup nest? if - [ dup [] = if done - dup size 1 = iff - [ 0 peek - dup named? iff - nested done - reflect nested ] - done - dup size 2 / split - recurse swap - recurse join ] ] resolves reflect ( x --> x ) - - [ stack ] is buildernest ( --> s ) - - [ buildernest share ] is builders ( --> s ) - - [ builders find - builders found ] is builder? ( $ --> b ) - - forward is jobs ( n --> x ) - - [ ' jobs ] is jobtable ( --> [ ) - - [ stack ] is message ( --> s ) - - [ stack ] is b.nesting ( --> s ) - protect b.nesting - - [ stack ] is b.to-do ( --> s ) - - [ $ '[' b.nesting put - [] swap ] is b.[ ( [ $ --> [ [ $ ) - - [ b.nesting take dup - $ '' = if - [ $ 'Unexpected "]".' - message put - bail ] - dup $ '[' = iff drop - else - [ $ 'Nest mismatch: ' - swap join $ ' ]' join - message put - bail ] - dip [ nested join ] ] is b.] ( [ [ $ --> [ $ ) - - [ over [] = if - [ $ '"is" needs something to name before it.' - message put - bail ] - dup $ '' = if - [ $ '"is" needs a name after it.' - message put - bail ] - nextword nested - namenest take - join - namenest put - dip - [ -1 pluck - actiontable take - 1 stuff - actiontable put ] ] is b.is ( [ $ --> [ $ ) - - [ over [] = if - [ $ '"builds" needs something to name before it.' - message put - bail ] - dup $ '' = if - [ $ '"builds" needs a name after it.' - message put - bail ] - nextword nested - buildernest take - join - buildernest put - dip - [ -1 pluck - jobtable take - 1 stuff - jobtable put ] ] is b.builds ( [ $ --> [ $ ) - - [ trim nextword - dup $ '' = if - [ $ 'Unfinished comment.' - message put - bail ] - $ ')' = until ] is b.( ( [ $ --> $ [ ) - - [ $ 'Unexpected ")".' - message put - bail ] is b.) ( [ $ --> $ [ ) - - [ $ 'Unresolved reference.' - fail ] is unresolved ( --> ) - - [ dip - [ ' [ unresolved ] - copy nested join ] ] is b.forward ( [ $ --> [ $ ) - - [ over [] = if - [ $ '"resolves" needs something to resolve.' - message put - bail ] - dup $ '' = if - [ $ '"resolves" needs a name to resolve into.' - message put - bail ] - dip [ -1 split ] - nextword dup temp put - names find - dup names found not if - [ $ 'Unknown word after "resolves": ' - temp take join - message put - bail ] - actions - dup ' [ unresolved ] = not if - [ char " temp take join - $ '" is not an unresolved forward reference.' - join - message put - bail ] - rot 0 peek over - replace - ' unresolved swap - ' replace 2 b.to-do add-to - temp release ] is b.resolves ( [ $ --> [ $ ) - - [ 1 split - over $ '' = if - [ $ '"char" needs a character after it.' - message put - bail ] - dip join ] is b.char ( [ $ --> [ $ ) - - [ dup $ '' = if - [ $ '"$" needs to be followed by a string.' - message put - bail ] - behead over find - 2dup swap found not if - [ $ 'Endless string discovered.' - message put - bail ] - split behead drop - ' ' nested - rot nested join - nested swap dip join ] is b.$ ( [ $ --> [ $ ) - - [ dup $ '' = if - [ $ '"say" needs to be followed by a string.' - message put - bail ] - $ '$' builders find jobs do - dip - [ -1 pluck - ' echo$ nested join - nested join ] ] is b.say ( [ $ --> [ $ ) - - [ 16 base put - nextword dup - $ '' = if - [ $ '"hex" needs a number after it.' - message put - bail ] - dup $->n iff - [ nip swap dip join ] - else - [ drop - char " swap join - $ '" is not hexadecimal.' - join message put - bail ] - base release ] is b.hex ( [ $ --> [ $ ) - - [ dip [ -1 split ] swap do ] is b.now! ( [ $ --> [ $ ) - - [ over [] = if - [ $ '"constant" needs something before it.' - message put - bail ] - dip - [ -1 pluck do - dup number? not if - [ ' ' nested swap - nested join - nested ] - join ] ] is b.constant ( [ $ --> [ $ ) - - [ ' [ namenest actiontable - buildernest jobtable ] - witheach - [ do share copy - history put ] ] is backupwords ( --> ) - - [ ' [ jobtable buildernest - actiontable namenest ] - witheach - [ do dup release - history swap move ] ] is restorewords ( --> ) - - [ 4 times - [ history release ] ] is releasewords ( --> ) - - [ backupwords - b.to-do new-do - 1 backup - [ $ '' b.nesting put - decimal - [] swap - [ trim - dup $ '' = iff drop done - nextword - dup builders find - dup builders found iff - [ dip [ drop trim ] - jobs do ] again - drop - dup names find - dup names found iff - [ actions nested - nip swap dip join ] again - drop - dup $->n iff - [ nip swap dip join ] again - drop - $ 'Unknown word: ' - swap join message put - bail ] - base release - b.nesting take dup - $ '' = iff drop - else - [ $ 'Unfinished nest: ' - swap join message put - bail ] ] - bailed iff - [ drop b.to-do now-do - restorewords - ' ' nested - message take nested join - ' echo$ nested join ] - else - [ b.to-do not-do - releasewords ] ] is build ( $ --> [ ) - - [ build do ] is quackery ( $ --> ) - - [ stack -1 ] is nesting ( --> [ ) - - forward is unbuild ( x --> $ ) - - [ nesting share - 0 = iff [ drop $ '...' ] done - $ '' swap - dup number? iff - [ number$ join ] done - actiontable share - behead drop - [ dup [] = iff - [ drop false ] done - behead - rot tuck oats iff - [ drop size 2 + - actiontable share - size swap - - names swap peek join - true ] done - swap again ] - if done - dup nest? iff - [ $ '[ ' rot join swap - [ dup [] = iff drop done - behead - -1 nesting tally - unbuild - 1 nesting tally - space join - swap dip join again ] - $ ']' join ] - else - [ drop - $ "Quackery was worried by a python." - fail ] ] resolves unbuild ( x --> $ ) - - [ unbuild echo$ ] is echo ( x --> ) - - [ $ '' - return -2 split drop - witheach - [ dup number? iff - [ number$ join - $ '} ' join ] - else - [ $ '{' swap dip join - actiontable share - findwith - [ over oats ] drop - dup actiontable share - found iff - [ 1 - names swap - peek join - space join ] - else - [ drop $ '[...] ' - join ] ] ] - -1 split drop ] is return$ ( --> $ ) - - [ return$ echo$ ] is echoreturn ( --> ) - - [ stacksize dup 0 = iff - [ $ 'Stack empty.' echo$ drop ] - else - [ $ 'Stack: ' echo$ - pack dup - witheach [ echo sp ] - unpack ] - cr ] is echostack ( --> ) - - [ cr $ '' $ '/O> ' - [ input - dup $ '' != while - carriage join join - $ '... ' again ] - drop - quackery - 5 nesting put - cr echostack - nesting release again ] is shell ( --> ) - - [ cr randomise 12 random - [ table - $ 'Goodbye.' $ 'Adieu.' $ 'So long.' - $ 'Cheerio.' $ 'Aloha.' $ 'Ciao.' - $ 'Farewell.' $ 'Be seeing you.' - $ 'Sayonara.' $ 'Auf wiedersehen.' - $ 'Toodles.' $ 'Hasta la vista.' ] - do echo$ cr cr - 3 ]bailby[ ] is leave ( --> ) - - [ stacksize times drop ] is empty ( all --> ) - - [ tuck temp put - witheach - [ dup size - rot + dup - temp share > iff - [ cr drop dup size ] - else sp 1+ swap echo$ ] - drop temp release ] is wrap$ ( [ n --> ) - - [ names reverse 70 wrap$ cr - builders reverse - 70 wrap$ cr ] is words ( --> ) - - [ dup name? iff drop - else - [ dup sharefile not if - [ $ |$ 'file not found: "| - swap join - $ |"' echo$| join ] - nip quackery ] ] is loadfile ( $ --> ) - - [ dup sharefile iff - [ swap releasefile ] - else [ drop false ] ] is takefile ( $ --> $ b ) - - [ dup releasefile iff - putfile - else [ 2drop false ] ] is replacefile ( $ $ --> b ) - - [ nested ' [ ' ] - swap join - decimal unbuild - base release ] is quackify ( x --> $ ) - - $ "quackify replacefile takefile loadfile words empty wrap$ leave - shell echostack echoreturn return$ echo unbuild nesting quackery - build releasewords restorewords backupwords unresolved b.to-do - b.nesting message jobtable jobs builder? builders buildernest - reflect named? actiontable actions name? names namenest nest$ oats - bailed bail backup history shuffle random randomise initrandom - prng prng.d prng.c prng.b prng.a rot64 64bits 64bitmask $->n - char->n sort$ $> $< qacsfot sort sortwith sort.test not-do do-now - now-do add-to new-do to-do unpack pack reverse nextword trim - printable found findwith matchitem mi.result mi.tidyup echo$ - witheach makewith with.hold number$ decimal base digit lower upper - sp space cr carriage findseq behead stuff pluck of table temp step - refresh conclude i^ i times times.action times.count times.start - abs decurse depth 2over 2swap dip dip.hold protect protected - nested move tally replace release share stack while until recurse - do this ' copy clamp max min else iff if done again 2drop 2dup - within unrot tuck bit mod nip / - < xor != or and not true false - sharefile releasefile putfile filepath input ding emit quid - operator? number? nest? size poke peek find join split [] take - immovable put ]bailby[ ]do[ ]this[ ]'[ ]else[ ]iff[ ]if[ ]again[ - ]done[ over rot swap drop dup return nestdepth stacksize time ~ ^ - | & >> << ** /mod * negate + 1+ > = nand fail python" - - nest$ namenest put - - [ table - quackify replacefile takefile loadfile words empty wrap$ leave - shell echostack echoreturn return$ echo unbuild nesting quackery - build releasewords restorewords backupwords unresolved b.to-do - b.nesting message jobtable jobs builder? builders buildernest - reflect named? actiontable actions name? names namenest nest$ oats - bailed bail backup history shuffle random randomise initrandom - prng prng.d prng.c prng.b prng.a rot64 64bits 64bitmask $->n - char->n sort$ $> $< qacsfot sort sortwith sort.test not-do do-now - now-do add-to new-do to-do unpack pack reverse nextword trim - printable found findwith matchitem mi.result mi.tidyup echo$ - witheach makewith with.hold number$ decimal base digit lower upper - sp space cr carriage findseq behead stuff pluck of table temp step - refresh conclude i^ i times times.action times.count times.start - abs decurse depth 2over 2swap dip dip.hold protect protected - nested move tally replace release share stack while until recurse - do this ' copy clamp max min else iff if done again 2drop 2dup - within unrot tuck bit mod nip / - < xor != or and not true false - sharefile releasefile putfile filepath input ding emit quid - operator? number? nest? size poke peek find join split [] take - immovable put ]bailby[ ]do[ ]this[ ]'[ ]else[ ]iff[ ]if[ ]again[ - ]done[ over rot swap drop dup return nestdepth stacksize time ~ ^ - | & >> << ** /mod * negate + 1+ > = nand fail python ] - - resolves actions ( n --> x ) - - $ "constant now! hex say $ char resolves forward ) ( builds is ] [" - nest$ buildernest put - - [ table - b.constant b.now! b.hex b.say b.$ b.char b.resolves - b.forward b.) b.( b.builds b.is b.] b.[ ] - - resolves jobs ( n --> x )""" - -async def bootstrap(): - global predefined_operators - _qs = QuackeryContext() - await _qs.run(predefined_qky) - predefined_operators = _qs.operators - del _qs - - -async def quackery(source_string, ctx = None): - global bootstrap - if bootstrap is not None: - await bootstrap() - bootstrap = None - - if ctx is None: - ctx = QuackeryContext() - else: - for required_word in ('stacksize', 'pack', 'decimal', 'unbuild', 'quackery'): - if required_word not in ctx.operators: - raise NameError('QuackeryContext must have word %s defined.' % required_word) - - while True: - ctx.to_stack([ord(char) for char in source_string]) - try: - ctx.run('quackery') - except QuackeryError as diagnostics: - if __name__ == '__main__' and len(sys.argv) == 1: - print(diagnostics) - continue - else: - raise - except Exception as diagnostics: - print('Quackery system damage detected.') - print('Python error: ' + str(diagnostics)) - raise - else: - ctx.run('stacksize pack decimal unbuild') - the_stack = ctx.from_stack() - return ''.join(map(chr, the_stack[2:-2])) From 80dcf2b7c0301e51e88b1d9e633b015698738dad Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Wed, 14 Dec 2022 07:48:59 -0500 Subject: [PATCH 118/121] improved approx= Apply changes from GordonCharlton/Quackery@bb898425692298e282c2afdad442856640d6e597 --- bigrat.qky | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/bigrat.qky b/bigrat.qky index d5a6073..ad2c4bb 100644 --- a/bigrat.qky +++ b/bigrat.qky @@ -39,7 +39,7 @@ [ rot * dip * reduce ] is v* ( n/d n/d --> n/d ) [ over iff - [ 1/v v* ] done + [ 1/v v* ] done 2drop 2drop 1 0 ] is v/ ( n/d n/d --> n/d ) [ 1 ] is n->v ( n --> n/d ) @@ -111,8 +111,11 @@ rot do temp release ] is round ( n/d n --> n/d ) [ temp put v- + 2dup overflow iff + [ temp release + 2drop false ] done 2dup v0= iff - [ temp release + [ temp release 2drop true ] done vabs proper rot iff [ temp release @@ -176,9 +179,9 @@ rot dup 0 = dip [ unrot 2dup v0< dip - [ vabs - 2dup < dup dip - [ if [ 1 1 v+ ] + [ vabs + 2dup < dup dip + [ if [ 1 1 v+ ] rot dup dip [ base share dup dip @@ -186,9 +189,9 @@ rot * swap / ] tuck 2 / + swap / number$ ] ] - if [ char 0 rot 0 poke swap ] + if [ char 0 rot 0 poke swap ] negate split - char . swap join join ] ] + char . swap join join ] ] rot swap iff [ 1 split nip ] else [ +zero -zeroes -point ] From 4e2ee1242e0695e01e7e14f452a1c81a28edcf44 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Wed, 21 Dec 2022 07:53:27 -0500 Subject: [PATCH 119/121] Update quackery_OOP.py Sync changes from GordonCharlton@e74290660f8e7632cac754d6311dac31c6a7e4e2 --- quackery_OOP.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quackery_OOP.py b/quackery_OOP.py index 2a07551..28e31d4 100644 --- a/quackery_OOP.py +++ b/quackery_OOP.py @@ -112,7 +112,7 @@ def from_return(self): return self.rstack.pop() def failed(self, message): - self.traverse(self.build("stacksize pack decimal unbuild base release return$ nestdepth ]bailby[")) + self.traverse(self.build("stacksize pack decimal unbuild ' base size 2 > if [ base release ] return$ nestdepth ]bailby[")) returnstack = self.string_from_stack() thestack = self.string_from_stack() raise QuackeryError('\n Problem: ' + message + From 9c6217457018739c52aad639396abb71fef12008 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Wed, 21 Dec 2022 07:53:35 -0500 Subject: [PATCH 120/121] Update quackery.py Sync changes from GordonCharlton@e74290660f8e7632cac754d6311dac31c6a7e4e2 --- quackery.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/quackery.py b/quackery.py index c055117..db8b804 100644 --- a/quackery.py +++ b/quackery.py @@ -21,7 +21,8 @@ def quackery(source_string): def failed(message): traverse(build(""" stacksize pack decimal unbuild - base release + ' base size 2 > if + [ base release ] return$ nestdepth ]bailby[ """)) returnstack = string_from_stack() From ebfe2bd62dee87279b9384833084ba5936fd1185 Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Wed, 25 Jan 2023 07:58:57 -0500 Subject: [PATCH 121/121] bump versions on everything --- index.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/index.html b/index.html index 2d22682..512c8f3 100644 --- a/index.html +++ b/index.html @@ -3,11 +3,11 @@ Online Quackery Console - - - + + + - +