diff --git a/bootstrap/pkg/ast/ast.go b/bootstrap/pkg/ast/ast.go index 8bd421f..415bc38 100644 --- a/bootstrap/pkg/ast/ast.go +++ b/bootstrap/pkg/ast/ast.go @@ -53,23 +53,65 @@ type Source struct { LineIndex *[]int } +func (s *Source) GetPos(start, end int) *string { + if start < len(*s.Buffer) && start >= 0 && end < len(*s.Buffer) && end > 0 && start <= end { + result := strings.Join((*s.Buffer)[start:end], "") + return &result + } else { + return nil + } +} func (s *Source) GetLine(linenum int) string { lines := strings.Split(strings.Join(*s.Buffer, ""), "\n") - return lines[linenum-1] + if linenum > 0 && linenum < len(lines) { + return lines[linenum-1] + } + return "!!!" } func (s *Source) LineNumberFor(pos int) int { + + // Some dirty print debugger code + // for i, r := range *s.LineIndex { + // empty := "" + // var line *string + // var num int + // if i == 0 { + // line = s.GetPos(0, r) + // num = 0 + // } else { + // line = s.GetPos((*s.LineIndex)[i-1], r) + // num = (*s.LineIndex)[i-1] + + // } + + // if line == nil { + // line = &empty + // } + + // fmt.Print(">>>> ", num, r, *line) + // } + if pos < 0 { return -1 } - return sort.Search(len(*s.LineIndex), func(i int) bool { + result := 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] } }) + + // We've found something + if result > -1 { + // Since line numbers start from 1 unlike arrays :)) + result += 1 + } + + return result + } type Location struct { diff --git a/bootstrap/pkg/core/call_stack.go b/bootstrap/pkg/core/call_stack.go index d8023c7..2d0ffc8 100644 --- a/bootstrap/pkg/core/call_stack.go +++ b/bootstrap/pkg/core/call_stack.go @@ -52,7 +52,7 @@ type Frame struct { Caller IExpr } -type TraceBack = []Frame +type TraceBack = []*Frame type CallStackItem struct { prev *CallStackItem @@ -158,7 +158,7 @@ func (c *CallStack) ToTraceBack() *TraceBack { if item == nil { break } - tr = append(tr, item.data) + tr = append(tr, &item.data) item = item.prev } @@ -172,3 +172,11 @@ func MakeCallStack(debugMode bool) CallStack { debug: debugMode, } } + +func MakeFrame(caller IExpr, f IFn, count uint) *Frame { + return &Frame{ + Count: count, + Caller: caller, + Fn: f, + } +} diff --git a/bootstrap/pkg/core/errors.go b/bootstrap/pkg/core/errors.go index 35f8a83..1597a30 100644 --- a/bootstrap/pkg/core/errors.go +++ b/bootstrap/pkg/core/errors.go @@ -38,6 +38,7 @@ import ( "fmt" "serene-lang.org/bootstrap/pkg/ast" + "serene-lang.org/bootstrap/pkg/errors" ) // IError defines the necessary functionality of the internal errors. @@ -49,6 +50,7 @@ type IError interface { IRepresentable IDebuggable + GetDescription() *string GetStackTrace() *TraceBack // To wrap Golan rrrrors WithError(err error) IError @@ -61,6 +63,7 @@ type IError interface { type Error struct { Node + errno errors.Errno WrappedErr error msg string trace *TraceBack @@ -95,6 +98,16 @@ func (e *Error) GetStackTrace() *TraceBack { return e.trace } +func (e *Error) GetDescription() *string { + desc, ok := errors.ErrorsDescription[e.errno] + if ok { + return &desc + } + + desc = errors.ErrorsDescription[0] + return &desc +} + func MakePlainError(msg string) IError { return &Error{ msg: msg, @@ -104,7 +117,7 @@ func MakePlainError(msg string) IError { // MakeError creates an Error which points to the given IExpr `e` as // the root of the error. func MakeError(rt *Runtime, e IExpr, msg string) IError { - trace := append(*rt.Stack.ToTraceBack(), Frame{0, rt.Stack.GetCurrentFn(), e}) + trace := append(*rt.Stack.ToTraceBack(), &Frame{0, rt.Stack.GetCurrentFn(), e}) return &Error{ Node: MakeNodeFromExpr(e), @@ -119,3 +132,18 @@ func MakeParsetimeErrorf(n Node, msg string, a ...interface{}) IError { msg: fmt.Sprintf(msg, a...), } } + +func MakeSemanticError(rt *Runtime, e IExpr, errno errors.Errno, msg string) IError { + currentFn := rt.Stack.GetCurrentFn() + + frames := &[]*Frame{ + MakeFrame(e, currentFn, 1), + } + + return &Error{ + Node: MakeNodeFromExpr(e), + errno: errno, + msg: msg, + trace: frames, + } +} diff --git a/bootstrap/pkg/core/eval.go b/bootstrap/pkg/core/eval.go index b73a950..717aefb 100644 --- a/bootstrap/pkg/core/eval.go +++ b/bootstrap/pkg/core/eval.go @@ -22,6 +22,7 @@ import ( "fmt" "serene-lang.org/bootstrap/pkg/ast" + "serene-lang.org/bootstrap/pkg/errors" ) func restOfExprs(es []IExpr, i int) []IExpr { @@ -623,7 +624,12 @@ func EvalNSBody(rt *Runtime, ns *Namespace) (*Namespace, IError) { exprs := body.ToSlice() if len(exprs) == 0 { - return nil, MakeError(rt, ns, fmt.Sprintf("the 'ns' form is missing from '%s'", ns.GetName())) + return nil, MakeSemanticError( + rt, + ns, + errors.E0001, + fmt.Sprintf("the 'ns' form is missing from '%s'", ns.GetName()), + ) } if exprs[0].GetType() == ast.List { diff --git a/bootstrap/pkg/core/function.go b/bootstrap/pkg/core/function.go index da357a4..1b41f6a 100644 --- a/bootstrap/pkg/core/function.go +++ b/bootstrap/pkg/core/function.go @@ -42,6 +42,7 @@ import ( "fmt" "serene-lang.org/bootstrap/pkg/ast" + "serene-lang.org/bootstrap/pkg/errors" "serene-lang.org/bootstrap/pkg/hash" ) @@ -175,9 +176,12 @@ 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.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())) + return nil, MakeSemanticError( + rt, + values.(IExpr), + errors.E0002, + fmt.Sprintf("expected '%d' arguments, got '%d'.", bindings.Count(), values.Count()), + ) } for i := 0; i < len(binds); i += 1 { diff --git a/bootstrap/pkg/core/parser.go b/bootstrap/pkg/core/parser.go index 59234c9..214cd3e 100644 --- a/bootstrap/pkg/core/parser.go +++ b/bootstrap/pkg/core/parser.go @@ -66,26 +66,45 @@ type IParsable interface { // 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 - source string + buffer []string + pos int + source string + + // This slice holds the boundaries of lines in the buffer. Basically + // each element determines the position which a line ends and the line + // number directly maps to the position of it's boundary in the slice. lineIndex []int } // Implementing IParsable for StringParser --- +// updateLineIndex reads the current character and if it is an end of line, then +// it will update the line index to add the boundaries of the current line. +func (sp *StringParser) updateLineIndex(pos int) { + if pos < len(sp.buffer) { + c := sp.buffer[pos] + if c == "\n" { + if len(sp.lineIndex) > 0 { + if sp.lineIndex[len(sp.lineIndex)-1] != pos+1 { + // Including the \n itself + sp.lineIndex = append(sp.lineIndex, pos+1) + } + } else { + sp.lineIndex = append(sp.lineIndex, pos+1) + } + + } + } + +} + // Returns the next character in the buffer func (sp *StringParser) next(skipWhitespace bool) *string { if sp.pos >= len(sp.buffer) { return nil } char := sp.buffer[sp.pos] - - if char == "\n" { - // Including the \n itself - sp.lineIndex = append(sp.lineIndex, sp.pos+1) - } - + sp.updateLineIndex(sp.pos) sp.pos = sp.pos + 1 if skipWhitespace && isSeparator(&char) { @@ -121,6 +140,7 @@ func (sp *StringParser) peek(skipWhitespace bool) *string { c := sp.buffer[sp.pos] if isSeparator(&c) && skipWhitespace { + sp.updateLineIndex(sp.pos) sp.pos = sp.pos + 1 return sp.peek(skipWhitespace) } diff --git a/bootstrap/pkg/core/printer.go b/bootstrap/pkg/core/printer.go index 97fd7a9..61e0c90 100644 --- a/bootstrap/pkg/core/printer.go +++ b/bootstrap/pkg/core/printer.go @@ -67,29 +67,29 @@ 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 + for i, t := range *trace { + caller := t.Caller + callerLoc := caller.GetLocation() + callerSource := callerLoc.GetSource() + + startline := callerSource.LineNumberFor(callerLoc.GetStart()) + + if startline > 0 { + startline -= 1 + } + + endline := callerSource.LineNumberFor(callerLoc.GetEnd()) + 1 var lines string for i := startline; i <= endline; i++ { - lines += fmt.Sprintf("%d:\t%s\n", i, source.GetLine(i)) + lines += fmt.Sprintf("%d:\t%s\n", i, t.Fn.GetLocation().GetSource().GetLine(i)) } color.Yellow.Printf( "%d: In function '%s' at '%s'\n", i, - f.Fn.GetName(), - loc.GetSource().Path, + t.Fn.GetName(), + callerLoc.GetSource().Path, ) color.White.Printf("%s\n", lines) }