Skip to content

Commit

Permalink
WIP: struct support.
Browse files Browse the repository at this point in the history
This pull-request contains a proof of concept "struct" implementation,
which currently allows structures to be defined - as a list of fields,
and objects created assuming all fields are present.

This will close #82, once complete.

Sample usage:

      ;; define a structure
      (struct person name address)

      ;; create an instance
      (set! self (person "Steve Kemp" "My home address, Helsinki, Finland"))

      (print "struct.type is %s" (type self))
      (print "struct.address contains:%s" (get self "address"))
      (print "struct.name contains:%s" (get self "name"))

Here you'll notice that the struct is actually a hash, and we're
merely setting the named fields as key/val pairs.

This is pretty smart, all we've really done is recorded the fields
a structure should contain, and hooked into our execution to
instantiate a new hash when we see a named structure - before falling
back to invoking a command instead.

The bit that's missing?  We want to have "type?" support, and we
want to have accessors created.

type? support is trivial if we copy/paste primitive.Hash into
primitive.Struct and add a "type" member.  We just return:

      struct-%s, primitive.Type

However adding accessors get's tricky.  I could have another map
but getting type validaiton is gonna be a pain.  It also seems like
if I continue to abuse/reuse the Hash type we could use something
siumilar to allow:

        hash.field

To either get/set values and that feels like a useful piece of
syntatic sugar.  Will experiment before committing.
  • Loading branch information
skx committed Nov 12, 2022
1 parent 508f7c2 commit c137c6f
Showing 1 changed file with 47 additions and 1 deletion.
48 changes: 47 additions & 1 deletion eval/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ type Eval struct {

// aliases contains any record of aliased functionality
aliases map[string]string

// structs contains a list of known structures
structs map[string][]string
}

// New constructs a new lisp interpreter.
Expand All @@ -51,6 +54,7 @@ func New(src string) *Eval {
e := &Eval{
context: context.Background(),
symbols: make(map[string]primitive.Primitive),
structs: make(map[string][]string),
}

// Setup the default symbol-table entries
Expand Down Expand Up @@ -641,6 +645,27 @@ func (ev *Eval) eval(exp primitive.Primitive, e *env.Environment, expandMacro bo
}
return ev.atom(listExp[1].ToString())

// (structure
case primitive.Symbol("struct"):
if len(listExp) <= 2 {
return primitive.ArityError()
}

// name of structure
name := listExp[1].ToString()

// the fields it contains
fields := []string{}

// convert the fields to strings
for _, field := range listExp[2:] {
fields = append(fields, field.ToString())
}

// save the structure as a known-thing
ev.structs[name] = fields
return primitive.Nil{}

// (env
case primitive.Symbol("env"):

Expand Down Expand Up @@ -792,9 +817,30 @@ func (ev *Eval) eval(exp primitive.Primitive, e *env.Environment, expandMacro bo
}

// Anything else is either a built-in function,
// or a user-function.
// a structure, or a user-function.
default:

// Is this a structure creation?
fields, ok := ev.structs[listExp[0].ToString()]
if ok {

// args supplied to this call
args := listExp[1:]

if len(fields) != len(args) {
return primitive.ArityError()
}

// TODO: Distinct type
hash := primitive.NewHash()

// Set the fields
for i, name := range fields {
hash.Set(name, args[i])
}
return hash
}

// Find the thing we're gonna call.
procExp := ev.eval(listExp[0], e, expandMacro)

Expand Down

0 comments on commit c137c6f

Please sign in to comment.