Skip to content

Commit

Permalink
Allow default arguments
Browse files Browse the repository at this point in the history
We typically define a function like this:

     (set! hello1 (fn* (name)
       (print "Hello %s" name)))

Here we say there is a parameter "name" which must be supplied, and that
is later used.

If we instead say that parameters can be lists we can allow the first
value to be the name, and the second a default value if nothing is
actually passed, like so:

     (set! hello2 (fn* ( (name "World") )
        (print "Hello %s"  name)))

These could be used like so:

     (hello1 "Steve")  ; "Hello Steve"

     (hello2 "Steve")  ; "Hello Steve"
     (hello2)          ; "Hello World"

I've not yet experimented with supplying defaults in the non-final
arguments, because that could screw things up.  But for simple cases
like the one above it seems to work.

Once complete this will close #130, however I need to add test-cases..
  • Loading branch information
skx committed Apr 9, 2023
1 parent bd77719 commit 552d71e
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 12 deletions.
7 changes: 6 additions & 1 deletion eval/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -605,14 +605,19 @@ func (ev *Eval) eval(exp primitive.Primitive, e *env.Environment, expandMacro bo
// Variadic arguments would add _extra_ arguments, so this check
// is still safe for those.
//
if len(args) < min {
if (len(args) + len(proc.Defaults)) < min {
return primitive.ArityError()
}

// Create a new environment/scope to set the
// parameter values within.
e = env.NewEnvironment(proc.Env)

// For each default argument set it
for k, v := range proc.Defaults {
e.Set(k.ToString(),v)
}

// For each of the arguments that have been supplied
for i, x := range args {

Expand Down
46 changes: 35 additions & 11 deletions eval/specials.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,15 +185,39 @@ func (ev *Eval) evalSpecialForm(name string, args []primitive.Primitive, e *env.
return primitive.Error(fmt.Sprintf("expected a list for arguments, got %v", args[0])), true
}

proc := &primitive.Procedure{
Defaults: make(map[primitive.Symbol]primitive.Primitive),
}

// Collect arguments
arguments := []primitive.Symbol{}
for _, x := range argMarkers {

xs, ok := x.(primitive.Symbol)
if !ok {
return primitive.Error(fmt.Sprintf("expected a symbol for an argument, got %v", x)), true
lst, ok1 := x.(primitive.List)
if ok1 {
// First term is the name
// Second term is the default
// TODO: More flexible
if len(lst) != 2 {
return primitive.Error(fmt.Sprintf("only two list items allowed for a default-value, got %d", len(lst))), true
}
arg := lst[0]
val := lst[1]

xs, ok2 := arg.(primitive.Symbol)
if !ok2 {
return primitive.Error(fmt.Sprintf("expected a symbol for an argument, got %v", arg)), true

}
arguments = append(arguments, xs)
proc.Defaults[xs] = val
} else {
xs, ok2 := x.(primitive.Symbol)
if !ok2 {
return primitive.Error(fmt.Sprintf("expected a symbol for an argument, got %v", x)), true
}
arguments = append(arguments, xs)
}
arguments = append(arguments, xs)
}

body := args[1]
Expand All @@ -210,13 +234,13 @@ func (ev *Eval) evalSpecialForm(name string, args []primitive.Primitive, e *env.
//
// To make it a macro it should be set with
// "(defmacro!..)"
return &primitive.Procedure{
Args: arguments,
Body: body,
Env: e,
Help: help,
Macro: false,
}, true
proc.Args = arguments
proc.Body = body
proc.Env = e
proc.Help= help
proc.Macro = false

return proc,true

case "let*":
// We need to have at least one argument.
Expand Down
3 changes: 3 additions & 0 deletions primitive/procedure.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ type Procedure struct {
// Arguments to this procedure.
Args []Symbol

// Defaults supplied when the procedure was defined
Defaults map[Symbol]Primitive

// Body is the body to execute, in the case where F is nil.
Body Primitive

Expand Down

0 comments on commit 552d71e

Please sign in to comment.