diff --git a/README.md b/README.md index 5a4ab60..ac7b458 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ for the whole project when speaking aloud. In writing, the full $ go get github.com/glycerine/zygomys/cmd/zygo ~~~ -### not your average parentheses... features in zygomys 4.3.5 include +### not your average parentheses... features in zygomys 4.3.6 include * [x] struct defintion and type checking. [See `tests/declare.zy` for examples.](https://github.com/glycerine/zygomys/blob/master/tests/declare.zy) * [x] dot-symbols such as `.plane` or `.plane.wing` give OO-flavor and compact, expressive notation. [See the wiki](https://github.com/glycerine/zygomys/wiki#differences-from-traditional-lisp-syntax) for discussion diff --git a/emacs/zygo.el b/emacs/zygo.el index 40bc06e..c8fc7aa 100644 --- a/emacs/zygo.el +++ b/emacs/zygo.el @@ -61,11 +61,11 @@ (setq *inferior-zygo-regex-prompt* "[^\(zygo>\)\|\(\.\.\.\)]*[> ]") -(defvar *zygo-keypress-to-sendline* (kbd "C-u") +(defvar *zygo-keypress-to-sendline* (kbd "C-n") "keypress that, when in a pure mode script, sends a line to the interpreter and then steps to the next line.") -(defvar *zygo-keypress-to-send-sexp-jdev* (kbd "C-n") +(defvar *zygo-keypress-to-send-sexp-jdev* (kbd "C-u") "keypress that sends the next sexp to the repl, and advances past it.") (defvar *zygo-keypress-to-send-sexp-jdev-prev* (kbd "C-p") diff --git a/repl/gitcommit.go b/repl/gitcommit.go index b718c5a..b51bef6 100644 --- a/repl/gitcommit.go +++ b/repl/gitcommit.go @@ -1,2 +1,2 @@ package zygo -func init() { GITLASTTAG = "v4.3.5"; GITLASTCOMMIT = "9f55b209465717cf85b78961dde47a020994d896" } +func init() { GITLASTTAG = "v4.3.6"; GITLASTCOMMIT = "9689ebf1fd897697d560058bdd185fda5b57e02e" } diff --git a/repl/hashutils.go b/repl/hashutils.go index 31027d8..e6a1ca0 100644 --- a/repl/hashutils.go +++ b/repl/hashutils.go @@ -194,8 +194,8 @@ func (hash *SexpHash) HashGet(env *Glisp, key Sexp) (Sexp, error) { } if val == SexpEnd { - msg := fmt.Sprintf("HashGet: key %s not found", key.SexpString(0)) - return SexpNull, errors.New(msg) + return SexpNull, fmt.Errorf("%s has no field '%s'", hash.TypeName, key.SexpString(0)) + //return SexpNull, fmt.Errorf("%s has no field '%s'", hash.UserStructDefn.Name, key.SexpString(0)) } return val, nil } diff --git a/repl/vm.go b/repl/vm.go index 902d319..01aa4e7 100644 --- a/repl/vm.go +++ b/repl/vm.go @@ -227,18 +227,21 @@ func (c CallInstr) Execute(env *Glisp) error { if err != nil { return err } - //Q("\n in CallInstr, after looking up c.sym='%s', got funcobj='%v'. datastack is:\n", c.sym.name, funcobj.SexpString(0)) + Q("\n in CallInstr, after looking up c.sym='%s', got funcobj='%v'. datastack is:\n", c.sym.name, funcobj.SexpString(0)) //env.datastack.PrintStack() switch f := funcobj.(type) { case *SexpSymbol: // is it a dot-symbol call? - //Q("\n in CallInstr, found symbol\n") + Q("\n in CallInstr, found symbol\n") if c.sym.isDot { + Q("\n in CallInstr, found symbol, c.sym.isDot is true\n") dotSymRef, dotLookupErr := dotGetSetHelper(env, c.sym.name, nil) + // cannot error out yet, we might be assigning to a new field, + // not already set. // are we a value request (no further args), or a fuction/method call? - //Q("\n in CallInstr, found dot-symbol\n") + Q("\n in CallInstr, found dot-symbol\n") if c.nargs == 0 { // value request if dotLookupErr != nil { @@ -256,43 +259,43 @@ func (c CallInstr) Execute(env *Glisp) error { return err } - top := expressions[0] - switch ftop := top.(type) { + // does our dot-symbol itself refer to a function? + Q("in CallInstr, found dot-symbol, dot-symbol itself is of type %T", dotSymRef) + switch fn := dotSymRef.(type) { case *SexpFunction: - //Q("\n in CallInstr, fetched out function call from top of datastack.\n") - indirectFuncName = ftop - if ftop.user { - //Q("\n in CallInstr, with user func, passing dot-symbol in directly so assignment will work.\n") - env.datastack.PushExpr(c.sym) - } else { - //Q("\n in CallInstr, with sexp func, dereferencing dot-symbol '%s' -> '%s'\n", c.sym.name, dotSymRef.SexpString(0)) - if dotLookupErr != nil { - return dotLookupErr - } - env.datastack.PushExpr(dotSymRef) - } - pushme := expressions[1:] - for j := range pushme { - env.datastack.PushExpr(pushme[j]) - } - //Q("\n in CallInstr, after setting up stack for dot-symbol call, datastack:\n") - //env.datastack.PrintStack() - + c.setupDotCallHelper(env, fn, &indirectFuncName, expressions, 0, dotSymRef) default: - return fmt.Errorf("dot-symbol '%s' was followed by non-function '%s'.", - c.sym.name, ftop.SexpString(0)) + top := expressions[0] + Q("in CallInstr, found dot-symbol, first arg to dot-symbol is of type %T", top) + switch ftop := top.(type) { + case *SexpFunction: + c.setupDotCallHelper(env, ftop, &indirectFuncName, expressions, 1, dotSymRef) + default: + return fmt.Errorf("dot-symbol '%s' was followed by non-function '%s'.", + c.sym.name, ftop.SexpString(0)) + } } } } else { // not isDot - // allow symbols to refer to functions that we then call - indirectFuncName, err, _ = env.LexicalLookupSymbol(f, false) - + // allow symbols to refer to dot-symbols, that then we call + indirectFuncName, err = dotGetSetHelper(env, f.name, nil) if err != nil { return fmt.Errorf("'%s' refers to symbol '%s', but '%s' could not be resolved: '%s'.", c.sym.name, f.name, f.name, err) } + + // allow symbols to refer to functions that we then call + /* + indirectFuncName, err, _ = env.LexicalLookupSymbol(f, false) + if err != nil { + return fmt.Errorf("'%s' refers to symbol '%s', but '%s' could not be resolved: '%s'.", + c.sym.name, f.name, f.name, err) + } + */ + Q("\n in CallInstr, found symbol, c.sym.isDot is false. f of type %T/val = %v. indirectFuncName = '%v'\n", f, f.SexpString(0), indirectFuncName.SexpString(0)) + } switch g := indirectFuncName.(type) { @@ -804,3 +807,28 @@ func (a AssignInstr) Execute(env *Glisp) error { } return fmt.Errorf("AssignInstr: don't know how to assign to %T", lhs) } + +func (c *CallInstr) setupDotCallHelper( + env *Glisp, + ftop *SexpFunction, + indirectFuncName *Sexp, + expressions []Sexp, + xprBegin int, + dotSymRef Sexp) { + + Q("\n in CallInstr, fetched out function call from top of datastack.\n") + *indirectFuncName = ftop + if ftop.user { + Q("\n in CallInstr, with user func, passing dot-symbol in directly so assignment will work.\n") + env.datastack.PushExpr(c.sym) + } else { + Q("\n in CallInstr, with sexp func, dereferencing dot-symbol '%s' -> '%s'\n", c.sym.name, dotSymRef.SexpString(0)) + env.datastack.PushExpr(dotSymRef) + } + pushme := expressions[xprBegin:] + for j := range pushme { + env.datastack.PushExpr(pushme[j]) + } + Q("\n in CallInstr, after setting up stack for dot-symbol call, datastack:\n") + //env.datastack.PrintStack() +} diff --git a/tests/dotcall.zy b/tests/dotcall.zy index 0cc7de5..b024b17 100644 --- a/tests/dotcall.zy +++ b/tests/dotcall.zy @@ -16,3 +16,15 @@ (assert (== (g greet) expected)) (assert (== (.greet g) expected)) (assert (== (.greet) "hello")) + +// automatic * dereference of a.f dot-symbol should +// happen for call-position +(def h2 (hash g:3 f:(fn [x] {x*x}))) +(assert (== (h2.f 4) 16)) + +(def g h2.f) +(assert (== (g 3) 9)) + +// perhaps more properly, not depending on the auto-deference mechanism: +(def ff (* h2.f)) +(assert (== (ff 3) 9))