Record the caller details in the stack frame
This commit is contained in:
parent
3904b050bb
commit
9d106d4278
|
@ -9,7 +9,7 @@
|
|||
(name args &body)
|
||||
(list 'def name (cons 'fn (cons args body))))
|
||||
|
||||
(defn pp (x)
|
||||
(defn pp (x y)
|
||||
(println x))
|
||||
|
||||
(def main
|
||||
|
|
|
@ -4,6 +4,7 @@ go 1.15
|
|||
|
||||
require (
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e
|
||||
github.com/gookit/color v1.3.5
|
||||
github.com/rjeczalik/pkgconfig v0.0.0-20190903131546-94d388dab445
|
||||
github.com/spf13/cobra v1.1.1
|
||||
github.com/spf13/viper v1.7.1
|
||||
|
|
|
@ -68,6 +68,8 @@ github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OI
|
|||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/gookit/color v1.3.5 h1:1nszcmDVrfti1Su5fhtuS5YBs/Xs6v8UIi0bJ/2oDHY=
|
||||
github.com/gookit/color v1.3.5/go.mod h1:GqqLKF1le3EfrbHbYsYa5WdLqfc/PHMdMRbt6tMnqIc=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
|
|
|
@ -20,6 +20,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
// Serene's AST.
|
||||
package ast
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type NodeType int
|
||||
|
||||
const (
|
||||
|
@ -40,13 +45,42 @@ const (
|
|||
|
||||
)
|
||||
|
||||
type Source struct {
|
||||
Buffer *[]string
|
||||
// It can be the path to the source file or something like "*in*"
|
||||
// for standard in
|
||||
Path string
|
||||
LineIndex *[]int
|
||||
}
|
||||
|
||||
func (s *Source) GetLine(linenum int) string {
|
||||
lines := strings.Split(strings.Join(*s.Buffer, ""), "\n")
|
||||
return lines[linenum-1]
|
||||
}
|
||||
|
||||
func (s *Source) LineNumberFor(pos int) int {
|
||||
if pos < 0 {
|
||||
return -1
|
||||
}
|
||||
|
||||
return sort.Search(len(*s.LineIndex), func(i int) bool {
|
||||
if i == 0 {
|
||||
return pos < (*s.LineIndex)[i]
|
||||
} else {
|
||||
return (*s.LineIndex)[i-1] < pos && pos < (*s.LineIndex)[i]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
type Location struct {
|
||||
start int
|
||||
end int
|
||||
source *[]string
|
||||
source Source
|
||||
knownLocation bool
|
||||
}
|
||||
|
||||
var UnknownLocation *Location = &Location{knownLocation: false}
|
||||
|
||||
func (l *Location) GetStart() int {
|
||||
return l.start
|
||||
}
|
||||
|
@ -55,8 +89,42 @@ func (l *Location) GetEnd() int {
|
|||
return l.end
|
||||
}
|
||||
|
||||
func (l *Location) GetSource() *[]string {
|
||||
return l.source
|
||||
func (l *Location) GetSource() *Source {
|
||||
return &l.source
|
||||
}
|
||||
|
||||
func (l *Location) IncStart(x int) {
|
||||
if x+l.start < len(*l.source.Buffer) {
|
||||
l.start += x
|
||||
} else {
|
||||
l.start = len(*l.source.Buffer) - 1
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Location) DecStart(x int) {
|
||||
if l.start-x >= 0 {
|
||||
l.start -= x
|
||||
} else {
|
||||
l.start = 0
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (l *Location) IncEnd(x int) {
|
||||
if x+l.end < len(*l.source.Buffer) {
|
||||
l.end += x
|
||||
} else {
|
||||
l.end = len(*l.source.Buffer) - 1
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (l *Location) DecEnd(x int) {
|
||||
if l.end-x >= 0 {
|
||||
l.end -= x
|
||||
} else {
|
||||
l.end = 0
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Location) IsKnownLocaiton() bool {
|
||||
|
@ -64,12 +132,12 @@ func (l *Location) IsKnownLocaiton() bool {
|
|||
}
|
||||
|
||||
type ILocatable interface {
|
||||
GetLocation() Location
|
||||
GetLocation() *Location
|
||||
}
|
||||
|
||||
func MakeLocation(input *[]string, start int, end int) Location {
|
||||
return Location{
|
||||
source: input,
|
||||
func MakeLocation(input *Source, start int, end int) *Location {
|
||||
return &Location{
|
||||
source: *input,
|
||||
start: start,
|
||||
end: end,
|
||||
knownLocation: true,
|
||||
|
@ -80,8 +148,6 @@ type ITypable interface {
|
|||
GetType() NodeType
|
||||
}
|
||||
|
||||
func MakeUnknownLocation() Location {
|
||||
return Location{
|
||||
knownLocation: false,
|
||||
}
|
||||
func MakeUnknownLocation() *Location {
|
||||
return UnknownLocation
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ func (b *Block) ToDebugStr() string {
|
|||
return fmt.Sprintf("%#v", b)
|
||||
}
|
||||
|
||||
func (b *Block) GetLocation() ast.Location {
|
||||
func (b *Block) GetLocation() *ast.Location {
|
||||
if len(b.body) > 0 {
|
||||
return b.body[0].GetLocation()
|
||||
}
|
||||
|
|
|
@ -53,9 +53,9 @@ func PrintlnNativeFn(rt *Runtime, scope IScope, n Node, args *List) (IExpr, IErr
|
|||
func RequireNativeFn(rt *Runtime, scope IScope, n Node, args *List) (IExpr, IError) {
|
||||
switch args.Count() {
|
||||
case 0:
|
||||
return nil, MakeErrorFor(rt, args, "'require' function is missing")
|
||||
return nil, MakeError(rt, args, "'require' function is missing")
|
||||
case 1:
|
||||
return nil, MakeErrorFor(rt, args.First(), "'require' function needs at least one argument")
|
||||
return nil, MakeError(rt, args.First(), "'require' function needs at least one argument")
|
||||
default:
|
||||
}
|
||||
|
||||
|
@ -74,7 +74,7 @@ func RequireNativeFn(rt *Runtime, scope IScope, n Node, args *List) (IExpr, IErr
|
|||
|
||||
func HashNativeFn(rt *Runtime, scope IScope, n Node, args *List) (IExpr, IError) {
|
||||
if args.Count() != 2 {
|
||||
return nil, MakeErrorFor(rt, args.First(), "'hash' function needs exactly one argument")
|
||||
return nil, MakeError(rt, args.First(), "'hash' function needs exactly one argument")
|
||||
}
|
||||
|
||||
expr := args.Rest().First()
|
||||
|
|
|
@ -33,38 +33,51 @@ package core
|
|||
// compare the stack items by their address, identity and location.
|
||||
// * Add support for iteration on the stack.
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type ICallStack interface {
|
||||
// Push the given callable `f` to the stack
|
||||
Push(f IFn) IError
|
||||
Pop() FnCall
|
||||
Pop() *Frame
|
||||
Peek() *Frame
|
||||
Count() uint
|
||||
}
|
||||
|
||||
type FnCall struct {
|
||||
Fn IFn
|
||||
type Frame struct {
|
||||
// Number of recursive calls to this function
|
||||
count uint
|
||||
Count uint
|
||||
Fn IFn
|
||||
Caller IExpr
|
||||
}
|
||||
|
||||
type TraceBack = []Frame
|
||||
|
||||
type CallStackItem struct {
|
||||
prev *CallStackItem
|
||||
data FnCall
|
||||
data Frame
|
||||
}
|
||||
|
||||
type CallStack struct {
|
||||
debug bool
|
||||
head *CallStackItem
|
||||
count uint
|
||||
debug bool
|
||||
}
|
||||
|
||||
func (c *CallStack) Count() uint {
|
||||
return c.count
|
||||
}
|
||||
|
||||
func (c *CallStack) Push(f IFn) IError {
|
||||
func (c *CallStack) GetCurrentFn() IFn {
|
||||
if c.head == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return c.head.data.Fn
|
||||
}
|
||||
|
||||
func (c *CallStack) Push(caller IExpr, f IFn) IError {
|
||||
if c.debug {
|
||||
fmt.Println("[Stack] -->", f)
|
||||
}
|
||||
|
@ -73,12 +86,17 @@ func (c *CallStack) Push(f IFn) IError {
|
|||
return MakePlainError("Can't push 'nil' pointer to the call stack.")
|
||||
}
|
||||
|
||||
if caller == nil {
|
||||
return MakePlainError("Can't push 'nil' pointer to the call stack for the caller.")
|
||||
}
|
||||
|
||||
// Empty Stack
|
||||
if c.head == nil {
|
||||
c.head = &CallStackItem{
|
||||
data: FnCall{
|
||||
Fn: f,
|
||||
count: 0,
|
||||
data: Frame{
|
||||
Fn: f,
|
||||
Caller: caller,
|
||||
Count: 0,
|
||||
},
|
||||
}
|
||||
c.count++
|
||||
|
@ -87,15 +105,16 @@ func (c *CallStack) Push(f IFn) IError {
|
|||
nodeData := &c.head.data
|
||||
|
||||
// If the same function was on top of the stack
|
||||
if nodeData.Fn == f {
|
||||
if nodeData.Fn == f && caller == nodeData.Caller {
|
||||
// TODO: expand the check here to support address and location as well
|
||||
nodeData.count++
|
||||
nodeData.Count++
|
||||
} else {
|
||||
c.head = &CallStackItem{
|
||||
prev: c.head,
|
||||
data: FnCall{
|
||||
Fn: f,
|
||||
count: 0,
|
||||
data: Frame{
|
||||
Fn: f,
|
||||
Caller: caller,
|
||||
Count: 0,
|
||||
},
|
||||
}
|
||||
c.count++
|
||||
|
@ -103,7 +122,7 @@ func (c *CallStack) Push(f IFn) IError {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *CallStack) Pop() *FnCall {
|
||||
func (c *CallStack) Pop() *Frame {
|
||||
if c.head == nil {
|
||||
if c.debug {
|
||||
fmt.Println("[Stack] <-- nil")
|
||||
|
@ -120,6 +139,32 @@ func (c *CallStack) Pop() *FnCall {
|
|||
return &result.data
|
||||
}
|
||||
|
||||
func (c *CallStack) Peek() *Frame {
|
||||
if c.head == nil {
|
||||
if c.debug {
|
||||
fmt.Println("[Stack] <-- nil")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
result := c.head
|
||||
return &result.data
|
||||
}
|
||||
|
||||
func (c *CallStack) ToTraceBack() *TraceBack {
|
||||
var tr TraceBack
|
||||
item := c.head
|
||||
for {
|
||||
if item == nil {
|
||||
break
|
||||
}
|
||||
tr = append(tr, item.data)
|
||||
item = item.prev
|
||||
}
|
||||
|
||||
return &tr
|
||||
}
|
||||
|
||||
func MakeCallStack(debugMode bool) CallStack {
|
||||
return CallStack{
|
||||
count: 0,
|
||||
|
|
|
@ -23,13 +23,14 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/chzyer/readline"
|
||||
"serene-lang.org/bootstrap/pkg/ast"
|
||||
)
|
||||
|
||||
func rep(rt *Runtime, line string) {
|
||||
ast, err := ReadString(line)
|
||||
ast, err := ReadString("*REPL*", line)
|
||||
|
||||
if err != nil {
|
||||
PrintError(rt, err)
|
||||
|
@ -120,7 +121,17 @@ func Run(flags map[string]bool, args []string) {
|
|||
}
|
||||
|
||||
ns := args[0]
|
||||
loadedNS, err := requireNS(rt, ns)
|
||||
nsAsBuffer := strings.Split(ns, "")
|
||||
source := &ast.Source{Buffer: &nsAsBuffer, Path: "*input-argument*"}
|
||||
node := MakeNode(source, 0, len(ns))
|
||||
nsSym, err := MakeSymbol(node, ns)
|
||||
|
||||
if err != nil {
|
||||
PrintError(rt, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
loadedNS, err := requireNS(rt, nsSym)
|
||||
|
||||
if err != nil {
|
||||
PrintError(rt, err)
|
||||
|
@ -133,6 +144,7 @@ func Run(flags map[string]bool, args []string) {
|
|||
if !inserted {
|
||||
err := MakeError(
|
||||
rt,
|
||||
loadedNS,
|
||||
fmt.Sprintf(
|
||||
"the namespace '%s' didn't get inserted in the runtime.",
|
||||
loadedNS.GetName()),
|
||||
|
@ -163,24 +175,22 @@ func Run(flags map[string]bool, args []string) {
|
|||
mainFn := mainBinding.Value.(*Function)
|
||||
|
||||
var fnArgs []IExpr
|
||||
var argNode Node
|
||||
|
||||
if len(args) > 1 {
|
||||
for _, arg := range args[1:] {
|
||||
node := MakeNodeFromExpr(mainFn)
|
||||
fnArgs = append(fnArgs, MakeString(node, arg))
|
||||
}
|
||||
argNode = MakeNodeFromExprs(fnArgs)
|
||||
} else {
|
||||
argNode = node
|
||||
}
|
||||
|
||||
//rt.Stack.Push(mainFn)
|
||||
_, err = mainFn.Apply(rt, loadedNS.GetRootScope(), mainFn.Node, MakeList(fnArgs))
|
||||
_, err = mainFn.Apply(rt, loadedNS.GetRootScope(), mainFn.Node, MakeList(argNode, fnArgs))
|
||||
|
||||
if err != nil {
|
||||
PrintError(rt, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// rt.Stack.Pop()
|
||||
// if rt.Stack.Count() != 0 {
|
||||
// panic("Call stack is not empty.")
|
||||
// }
|
||||
}
|
||||
|
|
|
@ -49,6 +49,7 @@ type IError interface {
|
|||
IRepresentable
|
||||
IDebuggable
|
||||
|
||||
GetStackTrace() *TraceBack
|
||||
// To wrap Golan rrrrors
|
||||
WithError(err error) IError
|
||||
|
||||
|
@ -62,6 +63,7 @@ type Error struct {
|
|||
Node
|
||||
WrappedErr error
|
||||
msg string
|
||||
trace *TraceBack
|
||||
}
|
||||
|
||||
func (e *Error) String() string {
|
||||
|
@ -89,32 +91,25 @@ func (e *Error) Error() string {
|
|||
return e.msg
|
||||
}
|
||||
|
||||
func (e *Error) GetStackTrace() *TraceBack {
|
||||
return e.trace
|
||||
}
|
||||
|
||||
func MakePlainError(msg string) IError {
|
||||
return &Error{
|
||||
msg: msg,
|
||||
}
|
||||
}
|
||||
|
||||
// MakeError creates an Error without any location.
|
||||
func MakeError(rt *Runtime, msg string) IError {
|
||||
return MakePlainError(msg)
|
||||
}
|
||||
|
||||
// MakeErrorFor creates an Error which points to the given IExpr `e` as
|
||||
// MakeError creates an Error which points to the given IExpr `e` as
|
||||
// the root of the error.
|
||||
func MakeErrorFor(rt *Runtime, e IExpr, msg string) IError {
|
||||
loc := e.GetLocation()
|
||||
func MakeError(rt *Runtime, e IExpr, msg string) IError {
|
||||
trace := append(*rt.Stack.ToTraceBack(), Frame{0, rt.Stack.GetCurrentFn(), e})
|
||||
|
||||
return &Error{
|
||||
Node: MakeNodeFromLocation(loc),
|
||||
msg: msg,
|
||||
}
|
||||
}
|
||||
|
||||
//MakeRuntimeErrorf is a helper function which works like `fmt.Errorf`
|
||||
func MakeRuntimeErrorf(rt *Runtime, msg string, a ...interface{}) IError {
|
||||
return &Error{
|
||||
msg: fmt.Sprintf(msg, a...),
|
||||
Node: MakeNodeFromExpr(e),
|
||||
msg: msg,
|
||||
trace: &trace,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -84,7 +84,7 @@ func evalForm(rt *Runtime, scope IScope, form IExpr) (IExpr, IError) {
|
|||
if sym.IsNSQualified() {
|
||||
// Whether a namespace with the given alias loaded or not
|
||||
if !rt.CurrentNS().hasExternal(sym.GetNSPart()) {
|
||||
return nil, MakeErrorFor(rt, sym,
|
||||
return nil, MakeError(rt, sym,
|
||||
fmt.Sprintf("Namespace '%s' is no loaded", sym.GetNSPart()),
|
||||
)
|
||||
}
|
||||
|
@ -97,11 +97,14 @@ func evalForm(rt *Runtime, scope IScope, form IExpr) (IExpr, IError) {
|
|||
}
|
||||
|
||||
if expr == nil {
|
||||
return nil, MakeRuntimeErrorf(
|
||||
return nil, MakeError(
|
||||
rt,
|
||||
"can't resolve symbol '%s' in ns '%s'",
|
||||
symbolName,
|
||||
nsName,
|
||||
sym,
|
||||
fmt.Sprintf(
|
||||
"can't resolve symbol '%s' in ns '%s'",
|
||||
symbolName,
|
||||
nsName,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -126,11 +129,12 @@ func evalForm(rt *Runtime, scope IScope, form IExpr) (IExpr, IError) {
|
|||
break
|
||||
}
|
||||
}
|
||||
return MakeList(result), nil
|
||||
|
||||
return MakeList(MakeNodeFromExpr(lst), result), nil
|
||||
}
|
||||
|
||||
// Default case
|
||||
return nil, MakeError(rt, fmt.Sprintf("support for '%d' is not implemented", form.GetType()))
|
||||
return nil, MakeError(rt, form, fmt.Sprintf("support for '%d' is not implemented", form.GetType()))
|
||||
}
|
||||
|
||||
// EvalForms evaluates the given expr `expressions` (it can be a list, block, symbol or anything else)
|
||||
|
@ -185,7 +189,6 @@ tco:
|
|||
|
||||
body:
|
||||
for i := 0; i < len(exprs); i++ {
|
||||
//for i, forms := range exprs {
|
||||
forms := exprs[i]
|
||||
executionScope := forms.GetExecutionScope()
|
||||
scope := scope
|
||||
|
@ -275,7 +278,7 @@ tco:
|
|||
case "quote":
|
||||
// Including the `quote` itself
|
||||
if list.Count() != 2 {
|
||||
return nil, MakeErrorFor(rt, list, "'quote' quote only accepts one argument.")
|
||||
return nil, MakeError(rt, list, "'quote' quote only accepts one argument.")
|
||||
}
|
||||
ret = list.Rest().First()
|
||||
err = nil
|
||||
|
@ -310,12 +313,18 @@ tco:
|
|||
result := []IExpr{}
|
||||
for _, lst := range lists {
|
||||
if lst.GetType() != ast.List {
|
||||
return nil, MakeErrorFor(rt, lst, fmt.Sprintf("don't know how to concat '%s'", lst.String()))
|
||||
return nil, MakeError(rt, lst, fmt.Sprintf("don't know how to concat '%s'", lst.String()))
|
||||
}
|
||||
|
||||
result = append(result, lst.(*List).ToSlice()...)
|
||||
}
|
||||
ret, err = MakeList(result), nil
|
||||
|
||||
node := MakeNodeFromExpr(list)
|
||||
if len(result) > 0 {
|
||||
node = MakeNodeFromExprs(result)
|
||||
}
|
||||
|
||||
ret, err = MakeList(node, result), nil
|
||||
continue body // no rewrite
|
||||
|
||||
// TODO: Implement `list` in serene itself when we have destructuring available
|
||||
|
@ -324,7 +333,7 @@ tco:
|
|||
// given in the second argument.
|
||||
case "cons":
|
||||
if list.Count() != 3 {
|
||||
return nil, MakeErrorFor(rt, list, "'cons' needs exactly 3 arguments")
|
||||
return nil, MakeError(rt, list, "'cons' needs exactly 3 arguments")
|
||||
}
|
||||
|
||||
evaledForms, err := evalForm(rt, scope, list.Rest().(*List))
|
||||
|
@ -335,7 +344,7 @@ tco:
|
|||
coll, ok := evaledForms.(*List).Rest().First().(IColl)
|
||||
|
||||
if !ok {
|
||||
return nil, MakeErrorFor(rt, list, "second arg of 'cons' has to be a collection")
|
||||
return nil, MakeError(rt, list, "second arg of 'cons' has to be a collection")
|
||||
}
|
||||
|
||||
ret, err = coll.Cons(evaledForms.(*List).First()), nil
|
||||
|
@ -366,7 +375,7 @@ tco:
|
|||
// as a macro and returns the expanded forms.
|
||||
case "macroexpand":
|
||||
if list.Count() != 2 {
|
||||
return nil, MakeErrorFor(rt, list, "'macroexpand' needs exactly one argument.")
|
||||
return nil, MakeError(rt, list, "'macroexpand' needs exactly one argument.")
|
||||
}
|
||||
evaledForm, e := evalForm(rt, scope, list.Rest().(*List))
|
||||
|
||||
|
@ -381,7 +390,7 @@ tco:
|
|||
// * It needs at least a collection of arguments
|
||||
// * Defines an anonymous function.
|
||||
case "fn":
|
||||
ret, err = Fn(rt, scope, list.Rest().(*List))
|
||||
ret, err = Fn(rt, scope, list)
|
||||
continue body // no rewrite
|
||||
|
||||
// `if` evaluation rules:
|
||||
|
@ -392,7 +401,7 @@ tco:
|
|||
case "if":
|
||||
args := list.Rest().(*List)
|
||||
if args.Count() != 3 {
|
||||
return nil, MakeError(rt, "'if' needs exactly 3 aruments")
|
||||
return nil, MakeError(rt, args, "'if' needs exactly 3 aruments")
|
||||
}
|
||||
|
||||
pred, err := EvalForms(rt, scope, args.First())
|
||||
|
@ -436,7 +445,7 @@ tco:
|
|||
// the result.
|
||||
case "eval":
|
||||
if list.Count() != 2 {
|
||||
return nil, MakeErrorFor(rt, list, "'eval' needs exactly 1 arguments")
|
||||
return nil, MakeError(rt, list, "'eval' needs exactly 1 arguments")
|
||||
}
|
||||
form, err := evalForm(rt, scope, list.Rest().(*List))
|
||||
if err != nil {
|
||||
|
@ -457,7 +466,7 @@ tco:
|
|||
// which is the result of the last expre in `BODY`
|
||||
case "let":
|
||||
if list.Count() < 2 {
|
||||
return nil, MakeError(rt, "'let' needs at list 1 aruments")
|
||||
return nil, MakeError(rt, list, "'let' needs at list 1 aruments")
|
||||
}
|
||||
|
||||
letScope := MakeScope(scope.(*Scope))
|
||||
|
@ -470,7 +479,7 @@ tco:
|
|||
body := list.Rest().Rest().(*List).ToSlice()
|
||||
|
||||
if bindings.Count()%2 != 0 {
|
||||
return nil, MakeError(rt, "'let' bindings has to have even number of forms.")
|
||||
return nil, MakeError(rt, list.Rest().First(), "'let' bindings has to have even number of forms.")
|
||||
}
|
||||
|
||||
for {
|
||||
|
@ -485,7 +494,7 @@ tco:
|
|||
// TODO: We need to destruct the bindings here and remove this check
|
||||
// for the symbol type
|
||||
if name.GetType() != ast.Symbol {
|
||||
err := MakeErrorFor(rt, name, "'let' doesn't support desbbtructuring yet, use a symbol.")
|
||||
err := MakeError(rt, name, "'let' doesn't support desbbtructuring yet, use a symbol.")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -546,7 +555,7 @@ tco:
|
|||
break body //return
|
||||
}
|
||||
|
||||
rt.Stack.Push(fn)
|
||||
rt.Stack.Push(list, fn)
|
||||
body := append(
|
||||
fn.GetBody().ToSlice(),
|
||||
// Add the PopStack instruction to clean up the stack after
|
||||
|
@ -561,7 +570,8 @@ tco:
|
|||
// by the `NativeFunction` struct
|
||||
case ast.NativeFn:
|
||||
fn := f.(*NativeFunction)
|
||||
rt.Stack.Push(fn)
|
||||
|
||||
rt.Stack.Push(list, fn)
|
||||
ret, err = fn.Apply(
|
||||
rt,
|
||||
scope,
|
||||
|
@ -572,7 +582,7 @@ tco:
|
|||
continue body // no rewrite
|
||||
|
||||
default:
|
||||
err = MakeError(rt, "don't know how to execute anything beside function")
|
||||
err = MakeError(rt, f, "don't know how to execute anything beside function")
|
||||
ret = nil
|
||||
break tco
|
||||
}
|
||||
|
@ -613,7 +623,7 @@ func EvalNSBody(rt *Runtime, ns *Namespace) (*Namespace, IError) {
|
|||
exprs := body.ToSlice()
|
||||
|
||||
if len(exprs) == 0 {
|
||||
return nil, MakeError(rt, fmt.Sprintf("the 'ns' form is missing from '%s'", ns.GetName()))
|
||||
return nil, MakeError(rt, ns, fmt.Sprintf("the 'ns' form is missing from '%s'", ns.GetName()))
|
||||
}
|
||||
|
||||
if exprs[0].GetType() == ast.List {
|
||||
|
@ -627,5 +637,5 @@ func EvalNSBody(rt *Runtime, ns *Namespace) (*Namespace, IError) {
|
|||
}
|
||||
}
|
||||
|
||||
return nil, MakeError(rt, fmt.Sprintf("the 'ns' form is missing from '%s'", ns.GetName()))
|
||||
return nil, MakeError(rt, ns, fmt.Sprintf("the 'ns' form is missing from '%s'", ns.GetName()))
|
||||
}
|
||||
|
|
|
@ -48,8 +48,9 @@ import (
|
|||
type nativeFnHandler = func(rt *Runtime, scope IScope, n Node, args *List) (IExpr, IError)
|
||||
|
||||
type IFn interface {
|
||||
ast.ILocatable
|
||||
IExpr
|
||||
Apply(rt *Runtime, scope IScope, n Node, args *List) (IExpr, IError)
|
||||
GetName() string
|
||||
}
|
||||
|
||||
// Function struct represent a user defined function.
|
||||
|
@ -142,8 +143,9 @@ func (f *Function) Apply(rt *Runtime, scope IScope, n Node, args *List) (IExpr,
|
|||
|
||||
// MakeFunction Create a function with the given `params` and `body` in
|
||||
// the given `scope`.
|
||||
func MakeFunction(scope IScope, params IColl, body *Block) *Function {
|
||||
func MakeFunction(n Node, scope IScope, params IColl, body *Block) *Function {
|
||||
return &Function{
|
||||
Node: n,
|
||||
scope: scope,
|
||||
params: params,
|
||||
body: body,
|
||||
|
@ -173,7 +175,8 @@ func MakeFnScope(rt *Runtime, parent IScope, bindings IColl, values IColl) (*Sco
|
|||
fmt.Printf("[DEBUG] Mismatch on bindings and values: Bindings: %s, Values: %s\n", bindings, values)
|
||||
}
|
||||
|
||||
return nil, MakeError(rt,
|
||||
fmt.Println("3333333", values.(IExpr).GetLocation(), bindings.(IExpr).GetLocation())
|
||||
return nil, MakeError(rt, values.(IExpr),
|
||||
fmt.Sprintf("expected '%d' arguments, got '%d'.", bindings.Count(), values.Count()))
|
||||
}
|
||||
|
||||
|
@ -185,20 +188,29 @@ func MakeFnScope(rt *Runtime, parent IScope, bindings IColl, values IColl) (*Sco
|
|||
if binds[i].GetType() == ast.Symbol && binds[i].(*Symbol).IsRestable() {
|
||||
|
||||
if i != len(binds)-1 {
|
||||
return nil, MakeErrorFor(rt, binds[i], "The function argument with '&' has to be the last argument.")
|
||||
return nil, MakeError(rt, binds[i], "The function argument with '&' has to be the last argument.")
|
||||
}
|
||||
|
||||
// if the number of values are one less than the number of bindings
|
||||
// but the last binding is a Restable (e.g &x) the the last bindings
|
||||
// has to be an empty list. Note the check for number of vlaues comes
|
||||
// next.
|
||||
rest := MakeEmptyList()
|
||||
rest := MakeEmptyList(MakeNodeFromExpr(binds[i]))
|
||||
|
||||
if i == len(exprs)-1 {
|
||||
// If the number of values matches the number of bindings
|
||||
// or it is more than that create a list from them
|
||||
// to pass it to the last argument that has to be Restable (e.g &x)
|
||||
rest = MakeList(exprs[i:])
|
||||
elements := exprs[i:]
|
||||
var node Node
|
||||
|
||||
if len(elements) > 0 {
|
||||
node = MakeNodeFromExprs(elements)
|
||||
} else {
|
||||
node = MakeNodeFromExpr(binds[i])
|
||||
}
|
||||
|
||||
rest = MakeList(node, elements)
|
||||
}
|
||||
|
||||
scope.Insert(binds[i].(*Symbol).GetName()[1:], rest, false)
|
||||
|
@ -215,6 +227,10 @@ func (f *NativeFunction) GetType() ast.NodeType {
|
|||
return ast.NativeFn
|
||||
}
|
||||
|
||||
func (f *NativeFunction) GetName() string {
|
||||
return f.name
|
||||
}
|
||||
|
||||
func (f *NativeFunction) String() string {
|
||||
return fmt.Sprintf("<NativeFn: %s at %p>", f.name, f)
|
||||
}
|
||||
|
|
|
@ -159,7 +159,7 @@ func (k *Keyword) Eval(rt *Runtime, scope IScope) (*Keyword, IError) {
|
|||
}
|
||||
|
||||
if aliasedNS == nil {
|
||||
return nil, MakeErrorFor(rt, k, fmt.Sprintf("can't find the alias '%s' in the current namespace.", k.nsName))
|
||||
return nil, MakeError(rt, k, fmt.Sprintf("can't find the alias '%s' in the current namespace.", k.nsName))
|
||||
}
|
||||
k.ns = aliasedNS
|
||||
return k, nil
|
||||
|
|
|
@ -70,9 +70,16 @@ func (l *List) First() IExpr {
|
|||
|
||||
func (l *List) Rest() ISeq {
|
||||
if l.Count() < 2 {
|
||||
return MakeEmptyList()
|
||||
return MakeEmptyList(l.Node)
|
||||
}
|
||||
return MakeList(l.exprs[1:])
|
||||
|
||||
rest := l.exprs[1:]
|
||||
node := l.Node
|
||||
if len(rest) > 0 {
|
||||
node = MakeNodeFromExprs(rest)
|
||||
}
|
||||
|
||||
return MakeList(node, rest)
|
||||
}
|
||||
|
||||
func (l *List) Hash() uint32 {
|
||||
|
@ -97,8 +104,8 @@ func (l *List) ToSlice() []IExpr {
|
|||
}
|
||||
|
||||
func (l *List) Cons(e IExpr) IExpr {
|
||||
elems := l.ToSlice()
|
||||
return MakeList(append([]IExpr{e}, elems...))
|
||||
elements := append([]IExpr{e}, l.ToSlice()...)
|
||||
return MakeList(MakeNodeFromExprs(elements), elements)
|
||||
}
|
||||
|
||||
// END: IColl ---
|
||||
|
@ -118,14 +125,16 @@ func ListStartsWith(l *List, sym string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func MakeList(elements []IExpr) *List {
|
||||
func MakeList(n Node, elements []IExpr) *List {
|
||||
return &List{
|
||||
Node: n,
|
||||
exprs: elements,
|
||||
}
|
||||
}
|
||||
|
||||
func MakeEmptyList() *List {
|
||||
func MakeEmptyList(n Node) *List {
|
||||
return &List{
|
||||
Node: n,
|
||||
exprs: []IExpr{},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,7 +56,7 @@ func (n *Namespace) GetType() ast.NodeType {
|
|||
return ast.Namespace
|
||||
}
|
||||
|
||||
func (n *Namespace) GetLocation() ast.Location {
|
||||
func (n *Namespace) GetLocation() *ast.Location {
|
||||
return ast.MakeUnknownLocation()
|
||||
}
|
||||
|
||||
|
@ -141,7 +141,7 @@ func (n *Namespace) getForms() *Block {
|
|||
|
||||
// requireNS finds and loads the namespace addressed by the given
|
||||
// `ns` string.
|
||||
func requireNS(rt *Runtime, ns string) (*Namespace, IError) {
|
||||
func requireNS(rt *Runtime, ns *Symbol) (*Namespace, IError) {
|
||||
// TODO: use a hashing algorithm to avoid reloading an unchanged namespace
|
||||
loadedForms, err := rt.LoadNS(ns)
|
||||
|
||||
|
@ -155,11 +155,12 @@ func requireNS(rt *Runtime, ns string) (*Namespace, IError) {
|
|||
if body.Count() == 0 {
|
||||
return nil, MakeError(
|
||||
rt,
|
||||
body,
|
||||
fmt.Sprintf("The '%s' ns source code doesn't start with an 'ns' form.", ns),
|
||||
)
|
||||
}
|
||||
|
||||
namespace := MakeNS(ns, source)
|
||||
namespace := MakeNS(ns.GetName(), source)
|
||||
namespace.setForms(body)
|
||||
|
||||
return &namespace, nil
|
||||
|
@ -188,21 +189,21 @@ func RequireNamespace(rt *Runtime, namespace IExpr) (IExpr, IError) {
|
|||
first := list.First()
|
||||
|
||||
if first.GetType() != ast.Symbol {
|
||||
return nil, MakeErrorFor(rt, first, "The first element has to be a symbol")
|
||||
return nil, MakeError(rt, first, "The first element has to be a symbol")
|
||||
}
|
||||
|
||||
second := list.Rest().First()
|
||||
if second.GetType() != ast.Symbol {
|
||||
return nil, MakeErrorFor(rt, first, "The second element has to be a symbol")
|
||||
return nil, MakeError(rt, first, "The second element has to be a symbol")
|
||||
}
|
||||
|
||||
ns = first.(*Symbol)
|
||||
alias = second.(*Symbol).GetName()
|
||||
default:
|
||||
return nil, MakeErrorFor(rt, ns, "Don't know how to load the given namespace")
|
||||
return nil, MakeError(rt, ns, "Don't know how to load the given namespace")
|
||||
}
|
||||
|
||||
loadedNS, err := requireNS(rt, ns.GetName())
|
||||
loadedNS, err := requireNS(rt, ns)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -217,6 +218,7 @@ func RequireNamespace(rt *Runtime, namespace IExpr) (IExpr, IError) {
|
|||
if !inserted {
|
||||
return nil, MakeError(
|
||||
rt,
|
||||
loadedNS,
|
||||
fmt.Sprintf(
|
||||
"the namespace '%s' didn't get inserted in the runtime.",
|
||||
loadedNS.GetName()),
|
||||
|
@ -231,6 +233,7 @@ func RequireNamespace(rt *Runtime, namespace IExpr) (IExpr, IError) {
|
|||
if !inserted {
|
||||
return nil, MakeError(
|
||||
rt,
|
||||
loadedNS,
|
||||
fmt.Sprintf(
|
||||
"can't set the current ns back to '%s' from '%s'.",
|
||||
prevNS.GetName(),
|
||||
|
|
|
@ -36,7 +36,7 @@ func (n NothingType) Hash() uint32 {
|
|||
return hash.HashOf(append([]byte{byte(ast.Block)}, bytes...))
|
||||
}
|
||||
|
||||
func (n NothingType) GetLocation() ast.Location {
|
||||
func (n NothingType) GetLocation() *ast.Location {
|
||||
return ast.MakeUnknownLocation()
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,8 @@ package core
|
|||
import (
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"serene-lang.org/bootstrap/pkg/ast"
|
||||
)
|
||||
|
||||
// An array of the valid characters that be be used in a symbol
|
||||
|
@ -57,14 +59,17 @@ type IParsable interface {
|
|||
|
||||
// Returns the current position in the buffer
|
||||
GetLocation() int
|
||||
GetSource() *ast.Source
|
||||
Buffer() *[]string
|
||||
}
|
||||
|
||||
// StringParser is an implementation of the IParsable that operates on strings.
|
||||
// To put it simply it parses input strings
|
||||
type StringParser struct {
|
||||
buffer []string
|
||||
pos int
|
||||
buffer []string
|
||||
pos int
|
||||
source string
|
||||
lineIndex []int
|
||||
}
|
||||
|
||||
// Implementing IParsable for StringParser ---
|
||||
|
@ -75,6 +80,12 @@ func (sp *StringParser) next(skipWhitespace bool) *string {
|
|||
return nil
|
||||
}
|
||||
char := sp.buffer[sp.pos]
|
||||
|
||||
if char == "\n" {
|
||||
// Including the \n itself
|
||||
sp.lineIndex = append(sp.lineIndex, sp.pos+1)
|
||||
}
|
||||
|
||||
sp.pos = sp.pos + 1
|
||||
|
||||
if skipWhitespace && isSeparator(&char) {
|
||||
|
@ -127,6 +138,14 @@ func (sp *StringParser) GetLocation() int {
|
|||
return sp.pos
|
||||
}
|
||||
|
||||
func (sp *StringParser) GetSource() *ast.Source {
|
||||
return &ast.Source{
|
||||
Buffer: &sp.buffer,
|
||||
Path: sp.source,
|
||||
LineIndex: &sp.lineIndex,
|
||||
}
|
||||
}
|
||||
|
||||
func (sp *StringParser) Buffer() *[]string {
|
||||
return &sp.buffer
|
||||
}
|
||||
|
@ -136,7 +155,7 @@ func (sp *StringParser) Buffer() *[]string {
|
|||
// makeErrorAtPoint is a helper function which generates an `IError` that
|
||||
// points at the current position of the buffer.
|
||||
func makeErrorAtPoint(p IParsable, msg string, a ...interface{}) IError {
|
||||
n := MakeSinglePointNode(p.Buffer(), p.GetLocation())
|
||||
n := MakeSinglePointNode(p.GetSource(), p.GetLocation())
|
||||
return MakeParsetimeErrorf(n, msg, a...)
|
||||
}
|
||||
|
||||
|
@ -207,7 +226,7 @@ func readRawSymbol(parser IParsable) (IExpr, IError) {
|
|||
}
|
||||
}
|
||||
|
||||
node := MakeNode(parser.Buffer(), parser.GetLocation()-len(symbol), parser.GetLocation())
|
||||
node := MakeNode(parser.GetSource(), parser.GetLocation()-len(symbol), parser.GetLocation())
|
||||
sym, err := MakeSymbol(node, symbol)
|
||||
|
||||
if err != nil {
|
||||
|
@ -228,7 +247,7 @@ func readString(parser IParsable) (IExpr, IError) {
|
|||
}
|
||||
|
||||
if *c == "\"" {
|
||||
node := MakeNode(parser.Buffer(), parser.GetLocation()-len(str), parser.GetLocation())
|
||||
node := MakeNode(parser.GetSource(), parser.GetLocation()-len(str), parser.GetLocation())
|
||||
return MakeString(node, str), nil
|
||||
}
|
||||
|
||||
|
@ -367,7 +386,10 @@ func readList(parser IParsable) (IExpr, IError) {
|
|||
}
|
||||
}
|
||||
|
||||
return MakeList(list), nil
|
||||
node := MakeNodeFromExprs(list)
|
||||
node.location.DecStart(1)
|
||||
node.location.IncEnd(1)
|
||||
return MakeList(node, list), nil
|
||||
}
|
||||
|
||||
func readComment(parser IParsable) (IExpr, IError) {
|
||||
|
@ -387,7 +409,7 @@ func readQuotedExpr(parser IParsable) (IExpr, IError) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
symNode := MakeNode(parser.Buffer(), parser.GetLocation(), parser.GetLocation())
|
||||
symNode := MakeNode(parser.GetSource(), parser.GetLocation(), parser.GetLocation())
|
||||
sym, err := MakeSymbol(symNode, "quote")
|
||||
|
||||
if err != nil {
|
||||
|
@ -395,10 +417,15 @@ func readQuotedExpr(parser IParsable) (IExpr, IError) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return MakeList([]IExpr{
|
||||
listElems := []IExpr{
|
||||
sym,
|
||||
expr,
|
||||
}), nil
|
||||
}
|
||||
|
||||
listNode := MakeNodeFromExprs(listElems)
|
||||
listNode.location.DecStart(1)
|
||||
listNode.location.IncStart(1)
|
||||
return MakeList(listNode, listElems), nil
|
||||
}
|
||||
|
||||
// readUnquotedExpr reads different unquoting expressions from their short representaions.
|
||||
|
@ -417,7 +444,7 @@ func readUnquotedExpr(parser IParsable) (IExpr, IError) {
|
|||
var err IError
|
||||
var expr IExpr
|
||||
|
||||
node := MakeNode(parser.Buffer(), parser.GetLocation(), parser.GetLocation())
|
||||
node := MakeNode(parser.GetSource(), parser.GetLocation(), parser.GetLocation())
|
||||
|
||||
if *c == "@" {
|
||||
parser.next(true)
|
||||
|
@ -441,7 +468,11 @@ func readUnquotedExpr(parser IParsable) (IExpr, IError) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return MakeList([]IExpr{sym, expr}), nil
|
||||
listElems := []IExpr{sym, expr}
|
||||
listNode := MakeNodeFromExprs(listElems)
|
||||
listNode.location.DecStart(1)
|
||||
listNode.location.IncStart(1)
|
||||
return MakeList(listNode, listElems), nil
|
||||
}
|
||||
|
||||
// readQuasiquotedExpr reads the backquote and replace it with a call
|
||||
|
@ -452,16 +483,19 @@ func readQuasiquotedExpr(parser IParsable) (IExpr, IError) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
node := MakeNode(parser.Buffer(), parser.GetLocation(), parser.GetLocation())
|
||||
node := MakeNode(parser.GetSource(), parser.GetLocation(), parser.GetLocation())
|
||||
sym, err := MakeSymbol(node, "quasiquote")
|
||||
if err != nil {
|
||||
err.SetNode(&node)
|
||||
return nil, err
|
||||
}
|
||||
return MakeList([]IExpr{
|
||||
sym,
|
||||
expr,
|
||||
}), nil
|
||||
|
||||
listElems := []IExpr{sym, expr}
|
||||
listNode := MakeNodeFromExprs(listElems)
|
||||
listNode.location.DecStart(1)
|
||||
listNode.location.IncStart(1)
|
||||
|
||||
return MakeList(listNode, listElems), nil
|
||||
}
|
||||
|
||||
// readExpr reads one expression from the input. This function is the most
|
||||
|
@ -516,13 +550,14 @@ loop:
|
|||
// by itself is not something available to the language. It's
|
||||
// just anbstraction for a ordered collection of expressions.
|
||||
// It doesn't have anything to do with the concept of blocks
|
||||
// from other programming languages
|
||||
func ParseToAST(input string) (*Block, IError) {
|
||||
// from other programming languages.
|
||||
func ParseToAST(source string, input string) (*Block, IError) {
|
||||
|
||||
var ast Block
|
||||
parser := StringParser{
|
||||
buffer: strings.Split(input, ""),
|
||||
pos: 0,
|
||||
source: source,
|
||||
}
|
||||
|
||||
for {
|
||||
|
|
|
@ -21,6 +21,8 @@ package core
|
|||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/gookit/color"
|
||||
)
|
||||
|
||||
func toRepresanbleString(ast ...IRepresentable) string {
|
||||
|
@ -63,6 +65,36 @@ func Println(rt *Runtime, ast ...IRepresentable) {
|
|||
}
|
||||
|
||||
func PrintError(rt *Runtime, err IError) {
|
||||
trace := err.GetStackTrace()
|
||||
|
||||
for i, f := range *trace {
|
||||
loc := f.Caller.GetLocation()
|
||||
fmt.Println("===============")
|
||||
fmt.Println(f.Fn.GetLocation())
|
||||
fmt.Println(loc)
|
||||
source := loc.GetSource()
|
||||
// if loc.GetSource().Buffer != nil {
|
||||
// fmt.Println(loc.GetSource().LineIndex)
|
||||
// source = *loc.GetSource().Buffer
|
||||
// }
|
||||
startline := source.LineNumberFor(loc.GetStart()) - 1
|
||||
endline := source.LineNumberFor(loc.GetEnd()) + 1
|
||||
|
||||
var lines string
|
||||
for i := startline; i <= endline; i++ {
|
||||
lines += fmt.Sprintf("%d:\t%s\n", i, source.GetLine(i))
|
||||
}
|
||||
|
||||
color.Yellow.Printf(
|
||||
"%d: In function '%s' at '%s'\n",
|
||||
i,
|
||||
f.Fn.GetName(),
|
||||
loc.GetSource().Path,
|
||||
)
|
||||
color.White.Printf("%s\n", lines)
|
||||
}
|
||||
loc := err.GetLocation()
|
||||
fmt.Printf("Error: %s\nAt: %d to %d\n", err.String(), loc.GetStart(), loc.GetEnd())
|
||||
|
||||
errTag := color.Red.Sprint("ERROR")
|
||||
fmt.Printf("%s: %s\nAt: %d to %d\n", errTag, err.String(), loc.GetStart(), loc.GetEnd())
|
||||
}
|
||||
|
|
|
@ -98,10 +98,15 @@ func qqProcess(rt *Runtime, e IExpr) (IExpr, IError) {
|
|||
// newErr.stack(err)
|
||||
return nil, err
|
||||
}
|
||||
return MakeList([]IExpr{
|
||||
elems := []IExpr{
|
||||
sym,
|
||||
e,
|
||||
}), nil
|
||||
}
|
||||
|
||||
return MakeList(
|
||||
MakeNodeFromExprs(elems),
|
||||
elems,
|
||||
), nil
|
||||
|
||||
case ast.List:
|
||||
list := e.(*List)
|
||||
|
@ -125,7 +130,7 @@ func qqProcess(rt *Runtime, e IExpr) (IExpr, IError) {
|
|||
}
|
||||
// ???
|
||||
if isUnquoteSplicing(first) {
|
||||
return nil, MakeErrorFor(rt, first, "'unquote-splicing' is not allowed out of a collection.")
|
||||
return nil, MakeError(rt, first, "'unquote-splicing' is not allowed out of a collection.")
|
||||
}
|
||||
|
||||
// p := list
|
||||
|
|
|
@ -18,6 +18,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
|
||||
package core
|
||||
|
||||
func ReadString(input string) (*Block, IError) {
|
||||
return ParseToAST(input)
|
||||
func ReadString(src string, input string) (*Block, IError) {
|
||||
return ParseToAST(src, input)
|
||||
}
|
||||
|
|
|
@ -147,8 +147,8 @@ func nsNameToPath(ns string) string {
|
|||
// LoadNS looks up the namespace specified by the given name `ns`
|
||||
// and reads the content as expressions (parse it) and returns the
|
||||
// expressions.
|
||||
func (r *Runtime) LoadNS(ns string) (*loadedForms, IError) {
|
||||
nsFile := nsNameToPath(ns)
|
||||
func (r *Runtime) LoadNS(ns *Symbol) (*loadedForms, IError) {
|
||||
nsFile := nsNameToPath(ns.GetName())
|
||||
for _, loadPath := range r.paths {
|
||||
possibleFile := path.Join(loadPath, nsFile)
|
||||
|
||||
|
@ -167,13 +167,14 @@ func (r *Runtime) LoadNS(ns string) (*loadedForms, IError) {
|
|||
if err != nil {
|
||||
readError := MakeError(
|
||||
r,
|
||||
ns,
|
||||
fmt.Sprintf("error while reading the file at %s", possibleFile),
|
||||
)
|
||||
readError.WithError(err)
|
||||
return nil, readError
|
||||
}
|
||||
|
||||
body, e := ReadString(string(data))
|
||||
body, e := ReadString(possibleFile, string(data))
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
|
@ -182,7 +183,7 @@ func (r *Runtime) LoadNS(ns string) (*loadedForms, IError) {
|
|||
}
|
||||
|
||||
// TODO: Add the load paths to the error message here
|
||||
return nil, MakeError(r, fmt.Sprintf("Can't find the namespace '%s' in any of load paths.", ns))
|
||||
return nil, MakeError(r, ns, fmt.Sprintf("Can't find the namespace '%s' in any of load paths.", ns))
|
||||
}
|
||||
|
||||
func (r *Runtime) InsertNS(nsName string, ns *Namespace) {
|
||||
|
|
|
@ -34,7 +34,7 @@ func Def(rt *Runtime, scope IScope, args *List) (IExpr, IError) {
|
|||
name := args.First()
|
||||
|
||||
if name.GetType() != ast.Symbol {
|
||||
return nil, MakeError(rt, "the first argument of 'def' has to be a symbol")
|
||||
return nil, MakeError(rt, name, "the first argument of 'def' has to be a symbol")
|
||||
}
|
||||
|
||||
sym := name.(*Symbol)
|
||||
|
@ -55,7 +55,7 @@ func Def(rt *Runtime, scope IScope, args *List) (IExpr, IError) {
|
|||
return sym, nil
|
||||
}
|
||||
|
||||
return nil, MakeError(rt, "'def' form need at least 2 arguments")
|
||||
return nil, MakeError(rt, args, "'def' form need at least 2 arguments")
|
||||
}
|
||||
|
||||
// Def defines a macro in the current namespace. The first
|
||||
|
@ -72,7 +72,7 @@ func DefMacro(rt *Runtime, scope IScope, args *List) (IExpr, IError) {
|
|||
name := args.First()
|
||||
|
||||
if name.GetType() != ast.Symbol {
|
||||
return nil, MakeError(rt, "the first argument of 'defmacro' has to be a symbol")
|
||||
return nil, MakeError(rt, name, "the first argument of 'defmacro' has to be a symbol")
|
||||
}
|
||||
|
||||
sym := name.(*Symbol)
|
||||
|
@ -100,21 +100,21 @@ func DefMacro(rt *Runtime, scope IScope, args *List) (IExpr, IError) {
|
|||
return macro, nil
|
||||
}
|
||||
|
||||
return nil, MakeError(rt, "'defmacro' form need at least 2 arguments")
|
||||
return nil, MakeError(rt, args, "'defmacro' form need at least 2 arguments")
|
||||
}
|
||||
|
||||
// Fn defines a function inside the given scope `scope` with the given `args`.
|
||||
// `args` contains the arugment list, docstring and body of the function.
|
||||
func Fn(rt *Runtime, scope IScope, args *List) (IExpr, IError) {
|
||||
|
||||
if args.Count() < 1 {
|
||||
return nil, MakeError(rt, "'fn' needs at least an arguments list")
|
||||
if args.Count() < 2 {
|
||||
return nil, MakeError(rt, args, "'fn' needs at least an arguments list")
|
||||
}
|
||||
|
||||
var params IColl
|
||||
body := MakeEmptyBlock()
|
||||
|
||||
arguments := args.First()
|
||||
arguments := args.Rest().First()
|
||||
|
||||
// TODO: Add vector in here
|
||||
// Or any other icoll
|
||||
|
@ -123,26 +123,26 @@ func Fn(rt *Runtime, scope IScope, args *List) (IExpr, IError) {
|
|||
}
|
||||
|
||||
if args.Count() > 1 {
|
||||
body.SetContent(args.Rest().(*List).ToSlice())
|
||||
body.SetContent(args.Rest().Rest().(*List).ToSlice())
|
||||
}
|
||||
|
||||
return MakeFunction(scope, params, body), nil
|
||||
return MakeFunction(MakeNodeFromExpr(args.First()), 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")
|
||||
return nil, MakeError(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")
|
||||
return nil, MakeError(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(
|
||||
return nil, MakeError(
|
||||
rt,
|
||||
args,
|
||||
fmt.Sprintf("the namespace '%s' doesn't match the file name.", nsName),
|
||||
|
@ -151,7 +151,7 @@ func NSForm(rt *Runtime, scope IScope, args *List) (IExpr, IError) {
|
|||
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 nil, MakeError(rt, name, fmt.Sprintf("can't find the namespace '%s'. Is it the same as the file name?", nsName))
|
||||
}
|
||||
|
||||
return ns, nil
|
||||
|
|
|
@ -81,8 +81,8 @@ type Node struct {
|
|||
}
|
||||
|
||||
// GetLocation returns the location of the Node in the source input
|
||||
func (n Node) GetLocation() ast.Location {
|
||||
return n.location
|
||||
func (n Node) GetLocation() *ast.Location {
|
||||
return &n.location
|
||||
}
|
||||
|
||||
type ExecutionScope struct {
|
||||
|
@ -123,9 +123,9 @@ func toRepresentables(ast IColl) []IRepresentable {
|
|||
}
|
||||
|
||||
// MakeNodeFromLocation creates a new Node for the given Location `loc`
|
||||
func MakeNodeFromLocation(loc ast.Location) Node {
|
||||
func MakeNodeFromLocation(loc *ast.Location) Node {
|
||||
return Node{
|
||||
location: loc,
|
||||
location: *loc,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -136,14 +136,30 @@ func MakeNodeFromExpr(e IExpr) Node {
|
|||
return MakeNodeFromLocation(e.GetLocation())
|
||||
}
|
||||
|
||||
// MakeNodeFromExprs creates a new Node from the given slice of `IExpr`s.
|
||||
// We use the Node to pass it to other IExpr constructors to
|
||||
// keep the reference to the original form in the input string
|
||||
func MakeNodeFromExprs(es []IExpr) Node {
|
||||
if len(es) == 0 {
|
||||
// TODO: This is temporary, fix it.
|
||||
panic("can't create a node from empty elements.")
|
||||
}
|
||||
|
||||
firstLoc := es[0].GetLocation()
|
||||
endLoc := es[len(es)-1].GetLocation()
|
||||
loc := ast.MakeLocation(firstLoc.GetSource(), firstLoc.GetStart(), endLoc.GetEnd())
|
||||
|
||||
return MakeNodeFromLocation(loc)
|
||||
}
|
||||
|
||||
// MakeNode creates a new Node in the the given `input` that points to a
|
||||
// range of characters starting from the `start` till the `end`.
|
||||
func MakeNode(input *[]string, start int, end int) Node {
|
||||
func MakeNode(input *ast.Source, start int, end int) Node {
|
||||
return MakeNodeFromLocation(ast.MakeLocation(input, start, end))
|
||||
}
|
||||
|
||||
// MakeSinglePointNode creates a not the points to a single char in the
|
||||
// input
|
||||
func MakeSinglePointNode(input *[]string, point int) Node {
|
||||
func MakeSinglePointNode(input *ast.Source, point int) Node {
|
||||
return MakeNode(input, point, point)
|
||||
}
|
||||
|
|
1
dev.org
1
dev.org
|
@ -35,6 +35,7 @@ https://www.reddit.com/r/ProgrammingLanguages/comments/8ggx2n/is_llvm_a_good_bac
|
|||
- Official LLVM tutorial C++ :: https://llvm.org/docs/tutorial/
|
||||
- Interactive C++ with Cling :: https://blog.llvm.org/posts/2020-11-30-interactive-cpp-with-cling/
|
||||
- My First LLVM Compiler :: https://www.wilfred.me.uk/blog/2015/02/21/my-first-llvm-compiler/
|
||||
- A Complete Guide to LLVM for Programming Language Creators :: https://mukulrathi.co.uk/create-your-own-programming-language/llvm-ir-cpp-api-tutorial/
|
||||
** Data structures
|
||||
- Pure functional datastructures papaer :: https://www.cs.cmu.edu/~rwh/theses/okasaki.pdf
|
||||
- Dynamic typing: syntax and proof theory :: https://reader.elsevier.com/reader/sd/pii/0167642394000042?token=CEFF5C5D1B03FD680762FC4889A14C0CA2BB28FE390EC51099984536E12AC358F3D28A5C25C274296ACBBC32E5AE23CD
|
||||
|
|
Loading…
Reference in New Issue