Skip to content

Commit

Permalink
Merge pull request #117 from skx/115-trig
Browse files Browse the repository at this point in the history
Implement trig. functions.
  • Loading branch information
skx authored Dec 20, 2022
2 parents 69b9183 + 2b473f0 commit 49af236
Show file tree
Hide file tree
Showing 5 changed files with 286 additions and 2 deletions.
20 changes: 20 additions & 0 deletions PRIMITIVES.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,14 @@ Things you'll find here include:
* `=`
* Numerical comparison function.
* Note that multiple arguments are supported, not just two.
* `acos`
* Trig. function.
* `arch`
* Return the operating system architecture.
* `asin`
* Trig. function.
* `atan`
* Trig. function.
* `base`
* Convert the specified integer to a string, in the given base.
* `car`
Expand All @@ -134,6 +140,10 @@ Things you'll find here include:
* Add the element to the start of the given (potentialy empty) list.
* `contains?`
* Does the specified hash contain the given key?
* `cos`
* Trig. function.
* `cosh`
* Trig. function.
* `date`
* Return details of today's date, as a list.
* Demonstrated in [time.lisp](time.lisp).
Expand Down Expand Up @@ -207,6 +217,10 @@ Things you'll find here include:
* Update the value of the specified hash-key.
* `shell`
* Run a command via the shell, and return STDOUT and STDERR it generated.
* `sin`
* Trig. function.
* `sinh`
* Trig. function.
* `sort`
* Sort the given list.
* `split`
Expand All @@ -223,6 +237,10 @@ Things you'll find here include:
* Return true if the first string is greater than the second.
* `string>=`
* Return true if the first string is greater than, or equal to the second.
* `tan`
* Trig. function.
* `tanh`
* Trig. function.
* `time`
* Return values relating to the current time, as a list.
* Demonstrated in [time.lisp](time.lisp).
Expand Down Expand Up @@ -384,6 +402,8 @@ Functions here include:
* Is the given number equal to one?
* `or`
* Logical operator, are any elements true?
* `pi`
* Return the value of PI - calculated via `atan` as per [this reference](https://en.m.wikibooks.org/wiki/Trigonometry/Calculating_Pi).
* `pos?`
* Is the given number positive?
* `range`
Expand Down
172 changes: 170 additions & 2 deletions builtins/builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,15 +99,20 @@ func PopulateEnvironment(env *env.Environment) {
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("acos", &primitive.Procedure{F: acosFn, Help: helpMap["acos"], Args: []primitive.Symbol{primitive.Symbol("n")}})
env.Set("arch", &primitive.Procedure{F: archFn, Help: helpMap["arch"]})
env.Set("asin", &primitive.Procedure{F: asinFn, Help: helpMap["asin"], Args: []primitive.Symbol{primitive.Symbol("n")}})
env.Set("atan", &primitive.Procedure{F: atanFn, Help: helpMap["atan"], Args: []primitive.Symbol{primitive.Symbol("n")}})
env.Set("base", &primitive.Procedure{F: baseFn, Help: helpMap["base"], Args: []primitive.Symbol{primitive.Symbol("number"), primitive.Symbol("base")}})
env.Set("car", &primitive.Procedure{F: carFn, Help: helpMap["car"], Args: []primitive.Symbol{primitive.Symbol("list")}})
env.Set("cdr", &primitive.Procedure{F: cdrFn, Help: helpMap["cdr"], Args: []primitive.Symbol{primitive.Symbol("list")}})
env.Set("char=", &primitive.Procedure{F: charEqualsFn, Help: helpMap["char="], Args: []primitive.Symbol{primitive.Symbol("a"), primitive.Symbol("b")}})
env.Set("char<", &primitive.Procedure{F: charLtFn, Help: helpMap["char<"], Args: []primitive.Symbol{primitive.Symbol("a"), primitive.Symbol("b")}})
env.Set("char=", &primitive.Procedure{F: charEqualsFn, Help: helpMap["char="], Args: []primitive.Symbol{primitive.Symbol("a"), primitive.Symbol("b")}})
env.Set("chr", &primitive.Procedure{F: chrFn, Help: helpMap["chr"], Args: []primitive.Symbol{primitive.Symbol("num")}})
env.Set("cons", &primitive.Procedure{F: consFn, Help: helpMap["cons"], Args: []primitive.Symbol{primitive.Symbol("a"), primitive.Symbol("b")}})
env.Set("contains?", &primitive.Procedure{F: containsFn, Help: helpMap["contains?"], Args: []primitive.Symbol{primitive.Symbol("hash"), primitive.Symbol("key")}})
env.Set("cos", &primitive.Procedure{F: cosFn, Help: helpMap["cos"], Args: []primitive.Symbol{primitive.Symbol("n")}})
env.Set("cosh", &primitive.Procedure{F: coshFn, Help: helpMap["cosh"], Args: []primitive.Symbol{primitive.Symbol("n")}})
env.Set("date", &primitive.Procedure{F: dateFn, Help: helpMap["date"]})
env.Set("directory:entries", &primitive.Procedure{F: directoryEntriesFn, Help: helpMap["directory:entries"]})
env.Set("directory?", &primitive.Procedure{F: directoryFn, Help: helpMap["directory?"], Args: []primitive.Symbol{primitive.Symbol("path")}})
Expand Down Expand Up @@ -141,24 +146,83 @@ func PopulateEnvironment(env *env.Environment) {
env.Set("random", &primitive.Procedure{F: randomFn, Help: helpMap["random"], Args: []primitive.Symbol{primitive.Symbol("max")}})
env.Set("set", &primitive.Procedure{F: setFn, Help: helpMap["set"], Args: []primitive.Symbol{primitive.Symbol("hash"), primitive.Symbol("key"), primitive.Symbol("val")}})
env.Set("shell", &primitive.Procedure{F: shellFn, Help: helpMap["shell"], Args: []primitive.Symbol{primitive.Symbol("list")}})
env.Set("sin", &primitive.Procedure{F: sinFn, Help: helpMap["sin"], Args: []primitive.Symbol{primitive.Symbol("n")}})
env.Set("sinh", &primitive.Procedure{F: sinhFn, Help: helpMap["sinh"], Args: []primitive.Symbol{primitive.Symbol("n")}})
env.Set("sort", &primitive.Procedure{F: sortFn, Help: helpMap["sort"], Args: []primitive.Symbol{primitive.Symbol("list")}})
env.Set("split", &primitive.Procedure{F: splitFn, Help: helpMap["split"], Args: []primitive.Symbol{primitive.Symbol("str"), primitive.Symbol("by")}})
env.Set("sprintf", &primitive.Procedure{F: sprintfFn, Help: helpMap["sprintf"], Args: []primitive.Symbol{primitive.Symbol("arg1..argN")}})
env.Set("str", &primitive.Procedure{F: strFn, Help: helpMap["str"], Args: []primitive.Symbol{primitive.Symbol("object")}})
env.Set("string=", &primitive.Procedure{F: stringEqualsFn, Help: helpMap["string="], Args: []primitive.Symbol{primitive.Symbol("a"), primitive.Symbol("b")}})
env.Set("string<", &primitive.Procedure{F: stringLtFn, Help: helpMap["string<"], Args: []primitive.Symbol{primitive.Symbol("a"), primitive.Symbol("b")}})
env.Set("string=", &primitive.Procedure{F: stringEqualsFn, Help: helpMap["string="], Args: []primitive.Symbol{primitive.Symbol("a"), primitive.Symbol("b")}})
env.Set("tan", &primitive.Procedure{F: tanFn, Help: helpMap["tan"], Args: []primitive.Symbol{primitive.Symbol("n")}})
env.Set("tanh", &primitive.Procedure{F: tanhFn, Help: helpMap["tanh"], Args: []primitive.Symbol{primitive.Symbol("n")}})
env.Set("time", &primitive.Procedure{F: timeFn, Help: helpMap["time"]})
env.Set("type", &primitive.Procedure{F: typeFn, Help: helpMap["type"], Args: []primitive.Symbol{primitive.Symbol("object")}})
env.Set("vals", &primitive.Procedure{F: valsFn, Help: helpMap["vals"], Args: []primitive.Symbol{primitive.Symbol("hash")}})

}

// Built in functions



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

// We only need a single argument
if len(args) != 1 {
return primitive.ArityError()
}

// Which is a number
n, ok := args[0].(primitive.Number)
if !ok {
return primitive.Error("argument not a number")
}

return primitive.Number( math.Acos(float64(n)))
}

// archFn implements (os)
func archFn(env *env.Environment, args []primitive.Primitive) primitive.Primitive {
return primitive.String(runtime.GOARCH)
}


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

// We only need a single argument
if len(args) != 1 {
return primitive.ArityError()
}

// Which is a number
n, ok := args[0].(primitive.Number)
if !ok {
return primitive.Error("argument not a number")
}

return primitive.Number( math.Asin(float64(n)))
}

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

// We only need a single argument
if len(args) != 1 {
return primitive.ArityError()
}

// Which is a number
n, ok := args[0].(primitive.Number)
if !ok {
return primitive.Error("argument not a number")
}

return primitive.Number( math.Atan(float64(n)))
}

// baseFn implements (base)
func baseFn(env *env.Environment, args []primitive.Primitive) primitive.Primitive {
if len(args) != 2 {
Expand Down Expand Up @@ -346,6 +410,40 @@ func containsFn(env *env.Environment, args []primitive.Primitive) primitive.Prim

}

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

// We only need a single argument
if len(args) != 1 {
return primitive.ArityError()
}

// Which is a number
n, ok := args[0].(primitive.Number)
if !ok {
return primitive.Error("argument not a number")
}

return primitive.Number( math.Cos(float64(n)))
}

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

// We only need a single argument
if len(args) != 1 {
return primitive.ArityError()
}

// Which is a number
n, ok := args[0].(primitive.Number)
if !ok {
return primitive.Error("argument not a number")
}

return primitive.Number( math.Cosh(float64(n)))
}

// dateFn returns the current (Weekday, DD, MM, YYYY) as a list.
func dateFn(env *env.Environment, args []primitive.Primitive) primitive.Primitive {
var ret primitive.List
Expand Down Expand Up @@ -1465,6 +1563,41 @@ func shellFn(env *env.Environment, args []primitive.Primitive) primitive.Primiti
return ret
}


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

// We only need a single argument
if len(args) != 1 {
return primitive.ArityError()
}

// Which is a number
n, ok := args[0].(primitive.Number)
if !ok {
return primitive.Error("argument not a number")
}

return primitive.Number( math.Sin(float64(n)))
}

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

// We only need a single argument
if len(args) != 1 {
return primitive.ArityError()
}

// Which is a number
n, ok := args[0].(primitive.Number)
if !ok {
return primitive.Error("argument not a number")
}

return primitive.Number( math.Sinh(float64(n)))
}

// sortFn implements (sort)
func sortFn(env *env.Environment, args []primitive.Primitive) primitive.Primitive {
// If we have only a single argument
Expand Down Expand Up @@ -1608,6 +1741,41 @@ func stringLtFn(env *env.Environment, args []primitive.Primitive) primitive.Prim
return primitive.Bool(a < b)
}


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

// We only need a single argument
if len(args) != 1 {
return primitive.ArityError()
}

// Which is a number
n, ok := args[0].(primitive.Number)
if !ok {
return primitive.Error("argument not a number")
}

return primitive.Number( math.Tan(float64(n)))
}

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

// We only need a single argument
if len(args) != 1 {
return primitive.ArityError()
}

// Which is a number
n, ok := args[0].(primitive.Number)
if !ok {
return primitive.Error("argument not a number")
}

return primitive.Number( math.Tanh(float64(n)))
}

// timeFn returns the current (HH, MM, SS) as a list.
func timeFn(env *env.Environment, args []primitive.Primitive) primitive.Primitive {
var ret primitive.List
Expand Down
54 changes: 54 additions & 0 deletions builtins/builtins_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3492,6 +3492,60 @@ func TestStringLt(t *testing.T) {
}
}

func TestTrig(t *testing.T ) {

funs := []primitive.GolangPrimitiveFn{
acosFn,
asinFn,
atanFn,
cosFn,
coshFn,
sinFn,
sinhFn,
tanFn,
tanhFn,
}

for _, fn := range(funs) {

out := fn(nil, []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), "argument") {
t.Fatalf("got error, but wrong one")
}

// Argument must be an umber
out = fn(ENV, []primitive.Primitive{
primitive.String("foo"),
})

// Will lead to an error
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 a valid result
res := fn(ENV, []primitive.Primitive{
primitive.Number(3),
})

// don't care about the result
_, ok2 := res.(primitive.Number)
if !ok2 {
t.Fatalf("expected number, got %v", out)
}
}
}

func TestType(t *testing.T) {

// No arguments
Expand Down
Loading

0 comments on commit 49af236

Please sign in to comment.