Add support for loading namespaces via 'require' (symbol only)
At this stage we only supports requiring a namespace via it's name and only one namespace like `(require 'blah.blah)`. But the support for name aliases and requiring more namespaces at once would be easy enough.
This commit is contained in:
parent
a4cc6c0368
commit
21d15787c9
|
@ -0,0 +1,3 @@
|
||||||
|
(ns examples.hello-world)
|
||||||
|
|
||||||
|
(def hello-world (fn () 'helloworld))
|
|
@ -42,7 +42,9 @@ func evalForm(rt *Runtime, scope IScope, form IExpr) (IExpr, IError) {
|
||||||
// * If it's not a NSQS Look up the name in the current scope.
|
// * If it's not a NSQS Look up the name in the current scope.
|
||||||
// * Otherwise throw an error
|
// * Otherwise throw an error
|
||||||
case ast.Symbol:
|
case ast.Symbol:
|
||||||
symbolName := form.(*Symbol).GetName()
|
var nsName string
|
||||||
|
sym := form.(*Symbol)
|
||||||
|
symbolName := sym.GetName()
|
||||||
|
|
||||||
switch symbolName {
|
switch symbolName {
|
||||||
case "true":
|
case "true":
|
||||||
|
@ -52,10 +54,28 @@ func evalForm(rt *Runtime, scope IScope, form IExpr) (IExpr, IError) {
|
||||||
case "nil":
|
case "nil":
|
||||||
return &Nil, nil
|
return &Nil, nil
|
||||||
default:
|
default:
|
||||||
expr := scope.Lookup(symbolName)
|
var expr *Binding
|
||||||
|
if sym.IsNSQualified() {
|
||||||
|
if !rt.CurrentNS().hasExternal(sym.GetNSPart()) {
|
||||||
|
return nil, MakeErrorFor(rt, sym,
|
||||||
|
fmt.Sprintf("Namespace '%s' is no loaded", sym.GetNSPart()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
expr = rt.CurrentNS().LookupGlobal(sym)
|
||||||
|
nsName = sym.GetNSPart()
|
||||||
|
} else {
|
||||||
|
expr = scope.Lookup(symbolName)
|
||||||
|
nsName = rt.CurrentNS().GetName()
|
||||||
|
}
|
||||||
|
|
||||||
if expr == nil {
|
if expr == nil {
|
||||||
return nil, MakeRuntimeErrorf(rt, "can't resolve symbol '%s' in ns '%s'", symbolName, rt.CurrentNS().GetName())
|
return nil, MakeRuntimeErrorf(
|
||||||
|
rt,
|
||||||
|
"can't resolve symbol '%s' in ns '%s'",
|
||||||
|
symbolName,
|
||||||
|
nsName,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return expr.Value, nil
|
return expr.Value, nil
|
||||||
|
@ -122,6 +142,9 @@ tco:
|
||||||
body:
|
body:
|
||||||
for _, forms := range exprs {
|
for _, forms := range exprs {
|
||||||
// Evaluating forms one by one
|
// Evaluating forms one by one
|
||||||
|
if rt.IsDebugMode() {
|
||||||
|
fmt.Printf("[DEBUG] Evaluating forms in NS: %s, Forms: %s\n", rt.CurrentNS().GetName(), forms)
|
||||||
|
}
|
||||||
|
|
||||||
if forms.GetType() != ast.List {
|
if forms.GetType() != ast.List {
|
||||||
ret, err = evalForm(rt, scope, forms)
|
ret, err = evalForm(rt, scope, forms)
|
||||||
|
@ -171,6 +194,14 @@ tco:
|
||||||
|
|
||||||
switch sform {
|
switch sform {
|
||||||
|
|
||||||
|
case "ns":
|
||||||
|
ret, err = NSForm(rt, scope, list)
|
||||||
|
continue // return
|
||||||
|
|
||||||
|
case "require":
|
||||||
|
ret, err = RequireForm(rt, scope, list)
|
||||||
|
continue // return
|
||||||
|
|
||||||
// `quote` evaluation rules:
|
// `quote` evaluation rules:
|
||||||
// * Only takes one argument
|
// * Only takes one argument
|
||||||
// * Returns the argument without evaluating it
|
// * Returns the argument without evaluating it
|
||||||
|
@ -444,6 +475,7 @@ tco:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
break tco
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret, err
|
return ret, err
|
||||||
|
@ -469,3 +501,25 @@ func Eval(rt *Runtime, forms *Block) (IExpr, IError) {
|
||||||
|
|
||||||
return v, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func EvalNSBody(rt *Runtime, ns *Namespace) (*Namespace, IError) {
|
||||||
|
body := ns.getForms()
|
||||||
|
exprs := body.ToSlice()
|
||||||
|
|
||||||
|
if len(exprs) == 0 {
|
||||||
|
return nil, MakeError(rt, fmt.Sprintf("the 'ns' form is missing from '%s'", ns.GetName()))
|
||||||
|
}
|
||||||
|
|
||||||
|
if exprs[0].GetType() == ast.List {
|
||||||
|
firstForm := exprs[0].(*List).First()
|
||||||
|
if firstForm.GetType() == ast.Symbol && firstForm.(*Symbol).GetName() == "ns" {
|
||||||
|
_, err := EvalForms(rt, ns.GetRootScope(), body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ns, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, MakeError(rt, fmt.Sprintf("the 'ns' form is missing from '%s'", ns.GetName()))
|
||||||
|
}
|
||||||
|
|
|
@ -19,6 +19,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"serene-lang.org/bootstrap/pkg/ast"
|
"serene-lang.org/bootstrap/pkg/ast"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -28,7 +30,6 @@ import (
|
||||||
func Def(rt *Runtime, scope IScope, args *List) (IExpr, IError) {
|
func Def(rt *Runtime, scope IScope, args *List) (IExpr, IError) {
|
||||||
|
|
||||||
// TODO: Add support for docstrings and meta
|
// TODO: Add support for docstrings and meta
|
||||||
|
|
||||||
switch args.Count() {
|
switch args.Count() {
|
||||||
case 2:
|
case 2:
|
||||||
name := args.First()
|
name := args.First()
|
||||||
|
@ -124,3 +125,96 @@ func Fn(rt *Runtime, scope IScope, args *List) (IExpr, IError) {
|
||||||
|
|
||||||
return MakeFunction(scope, params, body), nil
|
return MakeFunction(scope, params, body), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NSForm(rt *Runtime, scope IScope, args *List) (IExpr, IError) {
|
||||||
|
if args.Count() == 1 {
|
||||||
|
return nil, MakeErrorFor(rt, args, "namespace's name is missing")
|
||||||
|
}
|
||||||
|
|
||||||
|
name := args.Rest().First()
|
||||||
|
|
||||||
|
if name.GetType() != ast.Symbol {
|
||||||
|
return nil, MakeErrorFor(rt, name, "the first argument to the 'ns' has to be a symbol")
|
||||||
|
}
|
||||||
|
nsName := name.(*Symbol).GetName()
|
||||||
|
|
||||||
|
if nsName != rt.CurrentNS().GetName() {
|
||||||
|
return nil, MakeErrorFor(
|
||||||
|
rt,
|
||||||
|
args,
|
||||||
|
fmt.Sprintf("the namespace '%s' doesn't match the file name.", nsName),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
ns, ok := rt.GetNS(nsName)
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
return nil, MakeErrorFor(rt, name, fmt.Sprintf("can't find the namespace '%s'. Is it the same as the file name?", nsName))
|
||||||
|
}
|
||||||
|
|
||||||
|
return ns, nil
|
||||||
|
// TODO: Handle the params like `require` and `meta`
|
||||||
|
// params := args.Rest().Rest()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func RequireForm(rt *Runtime, scope IScope, args *List) (IExpr, IError) {
|
||||||
|
switch args.Count() {
|
||||||
|
case 0:
|
||||||
|
return nil, MakeErrorFor(rt, args, "'require' special form is missing")
|
||||||
|
case 2:
|
||||||
|
default:
|
||||||
|
return nil, MakeErrorFor(rt, args.First(), "'require' special form needs exactly one argument")
|
||||||
|
}
|
||||||
|
|
||||||
|
ns, err := EvalForms(rt, scope, args.Rest().First())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ns.GetType() {
|
||||||
|
case ast.Symbol:
|
||||||
|
loadedNS, err := rt.RequireNS(ns.(*Symbol).GetName())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
prevNS := rt.CurrentNS()
|
||||||
|
|
||||||
|
rt.InsertNS(ns.(*Symbol).GetName(), loadedNS)
|
||||||
|
inserted := rt.setCurrentNS(loadedNS.GetName())
|
||||||
|
|
||||||
|
if !inserted {
|
||||||
|
return nil, MakeError(
|
||||||
|
rt,
|
||||||
|
fmt.Sprintf(
|
||||||
|
"the namespace '%s' didn't get inserted in the runtime.",
|
||||||
|
loadedNS.GetName()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
loadedNS, e := EvalNSBody(rt, loadedNS)
|
||||||
|
|
||||||
|
inserted = rt.setCurrentNS(prevNS.GetName())
|
||||||
|
|
||||||
|
if !inserted {
|
||||||
|
return nil, MakeError(
|
||||||
|
rt,
|
||||||
|
fmt.Sprintf(
|
||||||
|
"can't set the current ns back to '%s' from '%s'.",
|
||||||
|
prevNS.GetName(),
|
||||||
|
loadedNS.GetName()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if e != nil {
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
|
||||||
|
prevNS.setExternal(ns.(*Symbol).GetName(), loadedNS)
|
||||||
|
return loadedNS, nil
|
||||||
|
case ast.List:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
return nil, MakeError(rt, "NotImplemented")
|
||||||
|
}
|
||||||
|
|
1
dev.org
1
dev.org
|
@ -105,6 +105,7 @@ on ADF
|
||||||
*** TODO Struct implementation
|
*** TODO Struct implementation
|
||||||
*** TODO Error handling
|
*** TODO Error handling
|
||||||
- [ ] Integration with callstacks
|
- [ ] Integration with callstacks
|
||||||
|
- [ ] Stackable errors
|
||||||
*** TODO Multi arity functions
|
*** TODO Multi arity functions
|
||||||
*** TODO QuasiQuotation
|
*** TODO QuasiQuotation
|
||||||
*** TODO Linter
|
*** TODO Linter
|
||||||
|
|
Loading…
Reference in New Issue