Skip to content

Commit

Permalink
Simplify the way that hashes are evaluated.
Browse files Browse the repository at this point in the history
Rather than evaluate their keys *every* time they are used
we only do so once, at parse time, as we should have done
from the start.

This involved changing the signature of readExpression, but
as a purely internal function that's 100% acceptible.

This closes #95.
  • Loading branch information
skx committed Nov 13, 2022
1 parent ebd8879 commit 1931966
Showing 1 changed file with 32 additions and 35 deletions.
67 changes: 32 additions & 35 deletions eval/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ func (ev *Eval) Evaluate(e *env.Environment) primitive.Primitive {
// loop over all input
for {
// Get the next expression
expr, err := ev.readExpression()
expr, err := ev.readExpression(e)

if err != nil {
// End of list?
Expand Down Expand Up @@ -291,7 +291,7 @@ func (ev *Eval) eval(exp primitive.Primitive, e *env.Environment, expandMacro bo
// Behaviour depends on the type of the primitive/expression
// we've been given to execute.
//
switch obj := exp.(type) {
switch exp.(type) {

// Booleans return themselves
case primitive.Bool:
Expand All @@ -305,20 +305,9 @@ func (ev *Eval) eval(exp primitive.Primitive, e *env.Environment, expandMacro bo
case primitive.Error:
return exp

// Hashes return themselves, but the values should be
// evaluated - see #8.
// Hashes return themselves
case primitive.Hash:
ret := primitive.NewHash()

for x, y := range obj.Entries {

val := ev.eval(y, e, expandMacro)

ret.Set(x, val)
}

ret.SetStruct(obj.GetStruct())
return ret
return exp

// Numbers return themselves
case primitive.Number:
Expand Down Expand Up @@ -419,7 +408,7 @@ func (ev *Eval) eval(exp primitive.Primitive, e *env.Environment, expandMacro bo
//
// So we don't need an environment, etc.
//
out, err := tmp.readExpression()
out, err := tmp.readExpression(e)
if err != nil {
return primitive.Error(fmt.Sprintf("failed to read %s:%s", arg, err.Error()))
}
Expand Down Expand Up @@ -895,29 +884,32 @@ func (ev *Eval) eval(exp primitive.Primitive, e *env.Environment, expandMacro bo
// Is this a type-check on a struct?
if strings.HasSuffix(thing.ToString(), "?") {

// We're looking for a function-call
// that has a trailing "?", and one
// argument
if len(listExp) != 2 {
return primitive.ArityError()
}

// Get the thing that is being tested.
typeName := strings.TrimSuffix(thing.ToString(), "?")

// Does that represent a known-type?
_, ok2 := ev.structs[typeName]
if ok2 {

// OK now we're sure we're not colliding
// with another function test the argument
// count.
if len(listExp) != 2 {
return primitive.ArityError()
}

// OK a type-check on a known struct
//
// Note we evaluate the object
// Note we evaluate the object, because it
// was probably a symbol, or return object
// of some kind.
obj := ev.eval(listExp[1], e, expandMacro)

// is it a hash?
hsh, ok2 := obj.(primitive.Hash)
if !ok2 {
// nope - then not a struct
// nope - if it isn't a hash
// then it can't be a struct.
return primitive.Bool(false)
}

Expand All @@ -930,7 +922,9 @@ func (ev *Eval) eval(exp primitive.Primitive, e *env.Environment, expandMacro bo

// just a method call with a trailing "?".
//
// could be "string?", etc, so we fall-through
// could be "string?", "contains?", etc,
// so we fall-through and keep processing as
// per usual.
}

// Find the thing we're gonna call.
Expand Down Expand Up @@ -1213,7 +1207,7 @@ func (ev *Eval) quasiquote(exp primitive.Primitive) primitive.Primitive {

// readExpression uses recursion to read a complete expression from
// our internal array of tokens - as produced by `tokenize`.
func (ev *Eval) readExpression() (primitive.Primitive, error) {
func (ev *Eval) readExpression(e *env.Environment) (primitive.Primitive, error) {

// Have we walked off the end of the program?
if ev.offset >= len(ev.toks) {
Expand All @@ -1229,31 +1223,31 @@ func (ev *Eval) readExpression() (primitive.Primitive, error) {
switch token {
case "'":
// '... => (quote ...)
quoted, err := ev.readExpression()
quoted, err := ev.readExpression(e)
if err != nil {
return nil, err
}
return primitive.List{ev.atom("quote"), quoted}, nil

case "`":
// `... => (quasiquote ...)
quoted, err := ev.readExpression()
quoted, err := ev.readExpression(e)
if err != nil {
return nil, err
}
return primitive.List{ev.atom("quasiquote"), quoted}, nil

case "~", ",":
// ~... => (unquote ...)
quoted, err := ev.readExpression()
quoted, err := ev.readExpression(e)
if err != nil {
return nil, err
}
return primitive.List{ev.atom("unquote"), quoted}, nil

case "~@", "`,", ",@":
// ~@... => (splice-unquote ...)
quoted, err := ev.readExpression()
quoted, err := ev.readExpression(e)
if err != nil {
return nil, err
}
Expand All @@ -1275,7 +1269,7 @@ func (ev *Eval) readExpression() (primitive.Primitive, error) {
for ev.toks[ev.offset] != ")" {

// Read the sub-expressions, recursively.
expr, err := ev.readExpression()
expr, err := ev.readExpression(e)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -1309,7 +1303,7 @@ func (ev *Eval) readExpression() (primitive.Primitive, error) {
for ev.toks[ev.offset] != "}" {

// Read the sub-expressions, recursively.
key, err := ev.readExpression()
key, err := ev.readExpression(e)
if err != nil {
return nil, err
}
Expand All @@ -1320,7 +1314,7 @@ func (ev *Eval) readExpression() (primitive.Primitive, error) {
}

// Read the sub-expressions, recursively.
val, err2 := ev.readExpression()
val, err2 := ev.readExpression(e)
if err2 != nil {
return nil, err2
}
Expand All @@ -1330,7 +1324,10 @@ func (ev *Eval) readExpression() (primitive.Primitive, error) {
return nil, ErrEOF
}

hash.Set(key.ToString(), val)
// Ensure the value is evaluated
v := ev.eval(val, e, true)

hash.Set(key.ToString(), v)
}

// We bump the current read-position one more here,
Expand Down

0 comments on commit 1931966

Please sign in to comment.