Skip to content

Commit

Permalink
Merge pull request #13 from skx/7-more-mal
Browse files Browse the repository at this point in the history
7 more mal
  • Loading branch information
skx authored Oct 1, 2022
2 parents 46c89e7 + 2cd3559 commit 3a6c153
Show file tree
Hide file tree
Showing 6 changed files with 299 additions and 11 deletions.
14 changes: 8 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ To set a global variable use `set!`:

(set! foo "bar")

To start a new local scope, with local variables, use `let*`:
To start a new scope, with local variables, use `let*`:

(let (foo "bar"
baz "bart")
Expand All @@ -45,7 +45,7 @@ To define a function use `set!` with `fn*`:
1
(* n (fact (- n 1))))))

To define a macro use `setmacro!`:
To define a macro use `defmacro!`:

(defmacro! debug (fn* (x) `(print "Variable '%s' has value %s" '~x ~x)))

Expand All @@ -68,7 +68,8 @@ We have a reasonable number of functions implemented, either in our golang core
* `true` and `false` are available as synonyms.
* Hash operations:
* Hashes are literals like this `{ :name "Steve" :location "Helsinki" }`
* Hash functions are `get`, `keys`, & `set`.
* Hash functions are `contains?`, `get`, `keys`, `set`, & `vals`.
* Note that keys are returned in sorted order. Values are returned in order of their sorted keys too.
* List operations:
* `car`, `cdr`, `list`, & `sort`.
* Logical operations:
Expand All @@ -80,13 +81,14 @@ We have a reasonable number of functions implemented, either in our golang core
* Comparison functions:
* `<`, `<=`, `>`, `>=`, `=`, & `eq`.
* Misc features:
* `arch`, `getenv`, `os`, `print`, `str` & `type`
* `arch`, `getenv`, `os`, `print`, `slurp`, `str` & `type`
* Special forms:
* `begin`, `define`, `env`, `eval`, `gensym`, `if`, `lambda`, `let`, `macroexpand`, `read`, `set!`, `quote`, & `quasiquote`.
* `begin`, `define`, `do`, `env`, `eval`, `gensym`, `if`, `lambda`, `let`, `macroexpand`, `read`, `set!`, `quote`, & `quasiquote`.
* Error handling:
* `error`, `try`, and `catch` - as demonstrated in [try.lisp](try.lisp).
* Tail recursion optimization.
* MAL compatability:
* `do` can be used as a synonym for `begin`.
* `def!` can be used as a synonym for `define`.
* `defmacro!` is used to define macros.
* `fn*` can be used as a synonym for `lambda`.
Expand Down Expand Up @@ -179,7 +181,7 @@ Once you've built, and optinall installed, the CLI driver there are two ways to

* By specifying sexpressions on the command-line.
* `yal -e "(print (os))"`
* By passing the name of a file to read adn execute.
* By passing the name of a file to read and execute.
* `yal test.lisp`


Expand Down
85 changes: 84 additions & 1 deletion builtins/builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,11 @@ func PopulateEnvironment(env *env.Environment) {
env.Set("list", &primitive.Procedure{F: listFn})

// Hash
env.Set("contains?", &primitive.Procedure{F: containsFn})
env.Set("get", &primitive.Procedure{F: getFn})
env.Set("keys", &primitive.Procedure{F: keysFn})
env.Set("set", &primitive.Procedure{F: setFn})
env.Set("vals", &primitive.Procedure{F: valsFn})

// core
env.Set("arch", &primitive.Procedure{F: archFn})
Expand All @@ -93,6 +95,7 @@ func PopulateEnvironment(env *env.Environment) {
env.Set("print", &primitive.Procedure{F: printFn})
env.Set("sort", &primitive.Procedure{F: sortFn})
env.Set("sprintf", &primitive.Procedure{F: sprintfFn})
env.Set("slurp", &primitive.Procedure{F: slurpFn})

// string
env.Set("chr", &primitive.Procedure{F: chrFn})
Expand Down Expand Up @@ -413,6 +416,20 @@ func typeFn(args []primitive.Primitive) primitive.Primitive {
return primitive.String(args[0].Type())
}

// slurpFn returns the contents of the specified file
func slurpFn(args []primitive.Primitive) primitive.Primitive {
if len(args) != 1 {
return primitive.Error("wrong number of arguments")
}

fName := args[0].ToString()
data, err := os.ReadFile(fName)
if err != nil {
return primitive.Error(fmt.Sprintf("error reading %s %s", fName, err))
}
return primitive.String(string(data))
}

// strFn implements "str"
func strFn(args []primitive.Primitive) primitive.Primitive {
return primitive.String(args[0].ToString())
Expand Down Expand Up @@ -465,7 +482,6 @@ func archFn(args []primitive.Primitive) primitive.Primitive {
return primitive.String(runtime.GOARCH)
}


// printFn implements (print).
func printFn(args []primitive.Primitive) primitive.Primitive {
// no args
Expand Down Expand Up @@ -670,6 +686,73 @@ func keysFn(args []primitive.Primitive) primitive.Primitive {
return c
}

// valsFn is the implementation of `(vals hash)`
func valsFn(args []primitive.Primitive) primitive.Primitive {

// We need a single argument
if len(args) != 1 {
return primitive.Error("invalid argument count")
}

// First is a Hash
if _, ok := args[0].(primitive.Hash); !ok {
return primitive.Error("argument not a hash")
}

// Create the list to hold the result
var c primitive.List

// Cast the argument
tmp := args[0].(primitive.Hash)

// Get the keys as a list
keys := []string{}

// Add the keys
for x := range tmp.Entries {
keys = append(keys, x)
}

// Sort the list
sort.Strings(keys)

// Now append the value
for _, x := range keys {
c = append(c, tmp.Entries[x])
}

return c
}

// containsFn implements (contains?)
func containsFn(args []primitive.Primitive) primitive.Primitive {

// We need a pair of arguments
if len(args) != 2 {
return primitive.Error("invalid argument count")
}

// First is a Hash
hsh, ok := args[0].(primitive.Hash)
if !ok {
return primitive.Error("argument not a hash")
}

// The second should be a string, but other things can be converted
str, ok := args[1].(primitive.String)
if !ok {
str = primitive.String(args[1].ToString())
}

_, found := hsh.Entries[str.ToString()]
if found {
return primitive.Bool(true)
}

return primitive.Bool(false)

}

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

Expand Down
Loading

0 comments on commit 3a6c153

Please sign in to comment.