From bc375214af23e7e331bbf32fb84a17d6740a30a6 Mon Sep 17 00:00:00 2001 From: sloum Date: Tue, 9 May 2023 09:25:56 -0700 Subject: [PATCH] Starts splitting out special forms into functions --- interpreter.go | 276 +++----------------------------------------- lib.go | 4 +- slope.1.gz | Bin 0 -> 1814 bytes specialForms.go | 297 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 315 insertions(+), 262 deletions(-) create mode 100644 slope.1.gz create mode 100644 specialForms.go diff --git a/interpreter.go b/interpreter.go index 23222f1..c808e96 100644 --- a/interpreter.go +++ b/interpreter.go @@ -4,7 +4,6 @@ import ( "fmt" "os" "path/filepath" - "reflect" "regexp" "sort" "strings" @@ -62,278 +61,31 @@ func eval(exp expression, en *env) (value expression) { case []expression: switch car, _ := e[0].(symbol); car { case "quote": - if len(e) < 2 { - value = exception("Invalid 'quote' syntax - too few arguments") - break - } - value = stringUnescapeEval(e[1]) + value = specialQuote(e) case "if": - if len(e) < 3 { - value = exception("Invalid 'if' syntax - too few arguments") - break - } - if AnythingToBool(eval(e[1], en)).(bool) { - value = eval(e[2], en) - } else if len(e) > 3 { - value = eval(e[3], en) - } else { - value = make([]expression, 0) - } + value = specialIf(e, en) case "and": - if len(e) < 2 { - value = exception("Invalid 'and' syntax - too few arguments") - break - } - OuterAnd: - for i := range e[1:] { - switch item := e[i+1].(type) { - case []expression: - value = eval(item, en) - if !AnythingToBool(value).(bool) { - break OuterAnd - } - default: - value = eval(item, en) - if !AnythingToBool(value).(bool) { - break OuterAnd - } - } - } + value = specialAnd(e, en) case "or": - if len(e) < 2 { - value = exception("Invalid 'or' syntax - too few arguments") - break - } - OuterOr: - for i := range e[1:] { - switch item := e[i+1].(type) { - case []expression: - value = eval(item, en) - if AnythingToBool(value).(bool) { - break OuterOr - } - default: - value = eval(item, en) - if AnythingToBool(value).(bool) { - break OuterOr - } - } - } + value = specialOr(e, en) case "case": - if len(e) < 3 { - value = exception("Invalid 'case' syntax - too few arguments") - } - target := eval(e[1], en) - CaseLoop: - for _, exp := range e[2:] { - switch i := exp.(type) { - case []expression: - if len(i) < 2 { - value = exception("Invalid 'case' case, cases must take the form: `( )") - break CaseLoop - } - if i[0] == "else" || i[0] == symbol("else") { - value = eval(i[1], en) - break CaseLoop - } - if reflect.DeepEqual(eval(i[0], en), target) { - value = eval(i[1], en) - break CaseLoop - } - default: - value = exception("Invalid 'case' case, cases must take the form: `( )") - break CaseLoop - } - } - if value == nil { - value = make([]expression, 0) - } - + value = specialCase(e, en) case "cond": - if len(e) < 2 { - value = exception("Invalid 'cond' syntax - too few arguments") - break - } - CondLoop: - for _, exp := range e[1:] { - switch i := exp.(type) { - case []expression: - if len(i) < 2 { - value = exception("Invalid 'cond' case, cases must take the form: `( )") - break CondLoop - } - if i[0] == "else" || i[0] == symbol("else") { - value = eval(i[1], en) - break CondLoop - } - if AnythingToBool(eval(i[0], en)).(bool) { - value = eval(i[1], en) - break CondLoop - } - default: - value = exception("Invalid 'cond' case, cases must take the form: `( )") - break CondLoop - } - } - if value == nil { - value = make([]expression, 0) - } + value = specialCond(e, en) case "for": - if len(e) < 3 { - value = exception("'for' requires at least two arguments: a list of initializers and a test") - break - } - // TODO follow the lisp do style: https://www.tutorialspoint.com/lisp/lisp_do.htm - - // Set up args/iterators - args, ok := e[1].([]expression) - if !ok { - value = exception("'for' expected a list of arguments and values as its first argument, a non-list value was received") - break - } - newEnv := &env{make(map[symbol]expression), en} - updates := []expression{symbol("begin")} - for _, v := range args { - arg, ok := v.([]expression) - if varname, varok := arg[0].(symbol); varok && ok && len(arg) >= 2 { - newEnv.vars[varname] = eval(arg[1], en) - } else { - value = exception("'for' expected its first argument to be a list of lists, each with a symbol, a value, and an optional update expression. A list was given, but it contained a non-list value") - break - } - if len(arg) >= 3 { - updates = append(updates, []expression{symbol("set!"), arg[0], arg[2]}) - } - } - - // Get test and return expression - testReturn, ok := e[2].([]expression) - if !ok || len(testReturn) == 0 { - value = exception("'for' expected a list containing a logic test and an optional return value expression for its second argument, a non-list was given") - break - } - test := testReturn[0] - var returnExpression expression = []expression{symbol("list")} - if len(testReturn) > 1 { - returnExpression = testReturn[1] - } - - body := []expression{symbol("begin")} - body = append(body, e[3:]...) - - for { - // Check the condition by evaluating the test - if !AnythingToBool(eval(test, newEnv)).(bool) { - break - } - - // Run the body code - eval(body, newEnv) - - // Run the iteration updates - eval(updates, newEnv) - } - - value = eval(returnExpression, newEnv) + value = specialFor(e, en) case "set!": - if len(e) < 3 { - value = exception("Invalid 'set!' syntax - too few arguments") - break - } - v, ok := e[1].(symbol) - if !ok { - e[1] = eval(e[1], en) - v, ok = e[1].(symbol) - if !ok { - value = exception("'set!' expected a symbol as its first argument, a non-symbol was provided") - break - } - } - if strings.Contains(string(v), "::") { - value = exception("'set!' cannot modify a symbol reference within a module, designated by '::' within a symbol name") - break - } - val := eval(e[2], en) - ex, err := en.Find(v) - if err != nil { - value = exception(err.Error()) - } else { - ex.vars[v] = val - value = val - } + value = specialSet(e, en) case "define": - if len(e) < 3 { - value = exception("Invalid 'define' syntax - too few arguments") - break - } - sym, ok := e[1].(symbol) - if !ok { - e[1] = eval(e[1], en) - sym, ok = e[1].(symbol) - if !ok { - value = exception("'define' expects a symbol as its first argument") - break - } - } - if strings.Contains(string(sym), "::") { - value = exception("'define' cannot create a symbol reference within a module, designated by '::' within a symbol name") - break - } - val := eval(e[2], en) - en.vars[sym] = val - value = val + value = specialDefine(e, en) case "lambda", "λ": - if len(e) < 3 { - value = exception("'lambda' expects at least three arguments") - break - } - b := []expression{symbol("begin")} - b = append(b, e[2:]...) - predicates := make(map[symbol]symbol) - switch a := e[1].(type) { - case []expression: - for i, v := range a { - s := String(v, false) - ind := strings.LastIndex(s, "@") - if ind < 1 { - continue - } - a[i] = symbol(s[:ind]) - predicates[a[i].(symbol)] = symbol(s[ind+1:]) - } - e[1] = a - default: - s := String(e[1], false) - ind := strings.LastIndex(s, "@") - if ind < 1 { - e[1] = []expression{e[1]} - break - } - e[1] = symbol(s[:ind]) - predicates[e[1].(symbol)] = symbol(s[ind+1:]) - e[1] = []expression{e[1]} - } - value = proc{e[1], stringUnescapeEval(b), en, predicates} + value = specialLambda(e, en) case "macro": - if len(e) < 3 { - value = exception("'macro' expects at least three arguments") - break - } - b := []expression{symbol("begin")} - b = append(b, e[2:]...) - value = macro{e[1], b, en} + value = specialMacro(e, en) case "begin": - for _, i := range e[1:] { - value = eval(i, en) - } + value = specialBegin(e, en) case "begin0": - for ii, i := range e[1:] { - if ii == 0 { - value = eval(i, en) - } else { - eval(i, en) - } - } + value = specialBegin0(e, en) case "usage": var procSigRE = regexp.MustCompile(`(?s)(\()([^() ]+\b)([^)]*)(\))(?:(\s*=>)([^\n]*))?(.*)`) var replacer = "\033[40;33;1m$1\033[95m$2\033[92m$3\033[33m$4\033[94m$5\033[36m$6\033[0m$7" @@ -734,6 +486,8 @@ func apply(procedure expression, args []expression, name expression) (value expr } value = eval(p.body, en) case proc: // Mostly used by lambda + // FIXME the parsing or the application of lists is donked in certain contexts... + // not sure what is happening: (apply display-lines [1 2 [3 4]]) does not work en := &env{make(vars), p.en} switch params := p.params.(type) { case []expression: diff --git a/lib.go b/lib.go index 05b5070..e4a00cd 100644 --- a/lib.go +++ b/lib.go @@ -1255,7 +1255,9 @@ var stdLibrary = vars{ } }, - "list": eval(Parse("(lambda ... ...)").([]expression)[0], &globalenv), + "list": func(a ...expression) expression { + return a + }, "list-ref": func(a ...expression) expression { // TODO remove this proc entirely someday? return globalenv.vars["ref"].(func(...expression) expression)(a...) diff --git a/slope.1.gz b/slope.1.gz new file mode 100644 index 0000000000000000000000000000000000000000..51cfe2f306d6814b2a0066597761c8d5c5d713d2 GIT binary patch literal 1814 zcmV+x2kH19iwFqV;+SFp19NO|aAhtr0JT?5ZyQGvz3W%>3c;=eki*1B?}@f(iCD=b zL^{aAHiYI(57|gh_qabuaS!|3_f_=_IkdIA$ss@-iR$Y5c=hU4vsmAW>tI}~uEo#d z`t?s@`ter0{^|A4*Ww!g-72fRG-A=>N7T-W2WgWu*H^{rPJEa?%&tDyZ`r};dMU--1|rxAn3Sv%NG)M(6p$>YpDL%hh~!RoMEfsB!-8;{K=4_1v`-C*b23{v1lL z+t{BJ(m+SfB!-Y#7gcPBX#*D-!MUjH46Mhe4_1to7qw+As|3&S*h&v@#-C4u!gTizE}#d>+QhR3cQ08A;| zGVU-EtqgQGI*vI+NP5+$SRok$bNsS74q)q2KEZ{^R^%p&S{uMgVX4pG#OZ7(gxpJQ zZ}jM05Ibe6-v3^D&6Y%^V;FY?8V!7G96CYmz!iB41GM&9vX3(&^D|tc zAyA24CE7%J9(Jh2I)nN77<^B`mmpKuiM{fy z6WUTi@_*d)$%;=M#lMi3$7I8R=-tt2hNu%%!Pn$yWHN(CXXh}aG)R)9g@~lsLc}CB z#j7%&hwH*UcOHZvk2?v>rxeN3XeI6sh}oq~R!l(v1qivZXRdWUo-N*AlzN{*QY22C zg0g$iztFwxX0f zIU=wk&E6@K??ii0?{=&lvo!;qkmvP}%5RIZNe-jZx zpfXH6t+rC$9H}l(fHg|I+B(_-Ka$HiP|D(RPc$wXl2|nH|O17DIkhXz^Qc}QF1I>) zTqclPg&x~d!wf9W;eufNv1^&~A#J+97veom&|T9fwnGVqDr$3hqBG+OfbqIQiEV3i zndOC-LW?}ILNe#YU-5!kt!EG7m+9l`;+ZpC^bEj=`Ufq?AdtTiqVID!o(H@Slnpu1 zynve=35*$f#pJoI)&HUmb8y`2Kq}HGu-R^jy1exA9%BCS%!N0n_ruWLfHM3N!6T$n zHFTO)ET|ZJ`VY7E4|r29XHO4{o2TV;eMgCsym9|eb~+;1HO~FU1KM+i(V_x* z4yweL=B#rt>BJfVjjD1tZjIYe{A0jU9UrjoPQI|rXlOR4DavCDf0GpaBqwh4=7qEe zS})B(9s>yxgpFPtW4_q;3UlhL%EK7`P9F(oR@;G-prkLe-fubKQ<6KSJ)Iqk7a7rs zyf}&cALqsLady9$-aOsRAOA#Zbcy~SYMiZpKQ*#kA#}ef&m^G@PFq&gMs5c$N>taa zn2hRhvk8P{AjzDbEg%lP#q2PJ{KoDr`}ZoF|KjxJs8wP)#x4T!ZDY^vsIZjZhXGxF9H!n>6q$nmY-W0D=;I{<#k=w}nQs*{( zFCNTctRU6BJ>P!3T@f$oYX$yckwUufNG$7Q|H3Bs^AG>Hl-mYRXnID|(=BhAQk}np z(bpdS66vy-cXEhbyvj2Dj%4~#wJvBX<7yXU8{SMNTfA$%JkYndqBN-+-7!5P@~^$0 zJJ(=oY}K37RX#3CXPmD!zW)@V#q)&IpMG55Exy?o^sGm(=wlu-<5F|=A9_L4+Yb-` E06$`kIRF3v literal 0 HcmV?d00001 diff --git a/specialForms.go b/specialForms.go new file mode 100644 index 0000000..4f6fbeb --- /dev/null +++ b/specialForms.go @@ -0,0 +1,297 @@ +package main + +import ( + "reflect" + "strings" +) + +func specialAnd(e []expression, en *env) expression { + if len(e) < 2 { + return exception("Invalid 'and' syntax - too few arguments") + } + var value expression + OuterAnd: + for i := range e[1:] { + switch item := e[i+1].(type) { + case []expression: + value = eval(item, en) + if !AnythingToBool(value).(bool) { + break OuterAnd + } + default: + value = eval(item, en) + if !AnythingToBool(value).(bool) { + break OuterAnd + } + } + } + return value +} + +func specialBegin0(e []expression, en *env) expression { + var v expression + for ii, i := range e[1:] { + if ii == 0 { + v = eval(i, en) + } else { + eval(i, en) + } + } + return v +} + +func specialBegin(e []expression, en *env) expression { + var v expression + for _, i := range e[1:] { + v = eval(i, en) + } + return v +} + +func specialCase(e []expression, en *env) expression { + if len(e) < 3 { + return exception("Invalid 'case' syntax - too few arguments") + } + target := eval(e[1], en) + var value expression + CaseLoop: + for _, exp := range e[2:] { + switch i := exp.(type) { + case []expression: + if len(i) < 2 { + return exception("Invalid 'case' case, cases must take the form: `( )") + } + if i[0] == "else" || i[0] == symbol("else") { + value = eval(i[1], en) + break CaseLoop + } + if reflect.DeepEqual(eval(i[0], en), target) { + value = eval(i[1], en) + break CaseLoop + } + default: + return exception("Invalid 'case' case, cases must take the form: `( )") + } + } + // TODO this may not work + if value == nil { + value = make([]expression, 0) + } + return value +} + +func specialCond(e []expression, en *env) expression { + if len(e) < 2 { + return exception("Invalid 'cond' syntax - too few arguments") + } + + var value expression + CondLoop: + for _, exp := range e[1:] { + switch i := exp.(type) { + case []expression: + if len(i) < 2 { + return exception("Invalid 'cond' case, cases must take the form: `( )") + } + if i[0] == "else" || i[0] == symbol("else") { + value = eval(i[1], en) + break CondLoop + } + if AnythingToBool(eval(i[0], en)).(bool) { + value = eval(i[1], en) + break CondLoop + } + default: + return exception("Invalid 'cond' case, cases must take the form: `( )") + } + } + if value == nil { + value = make([]expression, 0) + } + return value +} + +func specialDefine(e []expression, en *env) expression { + if len(e) < 3 { + return exception("Invalid 'define' syntax - too few arguments") + } + sym, ok := e[1].(symbol) + if !ok { + e[1] = eval(e[1], en) + sym, ok = e[1].(symbol) + if !ok { + return exception("'define' expects a symbol as its first argument") + } + } + if strings.Contains(string(sym), "::") { + return exception("'define' cannot create a symbol reference within a module, designated by '::' within a symbol name") + } + val := eval(e[2], en) + en.vars[sym] = val + return val +} + +func specialFor(e []expression, en *env) expression { + if len(e) < 3 { + return exception("'for' requires at least two arguments: a list of initializers and a test") + } + + // Set up args/iterators + args, ok := e[1].([]expression) + if !ok { + return exception("'for' expected a list of arguments and values as its first argument, a non-list value was received") + } + newEnv := &env{make(map[symbol]expression), en} + updates := []expression{symbol("begin")} + for _, v := range args { + arg, ok := v.([]expression) + if varname, varok := arg[0].(symbol); varok && ok && len(arg) >= 2 { + newEnv.vars[varname] = eval(arg[1], en) + } else { + return exception("'for' expected its first argument to be a list of lists, each with a symbol, a value, and an optional update expression. A list was given, but it contained a non-list value") + } + if len(arg) >= 3 { + updates = append(updates, []expression{symbol("set!"), arg[0], arg[2]}) + } + } + + // Get test and return expression + testReturn, ok := e[2].([]expression) + if !ok || len(testReturn) == 0 { + return exception("'for' expected a list containing a logic test and an optional return value expression for its second argument, a non-list was given") + } + test := testReturn[0] + var returnExpression expression = []expression{symbol("list")} + if len(testReturn) > 1 { + returnExpression = testReturn[1] + } + + body := []expression{symbol("begin")} + body = append(body, e[3:]...) + + for { + // Check the condition by evaluating the test + if !AnythingToBool(eval(test, newEnv)).(bool) { + break + } + + // Run the body code + eval(body, newEnv) + + // Run the iteration updates + eval(updates, newEnv) + } + + return eval(returnExpression, newEnv) +} + +func specialIf(e []expression, en *env) expression { + if len(e) < 3 { + return exception("Invalid 'if' syntax - too few arguments") + } + if AnythingToBool(eval(e[1], en)).(bool) { + return eval(e[2], en) + } + + if len(e) > 3 { + return eval(e[3], en) + } + return make([]expression, 0) +} + +func specialLambda(e []expression, en *env) expression { + if len(e) < 3 { + return exception("'lambda' expects at least three arguments") + } + b := []expression{symbol("begin")} + b = append(b, e[2:]...) + predicates := make(map[symbol]symbol) + switch a := e[1].(type) { + case []expression: + for i, v := range a { + s := String(v, false) + ind := strings.LastIndex(s, "@") + if ind < 1 { + continue + } + a[i] = symbol(s[:ind]) + predicates[a[i].(symbol)] = symbol(s[ind+1:]) + } + e[1] = a + default: + s := String(e[1], false) + ind := strings.LastIndex(s, "@") + if ind < 1 { + e[1] = []expression{e[1]} + break + } + e[1] = symbol(s[:ind]) + predicates[e[1].(symbol)] = symbol(s[ind+1:]) + e[1] = []expression{e[1]} + } + return proc{e[1], stringUnescapeEval(b), en, predicates} +} + +func specialMacro(e []expression, en *env) expression { + if len(e) < 3 { + return exception("'macro' expects at least three arguments") + } + b := []expression{symbol("begin")} + b = append(b, e[2:]...) + return macro{e[1], b, en} +} + +func specialOr(e []expression, en *env) expression { + if len(e) < 2 { + return exception("Invalid 'or' syntax - too few arguments") + } + var value expression + OuterOr: + for i := range e[1:] { + switch item := e[i+1].(type) { + case []expression: + value = eval(item, en) + if AnythingToBool(value).(bool) { + break OuterOr + } + default: + value = eval(item, en) + if AnythingToBool(value).(bool) { + break OuterOr + } + } + } + return value +} + +func specialQuote(e []expression) expression { + if len(e) < 2 { + return exception("Invalid 'quote' syntax - too few arguments") + } + return stringUnescapeEval(e[1]) +} + +func specialSet(e []expression, en *env) expression { + if len(e) < 3 { + return exception("Invalid 'set!' syntax - too few arguments") + } + v, ok := e[1].(symbol) + if !ok { + e[1] = eval(e[1], en) + v, ok = e[1].(symbol) + if !ok { + return exception("'set!' expected a symbol as its first argument, a non-symbol was provided") + } + } + if strings.Contains(string(v), "::") { + return exception("'set!' cannot modify a symbol reference within a module, designated by '::' within a symbol name") + } + val := eval(e[2], en) + ex, err := en.Find(v) + if err != nil { + return exception(err.Error()) + } + ex.vars[v] = val + return val +} +