Minor cleanup, subtraction.

Testing with the joytest test suite revealed that I had forgotten to
implement subtraction.  This also unconvered a (maybe) bug in the
BigInts package where it converts "-" to zero.

https://git.sr.ht/~sforman/joytest

https://github.com/nim-lang/bigints/issues/116
This commit is contained in:
Simon Forman 2022-09-14 21:01:50 -07:00
parent 39008b351b
commit 9d7a2a8fcb
3 changed files with 55 additions and 114 deletions

View File

@ -1,6 +1,5 @@
SOURCES = defs.nim joy.nim joylib.nim printer.nim reader.nim types.nim utils.nim
joy: $(SOURCES) defs.txt joy: joy.nim defs.txt
nim c joy.nim nim c joy.nim
defs.txt: ../defs.txt defs.txt: ../defs.txt

View File

@ -1,5 +1,7 @@
# Joy interpreter in Nim # Joy interpreter in Nim
> Simple pleasures are the best.
This interpreter written in Nim is part of the Thun project, which This interpreter written in Nim is part of the Thun project, which
includes interpreters written in Python and Prolog, and some explorations includes interpreters written in Python and Prolog, and some explorations
towards compilers for Joy written in Prolog. towards compilers for Joy written in Prolog.

View File

@ -95,7 +95,7 @@ proc as_list(thing: JoyType): JoyListType =
of joyList: of joyList:
return thing.listVal return thing.listVal
else: else:
raise newException(ValueError, "Only lists!") raise newException(ValueError, "Not a list.")
proc as_int(i: JoyType): BigInt = proc as_int(i: JoyType): BigInt =
@ -184,7 +184,7 @@ proc pop_bool(stack: JoyListType): (bool, JoyListType) =
╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚══════╝╚═╝ ╚═════╝ ╚═╝ ╚═══╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚══════╝╚═╝ ╚═════╝ ╚═╝ ╚═══╝
As elegant as it is to model the expression as a stack, it's not very As elegant as it is to model the expression as a stack, it's not very
ficient, as concatenating definitions and other quoted programs to efficient, as concatenating definitions and other quoted programs to
the expression is a common and expensive operation. the expression is a common and expensive operation.
Instead, let's keep a stack of sub-expressions, reading from them Instead, let's keep a stack of sub-expressions, reading from them
@ -294,22 +294,19 @@ proc text_to_expression(text: string): JoyListType =
proc pr_str(thing: JoyType): string proc pr_str(thing: JoyType): string
proc joystr(s: JoyListType): string = proc print_expression(s: JoyListType): string =
s.map(pr_str).asSeq.join(" ") s.map(pr_str).asSeq.join(" ")
proc pr_str(thing: JoyType): string = proc pr_str(thing: JoyType): string =
case thing.kind case thing.kind
of joySymbol: thing.symVal of joySymbol: thing.symVal
of joyInt: thing.intVal.toString of joyInt: thing.intVal.toString
of joyList: "[" & joystr(thing.listVal) & "]" of joyList: "[" & print_expression(thing.listVal) & "]"
of joyTrue: "true" of joyTrue: "true"
of joyFalse: "false" of joyFalse: "false"
proc print_expression*(stack: JoyListType): string =
joystr(stack)
proc print_stack*(stack: JoyListType): string = proc print_stack*(stack: JoyListType): string =
joystr(stack.reverse) print_expression(stack.reverse)
# ██████╗ ██████╗ ███╗ ███╗██████╗ ██╗███╗ ██╗ █████╗ ████████╗ ██████╗ ██████╗ ███████╗ # ██████╗ ██████╗ ███╗ ███╗██████╗ ██╗███╗ ██╗ █████╗ ████████╗ ██████╗ ██████╗ ███████╗
@ -320,8 +317,7 @@ proc print_stack*(stack: JoyListType): string =
# ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═════╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ # ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═════╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝
proc branch(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): ( proc branch(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (JoyListType, JoyListType, JoyMapType) =
JoyListType, JoyListType, JoyMapType) =
let (true_body_node, s0) = pop_list_node(stack) let (true_body_node, s0) = pop_list_node(stack)
let (false_body_node, s1) = pop_list_node(s0) let (false_body_node, s1) = pop_list_node(s0)
let (flag, s2) = pop_bool(s1) let (flag, s2) = pop_bool(s1)
@ -330,9 +326,10 @@ proc branch(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType)
return (s2, push_quote(false_body_node, expression), dictionary) return (s2, push_quote(false_body_node, expression), dictionary)
proc dip(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): ( proc dip(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (JoyListType, JoyListType, JoyMapType) =
JoyListType, JoyListType, JoyMapType) =
let (body_node, s0) = pop_list_node(stack) let (body_node, s0) = pop_list_node(stack)
if s0.isEmpty:
raise newException(ValueError, "Not enough values on stack.")
let tos_as_list_of_one = s0.head ^^ empty_list.listVal let tos_as_list_of_one = s0.head ^^ empty_list.listVal
return ( return (
s0.tail, s0.tail,
@ -341,8 +338,7 @@ proc dip(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (
) )
proc i(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): ( proc i(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (JoyListType, JoyListType, JoyMapType) =
JoyListType, JoyListType, JoyMapType) =
let (body_node, s0) = pop_list_node(stack) let (body_node, s0) = pop_list_node(stack)
return ( return (
s0, s0,
@ -351,8 +347,7 @@ proc i(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (
) )
proc loop(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): ( proc loop(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (JoyListType, JoyListType, JoyMapType) =
JoyListType, JoyListType, JoyMapType) =
let (body_node, s0) = pop_list_node(stack) let (body_node, s0) = pop_list_node(stack)
let (flag, s1) = pop_bool(s0) let (flag, s1) = pop_bool(s0)
if flag: if flag:
@ -373,76 +368,65 @@ proc loop(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType):
]# ]#
proc clear(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): ( proc clear(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (JoyListType, JoyListType, JoyMapType) =
JoyListType, JoyListType, JoyMapType) =
return (empty_list.listVal, expression, dictionary) return (empty_list.listVal, expression, dictionary)
proc concat(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): ( proc concat(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (JoyListType, JoyListType, JoyMapType) =
JoyListType, JoyListType, JoyMapType) =
let (tos, s0) = pop_list(stack) let (tos, s0) = pop_list(stack)
let (second, s1) = pop_list(s0) let (second, s1) = pop_list(s0)
return (push_list((second ++ tos), s1), expression, dictionary) return (push_list((second ++ tos), s1), expression, dictionary)
proc cons(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): ( proc cons(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (JoyListType, JoyListType, JoyMapType) =
JoyListType, JoyListType, JoyMapType) =
let (tos, s0) = pop_list(stack) let (tos, s0) = pop_list(stack)
if s0.isEmpty: if s0.isEmpty:
raise newException(ValueError, "Not enough values on stack.") raise newException(ValueError, "Not enough values on stack.")
return (push_list((s0.head ^^ tos), s0.tail), expression, dictionary) return (push_list((s0.head ^^ tos), s0.tail), expression, dictionary)
proc dup(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): ( proc dup(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (JoyListType, JoyListType, JoyMapType) =
JoyListType, JoyListType, JoyMapType) =
if stack.isEmpty: if stack.isEmpty:
raise newException(ValueError, "Cannot dup empty stack.") raise newException(ValueError, "Cannot dup empty stack.")
return (stack.head ^^ stack, expression, dictionary) return (stack.head ^^ stack, expression, dictionary)
proc first(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): ( proc first(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (JoyListType, JoyListType, JoyMapType) =
JoyListType, JoyListType, JoyMapType) =
let (tos, s0) = pop_list(stack) let (tos, s0) = pop_list(stack)
if tos.isEmpty: if tos.isEmpty:
raise newException(ValueError, "Cannot take first of empty list.") raise newException(ValueError, "Cannot take first of empty list.")
return (tos.head ^^ s0, expression, dictionary) return (tos.head ^^ s0, expression, dictionary)
proc pop(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): ( proc pop(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (JoyListType, JoyListType, JoyMapType) =
JoyListType, JoyListType, JoyMapType) =
if stack.isEmpty: if stack.isEmpty:
raise newException(ValueError, "Cannot pop empty stack.") raise newException(ValueError, "Cannot pop empty stack.")
return (stack.tail, expression, dictionary) return (stack.tail, expression, dictionary)
proc rest(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): ( proc rest(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (JoyListType, JoyListType, JoyMapType) =
JoyListType, JoyListType, JoyMapType) =
let (tos, s0) = pop_list(stack) let (tos, s0) = pop_list(stack)
if tos.isEmpty: if tos.isEmpty:
raise newException(ValueError, "Cannot take rest of empty list.") raise newException(ValueError, "Cannot take rest of empty list.")
return (push_list(tos.tail, s0), expression, dictionary) return (push_list(tos.tail, s0), expression, dictionary)
proc stack(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): ( proc stack(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (JoyListType, JoyListType, JoyMapType) =
JoyListType, JoyListType, JoyMapType) =
return (push_list(stack, stack), expression, dictionary) return (push_list(stack, stack), expression, dictionary)
proc swaack(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): ( proc swaack(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (JoyListType, JoyListType, JoyMapType) =
JoyListType, JoyListType, JoyMapType) =
let (tos, s0) = pop_list(stack) let (tos, s0) = pop_list(stack)
return (push_list(s0, tos), expression, dictionary) return (push_list(s0, tos), expression, dictionary)
proc swap(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): ( proc swap(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (JoyListType, JoyListType, JoyMapType) =
JoyListType, JoyListType, JoyMapType) =
let (tos, s0) = pop_any(stack) let (tos, s0) = pop_any(stack)
let (second, s1) = pop_any(s0) let (second, s1) = pop_any(s0)
return ((second ^^ tos ^^ s1), expression, dictionary) return ((second ^^ tos ^^ s1), expression, dictionary)
proc truthy(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): ( proc truthy(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (JoyListType, JoyListType, JoyMapType) =
JoyListType, JoyListType, JoyMapType) =
let (tos, s0) = pop_any(stack) let (tos, s0) = pop_any(stack)
case tos.kind: case tos.kind:
of joyTrue, joyFalse: of joyTrue, joyFalse:
@ -470,59 +454,63 @@ it looks up in the dictionary.
]# ]#
proc joy_eval(sym: string, stack: JoyListType, expression: JoyListType, proc joy_eval(sym: string, stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (JoyListType, JoyListType, JoyMapType) =
dictionary: JoyMapType): (JoyListType, JoyListType, JoyMapType) =
case sym case sym
of "add": of "+":
let (a, s0) = pop_int(stack) let (a, s0) = pop_int(stack)
let (b, s1) = pop_int(s0) let (b, s1) = pop_int(s0)
return (push_int(a + b, s1), expression, dictionary) return (push_int(a + b, s1), expression, dictionary)
of "mul": of "-":
let (a, s0) = pop_int(stack)
let (b, s1) = pop_int(s0)
return (push_int(b - a, s1), expression, dictionary)
of "*":
let (a, s0) = pop_int(stack) let (a, s0) = pop_int(stack)
let (b, s1) = pop_int(s0) let (b, s1) = pop_int(s0)
return (push_int(a * b, s1), expression, dictionary) return (push_int(a * b, s1), expression, dictionary)
of "div": of "/":
let (a, s0) = pop_int(stack) let (a, s0) = pop_int(stack)
let (b, s1) = pop_int(s0) let (b, s1) = pop_int(s0)
return (push_int(b div a, s1), expression, dictionary) return (push_int(b div a, s1), expression, dictionary)
of "mod": of "%":
let (a, s0) = pop_int(stack) let (a, s0) = pop_int(stack)
let (b, s1) = pop_int(s0) let (b, s1) = pop_int(s0)
return (push_int(b mod a, s1), expression, dictionary) return (push_int(b mod a, s1), expression, dictionary)
of "gt": of ">":
let (a, s0) = pop_int(stack) let (a, s0) = pop_int(stack)
let (b, s1) = pop_int(s0) let (b, s1) = pop_int(s0)
return (push_bool(b > a, s1), expression, dictionary) return (push_bool(b > a, s1), expression, dictionary)
of "lt": of "<":
let (a, s0) = pop_int(stack) let (a, s0) = pop_int(stack)
let (b, s1) = pop_int(s0) let (b, s1) = pop_int(s0)
return (push_bool(b < a, s1), expression, dictionary) return (push_bool(b < a, s1), expression, dictionary)
of "ge": of ">=":
let (a, s0) = pop_int(stack) let (a, s0) = pop_int(stack)
let (b, s1) = pop_int(s0) let (b, s1) = pop_int(s0)
return (push_bool(b >= a, s1), expression, dictionary) return (push_bool(b >= a, s1), expression, dictionary)
of "le": of "<=":
let (a, s0) = pop_int(stack) let (a, s0) = pop_int(stack)
let (b, s1) = pop_int(s0) let (b, s1) = pop_int(s0)
return (push_bool(b <= a, s1), expression, dictionary) return (push_bool(b <= a, s1), expression, dictionary)
of "ne": of "!=", "<>":
let (a, s0) = pop_int(stack) let (a, s0) = pop_int(stack)
let (b, s1) = pop_int(s0) let (b, s1) = pop_int(s0)
return (push_bool(b != a, s1), expression, dictionary) return (push_bool(b != a, s1), expression, dictionary)
of "eq": of "=":
let (a, s0) = pop_int(stack) let (a, s0) = pop_int(stack)
let (b, s1) = pop_int(s0) let (b, s1) = pop_int(s0)
return (push_bool(b != a, s1), expression, dictionary) return (push_bool(b == a, s1), expression, dictionary)
of "branch": of "branch":
return branch(stack, expression, dictionary) return branch(stack, expression, dictionary)
@ -552,7 +540,7 @@ proc joy_eval(sym: string, stack: JoyListType, expression: JoyListType,
return swaack(stack, expression, dictionary) return swaack(stack, expression, dictionary)
of "swap": of "swap":
return swap(stack, expression, dictionary) return swap(stack, expression, dictionary)
of "bool": # bool is a reserved word in Nim. of "bool": # bool is a reserved word in Nim.
return truthy(stack, expression, dictionary) return truthy(stack, expression, dictionary)
else: else:
@ -562,8 +550,7 @@ proc joy_eval(sym: string, stack: JoyListType, expression: JoyListType,
return (stack, push_quote_list(def.get(), expression), dictionary) return (stack, push_quote_list(def.get(), expression), dictionary)
proc joy(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): ( proc joy(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (JoyListType, JoyMapType) =
JoyListType, JoyMapType) =
var s = stack var s = stack
var d = dictionary var d = dictionary
var e = push_quote_list(expression, empty_list.listVal) var e = push_quote_list(expression, empty_list.listVal)
@ -581,7 +568,6 @@ proc joy(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (
return (s, d) return (s, d)
proc add_def(def: string, dictionary: var JoyMapType) = proc add_def(def: string, dictionary: var JoyMapType) =
let d = text_to_expression(def) let d = text_to_expression(def)
let sym = d.head let sym = d.head
@ -592,26 +578,17 @@ proc add_def(def: string, dictionary: var JoyMapType) =
raise newException(ValueError, def) raise newException(ValueError, def)
proc defs_file2dict(defs_filename: string = "defs.txt"): JoyMapType = #proc defs_file2dict(defs_filename: string = "defs.txt"): JoyMapType =
var strm = newFileStream(defs_filename, fmRead) # var strm = newFileStream(defs_filename, fmRead)
var dictionary = newMap[string, JoyListType]() # var dictionary = newMap[string, JoyListType]()
var line = "" # var line = ""
if not isNil(strm): # if not isNil(strm):
while strm.readLine(line): # while strm.readLine(line):
if line.isEmptyOrWhitespace: # if line.isEmptyOrWhitespace:
continue # continue
add_def(line, dictionary) # add_def(line, dictionary)
strm.close() # strm.close()
return dictionary # return dictionary
#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)
#let dictionary = defs_file2dict() #let dictionary = defs_file2dict()
@ -637,40 +614,3 @@ while true:
except: except:
echo getCurrentExceptionMsg() echo getCurrentExceptionMsg()
echo print_stack(s) 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("""[] [[]]""")