~technomancy/fennel

Make sym? take an optional second name check argument. v1 SUPERSEDED

Phil Hagelberg: 1
 Make sym? take an optional second name check argument.

 5 files changed, 20 insertions(+), 11 deletions(-)
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~technomancy/fennel/patches/20284/mbox | git am -3
Learn more about email & git
View this thread in the archives

[PATCH] Make sym? take an optional second name check argument. Export this patch

Since = does not work on symbols, expand the sym? function to also
check whether the name of the symbol matches a given string if one is
provided.
---
 reference.md            | 10 +++++++++-
 src/fennel/compiler.fnl |  7 +++----
 src/fennel/macros.fnl   |  4 ++--
 src/fennel/specials.fnl |  2 +-
 src/fennel/utils.fnl    |  8 +++++---
 5 files changed, 20 insertions(+), 11 deletions(-)

diff --git a/reference.md b/reference.md
index ac96573..450b109 100644
--- a/reference.md
+++ b/reference.md
@@ -1095,7 +1095,8 @@ can use `tostring` to get the name of a symbol.
* `list` - return a list, which is a special kind of table used for code
* `sym` - turn a string into a symbol
* `list?` - is the argument a list?
* `sym?` - is the argument a symbol?
* `sym?` - is the argument a symbol? if a second (string) argument is given,
  check that the name of the symbol matches it.
* `table?` - is the argument a non-list table?
* `sequence?` - is the argument a non-list _sequential_ table (created
  with `[]`, as opposed to `{}`)?
@@ -1114,6 +1115,13 @@ These functions can be used from within macros only, not from any
* `in-scope?` - does this symbol refer to an in-scope local?
* `macroexpand` - performs macroexpansion on its argument form; returns an AST

Note that Lua's equality works in terms of the identity of a table rather
than its contents; this means that since lists and symbols are implemented
with tables that `=` will not work as you might expect on them. For this
reason, the `sym?` function can be used to check the name of a symbol by
passing a second argument. Comparing two lists is easiest to do by running
`view` on each and comparing the results.

Note that other internals of the compiler exposed in compiler scope are
subject to change.

diff --git a/src/fennel/compiler.fnl b/src/fennel/compiler.fnl
index cffdd32..19f523d 100644
--- a/src/fennel/compiler.fnl
+++ b/src/fennel/compiler.fnl
@@ -669,7 +669,7 @@ which we have to do if we don't know."
        (each [k v (utils.stablepairs left)]
          (when (not (and (= :number (type k))
                          (: (tostring (. left (- k 1))) :find "^&")))
            (if (and (utils.sym? v) (= (utils.deref v) "&"))
            (if (utils.sym? v :&)
                (let [unpack-str "{(table.unpack or unpack)(%s, %s)}"
                      formatted (string.format unpack-str s k)
                      subexpr (utils.expr formatted :expression)]
@@ -678,7 +678,7 @@ which we have to do if we don't know."
                                  "expected rest argument before last parameter"
                                  left)
                  (destructure1 (. left (+ k 1)) [subexpr] left))
                (and (utils.sym? k) (= (utils.deref k) "&as"))
                (utils.sym? k :&as)
                (destructure-sym v [(utils.expr (tostring s))] left)
                (and (utils.sequence? left) (= (utils.deref v) "&as"))
                (let [(_ next-sym trailing) (select k (unpack left))]
@@ -872,8 +872,7 @@ compiler by default; these can be re-enabled with export FENNEL_DEBUG=trace."
            (string.format "sym('%s', nil, {quoted=true, filename=%s, line=%s})"
                           symstr filename (or form.line :nil))))
      (and (utils.list? form) ; unquote
           (utils.sym? (. form 1))
           (= (utils.deref (. form 1)) :unquote))
           (utils.sym? (. form 1) :unquote))
      (let [payload (. form 2)
            res (unpack (compile1 payload scope parent))]
        (. res 1))
diff --git a/src/fennel/macros.fnl b/src/fennel/macros.fnl
index 0185e44..8f14840 100644
--- a/src/fennel/macros.fnl
+++ b/src/fennel/macros.fnl
@@ -262,7 +262,7 @@ Example:
  (let [condition `(and (= (type ,val) :table))
        bindings []]
    (each [k pat (pairs pattern)]
      (if (and (sym? pat) (= "&" (tostring pat)))
      (if (sym? pat :?)
          (do (assert (not (. pattern (+ k 2)))
                      "expected rest argument before last parameter")
              (table.insert bindings (. pattern (+ k 1)))
@@ -306,7 +306,7 @@ introduce for the duration of the body if it does match."
                      true `(not= ,(sym :nil) ,val))
                  [pattern val]))
        ;; guard clause
        (and (list? pattern) (sym? (. pattern 2)) (= :? (tostring (. pattern 2))))
        (and (list? pattern) (sym? (. pattern 2) :?))
        (let [(pcondition bindings) (match-pattern vals (. pattern 1)
                                                   unifications)
              condition `(and ,pcondition)]
diff --git a/src/fennel/specials.fnl b/src/fennel/specials.fnl
index 686d3aa..cf9c045 100644
--- a/src/fennel/specials.fnl
+++ b/src/fennel/specials.fnl
@@ -683,7 +683,7 @@ Method name doesn't have to be known at compile-time; if it is, use
                                           [] f-scope ast)))
    ;; recursively walk the AST, transforming $... into ...
    (fn walker [idx node parent-node]
      (if (and (utils.sym? node) (= (utils.deref node) "$..."))
      (if (utils.sym? node :$...)
          (do
            (tset parent-node idx (utils.varg))
            (set f-scope.vararg true))
diff --git a/src/fennel/utils.fnl b/src/fennel/utils.fnl
index e50bb52..0098afe 100644
--- a/src/fennel/utils.fnl
+++ b/src/fennel/utils.fnl
@@ -179,9 +179,11 @@ except when certain macros need to look for binding forms, etc specifically."
  "Checks if an object is a list. Returns the object if is."
  (and (= (type x) "table") (= (getmetatable x) list-mt) x))

(fn sym? [x]
  "Checks if an object is a symbol. Returns the object if it is."
  (and (= (type x) "table") (= (getmetatable x) symbol-mt) x))
(fn sym? [x ?name]
  "Checks if an object is a symbol. Returns the object if it is.
If a second argument is given, check that the name of the symbol matches it."
  (and (= (type x) "table") (= (getmetatable x) symbol-mt)
       (or (= nil ?name) (= (deref x) ?name)) x))

(fn table? [x]
  "Checks if an object any kind of table, EXCEPT list or symbol or vararg."
-- 
2.20.1