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
defs.txt: ../defs.txt

View File

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

View File

@ -95,7 +95,7 @@ proc as_list(thing: JoyType): JoyListType =
of joyList:
return thing.listVal
else:
raise newException(ValueError, "Only lists!")
raise newException(ValueError, "Not a list.")
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
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.
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 joystr(s: JoyListType): string =
proc print_expression(s: JoyListType): string =
s.map(pr_str).asSeq.join(" ")
proc pr_str(thing: JoyType): string =
case thing.kind
of joySymbol: thing.symVal
of joyInt: thing.intVal.toString
of joyList: "[" & joystr(thing.listVal) & "]"
of joyList: "[" & print_expression(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)
print_expression(stack.reverse)
# ██████╗ ██████╗ ███╗ ███╗██████╗ ██╗███╗ ██╗ █████╗ ████████╗ ██████╗ ██████╗ ███████╗
@ -320,8 +317,7 @@ proc print_stack*(stack: JoyListType): string =
# ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═════╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝
proc branch(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (
JoyListType, JoyListType, JoyMapType) =
proc branch(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (JoyListType, JoyListType, JoyMapType) =
let (true_body_node, s0) = pop_list_node(stack)
let (false_body_node, s1) = pop_list_node(s0)
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)
proc dip(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (
JoyListType, JoyListType, JoyMapType) =
proc dip(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (JoyListType, JoyListType, JoyMapType) =
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
return (
s0.tail,
@ -341,8 +338,7 @@ proc dip(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (
)
proc i(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (
JoyListType, JoyListType, JoyMapType) =
proc i(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (JoyListType, JoyListType, JoyMapType) =
let (body_node, s0) = pop_list_node(stack)
return (
s0,
@ -351,8 +347,7 @@ proc i(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (
)
proc loop(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (
JoyListType, JoyListType, JoyMapType) =
proc loop(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (JoyListType, JoyListType, JoyMapType) =
let (body_node, s0) = pop_list_node(stack)
let (flag, s1) = pop_bool(s0)
if flag:
@ -373,76 +368,65 @@ proc loop(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType):
]#
proc clear(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (
JoyListType, JoyListType, JoyMapType) =
proc clear(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (JoyListType, JoyListType, JoyMapType) =
return (empty_list.listVal, expression, dictionary)
proc concat(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (
JoyListType, JoyListType, JoyMapType) =
proc concat(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (JoyListType, JoyListType, JoyMapType) =
let (tos, s0) = pop_list(stack)
let (second, s1) = pop_list(s0)
return (push_list((second ++ tos), s1), expression, dictionary)
proc cons(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (
JoyListType, JoyListType, JoyMapType) =
proc cons(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (JoyListType, JoyListType, JoyMapType) =
let (tos, s0) = pop_list(stack)
if s0.isEmpty:
raise newException(ValueError, "Not enough values on stack.")
return (push_list((s0.head ^^ tos), s0.tail), expression, dictionary)
proc dup(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (
JoyListType, JoyListType, JoyMapType) =
proc dup(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (JoyListType, JoyListType, JoyMapType) =
if stack.isEmpty:
raise newException(ValueError, "Cannot dup empty stack.")
return (stack.head ^^ stack, expression, dictionary)
proc first(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (
JoyListType, JoyListType, JoyMapType) =
proc first(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (JoyListType, JoyListType, JoyMapType) =
let (tos, s0) = pop_list(stack)
if tos.isEmpty:
raise newException(ValueError, "Cannot take first of empty list.")
return (tos.head ^^ s0, expression, dictionary)
proc pop(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (
JoyListType, JoyListType, JoyMapType) =
proc pop(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (JoyListType, JoyListType, JoyMapType) =
if stack.isEmpty:
raise newException(ValueError, "Cannot pop empty stack.")
return (stack.tail, expression, dictionary)
proc rest(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (
JoyListType, JoyListType, JoyMapType) =
proc rest(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (JoyListType, JoyListType, JoyMapType) =
let (tos, s0) = pop_list(stack)
if tos.isEmpty:
raise newException(ValueError, "Cannot take rest of empty list.")
return (push_list(tos.tail, s0), expression, dictionary)
proc stack(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (
JoyListType, JoyListType, JoyMapType) =
proc stack(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (JoyListType, JoyListType, JoyMapType) =
return (push_list(stack, stack), expression, dictionary)
proc swaack(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (
JoyListType, JoyListType, JoyMapType) =
proc swaack(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (JoyListType, JoyListType, JoyMapType) =
let (tos, s0) = pop_list(stack)
return (push_list(s0, tos), expression, dictionary)
proc swap(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (
JoyListType, JoyListType, JoyMapType) =
proc swap(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (JoyListType, JoyListType, JoyMapType) =
let (tos, s0) = pop_any(stack)
let (second, s1) = pop_any(s0)
return ((second ^^ tos ^^ s1), expression, dictionary)
proc truthy(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (
JoyListType, JoyListType, JoyMapType) =
proc truthy(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (JoyListType, JoyListType, JoyMapType) =
let (tos, s0) = pop_any(stack)
case tos.kind:
of joyTrue, joyFalse:
@ -470,59 +454,63 @@ it looks up in the dictionary.
]#
proc joy_eval(sym: string, stack: JoyListType, expression: JoyListType,
dictionary: JoyMapType): (JoyListType, JoyListType, JoyMapType) =
proc joy_eval(sym: string, stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (JoyListType, JoyListType, JoyMapType) =
case sym
of "add":
of "+":
let (a, s0) = pop_int(stack)
let (b, s1) = pop_int(s0)
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 (b, s1) = pop_int(s0)
return (push_int(a * b, s1), expression, dictionary)
of "div":
of "/":
let (a, s0) = pop_int(stack)
let (b, s1) = pop_int(s0)
return (push_int(b div a, s1), expression, dictionary)
of "mod":
of "%":
let (a, s0) = pop_int(stack)
let (b, s1) = pop_int(s0)
return (push_int(b mod a, s1), expression, dictionary)
of "gt":
of ">":
let (a, s0) = pop_int(stack)
let (b, s1) = pop_int(s0)
return (push_bool(b > a, s1), expression, dictionary)
of "lt":
of "<":
let (a, s0) = pop_int(stack)
let (b, s1) = pop_int(s0)
return (push_bool(b < a, s1), expression, dictionary)
of "ge":
of ">=":
let (a, s0) = pop_int(stack)
let (b, s1) = pop_int(s0)
return (push_bool(b >= a, s1), expression, dictionary)
of "le":
of "<=":
let (a, s0) = pop_int(stack)
let (b, s1) = pop_int(s0)
return (push_bool(b <= a, s1), expression, dictionary)
of "ne":
of "!=", "<>":
let (a, s0) = pop_int(stack)
let (b, s1) = pop_int(s0)
return (push_bool(b != a, s1), expression, dictionary)
of "eq":
of "=":
let (a, s0) = pop_int(stack)
let (b, s1) = pop_int(s0)
return (push_bool(b != a, s1), expression, dictionary)
return (push_bool(b == a, s1), expression, dictionary)
of "branch":
return branch(stack, expression, dictionary)
@ -562,8 +550,7 @@ proc joy_eval(sym: string, stack: JoyListType, expression: JoyListType,
return (stack, push_quote_list(def.get(), expression), dictionary)
proc joy(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (
JoyListType, JoyMapType) =
proc joy(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (JoyListType, JoyMapType) =
var s = stack
var d = dictionary
var e = push_quote_list(expression, empty_list.listVal)
@ -581,7 +568,6 @@ proc joy(stack: JoyListType, expression: JoyListType, dictionary: JoyMapType): (
return (s, d)
proc add_def(def: string, dictionary: var JoyMapType) =
let d = text_to_expression(def)
let sym = d.head
@ -592,26 +578,17 @@ proc add_def(def: string, dictionary: var JoyMapType) =
raise newException(ValueError, def)
proc defs_file2dict(defs_filename: string = "defs.txt"): JoyMapType =
var strm = newFileStream(defs_filename, fmRead)
var dictionary = newMap[string, JoyListType]()
var line = ""
if not isNil(strm):
while strm.readLine(line):
if line.isEmptyOrWhitespace:
continue
add_def(line, dictionary)
strm.close()
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)
#proc defs_file2dict(defs_filename: string = "defs.txt"): JoyMapType =
# var strm = newFileStream(defs_filename, fmRead)
# var dictionary = newMap[string, JoyListType]()
# var line = ""
# if not isNil(strm):
# while strm.readLine(line):
# if line.isEmptyOrWhitespace:
# continue
# add_def(line, dictionary)
# strm.close()
# return dictionary
#let dictionary = defs_file2dict()
@ -637,40 +614,3 @@ while true:
except:
echo getCurrentExceptionMsg()
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("""[] [[]]""")