spy16 / parens

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

Implement reader and repl packages. #19

Closed lthibault closed 4 years ago

lthibault commented 4 years ago

TODO:

lthibault commented 4 years ago

Cross-posting the comment that I had accidentally posted to #17 :


Added synchronization to stack frames in order to support GoExpr.

Keep in mind:

  1. Each Context is bound to a single goroutine (= not shared).
  2. A context's stack is copied when deriving a child context for a new goroutine.
  3. StackFrame holds a pointer to a map of values (via ConcurrentMap interface), so value maps are shared across goroutines and require synchronization.

Pros:

Cons: I can't think of any 😄

Edit: re-converting to draft because I'm still making minor improvements. Should nevertheless be testable/runnable.

spy16 commented 4 years ago

~I am doing some changes. Do you think I should go ahead on this branch or start from scratch or branch off from this and refactor ?~

I am adding stuff on the feature/interpreter branch for now since I didn't know the exact changes I was going to do 😅

spy16 commented 4 years ago

Also, Just to give you an idea on what I had in mind: I am thinking the Context should mostly be a closed type (except Eval, mostly private) and bunch of core Expr implementations that interact with internals of context and implement different functionality. Now, this approach will not leave much room for customisation at context level (except for whatever we make configurable through the Option params of New())..

This doesn't limit the user a lot since we are simply providing re-usable Exprs instead of public methods exposed on Context type. (For example, if user wants to bind a global variable, he/she can simply evaluate a DefExpr against a context)

Reasons for this:

With this, the usage patterns at context level remain same regardless of how the entire parens is being used (since, all forms turn into one of these Exprs by Analyser implementations). With that abstraction, we can make generic optimisations in the internals of Context (For example how we manage lifecycle of contexts and the gouroutine running against it) and stacks (we can experiment with stack implementations) and Exprs, without changing external behaviour.

This is similar to how different Lisps work. There are handful of special forms that are highly optimised. Everything else is built on top and it works great because the building blocks are solid.

To be honest, I was even tempted to define Expr as below (sealed). But this might be too limiting, so I guess it's okay to make it a publicly implementable interface which compose builtin exprs.

type Expr interface {
   eval(....) (...) // notice the private `eval`
}

(Another (arguably) good outcome is, if someone actually finds a way to optimise some builtin component, they will have to send a PR to get it integrated 😁 )

lthibault commented 4 years ago

~I am doing some changes. Do you think I should go ahead on this branch or start from scratch or branch off from this and refactor ?~

I am adding stuff on the feature/interpreter branch for now since I didn't know the exact changes I was going to do 😅

No worries! Let's shift discussion over to #20.