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

64 characters #65

Merged
merged 18 commits into from
Oct 27, 2022
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
26 changes: 23 additions & 3 deletions PRIMITIVES.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ the help file, to see further details. This is just intended as a summary.
## Symbols

The only notable special symbols are the following strings which
represent the nil value. and ourboolean values.
represent the nil value. and our boolean values.

* `nil`
* The nil value.
Expand All @@ -28,7 +28,15 @@ represent the nil value. and ourboolean values.
* `#f`
* `false` is also available as an alias.

In the future we _might_ support characters, via \#A, etc.
Characters are specified via the `#\X` syntax, for escaped characters you just need to add the escape:

* `#\a` -> "a"
* `#\b` -> "b"
* ..
* `#\X` -> "X"
* `#\\n` -> newline
* `#\\t` -> tab




Expand Down Expand Up @@ -101,6 +109,16 @@ Things you'll find here include:
* Return the first item of a list.
* `cdr`
* Return all items of the list, except the first.
* `char=`
* Return true if the supplied values are characters, equal in value.
* `char<`
* Return true if the first character is less than the second.
* `char<=`
* Return true if the first character is less than, or equal to the second.
* `char>`
* Return true if the first character is greater than the second.
* `char>=`
* Return true if the first character is greater than, or equal to the second.
* `chr`
* Return the ASCII character of the given number.
* `cons`
Expand All @@ -120,6 +138,8 @@ Things you'll find here include:
* Return an error.
* `exists?`
* Does the given path exist?
* `explode`
* Convert the supplied string to a list of characters.
* `file?`
* Does the given path exist, and is it not a directory?
* `file:lines`
Expand Down Expand Up @@ -156,7 +176,7 @@ Things you'll find here include:
* `now`
* Return the number of seconds past the Unix Epoch.
* `ord`
* Return the ASCII code of the specified character.
* Return the ASCII code of the specified character, or the first character of the supplied string.
* `os`
* Return a string describing the current operating-system.
* `print`
Expand Down
104 changes: 101 additions & 3 deletions builtins/builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ func PopulateEnvironment(env *env.Environment) {
env.Set("arch", &primitive.Procedure{F: archFn, Help: helpMap["arch"]})
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("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")}})
Expand All @@ -111,6 +113,7 @@ func PopulateEnvironment(env *env.Environment) {
env.Set("eq", &primitive.Procedure{F: eqFn, Help: helpMap["eq"], Args: []primitive.Symbol{primitive.Symbol("a"), primitive.Symbol("b")}})
env.Set("error", &primitive.Procedure{F: errorFn, Help: helpMap["error"], Args: []primitive.Symbol{primitive.Symbol("message")}})
env.Set("exists?", &primitive.Procedure{F: existsFn, Help: helpMap["exists?"], Args: []primitive.Symbol{primitive.Symbol("path")}})
env.Set("explode", &primitive.Procedure{F: explodeFn, Help: helpMap["explode"], Args: []primitive.Symbol{primitive.Symbol("string")}})
env.Set("file:lines", &primitive.Procedure{F: fileLinesFn, Help: helpMap["file:lines"], Args: []primitive.Symbol{primitive.Symbol("path")}})
env.Set("file:read", &primitive.Procedure{F: fileReadFn, Help: helpMap["file:read"], Args: []primitive.Symbol{primitive.Symbol("path")}})
env.Set("file:stat", &primitive.Procedure{F: fileStatFn, Help: helpMap["file:stat"], Args: []primitive.Symbol{primitive.Symbol("path")}})
Expand Down Expand Up @@ -191,6 +194,68 @@ func cdrFn(env *env.Environment, args []primitive.Primitive) primitive.Primitive
return primitive.Nil{}
}

// charEqualsFn implements "char="
func charEqualsFn(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 character
nA, ok := args[0].(primitive.Character)
if !ok {
return primitive.Error("argument was not a character")
}

// Now we'll loop over all other arguments
//
// If we got something that was NOT the same as our
// initial value 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)

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

// check we have a character
nB, ok2 := i.(primitive.Character)

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

// Record our failure, but keep testing in case
// we have a type violation to report in a later
// argument.
if nB != nA {
ret = primitive.Bool(false)
}
}

return ret
}


// charLtFn implements (char<)
func charLtFn(env *env.Environment, args []primitive.Primitive) primitive.Primitive {
if len(args) != 2 {
return primitive.ArityError()
}

a, ok1 := args[0].(primitive.Character)
if !ok1 {
return primitive.Error("argument not a character")
}

b, ok2 := args[1].(primitive.Character);
if !ok2 {
return primitive.Error("argument not a character")
}
return primitive.Bool(a < b)
}

// chrFn is the implementation of (chr ..)
func chrFn(env *env.Environment, args []primitive.Primitive) primitive.Primitive {

Expand All @@ -205,7 +270,7 @@ func chrFn(env *env.Environment, args []primitive.Primitive) primitive.Primitive
i := args[0].(primitive.Number)
rune := rune(i)

return primitive.String(rune)
return primitive.Character(rune)
}

// consFn implements (cons).
Expand Down Expand Up @@ -508,6 +573,33 @@ func expandStr(input string) string {
return out
}

// explodeFn splits a string into a list of characters
func explodeFn(env *env.Environment, args []primitive.Primitive) primitive.Primitive {

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

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

// Split it
out := strings.Split(str.ToString(), "")

// return a list of characters
var c primitive.List

for _, x := range out {
c = append(c, primitive.Character(x))
}

return c
}

// expnFn implements "#"
func expnFn(env *env.Environment, args []primitive.Primitive) primitive.Primitive {
if len(args) != 2 {
Expand Down Expand Up @@ -1085,8 +1177,14 @@ func ordFn(env *env.Environment, args []primitive.Primitive) primitive.Primitive
return primitive.ArityError()
}

if _, ok := args[0].(primitive.String); !ok {
return primitive.Error("argument not a string")
// We work on strings, or characters
switch args[0].Type() {
case "character":
// nop
case "string":
// nop
default:
return primitive.Error(fmt.Sprintf("argument not a character/string, got %v", args[0].Type()))
}

// We convert this to an array of runes because we
Expand Down
Loading