8000 Implement (body) and (source) from l1. by skx · Pull Request #132 · skx/yal · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Implement (body) and (source) from l1. #132

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

Merged
merged 4 commits into from
Apr 10, 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
4 changes: 4 additions & 0 deletions PRIMITIVES.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@ You can receive a full-list of these via `(builtins)`, this list will include:
* Trig. function.
* `base`
* Convert the specified integer to a string, in the given base.
* `body`
* Return the body of a lisp-function.
* `builtins`
* Return the list of built-in functions, implemented in golang.
* `car`
Expand Down Expand Up @@ -240,6 +242,8 @@ You can receive a full-list of these via `(builtins)`, this list will include:
* Trig. function.
* `sort`
* Sort the given list.
* `source`
* Return the source of a lisp-function.
* `specials`
* Return the list of built-in special-forms, implemented in golang.
* `split`
Expand Down
46 changes: 46 additions & 0 deletions builtins/builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ func PopulateEnvironment(env *env.Environment) {
registerBuiltin(env, "asin", &primitive.Procedure{F: asinFn, Help: helpMap["asin"], Args: []primitive.Symbol{primitive.Symbol("n")}})
registerBuiltin(env, "atan", &primitive.Procedure{F: atanFn, Help: helpMap["atan"], Args: []primitive.Symbol{primitive.Symbol("n")}})
registerBuiltin(env, "base", &primitive.Procedure{F: baseFn, Help: helpMap["base"], Args: []primitive.Symbol{primitive.Symbol("number"), primitive.Symbol("base")}})
registerBuiltin(env, "body", &primitive.Procedure{F: bodyFn, Help: helpMap["body"], Args: []primitive.Symbol{primitive.Symbol("function")}})
registerBuiltin(env, "builtins", &primitive.Procedure{F: builtinsFn, Help: helpMap["builtins"], Args: []primitive.Symbol{}})
registerBuiltin(env, "car", &primitive.Procedure{F: carFn, Help: helpMap["car"], Args: []primitive.Symbol{primitive.Symbol("list")}})
registerBuiltin(env, "cdr", &primitive.Procedure{F: cdrFn, Help: helpMap["cdr"], Args: []primitive.Symbol{primitive.Symbol("list")}})
Expand Down Expand Up @@ -170,6 +171,7 @@ func PopulateEnvironment(env *env.Environment) {
registerBuiltin(env, "sin", &primitive.Procedure{F: sinFn, Help: helpMap["sin"], Args: []primitive.Symbol{primitive.Symbol("n")}})
registerBuiltin(env, "sinh", &primitive.Procedure{F: sinhFn, Help: helpMap["sinh"], Args: []primitive.Symbol{primitive.Symbol("n")}})
registerBuiltin(env, "sort", &primitive.Procedure{F: sortFn, Help: helpMap["sort"], Args: []primitive.Symbol{primitive.Symbol("list")}})
registerBuiltin(env, "source", &primitive.Procedure{F: sourceFn, Help: helpMap["source"], Args: []primitive.Symbol{primitive.Symbol("symbol")}})
registerBuiltin(env, "specials", &primitive.Procedure{F: specialsFn, Help: helpMap["specials"], Args: []primitive.Symbol{}})
registerBuiltin(env, "split", &primitive.Procedure{F: splitFn, Help: helpMap["split"], Args: []primitive.Symbol{primitive.Symbol("str"), primitive.Symbol("by")}})
registerBuiltin(env, "sprintf", &primitive.Procedure{F: sprintfFn, Help: helpMap["sprintf"], Args: []primitive.Symbol{primitive.Symbol("arg1..argN")}})
Expand Down Expand Up @@ -266,6 +268,28 @@ func baseFn(env *env.Environment, args []primitive.Primitive) primitive.Primitiv
10000 return primitive.String(strconv.FormatInt(int64(n), int(base)))
}

// bodyFn implements (body)
func bodyFn(env *env.Environment, args []primitive.Primitive) primitive.Primitive {
// We need a single argument
if len(args) != 1 {
return primitive.ArityError()
}

// Which is a function
proc, ok := args[0].(*primitive.Procedure)

if !ok {
return primitive.Error("argument not a procedure")
}

if proc.F != nil {
return primitive.Error("procedure is implemented in golang")
}

// Return value
return primitive.String( proc.Body.ToString())
}

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

Expand Down Expand Up @@ -1764,6 +1788,28 @@ func sortFn(env *env.Environment, args []primitive.Primitive) primitive.Primitiv

}

// sourceFn implements (source)
func sourceFn(env *env.Environment, args []primitive.Primitive) primitive.Primitive {
// We need a single argument
if len(args) != 1 {
return primitive.ArityError()
}

// Which is a function
proc, ok := args[0].(*primitive.Procedure)

if !ok {
return primitive.Error("argument not a procedure")
}

if proc.F != nil {
return primitive.Error("procedure is implemented in golang")
}

// Return value
return primitive.String( proc.ToString())
}

// (specials
func specialsFn(env *env.Environment, args []primitive.Primitive) primitive.Primitive {

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


// TestBody tests (body)
func TestBody(t *testing.T) {
// no arguments
out := bodyFn(ENV, []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")
}

// First argument must be a procedure
out = bodyFn(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 procedure") {
t.Fatalf("got error, but wrong one %v", out)
}

//
// create a new environment, and populate it
//
env := env.New()
PopulateEnvironment(env)

for _, name := range []string{"inc", "dec"} {

// Load our standard library
st := stdlib.Contents()
std := string(st)

// Create a new interpreter
l := eval.New(std + "\n")

l.Evaluate(env)

fn, ok2 := env.Get(name)
if !ok2 {
t.Fatalf("failed to lookup function %s in environment", name)
}

result := bodyFn(ENV, []primitive.Primitive{fn.(*primitive.Procedure)})

txt, ok3 := result.(primitive.String)
if !ok3 {
t.Fatalf("expected a string, got %v", result)
}
if !strings.Contains(txt.ToString(), "(") {
t.Fatalf("got body text, but didn't find reasonable content: %v", result)
}
}

// Finally we get an error if we try to get the body of a golang
// primitive
fn, ok := env.Get("print")
if !ok {
t.Fatalf("failed to lookup function 'print' in environment")
}


result := bodyFn(ENV, []primitive.Primitive{fn.(*primitive.Procedure)})

_, ok2 := result.(primitive.Error)
if !ok2 {
t.Fatalf("expected an error, got %v", result)
}
}

// Test (builtins
func TestBuiltins(t *testing.T) {

Expand Down Expand Up @@ -3359,6 +3436,82 @@ func TestSort(t *testing.T) {

}

// TestSource tests (source)
func TestSource(t *testing.T) {
// no arguments
out := sourceFn(ENV, []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")
}

// First argument must be a procedure
out = sourceFn(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 procedure") {
t.Fatalf("got error, but wrong one %v", out)
}

//
// create a new environment, and populate it
//
env := env.New()
PopulateEnvironment(env)

for _, name := range []string{"inc", "dec"} {

// Load our standard library
st := stdlib.Contents()
std := string(st)

// Create a new interpreter
l := eval.New(std + "\n")

l.Evaluate(env)

fn, ok2 := env.Get(name)
if !ok2 {
t.Fatalf("failed to lookup function %s in environment", name)
}

result := sourceFn(ENV, []primitive.Primitive{fn.(*primitive.Procedure)})

txt, ok3 := result.(primitive.String)
if !ok3 {
t.Fatalf("expected a string, got %v", result)
}
if !strings.Contains(txt.ToString(), "(") {
t.Fatalf("got body text, but didn't find reasonable content: %v", result)
}
}

// Finally we get an error if we try to get the body of a golang
// primitive
fn, ok := env.Get("print")
if !ok {
t.Fatalf("failed to lookup function 'print' in environment")
}

result := sourceFn(ENV, []primitive.Primitive{fn.(*primitive.Procedure)})

_, ok2 := result.(primitive.Error)
if !ok2 {
t.Fatalf("expected an error, got %v", result)
}
}

func TestSpecials(t *testing.T) {

// No arguments
Expand Down
17 changes: 17 additions & 0 deletions builtins/help.txt
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@ See also: number, sprintf, str
Example: (print (base 255 2)) ; base two is binary
Example: (print (base 255 16)) ; base 16 is hexadecimal
%%
body

Return the body of a function implemented in lisp.

See also: help, source

Example: (print (body inc))
%%
builtins

Return a list of all functions builtin to our golang interpreter. This
Expand Down Expand Up @@ -229,6 +237,7 @@ help

help returns any help associated with the item specified as the single argument.

See also: body, source
Example: (print (help print))
%%
join
Expand Down Expand Up @@ -397,6 +406,14 @@ see also sort-by.

Example: (print (sort 3 43 1 "Steve" "Adam"))
%%
source

source will output the source of the given (lisp) function.

See also: body, help

Example: (print (source random:char))
%%
specials

Return a list of all functions which are implemented within our golang
Expand Down
0