Let's pile Nim code into one file too, why not?
This commit is contained in:
parent
aad2da35cf
commit
94929e8520
|
|
@ -0,0 +1,435 @@
|
|||
import rdstdin, strutils
|
||||
import bigints, fp
|
||||
|
||||
type
|
||||
|
||||
JoyListType* = List[JoyType]
|
||||
JoyMapType* = Map[string, JoyListType]
|
||||
|
||||
JoyTypeType* = enum
|
||||
joyAtom,
|
||||
joyFalse,
|
||||
joyInt,
|
||||
joyList,
|
||||
joyTrue
|
||||
|
||||
JoyType* = ref object
|
||||
case kind*: JoyTypeType
|
||||
of joyAtom: atomVal*: string
|
||||
of joyFalse, joyTrue: nil
|
||||
of joyInt: intVal*: BigInt
|
||||
of joyList: listVal*: JoyListType
|
||||
|
||||
Token = string
|
||||
|
||||
ParseError* = object of ValueError
|
||||
UnknownWordError* = object of ValueError
|
||||
|
||||
# Singleton values for Boolean type.
|
||||
|
||||
let j_true* = JoyType(kind: joyTrue)
|
||||
let j_false* = JoyType(kind: joyFalse)
|
||||
|
||||
# Singleton values for Symbols.
|
||||
|
||||
let j_loop* = JoyType(kind: joyAtom, atomVal: "loop")
|
||||
|
||||
let empty_list = JoyType(kind: joyList, listVal: Nil[JoyType]())
|
||||
|
||||
|
||||
# ███████╗██╗ ██╗██████╗ ██████╗ ███████╗███████╗███████╗██╗ ██████╗ ███╗ ██╗
|
||||
# ██╔════╝╚██╗██╔╝██╔══██╗██╔══██╗██╔════╝██╔════╝██╔════╝██║██╔═══██╗████╗ ██║
|
||||
# █████╗ ╚███╔╝ ██████╔╝██████╔╝█████╗ ███████╗███████╗██║██║ ██║██╔██╗ ██║
|
||||
# ██╔══╝ ██╔██╗ ██╔═══╝ ██╔══██╗██╔══╝ ╚════██║╚════██║██║██║ ██║██║╚██╗██║
|
||||
# ███████╗██╔╝ ██╗██║ ██║ ██║███████╗███████║███████║██║╚██████╔╝██║ ╚████║
|
||||
# ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚══════╝╚═╝ ╚═════╝ ╚═╝ ╚═══╝
|
||||
#
|
||||
# As elegant as it is to model the expression as a stack, it's not very
|
||||
# efficient, as concatenating definitions and other quoted programs to
|
||||
# the expression is a common and expensive operation.
|
||||
#
|
||||
# Instead, let's keep a stack of sub-expressions, reading from them
|
||||
# one-by-one, and prepending new sub-expressions to the stack rather than
|
||||
# concatenating them.
|
||||
|
||||
|
||||
proc push_int(n: BigInt, stack: JoyListType): JoyListType =
|
||||
JoyType(kind: joyInt, intVal: n) ^^ stack
|
||||
|
||||
|
||||
proc push_list(el: JoyListType, stack: JoyListType): JoyListType =
|
||||
JoyType(kind: joyList, listVal: el) ^^ stack
|
||||
|
||||
|
||||
proc pop_int(stack: JoyListType): (BigInt, JoyListType) =
|
||||
if stack.isEmpty:
|
||||
raise newException(ValueError, "Not enough values on stack.")
|
||||
let a = stack.head
|
||||
case a.kind:
|
||||
of joyInt:
|
||||
return (a.intVal, stack.tail)
|
||||
else:
|
||||
raise newException(ValueError, "Not an integer.")
|
||||
|
||||
|
||||
proc pop_list(stack: JoyListType): (JoyListType, JoyListType) =
|
||||
if stack.isEmpty:
|
||||
raise newException(ValueError, "Not enough values on stack.")
|
||||
let a = stack.head
|
||||
case a.kind:
|
||||
of joyList:
|
||||
return (a.listVal, stack.tail)
|
||||
else:
|
||||
raise newException(ValueError, "Not a list.")
|
||||
|
||||
|
||||
proc pop_bool(stack: JoyListType): (bool, JoyListType) =
|
||||
if stack.isEmpty:
|
||||
raise newException(ValueError, "Not enough values on stack.")
|
||||
let a = stack.head
|
||||
case a.kind:
|
||||
of joyTrue:
|
||||
return (true, stack.tail)
|
||||
of joyFalse:
|
||||
return (false, stack.tail)
|
||||
else:
|
||||
raise newException(ValueError, "Not a list.")
|
||||
|
||||
|
||||
proc push_bool(a: bool, stack: JoyListType): JoyListType =
|
||||
if a:
|
||||
return j_true ^^ stack
|
||||
return j_false ^^ stack
|
||||
|
||||
|
||||
proc as_list(thing: JoyType): JoyListType =
|
||||
case thing.kind
|
||||
of joyList:
|
||||
return thing.listVal
|
||||
else:
|
||||
raise newException(ValueError, "Only lists!")
|
||||
|
||||
|
||||
proc push_quote(quote: JoyType, expression: JoyType): JoyType =
|
||||
#[
|
||||
Put the quoted program onto the stack-of-stacks.
|
||||
]#
|
||||
let ql = as_list(quote)
|
||||
if ql.isEmpty:
|
||||
return expression
|
||||
let el = as_list(expression)
|
||||
return JoyType(kind: joyList, listVal: (quote ^^ el))
|
||||
|
||||
|
||||
proc push_quote_list(quote: JoyListType, expression: JoyType): JoyType =
|
||||
#[
|
||||
Put the quoted program onto the stack-of-stacks.
|
||||
]#
|
||||
return push_quote(JoyType(kind: joyList, listVal: quote), expression)
|
||||
|
||||
|
||||
proc next_term(expression: JoyType): (JoyType, JoyType) =
|
||||
#[
|
||||
Return the next term from the expression and the new expression.
|
||||
Raises ValueError if called on an empty expression.
|
||||
|
||||
(item, quote), expression = expression
|
||||
return item, push_quote(quote, expression)
|
||||
|
||||
]#
|
||||
let el = as_list(expression) ## JoyListType
|
||||
let ehead = el.head ## JoyType
|
||||
let eheadl = as_list(ehead) ## JoyListType
|
||||
let item = eheadl.head ## JoyType
|
||||
let quote = eheadl.tail ## JoyListType
|
||||
if quote.isEmpty:
|
||||
let t = JoyType(kind: joyList, listVal: el.tail)
|
||||
return (item, t)
|
||||
else:
|
||||
let q = JoyType(kind: joyList, listVal: quote)
|
||||
let t = JoyType(kind: joyList, listVal: (q ^^ el.tail))
|
||||
return (item, t)
|
||||
|
||||
|
||||
#██████╗ █████╗ ██████╗ ███████╗███████╗██████╗
|
||||
#██╔══██╗██╔══██╗██╔══██╗██╔════╝██╔════╝██╔══██╗
|
||||
#██████╔╝███████║██████╔╝███████╗█████╗ ██████╔╝
|
||||
#██╔═══╝ ██╔══██║██╔══██╗╚════██║██╔══╝ ██╔══██╗
|
||||
#██║ ██║ ██║██║ ██║███████║███████╗██║ ██║
|
||||
#╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚══════╝╚═╝ ╚═╝
|
||||
|
||||
|
||||
proc text_to_expression(text: string): JoyType =
|
||||
#[
|
||||
Convert a string to a Joy expression.
|
||||
|
||||
When supplied with a string this function returns a Python datastructure
|
||||
that represents the Joy datastructure described by the text expression.
|
||||
Any unbalanced square brackets will raise a ParseError.
|
||||
|
||||
:param str text: Text to convert.
|
||||
:rtype: stack
|
||||
:raises ParseError: if the parse fails.
|
||||
]#
|
||||
var frame : seq[JoyType] = @[]
|
||||
var stack : seq[seq[JoyType]] = @[]
|
||||
var thing : JoyType
|
||||
|
||||
for tok in text.replace("[", " [ ").replace("]", " ] ").splitWhitespace():
|
||||
|
||||
if tok == "[":
|
||||
stack.add(frame)
|
||||
frame = newSeq[JoyType](0)
|
||||
continue
|
||||
|
||||
if tok == "]":
|
||||
thing = JoyType(kind: joyList, listVal: frame.asList)
|
||||
try:
|
||||
frame = stack.pop()
|
||||
except IndexDefect:
|
||||
raise newException(ParseError, "Extra closing bracket.")
|
||||
elif tok == "true":
|
||||
thing = j_true
|
||||
elif tok == "false":
|
||||
thing = j_false
|
||||
else:
|
||||
try:
|
||||
thing = JoyType(kind: joyInt, intVal: tok.initBigInt)
|
||||
except ValueError:
|
||||
thing = JoyType(kind: joyAtom, atomVal: tok)
|
||||
|
||||
frame.add(thing)
|
||||
|
||||
if stack.len() != 0:
|
||||
raise newException(ParseError, "Unclosed bracket.")
|
||||
|
||||
JoyType(kind: joyList, listVal: frame.asList)
|
||||
|
||||
|
||||
#██████╗ ██████╗ ██╗███╗ ██╗████████╗███████╗██████╗
|
||||
#██╔══██╗██╔══██╗██║████╗ ██║╚══██╔══╝██╔════╝██╔══██╗
|
||||
#██████╔╝██████╔╝██║██╔██╗ ██║ ██║ █████╗ ██████╔╝
|
||||
#██╔═══╝ ██╔══██╗██║██║╚██╗██║ ██║ ██╔══╝ ██╔══██╗
|
||||
#██║ ██║ ██║██║██║ ╚████║ ██║ ███████╗██║ ██║
|
||||
#╚═╝ ╚═╝ ╚═╝╚═╝╚═╝ ╚═══╝ ╚═╝ ╚══════╝╚═╝ ╚═╝
|
||||
|
||||
|
||||
proc pr_str(thing: JoyType): string
|
||||
|
||||
proc joystr(s: JoyListType): string =
|
||||
s.map(pr_str).asSeq.join(" ")
|
||||
|
||||
proc pr_str(thing: JoyType): string =
|
||||
case thing.kind
|
||||
of joyAtom: thing.atomVal
|
||||
of joyInt: thing.intVal.toString
|
||||
of joyList: "[" & joystr(thing.listVal) & "]"
|
||||
of joyTrue: "true"
|
||||
of joyFalse: "false"
|
||||
|
||||
proc print_expression*(stack: JoyListType): string =
|
||||
joystr(stack)
|
||||
|
||||
proc print_stack*(stack: JoyListType): string =
|
||||
joystr(stack.reverse)
|
||||
|
||||
|
||||
# ██████╗ ██████╗ ███╗ ███╗██████╗ ██╗███╗ ██╗ █████╗ ████████╗ ██████╗ ██████╗ ███████╗
|
||||
#██╔════╝██╔═══██╗████╗ ████║██╔══██╗██║████╗ ██║██╔══██╗╚══██╔══╝██╔═══██╗██╔══██╗██╔════╝
|
||||
#██║ ██║ ██║██╔████╔██║██████╔╝██║██╔██╗ ██║███████║ ██║ ██║ ██║██████╔╝███████╗
|
||||
#██║ ██║ ██║██║╚██╔╝██║██╔══██╗██║██║╚██╗██║██╔══██║ ██║ ██║ ██║██╔══██╗╚════██║
|
||||
#╚██████╗╚██████╔╝██║ ╚═╝ ██║██████╔╝██║██║ ╚████║██║ ██║ ██║ ╚██████╔╝██║ ██║███████║
|
||||
# ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═════╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝
|
||||
|
||||
|
||||
proc branch(stack: JoyListType, expression: JoyType, dictionary: JoyMapType): (JoyListType, JoyType, JoyMapType) =
|
||||
let (true_body, s0) = pop_list(stack)
|
||||
let (false_body, s1) = pop_list(s0)
|
||||
let (flag, s2) = pop_bool(s1)
|
||||
if flag:
|
||||
return (s2, push_quote_list(true_body, expression), dictionary)
|
||||
return (s2, push_quote_list(false_body, expression), dictionary)
|
||||
# I don't like how this extracts the quotes only to immediately
|
||||
# re-wrap one of them in a joytype wrapper.
|
||||
|
||||
|
||||
proc clear(stack: JoyListType, expression: JoyType, dictionary: JoyMapType): (JoyListType, JoyType, JoyMapType) =
|
||||
return (empty_list.listVal, expression, dictionary)
|
||||
|
||||
|
||||
proc concat(stack: JoyListType, expression: JoyType, dictionary: JoyMapType): (JoyListType, JoyType, JoyMapType) =
|
||||
let (tos, s0) = pop_list(stack)
|
||||
let (second, s1) = pop_list(s0)
|
||||
return (push_list((second ++ tos), s1), expression, dictionary)
|
||||
|
||||
|
||||
# ██╗███╗ ██╗████████╗███████╗██████╗ ██████╗ ██████╗ ███████╗████████╗███████╗██████╗
|
||||
# ██║████╗ ██║╚══██╔══╝██╔════╝██╔══██╗██╔══██╗██╔══██╗██╔════╝╚══██╔══╝██╔════╝██╔══██╗
|
||||
# ██║██╔██╗ ██║ ██║ █████╗ ██████╔╝██████╔╝██████╔╝█████╗ ██║ █████╗ ██████╔╝
|
||||
# ██║██║╚██╗██║ ██║ ██╔══╝ ██╔══██╗██╔═══╝ ██╔══██╗██╔══╝ ██║ ██╔══╝ ██╔══██╗
|
||||
# ██║██║ ╚████║ ██║ ███████╗██║ ██║██║ ██║ ██║███████╗ ██║ ███████╗██║ ██║
|
||||
# ╚═╝╚═╝ ╚═══╝ ╚═╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝╚══════╝ ╚═╝ ╚══════╝╚═╝ ╚═╝
|
||||
#
|
||||
# The joy() interpreter function is extrememly simple. It accepts a stack,
|
||||
# an expression, and a dictionary, and it iterates through the expression
|
||||
# putting values onto the stack and delegating execution to functions which
|
||||
# it looks up in the dictionary.
|
||||
|
||||
|
||||
proc joy_eval(sym: string, stack: JoyListType, expression: JoyType, dictionary: JoyMapType): (JoyListType, JoyType, JoyMapType) =
|
||||
case sym
|
||||
|
||||
of "add":
|
||||
let (a, s0) = pop_int(stack)
|
||||
let (b, s1) = pop_int(s0)
|
||||
return (push_int(a + b, s1), expression, dictionary)
|
||||
|
||||
of "mul":
|
||||
let (a, s0) = pop_int(stack)
|
||||
let (b, s1) = pop_int(s0)
|
||||
return (push_int(a * b, s1), expression, dictionary)
|
||||
|
||||
of "div":
|
||||
let (a, s0) = pop_int(stack)
|
||||
let (b, s1) = pop_int(s0)
|
||||
return (push_int(b div a, s1), expression, dictionary)
|
||||
|
||||
of "mod":
|
||||
let (a, s0) = pop_int(stack)
|
||||
let (b, s1) = pop_int(s0)
|
||||
return (push_int(b mod a, s1), expression, dictionary)
|
||||
|
||||
of "gt":
|
||||
let (a, s0) = pop_int(stack)
|
||||
let (b, s1) = pop_int(s0)
|
||||
return (push_bool(b > a, s1), expression, dictionary)
|
||||
|
||||
of "lt":
|
||||
let (a, s0) = pop_int(stack)
|
||||
let (b, s1) = pop_int(s0)
|
||||
return (push_bool(b < a, s1), expression, dictionary)
|
||||
|
||||
of "ge":
|
||||
let (a, s0) = pop_int(stack)
|
||||
let (b, s1) = pop_int(s0)
|
||||
return (push_bool(b >= a, s1), expression, dictionary)
|
||||
|
||||
of "le":
|
||||
let (a, s0) = pop_int(stack)
|
||||
let (b, s1) = pop_int(s0)
|
||||
return (push_bool(b <= a, s1), expression, dictionary)
|
||||
|
||||
of "ne":
|
||||
let (a, s0) = pop_int(stack)
|
||||
let (b, s1) = pop_int(s0)
|
||||
return (push_bool(b != a, s1), expression, dictionary)
|
||||
|
||||
of "eq":
|
||||
let (a, s0) = pop_int(stack)
|
||||
let (b, s1) = pop_int(s0)
|
||||
return (push_bool(b != a, s1), expression, dictionary)
|
||||
|
||||
of "branch":
|
||||
return branch(stack, expression, dictionary)
|
||||
of "clear":
|
||||
return clear(stack, expression, dictionary)
|
||||
of "concat":
|
||||
return concat(stack, expression, dictionary)
|
||||
|
||||
else:
|
||||
raise newException(UnknownWordError, "Unknown: " & sym)
|
||||
|
||||
return (stack, expression, dictionary)
|
||||
|
||||
|
||||
|
||||
# = ≡ eq
|
||||
# != ≡ ne
|
||||
# <> ≡ ne
|
||||
|
||||
|
||||
proc joy(stack: JoyListType, expression: JoyType, dictionary: JoyMapType): (JoyListType, JoyMapType) =
|
||||
var s = stack
|
||||
var d = dictionary
|
||||
var e = push_quote(expression, empty_list)
|
||||
var term : JoyType
|
||||
|
||||
while not e.listVal.isEmpty:
|
||||
(term, e) = next_term(e)
|
||||
#echo pr_str(term)
|
||||
case term.kind
|
||||
of joyAtom:
|
||||
(s, e, d) = joy_eval(term.atomVal, s, e, d)
|
||||
else:
|
||||
s = term ^^ s
|
||||
|
||||
return (s, d)
|
||||
|
||||
|
||||
#
|
||||
|
||||
|
||||
|
||||
|
||||
let stack = empty_list.listVal
|
||||
let dict = newMap[string, JoyListType]()
|
||||
#let exp = text_to_expression("2 3 add 23 mul 45 gt")
|
||||
#let exp = text_to_expression("2 3 false [add] [mul] branch")
|
||||
#let exp = text_to_expression("2 3 true [add] [mul] branch")
|
||||
#let exp = text_to_expression("[add] [mul] concat")
|
||||
#let (s,d) = joy(stack, exp, dict)
|
||||
|
||||
#echo print_stack(s)
|
||||
|
||||
var s = stack
|
||||
var d = dict
|
||||
var exp : JoyType
|
||||
while true:
|
||||
try:
|
||||
exp = text_to_expression(readLineFromStdin("joy? "))
|
||||
except IOError:
|
||||
break
|
||||
try:
|
||||
(s,d) = joy(s, exp, dict)
|
||||
except:
|
||||
echo getCurrentExceptionMsg()
|
||||
echo print_stack(s)
|
||||
continue
|
||||
echo print_stack(s)
|
||||
|
||||
|
||||
|
||||
#echo pr_str(text_to_expression("""
|
||||
# [ [[abs] ii <=]
|
||||
# [
|
||||
# [<>] [pop !-] ||
|
||||
# ] &&
|
||||
# ]
|
||||
# [[ !-] [[++]] [[--]] ifte dip]
|
||||
# [[pop !-] [--] [++] ifte ]
|
||||
# ifte
|
||||
# true false 23
|
||||
#"""))
|
||||
|
||||
# we could start with an empty list and add two expressions
|
||||
# but instead let's preload a few "commands":
|
||||
#let e = text_to_expression("[55 true][42]")
|
||||
#let t = text_to_expression("23")
|
||||
#
|
||||
#let f = push_quote(t, e)
|
||||
|
||||
#echo pr_str(t)
|
||||
#echo pr_str(e)
|
||||
#echo pr_str(f)
|
||||
#
|
||||
#var (a, b) = next_term(f)
|
||||
#echo pr_str(a)
|
||||
#(a, b) = next_term(b)
|
||||
#echo pr_str(a)
|
||||
#(a, b) = next_term(b)
|
||||
#echo pr_str(a)
|
||||
#(a, b) = next_term(b)
|
||||
#echo pr_str(a)
|
||||
|
||||
|
||||
#text_to_expression("""[] [[]]""")
|
||||
Loading…
Reference in New Issue