[Bootstrap] Update the call stack to container the caller and add a line index to the parser

This commit is contained in:
Sameer Rahmani 2021-01-01 19:13:49 +00:00
parent 66e9340005
commit e895377811
7 changed files with 141 additions and 33 deletions

View File

@ -53,23 +53,65 @@ type Source struct {
LineIndex *[]int 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 { func (s *Source) GetLine(linenum int) string {
lines := strings.Split(strings.Join(*s.Buffer, ""), "\n") 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 { 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 { if pos < 0 {
return -1 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 { if i == 0 {
return pos < (*s.LineIndex)[i] return pos < (*s.LineIndex)[i]
} else { } else {
return (*s.LineIndex)[i-1] < pos && pos < (*s.LineIndex)[i] 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 { type Location struct {

View File

@ -52,7 +52,7 @@ type Frame struct {
Caller IExpr Caller IExpr
} }
type TraceBack = []Frame type TraceBack = []*Frame
type CallStackItem struct { type CallStackItem struct {
prev *CallStackItem prev *CallStackItem
@ -158,7 +158,7 @@ func (c *CallStack) ToTraceBack() *TraceBack {
if item == nil { if item == nil {
break break
} }
tr = append(tr, item.data) tr = append(tr, &item.data)
item = item.prev item = item.prev
} }
@ -172,3 +172,11 @@ func MakeCallStack(debugMode bool) CallStack {
debug: debugMode, debug: debugMode,
} }
} }
func MakeFrame(caller IExpr, f IFn, count uint) *Frame {
return &Frame{
Count: count,
Caller: caller,
Fn: f,
}
}

View File

@ -38,6 +38,7 @@ import (
"fmt" "fmt"
"serene-lang.org/bootstrap/pkg/ast" "serene-lang.org/bootstrap/pkg/ast"
"serene-lang.org/bootstrap/pkg/errors"
) )
// IError defines the necessary functionality of the internal errors. // IError defines the necessary functionality of the internal errors.
@ -49,6 +50,7 @@ type IError interface {
IRepresentable IRepresentable
IDebuggable IDebuggable
GetDescription() *string
GetStackTrace() *TraceBack GetStackTrace() *TraceBack
// To wrap Golan rrrrors // To wrap Golan rrrrors
WithError(err error) IError WithError(err error) IError
@ -61,6 +63,7 @@ type IError interface {
type Error struct { type Error struct {
Node Node
errno errors.Errno
WrappedErr error WrappedErr error
msg string msg string
trace *TraceBack trace *TraceBack
@ -95,6 +98,16 @@ func (e *Error) GetStackTrace() *TraceBack {
return e.trace 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 { func MakePlainError(msg string) IError {
return &Error{ return &Error{
msg: msg, msg: msg,
@ -104,7 +117,7 @@ func MakePlainError(msg string) IError {
// MakeError 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. // the root of the error.
func MakeError(rt *Runtime, e IExpr, msg string) IError { 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{ return &Error{
Node: MakeNodeFromExpr(e), Node: MakeNodeFromExpr(e),
@ -119,3 +132,18 @@ func MakeParsetimeErrorf(n Node, msg string, a ...interface{}) IError {
msg: fmt.Sprintf(msg, a...), 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,
}
}

View File

@ -22,6 +22,7 @@ import (
"fmt" "fmt"
"serene-lang.org/bootstrap/pkg/ast" "serene-lang.org/bootstrap/pkg/ast"
"serene-lang.org/bootstrap/pkg/errors"
) )
func restOfExprs(es []IExpr, i int) []IExpr { func restOfExprs(es []IExpr, i int) []IExpr {
@ -623,7 +624,12 @@ 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, 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 { if exprs[0].GetType() == ast.List {

View File

@ -42,6 +42,7 @@ import (
"fmt" "fmt"
"serene-lang.org/bootstrap/pkg/ast" "serene-lang.org/bootstrap/pkg/ast"
"serene-lang.org/bootstrap/pkg/errors"
"serene-lang.org/bootstrap/pkg/hash" "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.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, MakeSemanticError(
return nil, MakeError(rt, values.(IExpr), rt,
fmt.Sprintf("expected '%d' arguments, got '%d'.", bindings.Count(), values.Count())) values.(IExpr),
errors.E0002,
fmt.Sprintf("expected '%d' arguments, got '%d'.", bindings.Count(), values.Count()),
)
} }
for i := 0; i < len(binds); i += 1 { for i := 0; i < len(binds); i += 1 {

View File

@ -66,26 +66,45 @@ type IParsable interface {
// 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 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 lineIndex []int
} }
// Implementing IParsable for StringParser --- // 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 // Returns the next character in the buffer
func (sp *StringParser) next(skipWhitespace bool) *string { func (sp *StringParser) next(skipWhitespace bool) *string {
if sp.pos >= len(sp.buffer) { if sp.pos >= len(sp.buffer) {
return nil return nil
} }
char := sp.buffer[sp.pos] char := sp.buffer[sp.pos]
sp.updateLineIndex(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) {
@ -121,6 +140,7 @@ func (sp *StringParser) peek(skipWhitespace bool) *string {
c := sp.buffer[sp.pos] c := sp.buffer[sp.pos]
if isSeparator(&c) && skipWhitespace { if isSeparator(&c) && skipWhitespace {
sp.updateLineIndex(sp.pos)
sp.pos = sp.pos + 1 sp.pos = sp.pos + 1
return sp.peek(skipWhitespace) return sp.peek(skipWhitespace)
} }

View File

@ -67,29 +67,29 @@ func Println(rt *Runtime, ast ...IRepresentable) {
func PrintError(rt *Runtime, err IError) { func PrintError(rt *Runtime, err IError) {
trace := err.GetStackTrace() trace := err.GetStackTrace()
for i, f := range *trace { for i, t := range *trace {
loc := f.Caller.GetLocation() caller := t.Caller
fmt.Println("===============") callerLoc := caller.GetLocation()
fmt.Println(f.Fn.GetLocation()) callerSource := callerLoc.GetSource()
fmt.Println(loc)
source := loc.GetSource() startline := callerSource.LineNumberFor(callerLoc.GetStart())
// if loc.GetSource().Buffer != nil {
// fmt.Println(loc.GetSource().LineIndex) if startline > 0 {
// source = *loc.GetSource().Buffer startline -= 1
// } }
startline := source.LineNumberFor(loc.GetStart()) - 1
endline := source.LineNumberFor(loc.GetEnd()) + 1 endline := callerSource.LineNumberFor(callerLoc.GetEnd()) + 1
var lines string var lines string
for i := startline; i <= endline; i++ { 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( color.Yellow.Printf(
"%d: In function '%s' at '%s'\n", "%d: In function '%s' at '%s'\n",
i, i,
f.Fn.GetName(), t.Fn.GetName(),
loc.GetSource().Path, callerLoc.GetSource().Path,
) )
color.White.Printf("%s\n", lines) color.White.Printf("%s\n", lines)
} }