Skip to content

Commit

Permalink
Merge pull request #59 from skx/53-inequality
Browse files Browse the repository at this point in the history
Added /= function.
  • Loading branch information
skx authored Oct 17, 2022
2 parents ec0c2d6 + e19cd2e commit badb1d6
Show file tree
Hide file tree
Showing 5 changed files with 168 additions and 6 deletions.
2 changes: 2 additions & 0 deletions PRIMITIVES.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ Things you'll find here include:
* `/`
* Division function.
* Note that if only a single value is specified the reciprocal is returned - i.e. "(/ 3)" is equal to "1/3".
* `/=`
* Numerical inequality test, if any argument is the same as another return false, otherwise if all arguments are unique return true.
* `<`
* Less-than function.
* `=`
Expand Down
50 changes: 49 additions & 1 deletion builtins/builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ func PopulateEnvironment(env *env.Environment) {
env.Set("*", &primitive.Procedure{F: multiplyFn, Help: helpMap["*"], Args: []primitive.Symbol{primitive.Symbol("N"), primitive.Symbol("arg1..argN")}})
env.Set("+", &primitive.Procedure{F: plusFn, Help: helpMap["+"], Args: []primitive.Symbol{primitive.Symbol("N"), primitive.Symbol("arg1..argN")}})
env.Set("-", &primitive.Procedure{F: minusFn, Help: helpMap["-"], Args: []primitive.Symbol{primitive.Symbol("N"), primitive.Symbol("arg1..argN")}})
env.Set("/", &primitive.Procedure{F: divideFn, Help: helpMap["-"], Args: []primitive.Symbol{primitive.Symbol("N"), primitive.Symbol("arg1..argN")}})
env.Set("/", &primitive.Procedure{F: divideFn, Help: helpMap["/"], Args: []primitive.Symbol{primitive.Symbol("N"), primitive.Symbol("arg1..argN")}})
env.Set("/=", &primitive.Procedure{F: inequalityFn, Help: helpMap["/="], Args: []primitive.Symbol{primitive.Symbol("N"), primitive.Symbol("arg1..argN")}})
env.Set("<", &primitive.Procedure{F: ltFn, Help: helpMap["<"], Args: []primitive.Symbol{primitive.Symbol("a"), primitive.Symbol("b")}})
env.Set("=", &primitive.Procedure{F: equalsFn, Help: helpMap["="], Args: []primitive.Symbol{primitive.Symbol("arg1"), primitive.Symbol("arg2 .. argN")}})
env.Set("arch", &primitive.Procedure{F: archFn, Help: helpMap["arch"]})
Expand Down Expand Up @@ -786,6 +787,53 @@ func helpFn(env *env.Environment, args []primitive.Primitive) primitive.Primitiv
return primitive.String(str)
}

// inequalityFn implements /=
func inequalityFn(env *env.Environment, args []primitive.Primitive) primitive.Primitive {

// We need at least two arguments
if len(args) < 2 {
return primitive.ArityError()
}

// First argument must be a number.
nA, ok := args[0].(primitive.Number)
if !ok {
return primitive.Error("argument was not a number")
}

// Now we'll loop over all other numbers
//
// If we got something that was already seen we can
// terminate early but we don't because it is important
// to also report on failures to validate types - which
// we can't do if we bail.
//
ret := primitive.Bool(true)

// Keep track of things we've seen here
seen := make(map[float64]bool)
seen[float64(nA)] = true

for _, i := range args[1:] {

// check we have a number
nB, ok2 := i.(primitive.Number)

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

// Have we seen this?
_, found := seen[float64(nB)]
if found {
ret = primitive.Bool(false)
}
seen[float64(nB)] = true
}

return ret
}

// (join (1 2 3)
func joinFn(env *env.Environment, args []primitive.Primitive) primitive.Primitive {

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

// TestInequality tests /=
func TestInequality(t *testing.T) {

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

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

//
// Now a real one: unequal
//
out = inequalityFn(ENV, []primitive.Primitive{
primitive.Number(9),
primitive.Number(8),
})

// Will work
n, ok2 := out.(primitive.Bool)
if !ok2 {
t.Fatalf("expected bool, got %v", out)
}
if n != true {
t.Fatalf("got wrong result")
}

//
// Now a real one: unequal - but multiple values
//
out = inequalityFn(ENV, []primitive.Primitive{
primitive.Number(1),
primitive.Number(2),
primitive.Number(3),
primitive.Number(4),
primitive.Number(5),
primitive.Number(6),
})

// Will work
n, ok2 = out.(primitive.Bool)
if !ok2 {
t.Fatalf("expected bool, got %v", out)
}
if n != true {
t.Fatalf("got wrong result")
}

//
// Now a real one: unequal values
//
out = inequalityFn(ENV, []primitive.Primitive{
primitive.Number(1),
primitive.Number(2),
primitive.Number(2),
primitive.Number(1),
})

// Will work
n, ok2 = out.(primitive.Bool)
if !ok2 {
t.Fatalf("expected bool, got %v", out)
}
if n != false {
t.Fatalf("got wrong result")
}

//
// Now with wrong types - last one is wrong
//
out = inequalityFn(ENV, []primitive.Primitive{
primitive.Number(1),
primitive.Number(2),
primitive.Number(3),
primitive.Number(4),
primitive.String("5"),
})

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

//
// Now with wrong types
//
out = inequalityFn(ENV, []primitive.Primitive{
primitive.String("9"),
primitive.Number(9),
})

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

// TestJoin tests join
func TestJoin(t *testing.T) {

Expand Down
4 changes: 4 additions & 0 deletions builtins/help.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ Multiplies all arguments present with the first number.
/
Divides all arguments present with the first number.
%%
/=
Numerical inequality testing. If any argument is identical
to any other argument return false. Otherwise return true.
%%
<

Return true if a is less than b.
Expand Down
9 changes: 4 additions & 5 deletions eval/eval_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,6 @@ a
//
{"(+ 3)", "3"},
{"(- 3)", "3"},
{"(/ 3)", "3"},
{"(* 3)", "3"},

// strings
Expand Down Expand Up @@ -358,12 +357,12 @@ func TestStandardLibrary(t *testing.T) {
output: "(1 4 9 16 25)"},

// range
{input: "(range -5 5 1)", output: "(-5 -4 -3 -2 -1 0 1 2 3 4)"},
{input: "(range 1 11 2)", output: "(1 3 5 7 9)"},
{input: "(range -5 5 1)", output: "(-5 -4 -3 -2 -1 0 1 2 3 4 5)"},
{input: "(range 1 11 2)", output: "(1 3 5 7 9 11)"},

// seq/nat
{input: "(seq 10)", output: "(0 1 2 3 4 5 6 7 8 9)"},
{input: "(nat 10)", output: "(1 2 3 4 5 6 7 8 9)"},
{input: "(seq 10)", output: "(0 1 2 3 4 5 6 7 8 9 10)"},
{input: "(nat 10)", output: "(1 2 3 4 5 6 7 8 9 10)"},

{input: "(join (reverse (split \"Steve\" \"\")))", output: "evetS"},
}
Expand Down

0 comments on commit badb1d6

Please sign in to comment.