diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e847e8..9b2f2df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## v0.3.0 (2020-02-22) + +* Add support for macros through `macro*` special form. +* Use Macro support to add `defn` and `defmacro` macros. +* Rewrite `core.lisp` with `defn` macro. + +## v0.2.3 (2020-02-22) + +* Add support for custom special forms through `SpecialForm` type. +* Update package documentation. +* Remove all type specific functions in favour generic slang core. + ## v0.2.2 (2020-02-21) * Add type init through Type.Invoke() method diff --git a/examples/core.lisp b/examples/core.lisp index ff5cfa7..1ef8082 100644 --- a/examples/core.lisp +++ b/examples/core.lisp @@ -1,57 +1,57 @@ (ns 'core) -(def nil? (fn* nil? [arg] - (= nil arg))) +(def fn + (macro* fn + ([& decl] (cons 'fn* decl)))) -(def empty? (fn* empty? [coll] +(def defn (macro* defn [name & fdecl] + (let* [func (cons 'fn (cons name fdecl))] + `(def ~name ~func)))) + +(def defmacro (macro* defmacro [name & mdecl] + (let* [macro (cons 'macro* (cons name mdecl))] + `(def ~name ~macro)))) + +(defn nil? [arg] (= nil arg)) + +(defn empty? [coll] (if (nil? coll) true - (nil? (first coll))))) + (nil? (first coll)))) + +; Type check functions +(defn same-type? [a b] (= (type a) (type b))) +(defn seq? [arg] (impl? arg types/Seq)) +(defn set? [arg] (same-type? #{} arg)) +(defn list? [arg] (same-type? () arg)) +(defn vector? [arg] (same-type? [] arg)) +(defn int? [arg] (same-type? 0 arg)) +(defn float? [arg] (same-type? 0.0 arg)) +(defn boolean? [arg] (same-type? true arg)) +(defn string? [arg] (same-type? "" arg)) +(defn keyword? [arg] (same-type? :specimen arg)) +(defn symbol? [arg] (same-type? 'specimen arg)) -(def true? (fn* true? [arg] +; Type initialization functions +(defn set [coll] (apply-seq (type #{}) coll)) +(defn list [& coll] (apply-seq (type ()) coll)) +(defn vector [& coll] (apply-seq (type []) coll)) +(defn int [arg] (to-type arg (type 0))) +(defn float [arg] (to-type arg (type 0.0))) +(defn boolean [arg] (true? arg)) + +(defn true? [arg] (if (nil? arg) false (if (boolean? arg) arg - true)))) - -(def not (fn* not [arg] - (= false (true? arg)))) + true))) -(def same-type? (fn* same-type? [a b] (= (type a) (type b)))) +(defn not [arg] (= false (true? arg))) -; Type check functions -(def seq? (fn* seq? [arg] (impl? arg types/Seq))) -(def set? (fn* set? [s] (same-type? #{} s))) -(def list? (fn* list? [s] (same-type? () s))) -(def vector? (fn* vector? [s] (same-type? [] s))) -(def int? (fn* int? [arg] (same-type? 0 arg))) -(def float? (fn* float? [arg] (same-type? 0.0 arg))) -(def boolean? (fn* boolean? [arg] (same-type? true arg))) -(def string? (fn* string? [arg] (same-type? "" arg))) -(def keyword? (fn* keyword? [arg] (same-type? :keyword arg))) -(def symbol? (fn* symbol? [arg] (same-type? 'sample arg))) - -; Type initialization functions -(def set (fn* set [s] (apply-seq (type #{}) s))) -(def list (fn* list [& args] (realize args))) -(def vector (fn* list [& args] (realize args))) -(def int (fn* int [arg] (to-type arg (type 0)))) -(def float (fn* float [arg] (to-type arg (type 0.0)))) -(def boolean (fn* boolean [arg] (true? arg))) - -(def defn (fn* [name args & body] - (do - (if (not (symbol? name)) - (throw "name must be symbol, not " (type name))) - (if (not (vector? args)) - (throw "args must be a vector, not " (type args))) - (let* [f (concat `(fn* ~name ~args) body)] - (eval `(def ~name ~f)))))) - -(def last (fn* last [coll] +(defn last [coll] (let* [v (first coll) - rem (next coll)] + rem (next coll)] (if (nil? rem) v - (last (next coll)))))) + (last (next coll))))) diff --git a/examples/slang_test.lisp b/examples/slang_test.lisp index d9bd619..283fd44 100644 --- a/examples/slang_test.lisp +++ b/examples/slang_test.lisp @@ -116,6 +116,6 @@ (def sum-through-let (let* [numbers [1 2 3 4 5]] (do (println "Numbers: " numbers) (let* [sum (apply-seq + numbers)] - (println "Sum of numbers: " numbers) + (println "Sum of numbers: " sum) sum)))) (assert (= (+ 1 2 3 4 5) sum-through-let)) diff --git a/func.go b/func.go index a14719a..42c8ee9 100644 --- a/func.go +++ b/func.go @@ -18,6 +18,20 @@ func (multiFn MultiFn) Eval(_ Scope) (Value, error) { return multiFn, nil } +// Expand executes the macro body and returns the result of the expansion. +func (multiFn MultiFn) Expand(scope Scope, args []Value) (Value, error) { + fn, err := multiFn.selectMethod(args) + if err != nil { + return nil, err + } + + if !multiFn.IsMacro { + return &fn, nil + } + + return fn.Invoke(scope, args...) +} + func (multiFn MultiFn) String() string { var sb strings.Builder for _, fn := range multiFn.Methods { @@ -30,18 +44,18 @@ func (multiFn MultiFn) String() string { // Invoke dispatches the call to a method based on number of arguments. func (multiFn MultiFn) Invoke(scope Scope, args ...Value) (Value, error) { - fn, err := multiFn.selectMethod(args) - if err != nil { - return nil, err - } - if multiFn.IsMacro { - v, err := fn.Invoke(scope, args...) + form, err := multiFn.Expand(scope, args) if err != nil { return nil, err } - return Eval(scope, v) + return form.Eval(scope) + } + + fn, err := multiFn.selectMethod(args) + if err != nil { + return nil, err } argVals, err := evalValueList(scope, args) diff --git a/slang/core.go b/slang/core.go index c5ded35..bf01cf1 100644 --- a/slang/core.go +++ b/slang/core.go @@ -119,13 +119,13 @@ func Next(seq sabre.Seq) sabre.Value { // Cons inserts the first argument as first element in the second seq argument // and returns. func Cons(v sabre.Value, seq sabre.Seq) sabre.Value { - return seq.Cons(v) + return Realize(seq.Cons(v)) } // Conj appends the second argument as last element in the first seq argument // and returns. func Conj(seq sabre.Seq, args ...sabre.Value) sabre.Value { - return seq.Conj(args...) + return Realize(seq.Conj(args...)) } // ThreadFirst threads the expressions through forms by inserting result of diff --git a/slang/slang.go b/slang/slang.go index 6966ce8..45ce6cd 100644 --- a/slang/slang.go +++ b/slang/slang.go @@ -195,6 +195,7 @@ func BindAll(scope sabre.Scope) error { "core/def": sabre.Def, "core/if": sabre.If, "core/fn*": sabre.Lambda, + "core/macro*": sabre.Macro, "core/let*": sabre.Let, "core/quote": sabre.SimpleQuote, "core/syntax-quote": sabre.SyntaxQuote, diff --git a/specials.go b/specials.go index c9e6dfa..cb4e5fd 100644 --- a/specials.go +++ b/specials.go @@ -18,7 +18,14 @@ var ( // (fn* name? [arg*] expr*) or (fn* name? ([arg]* expr*)+) Lambda = SpecialForm{ Name: "fn*", - Parse: parseLambda, + Parse: fnParser(false), + } + + // Macro defines an anonymous function and returns. Must have the form + // (macro* name? [arg*] expr*) or (fn* name? ([arg]* expr*)+) + Macro = SpecialForm{ + Name: "macro*", + Parse: fnParser(true), } // Let implements the (let [binding*] expr*) form. expr are evaluated @@ -54,48 +61,52 @@ var ( } ) -func parseLambda(scope Scope, forms []Value) (*Fn, error) { - if len(forms) < 1 { - return nil, fmt.Errorf("insufficient args (%d) for 'fn'", len(forms)) - } +func fnParser(isMacro bool) func(scope Scope, forms []Value) (*Fn, error) { + return func(scope Scope, forms []Value) (*Fn, error) { + if len(forms) < 1 { + return nil, fmt.Errorf("insufficient args (%d) for 'fn'", len(forms)) + } - nextIndex := 0 - def := MultiFn{} + nextIndex := 0 + def := MultiFn{ + IsMacro: isMacro, + } - name, isName := forms[nextIndex].(Symbol) - if isName { - def.Name = name.String() - nextIndex++ - } + name, isName := forms[nextIndex].(Symbol) + if isName { + def.Name = name.String() + nextIndex++ + } - return &Fn{ - Func: func(_ Scope, args []Value) (Value, error) { - _, isList := forms[nextIndex].(*List) - if isList { - for _, arg := range forms[nextIndex:] { - spec, isList := arg.(*List) - if !isList { - return nil, fmt.Errorf("expected arg to be list, not %s", - reflect.TypeOf(arg)) + return &Fn{ + Func: func(_ Scope, args []Value) (Value, error) { + _, isList := forms[nextIndex].(*List) + if isList { + for _, arg := range forms[nextIndex:] { + spec, isList := arg.(*List) + if !isList { + return nil, fmt.Errorf("expected arg to be list, not %s", + reflect.TypeOf(arg)) + } + + fn, err := makeFn(scope, spec.Values) + if err != nil { + return nil, err + } + + def.Methods = append(def.Methods, *fn) } - - fn, err := makeFn(scope, spec.Values) + } else { + fn, err := makeFn(scope, forms[nextIndex:]) if err != nil { return nil, err } - def.Methods = append(def.Methods, *fn) } - } else { - fn, err := makeFn(scope, forms[nextIndex:]) - if err != nil { - return nil, err - } - def.Methods = append(def.Methods, *fn) - } - return def, def.validate() - }, - }, nil + return def, def.validate() + }, + }, nil + } } func parseLet(scope Scope, args []Value) (*Fn, error) { @@ -277,6 +288,9 @@ func analyze(scope Scope, form Value) error { case *List: return f.parse(scope) + case String: + return nil + case Seq: return analyzeSeq(scope, f) }