spy16 / parens

Parens is a highly flexible and embeddable LISP toolkit. :computer:
33 stars 3 forks source link

Implement the interpreter in parens package #12

Closed spy16 closed 4 years ago

spy16 commented 4 years ago

@lthibault Another concern is usually VM is not the one doing the syntax-analysis πŸ˜…. I would like to use this thread for discussing the design before implementing it.

I am thinking following is the approach we can take:

  1. A parens.Interpreter or parens.Instance struct that contains the Analyser, Expander etc.
  2. Above type will have a Eval(form Any) (Any, error) method that performs macro-expansion and syntax analysis and dispatches it for eval under a context..
  3. It can also house a main-thread context that it uses to execut all forms. A GoExpr (or something equivalent) can create a child from that context and launch the eval further. (just initial thoughts)
type Interpreter struct {
    analyser Analyser
    expander Expander
    mainCtx Context
}
func (i *Interpreter) Eval(form Any) (Any, error) {
     expr, _ := expandAnalyse(form)
     return  expr.Eval(i.mainCtx)
}
lthibault commented 4 years ago

Another concern is usually VM is not the one doing the syntax-analysis

Why not just call it Evaluator? That's the SICP nomenclature, and it's consistent with Eval being its main method.

It can also house a main-thread context that it uses to execute all forms. A GoExpr (or something equivalent).

I need to think a bit about this, but my gut says "no". If we embed a context in an Interpreter/Evaluator, then it's no longer stateless. I think I would prefer to pass in a context explicitly, and make it the caller's responsibility to evaluate a form in the appropriate context.

I'm going to hack around a bit and come back with something concrete (probably tomorrow evening, your time).

spy16 commented 4 years ago

Regarding the context being passed by caller, I am bit unclear on how that would work. For example, when GoExpr is evaluated it would have to create a new context and launch the goroutine with that. Problem is, creation of this would be happening as part of GoExpr itself, so the actual caller would not have access to this context (Also, in most cases, child contexts would be derived automatically from a parent/root context I think).

In gopher-lua, NewThread derives a State from the state this method is invoked on and returns. And LState type also has a DoString() method that can parse-compile-eval a Lua source. I kinda like this approach because as a user, I wouldn't have to deal with multiple types.

I'm going to hack around a bit and come back with something concrete (probably tomorrow evening, your time).

Yes please, I would definitely like to know the flow you are thinking of.

lthibault commented 4 years ago

I am bit unclear on how that would work. For example, when GoExpr is evaluated it would have to create a new context and launch the goroutine with that.

Let me get back to you with something concrete. I have a half-formed plan and I need to work the problem a bit.

In the meantime, a quick question about this line in Zulu. Shouldn't a def expression be setting a value at the top of the stack? Have I missed something?

Edit: nevermind πŸ€¦β€β™‚οΈ I suppose the idea is that def registers a global symbol, so it should indeed be frame 0.