Skip to content
This repository has been archived by the owner on May 23, 2021. It is now read-only.

Commit

Permalink
Add support for macros, add defn to core.lisp
Browse files Browse the repository at this point in the history
  • Loading branch information
spy16 committed Feb 22, 2020
1 parent ce6bf55 commit 83f8103
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 86 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
84 changes: 42 additions & 42 deletions examples/core.lisp
Original file line number Diff line number Diff line change
@@ -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)))))
2 changes: 1 addition & 1 deletion examples/slang_test.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -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))
28 changes: 21 additions & 7 deletions func.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions slang/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions slang/slang.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
82 changes: 48 additions & 34 deletions specials.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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)
}
Expand Down

0 comments on commit 83f8103

Please sign in to comment.