Refine compiler's error reporting.

This commit is contained in:
Qi Xiao 2016-02-19 21:16:23 +01:00
parent b2dbfaee39
commit 2b1b57f0aa
5 changed files with 41 additions and 23 deletions

View File

@ -51,22 +51,23 @@ func compileDel(cp *compiler, fn *parse.Form) Op {
// ensuring that variables can be resolved
var names, envNames []string
for _, cn := range fn.Args {
cp.compiling(cn)
qname := mustString(cp, cn, "should be a literal variable name")
splice, ns, name := parseVariable(qname)
if splice {
cp.errorf(cn.Begin(), "removing spliced variable makes no sense")
cp.errorf("removing spliced variable makes no sense")
}
switch ns {
case "", "local":
if !cp.thisScope()[name] {
cp.errorf(cn.Begin(), "variable $%s not found on current local scope", name)
cp.errorf("variable $%s not found on current local scope", name)
}
delete(cp.thisScope(), name)
names = append(names, name)
case "env":
envNames = append(envNames, name)
default:
cp.errorf(cn.Begin(), "can only delete a variable in local: or env:")
cp.errorf("can only delete a variable in local: or env:")
}
}
@ -98,20 +99,23 @@ func makeFnOp(op Op) Op {
// fn f []{foobar} is a shorthand for set '&'f = []{foobar}.
func compileFn(cp *compiler, fn *parse.Form) Op {
if len(fn.Args) == 0 {
cp.errorf(fn.End(), "should be followed by function name")
end := fn.End()
cp.errorpf(end, end, "should be followed by function name")
}
fnName := mustString(cp, fn.Args[0], "must be a literal string")
varName := FnPrefix + fnName
if len(fn.Args) == 1 {
cp.errorf(fn.Args[0].End(), "should be followed by a lambda")
end := fn.Args[0].End()
cp.errorpf(end, end, "should be followed by a lambda")
}
pn := mustPrimary(cp, fn.Args[1], "should be a lambda")
if pn.Type != parse.Lambda {
cp.errorf(pn.Begin(), "should be a lambda")
cp.compiling(pn)
cp.errorf("should be a lambda")
}
if len(fn.Args) > 2 {
cp.errorf(fn.Args[2].Begin(), "superfluous argument")
cp.errorpf(fn.Args[2].Begin(), fn.Args[len(fn.Args)-1].End(), "superfluous argument(s)")
}
cp.registerVariableSet(":" + varName)
@ -136,7 +140,8 @@ func compileUse(cp *compiler, fn *parse.Form) Op {
switch len(fn.Args) {
case 0:
cp.errorf(fn.Head.End(), "should be module name")
end := fn.Head.End()
cp.errorpf(end, end, "lack module name")
case 2:
filenameOp = cp.compound(fn.Args[1])
filenameBegin = fn.Args[1].Begin()
@ -144,7 +149,7 @@ func compileUse(cp *compiler, fn *parse.Form) Op {
case 1:
modname = mustString(cp, fn.Args[0], "should be a literal module name")
default:
cp.errorf(fn.Args[2].Begin(), "superfluous argument")
cp.errorpf(fn.Args[2].Begin(), fn.Args[len(fn.Args)-1].End(), "superfluous argument(s)")
}
return func(ec *EvalCtx) {

View File

@ -134,7 +134,7 @@ func (cp *compiler) pipeline(n *parse.Pipeline) Op {
func (cp *compiler) form(n *parse.Form) Op {
if len(n.Assignments) > 0 {
if n.Head != nil {
cp.errorf(n.Begin(), "temporary assignments not yet supported")
cp.errorpf(n.Assignments[0].Begin(), n.Assignments[len(n.Assignments)-1].End(), "temporary assignments not yet supported")
}
ops := cp.assignments(n.Assignments)
return func(ec *EvalCtx) {
@ -146,7 +146,7 @@ func (cp *compiler) form(n *parse.Form) Op {
if n.Control != nil {
if len(n.Args) > 0 {
cp.errorf(n.Args[0].Begin(), "control structure takes no arguments")
cp.errorpf(n.Args[0].Begin(), n.Args[len(n.Args)-1].End(), "control structure takes no arguments")
}
redirOps := cp.redirs(n.Redirs)
controlOp := cp.control(n.Control)
@ -259,7 +259,7 @@ func (cp *compiler) control(n *parse.Control) Op {
case parse.BeginControl:
return cp.chunk(n.Body)
default:
cp.errorf(n.Begin(), "unknown ControlKind %s, compiler bug", n.Kind)
cp.errorpf(n.Begin(), n.End(), "unknown ControlKind %s, compiler bug", n.Kind)
panic("unreachable")
}
}
@ -285,11 +285,12 @@ func (cp *compiler) multiVariable(n *parse.Indexing) []VariableOp {
indexings := make([]*parse.Indexing, len(compounds))
for i, cn := range compounds {
if len(cn.Indexings) != 1 {
cp.errorf(cn.Begin(), "must be a variable spec")
cp.compiling(cn)
cp.errorf("must be a variable spec")
}
indexings[i] = cn.Indexings[0]
}
variableOps = cp.singleVariables(indexings, "must be a variable spc")
variableOps = cp.singleVariables(indexings, "must be a variable spec")
} else {
variableOps = []VariableOp{cp.singleVariable(n, "must be a variable spec or a braced list of those")}
}
@ -384,7 +385,8 @@ func (cp *compiler) literal(n *parse.Primary, msg string) string {
case parse.Bareword, parse.SingleQuoted, parse.DoubleQuoted:
return n.Value
default:
cp.errorf(n.Begin(), msg)
cp.compiling(n)
cp.errorf(msg)
return "" // not reached
}
}

View File

@ -226,13 +226,14 @@ func variable(qname string, p int) ValuesOp {
}
func (cp *compiler) primary(n *parse.Primary) ValuesOp {
cp.compiling(n)
switch n.Type {
case parse.Bareword, parse.SingleQuoted, parse.DoubleQuoted:
return literalStr(n.Value)
case parse.Variable:
qname := n.Value
if !cp.registerVariableGet(qname) {
cp.errorf(n.Begin(), "variable $%s not found", n.Value)
cp.errorf("variable $%s not found", n.Value)
}
return variable(qname, n.Begin())
case parse.Wildcard:
@ -242,7 +243,7 @@ func (cp *compiler) primary(n *parse.Primary) ValuesOp {
return vs
}
case parse.Tilde:
cp.errorf(n.Begin(), "compiler bug: Tilde not handled in .compound")
cp.errorf("compiler bug: Tilde not handled in .compound")
return literalStr("~")
case parse.ErrorCapture:
return cp.errorCapture(n.Chunk)
@ -260,7 +261,7 @@ func (cp *compiler) primary(n *parse.Primary) ValuesOp {
case parse.Braced:
return cp.braced(n)
default:
cp.errorf(n.Begin(), "bad PrimaryType; parser bug")
cp.errorf("bad PrimaryType; parser bug")
return literalStr(n.SourceText())
}
}

View File

@ -26,16 +26,26 @@ type compiler struct {
scopes []scope
// Variables captured from outer scopes.
capture scope
// Position of what is being compiled.
begin, end int
}
func compile(sc scope, n *parse.Chunk) (op Op, err error) {
cp := &compiler{[]scope{sc}, scope{}}
cp := &compiler{[]scope{sc}, scope{}, 0, 0}
defer util.Catch(&err)
return cp.chunk(n), nil
}
func (cp *compiler) errorf(p int, format string, args ...interface{}) {
throw(&util.PosError{p, p, fmt.Errorf(format, args...)})
func (cp *compiler) compiling(n parse.Node) {
cp.begin, cp.end = n.Begin(), n.End()
}
func (cp *compiler) errorpf(begin, end int, format string, args ...interface{}) {
throw(&util.PosError{begin, end, fmt.Errorf(format, args...)})
}
func (cp *compiler) errorf(format string, args ...interface{}) {
cp.errorpf(cp.begin, cp.end, format, args...)
}
func (cp *compiler) thisScope() scope {

View File

@ -95,7 +95,7 @@ func oneString(cn *parse.Compound) (string, bool) {
func mustPrimary(cp *compiler, cn *parse.Compound, msg string) *parse.Primary {
p := onePrimary(cn)
if p == nil {
cp.errorf(cn.Begin(), msg)
cp.errorpf(cn.Begin(), cn.End(), msg)
}
return p
}
@ -105,7 +105,7 @@ func mustPrimary(cp *compiler, cn *parse.Compound, msg string) *parse.Primary {
func mustString(cp *compiler, cn *parse.Compound, msg string) string {
s, ok := oneString(cn)
if !ok {
cp.errorf(cn.Begin(), msg)
cp.errorpf(cn.Begin(), cn.End(), msg)
}
return s
}