[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
}
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 {

View File

@ -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,
}
}

View File

@ -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,
}
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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)
}

View File

@ -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)
}