Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add md5/sha1/sha256 digest functions. #126

Merged
merged 2 commits into from
Feb 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions PRIMITIVES.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,8 @@ Things you'll find here include:
* Create a new list.
* `match`
* Perform a regular expression test.
* `md5`
* Return the MD5 digest of the given string.
* `ms`
* Return the time, in milliseconds.
* `nil?`
Expand All @@ -215,6 +217,10 @@ Things you'll find here include:
* Output the specified string, or format string + values.
* `set`
* Update the value of the specified hash-key.
* `sha1`
* Return the SHA1 digest of the given string.
* `sha256`
* Return the SHA256 digest of the given string.
* `shell`
* Run a command via the shell, and return STDOUT and STDERR it generated.
* `sin`
Expand Down
48 changes: 48 additions & 0 deletions builtins/builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ package builtins
import (
"bufio"
"bytes"
"crypto/md5"
"crypto/sha1"
"crypto/sha256"
"fmt"
"math"
"math/rand"
Expand Down Expand Up @@ -135,6 +138,7 @@ func PopulateEnvironment(env *env.Environment) {
env.Set("keys", &primitive.Procedure{F: keysFn, Help: helpMap["keys"], Args: []primitive.Symbol{primitive.Symbol("hash")}})
env.Set("list", &primitive.Procedure{F: listFn, Help: helpMap["list"], Args: []primitive.Symbol{primitive.Symbol("arg1"), primitive.Symbol("arg...")}})
env.Set("match", &primitive.Procedure{F: matchFn, Help: helpMap["match"], Args: []primitive.Symbol{primitive.Symbol("regexp"), primitive.Symbol("str")}})
env.Set("md5", &primitive.Procedure{F: md5Fn, Help: helpMap["md5"], Args: []primitive.Symbol{primitive.Symbol("string")}})
env.Set("ms", &primitive.Procedure{F: msFn, Help: helpMap["ms"]})
env.Set("nil?", &primitive.Procedure{F: nilFn, Help: helpMap["nil?"], Args: []primitive.Symbol{primitive.Symbol("object")}})
env.Set("now", &primitive.Procedure{F: nowFn, Help: helpMap["now"]})
Expand All @@ -145,6 +149,8 @@ func PopulateEnvironment(env *env.Environment) {
env.Set("print", &primitive.Procedure{F: printFn, Help: helpMap["print"], Args: []primitive.Symbol{primitive.Symbol("arg1..argN")}})
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("sha1", &primitive.Procedure{F: sha1Fn, Help: helpMap["sha1"], Args: []primitive.Symbol{primitive.Symbol("string")}})
env.Set("sha256", &primitive.Procedure{F: sha256Fn, Help: helpMap["sha256"], Args: []primitive.Symbol{primitive.Symbol("string")}})
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")}})
Expand Down Expand Up @@ -1277,6 +1283,20 @@ func modFn(env *env.Environment, args []primitive.Primitive) primitive.Primitive
return primitive.Number(a % b)
}

// md5Fn is the implementation of `(md5)`
func md5Fn(env *env.Environment, args []primitive.Primitive) primitive.Primitive {
// We need one argument
if len(args) != 1 {
return primitive.ArityError()
}

// The argument must be a string
str := args[0].ToString()

// Get the output
return primitive.String(fmt.Sprintf("%X", md5.Sum([]byte(str))))
}

// msFn is the implementation of `(ms)`
func msFn(env *env.Environment, args []primitive.Primitive) primitive.Primitive {
return primitive.Number(time.Now().UnixNano() / int64(time.Millisecond))
Expand Down Expand Up @@ -1545,6 +1565,34 @@ func setFn(env *env.Environment, args []primitive.Primitive) primitive.Primitive
return args[2]
}

// sha1Fn runs a SHA1 hash
func sha1Fn(env *env.Environment, args []primitive.Primitive) primitive.Primitive {
// We need one argument
if len(args) != 1 {
return primitive.ArityError()
}

// The argument must be a string
str := args[0].ToString()

// Get the output
return primitive.String(fmt.Sprintf("%X", sha1.Sum([]byte(str))))
}

// sha256Fn runs a SHA256 hash
func sha256Fn(env *env.Environment, args []primitive.Primitive) primitive.Primitive {
// We need one argument
if len(args) != 1 {
return primitive.ArityError()
}

// The argument must be a string
str := args[0].ToString()

// Get the output
return primitive.String(fmt.Sprintf("%X", sha256.Sum256([]byte(str))))
}

// shellFn runs a command via the shell
func shellFn(env *env.Environment, args []primitive.Primitive) primitive.Primitive {

Expand Down
41 changes: 41 additions & 0 deletions builtins/builtins_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1907,6 +1907,47 @@ func TestGlob(t *testing.T) {

}

// TestHash tests our hash functions
func TestHash(t *testing.T) {

funs := []primitive.GolangPrimitiveFn{
md5Fn,
sha1Fn,
sha256Fn,
}

for _, fn := range funs {

// No eargs
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 e != primitive.ArityError() {
t.Fatalf("got error, but wrong one")

}

// Hash a string
out = fn(ENV, []primitive.Primitive{
primitive.String("foo"),
})

// Will lead to an error
r, ok2 := out.(primitive.String)
if !ok2 {
t.Fatalf("expected string, got %v", r)
}

if len(r) < 15 {
t.Fatalf("result '%s' was the wrong length", r)
}
}
}

// TestHelp tests help
func TestHelp(t *testing.T) {
// no arguments
Expand Down
24 changes: 24 additions & 0 deletions builtins/help.txt
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,14 @@ Any matches found will be returned as a list, with nil being returned on no matc

Example: (print (match "c.ke$" "cake"))
%%
md5

md5 returns the calculated MD5 digest of the provived string

See also: sha1, sha256

Example: (print (md5 "steve"))
%%
ms

ms returns the current time as a number of milliseconds, it is useful for benchmarking.
Expand Down Expand Up @@ -331,6 +339,22 @@ See also: get
Example: (set! person {:name "Steve"})
(set person :name "Bobby")
%%
sha1

sha1 returns the calculated SHA1 digest of the provived string

See also: md5sum, sha256

Example: (print (sha1 "steve"))
%%
sha256

sha256 returns the calculated SHA256 digest of the provived string

See also: md5sum, sha1

Example: (print (sha256 "steve"))
%%
shell

shell allows you to run a command, via the shell.
Expand Down
9 changes: 1 addition & 8 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,11 @@ import (
"bufio"
"flag"
"fmt"
"math/rand"
"os"
"path"
"regexp"
"sort"
"strings"
"time"

"github.com/skx/yal/builtins"
"github.com/skx/yal/config"
Expand Down Expand Up @@ -197,9 +195,6 @@ func help(show []string) {

func main() {

// (gensym) needs a decent random seed, as does (random).
rand.Seed(time.Now().UnixNano())

// Parse our command-line flags
exp := flag.String("e", "", "A string to evaluate.")
hlp := flag.Bool("h", false, "Show help information and exit.")
Expand Down Expand Up @@ -230,7 +225,6 @@ func main() {
//
create()


//
// By default we have no STDERR handler wired up, but if we set the
// debug flag we'll send that to the actual console's STDERR stream
Expand All @@ -240,13 +234,12 @@ func main() {
iohelper := ENV.GetIOConfig()

// Setup a destination for STDERR
iohelper.STDERR =os.Stderr
iohelper.STDERR = os.Stderr

// Update
ENV.SetIOConfig(iohelper)
}


// LSP?
if *lsp {
lspStart()
Expand Down