mirror of
https://github.com/osmarks/random-stuff
synced 2024-12-21 15:40:35 +00:00
publish things
This commit is contained in:
parent
aac9f8091d
commit
35aca96326
4
99-bottles.sql
Executable file
4
99-bottles.sql
Executable file
@ -0,0 +1,4 @@
|
||||
WITH RECURSIVE c(x)AS(VALUES(99)UNION ALL SELECT x-1 FROM c WHERE x>0),a(x, s)AS(SELECT x,SUBSTR(' bottles',1,(x<>1)+7)||' of beer'FROM c)SELECT PRINTF('%d%s on the wall
|
||||
%d%s
|
||||
Take one down, pass it around
|
||||
%d%s on the wall',a.x,a.s,a.x,a.s,a.x-1,b.s)FROM a JOIN a AS b ON b.x=a.x-1;
|
339
list-sort.py
Normal file
339
list-sort.py
Normal file
@ -0,0 +1,339 @@
|
||||
from dataclasses import dataclass, field
|
||||
import string
|
||||
import functools
|
||||
import operator
|
||||
import random
|
||||
|
||||
@dataclass
|
||||
class Symbol:
|
||||
name: str
|
||||
|
||||
@dataclass
|
||||
class Quote:
|
||||
content: any
|
||||
|
||||
# Raised in case of fatal parsing errors, i.e. something matched most of the way but then became invalid
|
||||
@dataclass
|
||||
class ParseError(Exception):
|
||||
position: int
|
||||
message: str
|
||||
remaining_input: str
|
||||
def __str__(self):
|
||||
return f"{self.message} at position {self.position}"
|
||||
|
||||
# Raised if the current parser cannot run on the current input
|
||||
class NoParse(Exception):
|
||||
pass
|
||||
|
||||
# approximate grammar
|
||||
# symbol ::= [^'0-9()" \t\n][^'()" \t\n]+
|
||||
# int ::= -? [0-9]+
|
||||
# str ::= '"' [^"]+ '"'
|
||||
# whitespace ::= [ \t\n]+
|
||||
# list ::= '(' (expr whitespace)+ ')'
|
||||
# quoted ::= ' expr
|
||||
# expr ::= symbol | int | str | list | quoted
|
||||
|
||||
# Recursive descent parser
|
||||
class Parser:
|
||||
def __init__(self, s):
|
||||
self.s = s
|
||||
self.pos = 0
|
||||
|
||||
# Helper function for parsing errors
|
||||
def error(self, msg):
|
||||
raise ParseError(self.pos, msg, self.s[self.pos:])
|
||||
|
||||
# Gets the current character being parsed
|
||||
def current(self):
|
||||
try:
|
||||
return self.s[self.pos]
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
# Advance if current character matches a condition
|
||||
def accept(self, f):
|
||||
c = self.current()
|
||||
if c:
|
||||
match = f(c) if callable(f) else c in f
|
||||
if match:
|
||||
self.pos += 1
|
||||
return c
|
||||
|
||||
# Advance if current character matches a condition, else switch to alternate parser
|
||||
def expect(self, f):
|
||||
x = self.accept(f)
|
||||
if not x: raise NoParse
|
||||
return x
|
||||
|
||||
# Advance if current character matches a condition, else raise parse error
|
||||
def expect_strong(self, f, m):
|
||||
x = self.accept(f)
|
||||
if not x: self.error(m)
|
||||
return x
|
||||
|
||||
# Try multiple parsers in sequence
|
||||
def choose(self, parsers):
|
||||
for parser in parsers:
|
||||
try:
|
||||
return parser()
|
||||
except NoParse:
|
||||
pass
|
||||
|
||||
# Parse an integer. Does not actually support negative numbers due to parsing ambiguities.
|
||||
def int(self):
|
||||
buf = self.expect("1234567890")
|
||||
while c := self.accept("1234567890"):
|
||||
buf += c
|
||||
return int(buf)
|
||||
|
||||
# Parse a string. No escape sequences are supported.
|
||||
def str(self):
|
||||
if not self.expect('"'): return
|
||||
buf = ""
|
||||
while c := self.accept(lambda x: True):
|
||||
if c == '"': return buf
|
||||
buf += c
|
||||
self.error("unterminated string")
|
||||
|
||||
# Parse a symbol.
|
||||
def symbol(self):
|
||||
buf = self.expect(lambda x: x not in "'0123456789()\"\t\n ")
|
||||
while c := self.accept(lambda x: x not in "'()\"\t\n "):
|
||||
buf += c
|
||||
return Symbol(buf)
|
||||
|
||||
# Parse a quoted expr.
|
||||
def quoted(self):
|
||||
self.expect("'")
|
||||
return Quote(self.expr())
|
||||
|
||||
# Skip whitespace
|
||||
def whitespace(self):
|
||||
while self.accept(" \t\n"): pass
|
||||
|
||||
# Parse a list of exprs
|
||||
def list(self):
|
||||
self.expect("(")
|
||||
items = []
|
||||
|
||||
def whitespace_expr():
|
||||
self.whitespace()
|
||||
r = self.expr()
|
||||
self.whitespace()
|
||||
return r
|
||||
|
||||
while (x := whitespace_expr()) != None:
|
||||
items.append(x)
|
||||
|
||||
self.expect_strong(")", "unterminated list")
|
||||
return items
|
||||
|
||||
def expr(self):
|
||||
return self.choose([self.list, self.str, self.int, self.quoted, self.symbol])
|
||||
|
||||
# Parse an entire program; error on trailing things, allow whitespace at start/end
|
||||
def parse(self):
|
||||
self.whitespace()
|
||||
expr = self.expr()
|
||||
self.whitespace()
|
||||
if self.pos != len(self.s):
|
||||
self.error(f"trailing {repr(self.s[self.pos:])}")
|
||||
return expr
|
||||
|
||||
# The environment is a stack of increasingly general scopes.
|
||||
class Environment:
|
||||
binding_stack: list[dict[str, any]]
|
||||
|
||||
def __init__(self, s):
|
||||
self.binding_stack = s
|
||||
|
||||
def __getitem__(self, key):
|
||||
for bindings in self.binding_stack:
|
||||
if bindings.get(key) != None: return bindings.get(key)
|
||||
raise KeyError(key)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self.binding_stack[0][key] = value
|
||||
|
||||
def child(self, initial_bindings=None):
|
||||
if initial_bindings == None: initial_bindings = {}
|
||||
return Environment([initial_bindings] + self.binding_stack)
|
||||
|
||||
@dataclass
|
||||
class Function:
|
||||
params: list[str]
|
||||
body: any
|
||||
environment: Environment
|
||||
name: str = "[anonymous]"
|
||||
|
||||
def __repr__(self):
|
||||
return f"<{self.name}({', '.join(self.params)})>"
|
||||
|
||||
# Evaluator with some tail recursion optimization capability. Env/scope handling is mildly broken and only per-function instead of per-expr.
|
||||
# special forms:
|
||||
# let: define functions or values
|
||||
# either mutate the existing environment or create a new temporary one
|
||||
# functions are (let (x y z) (+ x y z)), values are (let x "string")
|
||||
# if used as (let thing "stuff"), works mutably
|
||||
# if used as (let otherthing "not stuff" (+ otherthing " but things")) then creates new scope
|
||||
# cond: do different things based on some conditionals
|
||||
# used as (cond (condition1 action1) (condition2 action2)) - the expr corresponding to the first true condition is evaluated
|
||||
# do: group side-effectful exprs together - evaluate them in sequence and return the last one
|
||||
# lambda: define functions without binding them to a name
|
||||
def evaluate(x, env):
|
||||
while True:
|
||||
if isinstance(x, list):
|
||||
# special form handling
|
||||
if isinstance(x[0], Symbol):
|
||||
name = x[0].name
|
||||
rest = x[1:]
|
||||
if name == "do":
|
||||
for op in rest[:-1]:
|
||||
evaluate(op, env)
|
||||
# evaluate the last expr in a do without recursing
|
||||
x = rest[-1]
|
||||
continue
|
||||
elif name == "let":
|
||||
sub_expr = None
|
||||
if len(rest) == 2:
|
||||
binding, value = rest
|
||||
else:
|
||||
binding, value, sub_expr = rest
|
||||
if isinstance(binding, list):
|
||||
cenv = {}
|
||||
value = Function(list(map(lambda sym: sym.name, binding[1:])), value, env.child(cenv), binding[0].name)
|
||||
cenv[binding[0].name] = value
|
||||
binding = binding[0]
|
||||
else:
|
||||
value = evaluate(value, env)
|
||||
|
||||
if sub_expr:
|
||||
# evaluate the sub-expr nonrecursively
|
||||
x = sub_expr
|
||||
env = env.child({ binding.name: value })
|
||||
continue
|
||||
else:
|
||||
env[binding.name] = value
|
||||
return
|
||||
|
||||
elif name == "cond":
|
||||
# Check each condition in turn.
|
||||
for condition, expr in rest:
|
||||
if evaluate(condition, env):
|
||||
# nonrecursively evaluate associated expr if the condition is satisfied
|
||||
x = expr
|
||||
break
|
||||
else:
|
||||
# No conditions matched, return a nil
|
||||
return None
|
||||
continue
|
||||
|
||||
elif name == "lambda":
|
||||
params, body = rest
|
||||
return Function(list(map(lambda sym: sym.name, params)), body, env)
|
||||
|
||||
val = evaluate(x[0], env)
|
||||
|
||||
# evaluate user-defined function
|
||||
if isinstance(val, Function):
|
||||
params = dict(zip(val.params, map(lambda x: evaluate(x, env), x[1:])))
|
||||
env = val.environment.child(params)
|
||||
x = val.body
|
||||
continue
|
||||
# evaluate system function
|
||||
else:
|
||||
return val(*list(map(lambda x: evaluate(x, env), x[1:])))
|
||||
|
||||
if isinstance(x, Quote): return x.content
|
||||
if isinstance(x, Symbol): return env[x.name]
|
||||
return x
|
||||
|
||||
# Sorting functionality, as well as some other things in here for testing
|
||||
# Uses a tail-recursive continuation-passing-style Haskell-style quicksort (so not actually that quick)
|
||||
expr = Parser("""
|
||||
(do
|
||||
(let (id x) x)
|
||||
(let (snd xs) (head (tail xs)))
|
||||
|
||||
(let (take_rec out xs n) (cond
|
||||
((= n 0) out)
|
||||
(true (take_rec (cons (head xs) out) (tail xs) (- n 1)))
|
||||
))
|
||||
|
||||
(let (reverse_rec xs a) (cond
|
||||
((= xs '()) a)
|
||||
(true (reverse_rec (tail xs) (cons (head xs) a)))
|
||||
))
|
||||
|
||||
(let (drop xs n) (cond
|
||||
((= n 0) xs)
|
||||
(true (drop (tail xs) (- n 1)))
|
||||
))
|
||||
|
||||
(let (take xs n) (reverse_rec (take_rec '() xs n) '()))
|
||||
|
||||
(let (count_rec xs n) (cond
|
||||
((= n 0) xs)
|
||||
(true (count_rec (cons n xs) (- n 1)))
|
||||
))
|
||||
(let (count n) (count_rec '() n))
|
||||
|
||||
(let (filter_rec xs pred acc) (cond
|
||||
((= xs '()) acc)
|
||||
(true (filter_rec (tail xs) pred (cond
|
||||
((pred (head xs)) (cons (head xs) acc))
|
||||
(true acc)
|
||||
)))
|
||||
))
|
||||
(let (filter pred xs) (reverse (filter_rec xs pred '())))
|
||||
(let (partition_rec xs pred acc) (cond
|
||||
((= xs '()) acc)
|
||||
(true (partition_rec (tail xs) pred (cond
|
||||
((pred (head xs)) (list (cons (head xs) (head acc)) (snd acc)))
|
||||
(true (list (head acc) (cons (head xs) (snd acc))))
|
||||
)))
|
||||
))
|
||||
|
||||
(let (qsort xs cont) (cond
|
||||
((= xs '()) (cont '()))
|
||||
(true (do
|
||||
(let h (head xs))
|
||||
(let t (tail xs))
|
||||
(let part_result (partition_rec t (lambda (x) (< x h)) '(() ())))
|
||||
(qsort (head part_result)
|
||||
(lambda (ls) (qsort (snd part_result)
|
||||
(lambda (rs) (cont (+ ls (list h) rs))))))
|
||||
))
|
||||
))
|
||||
)
|
||||
""").parse()
|
||||
|
||||
env = Environment([{
|
||||
"+": lambda *args: functools.reduce(operator.add, args),
|
||||
"-": operator.sub,
|
||||
"*": lambda *args: functools.reduce(operator.mul, args),
|
||||
"/": operator.floordiv,
|
||||
"head": lambda xs: None if len(xs) == 0 else xs[0],
|
||||
"tail": lambda xs: xs[1:],
|
||||
"cons": lambda x, xs: [x] + xs,
|
||||
"reverse": lambda xs: xs[::-1], # can be implemented inside the language, but this is much faster
|
||||
"length": len,
|
||||
"print": print,
|
||||
"=": operator.eq,
|
||||
"!=": operator.ne,
|
||||
">": operator.gt,
|
||||
"<": operator.lt,
|
||||
">=": operator.ge,
|
||||
"<=": operator.le,
|
||||
"rand": lambda: random.randint(0, 1),
|
||||
"true": True,
|
||||
"false": False,
|
||||
"nil": None,
|
||||
"list": lambda *args: list(args),
|
||||
}])
|
||||
|
||||
evaluate(expr, env)
|
||||
|
||||
def entry(to_sort):
|
||||
return evaluate([Symbol("qsort"), Quote(to_sort), Symbol("id"), to_sort], env)
|
Loading…
Reference in New Issue
Block a user