Record the caller details in the stack frame

This commit is contained in:
Sameer Rahmani 2020-12-30 17:50:00 +00:00
parent 3904b050bb
commit 9d106d4278
23 changed files with 399 additions and 152 deletions

View File

@ -9,7 +9,7 @@
(name args &body) (name args &body)
(list 'def name (cons 'fn (cons args body)))) (list 'def name (cons 'fn (cons args body))))
(defn pp (x) (defn pp (x y)
(println x)) (println x))
(def main (def main

View File

@ -4,6 +4,7 @@ go 1.15
require ( require (
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e 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/rjeczalik/pkgconfig v0.0.0-20190903131546-94d388dab445
github.com/spf13/cobra v1.1.1 github.com/spf13/cobra v1.1.1
github.com/spf13/viper v1.7.1 github.com/spf13/viper v1.7.1

View File

@ -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/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.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 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/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/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= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=

View File

@ -20,6 +20,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// Serene's AST. // Serene's AST.
package ast package ast
import (
"sort"
"strings"
)
type NodeType int type NodeType int
const ( 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 { type Location struct {
start int start int
end int end int
source *[]string source Source
knownLocation bool knownLocation bool
} }
var UnknownLocation *Location = &Location{knownLocation: false}
func (l *Location) GetStart() int { func (l *Location) GetStart() int {
return l.start return l.start
} }
@ -55,8 +89,42 @@ func (l *Location) GetEnd() int {
return l.end return l.end
} }
func (l *Location) GetSource() *[]string { func (l *Location) GetSource() *Source {
return l.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 { func (l *Location) IsKnownLocaiton() bool {
@ -64,12 +132,12 @@ func (l *Location) IsKnownLocaiton() bool {
} }
type ILocatable interface { type ILocatable interface {
GetLocation() Location GetLocation() *Location
} }
func MakeLocation(input *[]string, start int, end int) Location { func MakeLocation(input *Source, start int, end int) *Location {
return Location{ return &Location{
source: input, source: *input,
start: start, start: start,
end: end, end: end,
knownLocation: true, knownLocation: true,
@ -80,8 +148,6 @@ type ITypable interface {
GetType() NodeType GetType() NodeType
} }
func MakeUnknownLocation() Location { func MakeUnknownLocation() *Location {
return Location{ return UnknownLocation
knownLocation: false,
}
} }

View File

@ -51,7 +51,7 @@ func (b *Block) ToDebugStr() string {
return fmt.Sprintf("%#v", b) return fmt.Sprintf("%#v", b)
} }
func (b *Block) GetLocation() ast.Location { func (b *Block) GetLocation() *ast.Location {
if len(b.body) > 0 { if len(b.body) > 0 {
return b.body[0].GetLocation() return b.body[0].GetLocation()
} }

View File

@ -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) { func RequireNativeFn(rt *Runtime, scope IScope, n Node, args *List) (IExpr, IError) {
switch args.Count() { switch args.Count() {
case 0: case 0:
return nil, MakeErrorFor(rt, args, "'require' function is missing") return nil, MakeError(rt, args, "'require' function is missing")
case 1: 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: 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) { func HashNativeFn(rt *Runtime, scope IScope, n Node, args *List) (IExpr, IError) {
if args.Count() != 2 { 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() expr := args.Rest().First()

View File

@ -33,38 +33,51 @@ package core
// compare the stack items by their address, identity and location. // compare the stack items by their address, identity and location.
// * Add support for iteration on the stack. // * Add support for iteration on the stack.
import "fmt" import (
"fmt"
)
type ICallStack interface { type ICallStack interface {
// Push the given callable `f` to the stack // Push the given callable `f` to the stack
Push(f IFn) IError Push(f IFn) IError
Pop() FnCall Pop() *Frame
Peek() *Frame
Count() uint Count() uint
} }
type FnCall struct { type Frame struct {
Fn IFn
// Number of recursive calls to this function // Number of recursive calls to this function
count uint Count uint
Fn IFn
Caller IExpr
} }
type TraceBack = []Frame
type CallStackItem struct { type CallStackItem struct {
prev *CallStackItem prev *CallStackItem
data FnCall data Frame
} }
type CallStack struct { type CallStack struct {
debug bool
head *CallStackItem head *CallStackItem
count uint count uint
debug bool
} }
func (c *CallStack) Count() uint { func (c *CallStack) Count() uint {
return c.count 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 { if c.debug {
fmt.Println("[Stack] -->", f) 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.") 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 // Empty Stack
if c.head == nil { if c.head == nil {
c.head = &CallStackItem{ c.head = &CallStackItem{
data: FnCall{ data: Frame{
Fn: f, Fn: f,
count: 0, Caller: caller,
Count: 0,
}, },
} }
c.count++ c.count++
@ -87,15 +105,16 @@ func (c *CallStack) Push(f IFn) IError {
nodeData := &c.head.data nodeData := &c.head.data
// If the same function was on top of the stack // 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 // TODO: expand the check here to support address and location as well
nodeData.count++ nodeData.Count++
} else { } else {
c.head = &CallStackItem{ c.head = &CallStackItem{
prev: c.head, prev: c.head,
data: FnCall{ data: Frame{
Fn: f, Fn: f,
count: 0, Caller: caller,
Count: 0,
}, },
} }
c.count++ c.count++
@ -103,7 +122,7 @@ func (c *CallStack) Push(f IFn) IError {
return nil return nil
} }
func (c *CallStack) Pop() *FnCall { func (c *CallStack) Pop() *Frame {
if c.head == nil { if c.head == nil {
if c.debug { if c.debug {
fmt.Println("[Stack] <-- nil") fmt.Println("[Stack] <-- nil")
@ -120,6 +139,32 @@ func (c *CallStack) Pop() *FnCall {
return &result.data 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 { func MakeCallStack(debugMode bool) CallStack {
return CallStack{ return CallStack{
count: 0, count: 0,

View File

@ -23,13 +23,14 @@ import (
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
"strings"
"github.com/chzyer/readline" "github.com/chzyer/readline"
"serene-lang.org/bootstrap/pkg/ast" "serene-lang.org/bootstrap/pkg/ast"
) )
func rep(rt *Runtime, line string) { func rep(rt *Runtime, line string) {
ast, err := ReadString(line) ast, err := ReadString("*REPL*", line)
if err != nil { if err != nil {
PrintError(rt, err) PrintError(rt, err)
@ -120,7 +121,17 @@ func Run(flags map[string]bool, args []string) {
} }
ns := args[0] 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 { if err != nil {
PrintError(rt, err) PrintError(rt, err)
@ -133,6 +144,7 @@ func Run(flags map[string]bool, args []string) {
if !inserted { if !inserted {
err := MakeError( err := MakeError(
rt, rt,
loadedNS,
fmt.Sprintf( fmt.Sprintf(
"the namespace '%s' didn't get inserted in the runtime.", "the namespace '%s' didn't get inserted in the runtime.",
loadedNS.GetName()), loadedNS.GetName()),
@ -163,24 +175,22 @@ func Run(flags map[string]bool, args []string) {
mainFn := mainBinding.Value.(*Function) mainFn := mainBinding.Value.(*Function)
var fnArgs []IExpr var fnArgs []IExpr
var argNode Node
if len(args) > 1 { if len(args) > 1 {
for _, arg := range args[1:] { for _, arg := range args[1:] {
node := MakeNodeFromExpr(mainFn) node := MakeNodeFromExpr(mainFn)
fnArgs = append(fnArgs, MakeString(node, arg)) 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(argNode, fnArgs))
_, err = mainFn.Apply(rt, loadedNS.GetRootScope(), mainFn.Node, MakeList(fnArgs))
if err != nil { if err != nil {
PrintError(rt, err) PrintError(rt, err)
os.Exit(1) os.Exit(1)
} }
// rt.Stack.Pop()
// if rt.Stack.Count() != 0 {
// panic("Call stack is not empty.")
// }
} }

View File

@ -49,6 +49,7 @@ type IError interface {
IRepresentable IRepresentable
IDebuggable IDebuggable
GetStackTrace() *TraceBack
// To wrap Golan rrrrors // To wrap Golan rrrrors
WithError(err error) IError WithError(err error) IError
@ -62,6 +63,7 @@ type Error struct {
Node Node
WrappedErr error WrappedErr error
msg string msg string
trace *TraceBack
} }
func (e *Error) String() string { func (e *Error) String() string {
@ -89,32 +91,25 @@ func (e *Error) Error() string {
return e.msg return e.msg
} }
func (e *Error) GetStackTrace() *TraceBack {
return e.trace
}
func MakePlainError(msg string) IError { func MakePlainError(msg string) IError {
return &Error{ return &Error{
msg: msg, msg: msg,
} }
} }
// MakeError creates an Error without any location. // MakeError creates an Error which points to the given IExpr `e` as
func MakeError(rt *Runtime, msg string) IError {
return MakePlainError(msg)
}
// MakeErrorFor creates an Error which points to the given IExpr `e` as
// the root of the error. // the root of the error.
func MakeErrorFor(rt *Runtime, e IExpr, msg string) IError { func MakeError(rt *Runtime, e IExpr, msg string) IError {
loc := e.GetLocation() trace := append(*rt.Stack.ToTraceBack(), Frame{0, rt.Stack.GetCurrentFn(), e})
return &Error{ return &Error{
Node: MakeNodeFromLocation(loc), Node: MakeNodeFromExpr(e),
msg: msg, msg: msg,
} trace: &trace,
}
//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...),
} }
} }

View File

@ -84,7 +84,7 @@ func evalForm(rt *Runtime, scope IScope, form IExpr) (IExpr, IError) {
if sym.IsNSQualified() { if sym.IsNSQualified() {
// Whether a namespace with the given alias loaded or not // Whether a namespace with the given alias loaded or not
if !rt.CurrentNS().hasExternal(sym.GetNSPart()) { 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()), 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 { if expr == nil {
return nil, MakeRuntimeErrorf( return nil, MakeError(
rt, rt,
"can't resolve symbol '%s' in ns '%s'", sym,
symbolName, fmt.Sprintf(
nsName, "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 break
} }
} }
return MakeList(result), nil
return MakeList(MakeNodeFromExpr(lst), result), nil
} }
// Default case // 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) // EvalForms evaluates the given expr `expressions` (it can be a list, block, symbol or anything else)
@ -185,7 +189,6 @@ tco:
body: body:
for i := 0; i < len(exprs); i++ { for i := 0; i < len(exprs); i++ {
//for i, forms := range exprs {
forms := exprs[i] forms := exprs[i]
executionScope := forms.GetExecutionScope() executionScope := forms.GetExecutionScope()
scope := scope scope := scope
@ -275,7 +278,7 @@ tco:
case "quote": case "quote":
// Including the `quote` itself // Including the `quote` itself
if list.Count() != 2 { 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() ret = list.Rest().First()
err = nil err = nil
@ -310,12 +313,18 @@ tco:
result := []IExpr{} result := []IExpr{}
for _, lst := range lists { for _, lst := range lists {
if lst.GetType() != ast.List { 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()...) 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 continue body // no rewrite
// TODO: Implement `list` in serene itself when we have destructuring available // TODO: Implement `list` in serene itself when we have destructuring available
@ -324,7 +333,7 @@ tco:
// given in the second argument. // given in the second argument.
case "cons": case "cons":
if list.Count() != 3 { 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)) evaledForms, err := evalForm(rt, scope, list.Rest().(*List))
@ -335,7 +344,7 @@ tco:
coll, ok := evaledForms.(*List).Rest().First().(IColl) coll, ok := evaledForms.(*List).Rest().First().(IColl)
if !ok { 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 ret, err = coll.Cons(evaledForms.(*List).First()), nil
@ -366,7 +375,7 @@ tco:
// as a macro and returns the expanded forms. // as a macro and returns the expanded forms.
case "macroexpand": case "macroexpand":
if list.Count() != 2 { 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)) evaledForm, e := evalForm(rt, scope, list.Rest().(*List))
@ -381,7 +390,7 @@ tco:
// * It needs at least a collection of arguments // * It needs at least a collection of arguments
// * Defines an anonymous function. // * Defines an anonymous function.
case "fn": case "fn":
ret, err = Fn(rt, scope, list.Rest().(*List)) ret, err = Fn(rt, scope, list)
continue body // no rewrite continue body // no rewrite
// `if` evaluation rules: // `if` evaluation rules:
@ -392,7 +401,7 @@ tco:
case "if": case "if":
args := list.Rest().(*List) args := list.Rest().(*List)
if args.Count() != 3 { 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()) pred, err := EvalForms(rt, scope, args.First())
@ -436,7 +445,7 @@ tco:
// the result. // the result.
case "eval": case "eval":
if list.Count() != 2 { 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)) form, err := evalForm(rt, scope, list.Rest().(*List))
if err != nil { if err != nil {
@ -457,7 +466,7 @@ tco:
// which is the result of the last expre in `BODY` // which is the result of the last expre in `BODY`
case "let": case "let":
if list.Count() < 2 { 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)) letScope := MakeScope(scope.(*Scope))
@ -470,7 +479,7 @@ tco:
body := list.Rest().Rest().(*List).ToSlice() body := list.Rest().Rest().(*List).ToSlice()
if bindings.Count()%2 != 0 { 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 { for {
@ -485,7 +494,7 @@ tco:
// TODO: We need to destruct the bindings here and remove this check // TODO: We need to destruct the bindings here and remove this check
// for the symbol type // for the symbol type
if name.GetType() != ast.Symbol { 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 return nil, err
} }
@ -546,7 +555,7 @@ tco:
break body //return break body //return
} }
rt.Stack.Push(fn) rt.Stack.Push(list, fn)
body := append( body := append(
fn.GetBody().ToSlice(), fn.GetBody().ToSlice(),
// Add the PopStack instruction to clean up the stack after // Add the PopStack instruction to clean up the stack after
@ -561,7 +570,8 @@ tco:
// by the `NativeFunction` struct // by the `NativeFunction` struct
case ast.NativeFn: case ast.NativeFn:
fn := f.(*NativeFunction) fn := f.(*NativeFunction)
rt.Stack.Push(fn)
rt.Stack.Push(list, fn)
ret, err = fn.Apply( ret, err = fn.Apply(
rt, rt,
scope, scope,
@ -572,7 +582,7 @@ tco:
continue body // no rewrite continue body // no rewrite
default: 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 ret = nil
break tco break tco
} }
@ -613,7 +623,7 @@ func EvalNSBody(rt *Runtime, ns *Namespace) (*Namespace, IError) {
exprs := body.ToSlice() exprs := body.ToSlice()
if len(exprs) == 0 { 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 { 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()))
} }

View File

@ -48,8 +48,9 @@ import (
type nativeFnHandler = func(rt *Runtime, scope IScope, n Node, args *List) (IExpr, IError) type nativeFnHandler = func(rt *Runtime, scope IScope, n Node, args *List) (IExpr, IError)
type IFn interface { type IFn interface {
ast.ILocatable IExpr
Apply(rt *Runtime, scope IScope, n Node, args *List) (IExpr, IError) Apply(rt *Runtime, scope IScope, n Node, args *List) (IExpr, IError)
GetName() string
} }
// Function struct represent a user defined function. // 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 // MakeFunction Create a function with the given `params` and `body` in
// the given `scope`. // 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{ return &Function{
Node: n,
scope: scope, scope: scope,
params: params, params: params,
body: body, 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) 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())) 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 binds[i].GetType() == ast.Symbol && binds[i].(*Symbol).IsRestable() {
if i != len(binds)-1 { 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 // 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 // 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 // has to be an empty list. Note the check for number of vlaues comes
// next. // next.
rest := MakeEmptyList() rest := MakeEmptyList(MakeNodeFromExpr(binds[i]))
if i == len(exprs)-1 { if i == len(exprs)-1 {
// If the number of values matches the number of bindings // If the number of values matches the number of bindings
// or it is more than that create a list from them // 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) // 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) scope.Insert(binds[i].(*Symbol).GetName()[1:], rest, false)
@ -215,6 +227,10 @@ func (f *NativeFunction) GetType() ast.NodeType {
return ast.NativeFn return ast.NativeFn
} }
func (f *NativeFunction) GetName() string {
return f.name
}
func (f *NativeFunction) String() string { func (f *NativeFunction) String() string {
return fmt.Sprintf("<NativeFn: %s at %p>", f.name, f) return fmt.Sprintf("<NativeFn: %s at %p>", f.name, f)
} }

View File

@ -159,7 +159,7 @@ func (k *Keyword) Eval(rt *Runtime, scope IScope) (*Keyword, IError) {
} }
if aliasedNS == nil { 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 k.ns = aliasedNS
return k, nil return k, nil

View File

@ -70,9 +70,16 @@ func (l *List) First() IExpr {
func (l *List) Rest() ISeq { func (l *List) Rest() ISeq {
if l.Count() < 2 { 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 { func (l *List) Hash() uint32 {
@ -97,8 +104,8 @@ func (l *List) ToSlice() []IExpr {
} }
func (l *List) Cons(e IExpr) IExpr { func (l *List) Cons(e IExpr) IExpr {
elems := l.ToSlice() elements := append([]IExpr{e}, l.ToSlice()...)
return MakeList(append([]IExpr{e}, elems...)) return MakeList(MakeNodeFromExprs(elements), elements)
} }
// END: IColl --- // END: IColl ---
@ -118,14 +125,16 @@ func ListStartsWith(l *List, sym string) bool {
return false return false
} }
func MakeList(elements []IExpr) *List { func MakeList(n Node, elements []IExpr) *List {
return &List{ return &List{
Node: n,
exprs: elements, exprs: elements,
} }
} }
func MakeEmptyList() *List { func MakeEmptyList(n Node) *List {
return &List{ return &List{
Node: n,
exprs: []IExpr{}, exprs: []IExpr{},
} }
} }

View File

@ -56,7 +56,7 @@ func (n *Namespace) GetType() ast.NodeType {
return ast.Namespace return ast.Namespace
} }
func (n *Namespace) GetLocation() ast.Location { func (n *Namespace) GetLocation() *ast.Location {
return ast.MakeUnknownLocation() return ast.MakeUnknownLocation()
} }
@ -141,7 +141,7 @@ func (n *Namespace) getForms() *Block {
// requireNS finds and loads the namespace addressed by the given // requireNS finds and loads the namespace addressed by the given
// `ns` string. // `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 // TODO: use a hashing algorithm to avoid reloading an unchanged namespace
loadedForms, err := rt.LoadNS(ns) loadedForms, err := rt.LoadNS(ns)
@ -155,11 +155,12 @@ func requireNS(rt *Runtime, ns string) (*Namespace, IError) {
if body.Count() == 0 { if body.Count() == 0 {
return nil, MakeError( return nil, MakeError(
rt, rt,
body,
fmt.Sprintf("The '%s' ns source code doesn't start with an 'ns' form.", ns), 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) namespace.setForms(body)
return &namespace, nil return &namespace, nil
@ -188,21 +189,21 @@ func RequireNamespace(rt *Runtime, namespace IExpr) (IExpr, IError) {
first := list.First() first := list.First()
if first.GetType() != ast.Symbol { 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() second := list.Rest().First()
if second.GetType() != ast.Symbol { 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) ns = first.(*Symbol)
alias = second.(*Symbol).GetName() alias = second.(*Symbol).GetName()
default: 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 { if err != nil {
return nil, err return nil, err
@ -217,6 +218,7 @@ func RequireNamespace(rt *Runtime, namespace IExpr) (IExpr, IError) {
if !inserted { if !inserted {
return nil, MakeError( return nil, MakeError(
rt, rt,
loadedNS,
fmt.Sprintf( fmt.Sprintf(
"the namespace '%s' didn't get inserted in the runtime.", "the namespace '%s' didn't get inserted in the runtime.",
loadedNS.GetName()), loadedNS.GetName()),
@ -231,6 +233,7 @@ func RequireNamespace(rt *Runtime, namespace IExpr) (IExpr, IError) {
if !inserted { if !inserted {
return nil, MakeError( return nil, MakeError(
rt, rt,
loadedNS,
fmt.Sprintf( fmt.Sprintf(
"can't set the current ns back to '%s' from '%s'.", "can't set the current ns back to '%s' from '%s'.",
prevNS.GetName(), prevNS.GetName(),

View File

@ -36,7 +36,7 @@ func (n NothingType) Hash() uint32 {
return hash.HashOf(append([]byte{byte(ast.Block)}, bytes...)) return hash.HashOf(append([]byte{byte(ast.Block)}, bytes...))
} }
func (n NothingType) GetLocation() ast.Location { func (n NothingType) GetLocation() *ast.Location {
return ast.MakeUnknownLocation() return ast.MakeUnknownLocation()
} }

View File

@ -33,6 +33,8 @@ package core
import ( import (
"strings" "strings"
"unicode" "unicode"
"serene-lang.org/bootstrap/pkg/ast"
) )
// An array of the valid characters that be be used in a symbol // 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 // Returns the current position in the buffer
GetLocation() int GetLocation() int
GetSource() *ast.Source
Buffer() *[]string Buffer() *[]string
} }
// StringParser is an implementation of the IParsable that operates on strings. // StringParser is an implementation of the IParsable that operates on strings.
// To put it simply it parses input strings // To put it simply it parses input strings
type StringParser struct { type StringParser struct {
buffer []string buffer []string
pos int pos int
source string
lineIndex []int
} }
// Implementing IParsable for StringParser --- // Implementing IParsable for StringParser ---
@ -75,6 +80,12 @@ func (sp *StringParser) next(skipWhitespace bool) *string {
return nil return nil
} }
char := sp.buffer[sp.pos] 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 sp.pos = sp.pos + 1
if skipWhitespace && isSeparator(&char) { if skipWhitespace && isSeparator(&char) {
@ -127,6 +138,14 @@ func (sp *StringParser) GetLocation() int {
return sp.pos 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 { func (sp *StringParser) Buffer() *[]string {
return &sp.buffer return &sp.buffer
} }
@ -136,7 +155,7 @@ func (sp *StringParser) Buffer() *[]string {
// makeErrorAtPoint is a helper function which generates an `IError` that // makeErrorAtPoint is a helper function which generates an `IError` that
// points at the current position of the buffer. // points at the current position of the buffer.
func makeErrorAtPoint(p IParsable, msg string, a ...interface{}) IError { 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...) 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) sym, err := MakeSymbol(node, symbol)
if err != nil { if err != nil {
@ -228,7 +247,7 @@ func readString(parser IParsable) (IExpr, IError) {
} }
if *c == "\"" { 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 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) { func readComment(parser IParsable) (IExpr, IError) {
@ -387,7 +409,7 @@ func readQuotedExpr(parser IParsable) (IExpr, IError) {
return nil, err return nil, err
} }
symNode := MakeNode(parser.Buffer(), parser.GetLocation(), parser.GetLocation()) symNode := MakeNode(parser.GetSource(), parser.GetLocation(), parser.GetLocation())
sym, err := MakeSymbol(symNode, "quote") sym, err := MakeSymbol(symNode, "quote")
if err != nil { if err != nil {
@ -395,10 +417,15 @@ func readQuotedExpr(parser IParsable) (IExpr, IError) {
return nil, err return nil, err
} }
return MakeList([]IExpr{ listElems := []IExpr{
sym, sym,
expr, 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. // readUnquotedExpr reads different unquoting expressions from their short representaions.
@ -417,7 +444,7 @@ func readUnquotedExpr(parser IParsable) (IExpr, IError) {
var err IError var err IError
var expr IExpr var expr IExpr
node := MakeNode(parser.Buffer(), parser.GetLocation(), parser.GetLocation()) node := MakeNode(parser.GetSource(), parser.GetLocation(), parser.GetLocation())
if *c == "@" { if *c == "@" {
parser.next(true) parser.next(true)
@ -441,7 +468,11 @@ func readUnquotedExpr(parser IParsable) (IExpr, IError) {
return nil, err 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 // readQuasiquotedExpr reads the backquote and replace it with a call
@ -452,16 +483,19 @@ func readQuasiquotedExpr(parser IParsable) (IExpr, IError) {
return nil, err return nil, err
} }
node := MakeNode(parser.Buffer(), parser.GetLocation(), parser.GetLocation()) node := MakeNode(parser.GetSource(), parser.GetLocation(), parser.GetLocation())
sym, err := MakeSymbol(node, "quasiquote") sym, err := MakeSymbol(node, "quasiquote")
if err != nil { if err != nil {
err.SetNode(&node) err.SetNode(&node)
return nil, err return nil, err
} }
return MakeList([]IExpr{
sym, listElems := []IExpr{sym, expr}
expr, listNode := MakeNodeFromExprs(listElems)
}), nil 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 // 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 // by itself is not something available to the language. It's
// just anbstraction for a ordered collection of expressions. // just anbstraction for a ordered collection of expressions.
// It doesn't have anything to do with the concept of blocks // It doesn't have anything to do with the concept of blocks
// from other programming languages // from other programming languages.
func ParseToAST(input string) (*Block, IError) { func ParseToAST(source string, input string) (*Block, IError) {
var ast Block var ast Block
parser := StringParser{ parser := StringParser{
buffer: strings.Split(input, ""), buffer: strings.Split(input, ""),
pos: 0, pos: 0,
source: source,
} }
for { for {

View File

@ -21,6 +21,8 @@ package core
import ( import (
"fmt" "fmt"
"strings" "strings"
"github.com/gookit/color"
) )
func toRepresanbleString(ast ...IRepresentable) string { func toRepresanbleString(ast ...IRepresentable) string {
@ -63,6 +65,36 @@ func Println(rt *Runtime, ast ...IRepresentable) {
} }
func PrintError(rt *Runtime, err IError) { 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() 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())
} }

View File

@ -98,10 +98,15 @@ func qqProcess(rt *Runtime, e IExpr) (IExpr, IError) {
// newErr.stack(err) // newErr.stack(err)
return nil, err return nil, err
} }
return MakeList([]IExpr{ elems := []IExpr{
sym, sym,
e, e,
}), nil }
return MakeList(
MakeNodeFromExprs(elems),
elems,
), nil
case ast.List: case ast.List:
list := e.(*List) list := e.(*List)
@ -125,7 +130,7 @@ func qqProcess(rt *Runtime, e IExpr) (IExpr, IError) {
} }
// ??? // ???
if isUnquoteSplicing(first) { 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 // p := list

View File

@ -18,6 +18,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package core package core
func ReadString(input string) (*Block, IError) { func ReadString(src string, input string) (*Block, IError) {
return ParseToAST(input) return ParseToAST(src, input)
} }

View File

@ -147,8 +147,8 @@ func nsNameToPath(ns string) string {
// LoadNS looks up the namespace specified by the given name `ns` // LoadNS looks up the namespace specified by the given name `ns`
// and reads the content as expressions (parse it) and returns the // and reads the content as expressions (parse it) and returns the
// expressions. // expressions.
func (r *Runtime) LoadNS(ns string) (*loadedForms, IError) { func (r *Runtime) LoadNS(ns *Symbol) (*loadedForms, IError) {
nsFile := nsNameToPath(ns) nsFile := nsNameToPath(ns.GetName())
for _, loadPath := range r.paths { for _, loadPath := range r.paths {
possibleFile := path.Join(loadPath, nsFile) possibleFile := path.Join(loadPath, nsFile)
@ -167,13 +167,14 @@ func (r *Runtime) LoadNS(ns string) (*loadedForms, IError) {
if err != nil { if err != nil {
readError := MakeError( readError := MakeError(
r, r,
ns,
fmt.Sprintf("error while reading the file at %s", possibleFile), fmt.Sprintf("error while reading the file at %s", possibleFile),
) )
readError.WithError(err) readError.WithError(err)
return nil, readError return nil, readError
} }
body, e := ReadString(string(data)) body, e := ReadString(possibleFile, string(data))
if e != nil { if e != nil {
return nil, e 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 // 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) { func (r *Runtime) InsertNS(nsName string, ns *Namespace) {

View File

@ -34,7 +34,7 @@ func Def(rt *Runtime, scope IScope, args *List) (IExpr, IError) {
name := args.First() name := args.First()
if name.GetType() != ast.Symbol { 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) sym := name.(*Symbol)
@ -55,7 +55,7 @@ func Def(rt *Runtime, scope IScope, args *List) (IExpr, IError) {
return sym, nil 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 // 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() name := args.First()
if name.GetType() != ast.Symbol { 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) sym := name.(*Symbol)
@ -100,21 +100,21 @@ func DefMacro(rt *Runtime, scope IScope, args *List) (IExpr, IError) {
return macro, nil 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`. // Fn defines a function inside the given scope `scope` with the given `args`.
// `args` contains the arugment list, docstring and body of the function. // `args` contains the arugment list, docstring and body of the function.
func Fn(rt *Runtime, scope IScope, args *List) (IExpr, IError) { func Fn(rt *Runtime, scope IScope, args *List) (IExpr, IError) {
if args.Count() < 1 { if args.Count() < 2 {
return nil, MakeError(rt, "'fn' needs at least an arguments list") return nil, MakeError(rt, args, "'fn' needs at least an arguments list")
} }
var params IColl var params IColl
body := MakeEmptyBlock() body := MakeEmptyBlock()
arguments := args.First() arguments := args.Rest().First()
// TODO: Add vector in here // TODO: Add vector in here
// Or any other icoll // Or any other icoll
@ -123,26 +123,26 @@ func Fn(rt *Runtime, scope IScope, args *List) (IExpr, IError) {
} }
if args.Count() > 1 { 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) { func NSForm(rt *Runtime, scope IScope, args *List) (IExpr, IError) {
if args.Count() == 1 { 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() name := args.Rest().First()
if name.GetType() != ast.Symbol { 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() nsName := name.(*Symbol).GetName()
if nsName != rt.CurrentNS().GetName() { if nsName != rt.CurrentNS().GetName() {
return nil, MakeErrorFor( return nil, MakeError(
rt, rt,
args, args,
fmt.Sprintf("the namespace '%s' doesn't match the file name.", nsName), 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) ns, ok := rt.GetNS(nsName)
if !ok { 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 return ns, nil

View File

@ -81,8 +81,8 @@ type Node struct {
} }
// GetLocation returns the location of the Node in the source input // GetLocation returns the location of the Node in the source input
func (n Node) GetLocation() ast.Location { func (n Node) GetLocation() *ast.Location {
return n.location return &n.location
} }
type ExecutionScope struct { type ExecutionScope struct {
@ -123,9 +123,9 @@ func toRepresentables(ast IColl) []IRepresentable {
} }
// MakeNodeFromLocation creates a new Node for the given Location `loc` // MakeNodeFromLocation creates a new Node for the given Location `loc`
func MakeNodeFromLocation(loc ast.Location) Node { func MakeNodeFromLocation(loc *ast.Location) Node {
return Node{ return Node{
location: loc, location: *loc,
} }
} }
@ -136,14 +136,30 @@ func MakeNodeFromExpr(e IExpr) Node {
return MakeNodeFromLocation(e.GetLocation()) 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 // MakeNode creates a new Node in the the given `input` that points to a
// range of characters starting from the `start` till the `end`. // 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)) return MakeNodeFromLocation(ast.MakeLocation(input, start, end))
} }
// MakeSinglePointNode creates a not the points to a single char in the // MakeSinglePointNode creates a not the points to a single char in the
// input // input
func MakeSinglePointNode(input *[]string, point int) Node { func MakeSinglePointNode(input *ast.Source, point int) Node {
return MakeNode(input, point, point) return MakeNode(input, point, point)
} }

View File

@ -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/ - 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/ - 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/ - 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 ** Data structures
- Pure functional datastructures papaer :: https://www.cs.cmu.edu/~rwh/theses/okasaki.pdf - 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 - Dynamic typing: syntax and proof theory :: https://reader.elsevier.com/reader/sd/pii/0167642394000042?token=CEFF5C5D1B03FD680762FC4889A14C0CA2BB28FE390EC51099984536E12AC358F3D28A5C25C274296ACBBC32E5AE23CD