Skip to content

Commit

Permalink
Merge pull request #44 from skx/43-random
Browse files Browse the repository at this point in the history
Add random functions.
  • Loading branch information
skx authored Oct 16, 2022
2 parents b4f41b5 + c661a53 commit 94f9f8d
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 0 deletions.
18 changes: 18 additions & 0 deletions builtins/builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ func PopulateEnvironment(env *env.Environment) {
env.Set("ord", &primitive.Procedure{F: ordFn, Help: helpMap["ord"]})
env.Set("os", &primitive.Procedure{F: osFn, Help: helpMap["os"]})
env.Set("print", &primitive.Procedure{F: printFn, Help: helpMap["print"]})
env.Set("random", &primitive.Procedure{F: randomFn, Help: helpMap["random"]})
env.Set("set", &primitive.Procedure{F: setFn, Help: helpMap["set"]})
env.Set("shell", &primitive.Procedure{F: shellFn, Help: helpMap["shell"]})
env.Set("sort", &primitive.Procedure{F: sortFn, Help: helpMap["sort"]})
Expand Down Expand Up @@ -1055,6 +1056,23 @@ func printFn(env *env.Environment, args []primitive.Primitive) primitive.Primiti
return primitive.String(out)
}

// randomFn implements (random).
func randomFn(env *env.Environment, args []primitive.Primitive) primitive.Primitive {
if len(args) != 1 {
return primitive.Error("wrong number of arguments")
}

// ensure we received a number
num, ok := args[0].(primitive.Number)

if !ok {
return primitive.Error("argument not a number")
}

return primitive.Number(rand.Intn(int(num)))

}

// setFn is the implementation of `(set hash key val)`
func setFn(env *env.Environment, args []primitive.Primitive) primitive.Primitive {

Expand Down
38 changes: 38 additions & 0 deletions builtins/builtins_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2250,6 +2250,44 @@ func TestPrint(t *testing.T) {
}
}

// TestRandom tests (random)
func TestRandom(t *testing.T) {

// No arguments
out := randomFn(ENV, []primitive.Primitive{})

// Will lead to an error
e, ok := out.(primitive.Error)
if !ok {
t.Fatalf("expected error, got %v", out)
}
if !strings.Contains(string(e), "wrong number of arguments") {
t.Fatalf("got error, but wrong one %v", out)
}

// One argument, of the wrong type
out = randomFn(ENV, []primitive.Primitive{
primitive.String("Hello!"),
})

e, ok = out.(primitive.Error)
if !ok {
t.Fatalf("expected error, got %v", out)
}
if !strings.Contains(string(e), "not a number") {
t.Fatalf("got error, but wrong one %v", out)
}

// Now call with a number
out = randomFn(ENV, []primitive.Primitive{
primitive.Number(1),
})
_, ok2 := out.(primitive.Number)
if !ok2 {
t.Fatalf("expected string, got %v", out)
}
}

// TestSet tests set
func TestSet(t *testing.T) {

Expand Down
7 changes: 7 additions & 0 deletions builtins/help.txt
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,13 @@ See also: sprintf
Example: (print "Hello, world")
Example: (print "Hello user %s you are %d" (getenv "USER") 32)
%%
random

random will return a number between zero and one less than the value specified.

See also: random:char random:item
Example: (random 100) ; A number between 0 and 99
%%
set

set updates the specified hash, setting the value given by name.
Expand Down
11 changes: 11 additions & 0 deletions stdlib/stdlib.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -462,3 +462,14 @@
"Invoke the specified callback on every file beneath the given path."

(apply (directory:entries path) fn)))


;; Add some simple random functions, using our "random" primitive.
(set! random:char (fn* ()
"Return a random character, from the set a-z."
(let* (chars (split "abcdefghijklmnopqrstuvwxyz" ""))
(random:item chars))))

(set! random:item (fn* (lst:list)
"Return a random element from the specified list."
(nth lst (random (length lst)))))

0 comments on commit 94f9f8d

Please sign in to comment.