diff --git a/cli/run.go b/cli/run.go index d124517de..19df0871e 100644 --- a/cli/run.go +++ b/cli/run.go @@ -152,7 +152,7 @@ func (obj *RunArgs) Run(ctx context.Context, data *cliUtil.Data) (bool, error) { deploy, err := gapiObj.Cli(info) if err != nil { - return false, cliUtil.CliParseError(err) // consistent errors + return false, cliUtil.CliParseError(err) // TODO: it seems unlikely that parsing the CLI failed at this stage, and then the error will be misleading } if cmd := obj.RunLang; cmd != nil && cmd.OnlyUnify && deploy == nil { diff --git a/lang/ast/structs.go b/lang/ast/structs.go index b8bf17b9f..1f42a76fa 100644 --- a/lang/ast/structs.go +++ b/lang/ast/structs.go @@ -177,17 +177,56 @@ var ( orderingGraphSingleton = false ) +// TextArea stores the coordinates of a statement or expression in the form of +// starting line/column and ending line/column +type TextArea struct { + startLine int + startColumn int + endLine int + endColumn int + + // Bug5819 works around issue https://github.com/golang/go/issues/5819 + Bug5819 interface{} // XXX: workaround +} + +// Locate is used by the parser to store the token positions in AST nodes +func (a *TextArea) Locate(line int, col int, endline int, endcol int) { + a.startLine = line + a.startColumn = col + a.endLine = endline + a.endColumn = endcol +} + +// LocalNode is the interface implemented by AST nodes that store their code +// position. It is implemented by node types that embed TextArea. +type LocalNode interface { + Locate(int, int, int, int) + GetPosition() (int, int) + GetEndPosition() (int, int) +} + +// GetPosition returns the starting line/column of an AST node +func (a TextArea) GetPosition() (int, int) { + return a.startLine, a.startColumn +} + +// GetEndPosition returns the end line/column of an AST node +func (a TextArea) GetEndPosition() (int, int) { + return a.endLine, a.endColumn +} + // StmtBind is a representation of an assignment, which binds a variable to an // expression. type StmtBind struct { Ident string Value interfaces.Expr Type *types.Type + TextArea } // String returns a short representation of this statement. func (obj *StmtBind) String() string { - return fmt.Sprintf("bind(%s)", obj.Ident) + return fmt.Sprintf("bind(%s) @ (%d %d)", obj.Ident, obj.startLine+1, obj.startColumn+1) } // Apply is a general purpose iterator method that operates on any AST node. It @@ -220,11 +259,9 @@ func (obj *StmtBind) Interpolate() (interfaces.Stmt, error) { if err != nil { return nil, err } - return &StmtBind{ - Ident: obj.Ident, - Value: interpolated, - Type: obj.Type, - }, nil + result := *obj + result.Value = interpolated + return &result, nil } // Copy returns a light copy of this struct. Anything static will not be copied. @@ -365,6 +402,7 @@ type StmtRes struct { Name interfaces.Expr // unique name for the res of this kind namePtr interfaces.Func // ptr for table lookup Contents []StmtResContents // list of fields/edges in parsed order + TextArea } // String returns a short representation of this statement. @@ -2059,6 +2097,7 @@ type StmtEdge struct { // TODO: should notify be an Expr? Notify bool // specifies that this edge sends a notification as well + TextArea } // String returns a short representation of this statement. @@ -2561,6 +2600,7 @@ type StmtIf struct { conditionPtr interfaces.Func // ptr for table lookup ThenBranch interfaces.Stmt // optional, but usually present ElseBranch interfaces.Stmt // optional + TextArea } // String returns a short representation of this statement. @@ -2576,6 +2616,8 @@ func (obj *StmtIf) String() string { s += fmt.Sprintf(" else { %s }", obj.ElseBranch.String()) } + s += fmt.Sprintf(" @ (%d %d)", obj.startLine+1, obj.startColumn+1) + return s } @@ -2642,11 +2684,11 @@ func (obj *StmtIf) Interpolate() (interfaces.Stmt, error) { return nil, errwrap.Wrapf(err, "could not interpolate ElseBranch") } } - return &StmtIf{ - Condition: condition, - ThenBranch: thenBranch, - ElseBranch: elseBranch, - }, nil + result := *obj + result.Condition = condition + result.ThenBranch = thenBranch + result.ElseBranch = elseBranch + return &result, nil } // Copy returns a light copy of this struct. Anything static will not be copied. @@ -2923,6 +2965,7 @@ type StmtProg struct { importFiles []string // list of files seen during the SetScope import Body []interfaces.Stmt + TextArea } // String returns a short representation of this statement. @@ -4312,6 +4355,7 @@ type StmtFunc struct { Name string Func interfaces.Expr Type *types.Type + TextArea } // String returns a short representation of this statement. @@ -4518,6 +4562,7 @@ type StmtClass struct { Name string Args []*interfaces.Arg Body interfaces.Stmt // probably a *StmtProg + TextArea } // String returns a short representation of this statement. @@ -4720,6 +4765,7 @@ type StmtInclude struct { Name string Args []interfaces.Expr Alias string + TextArea } // String returns a short representation of this statement. @@ -5085,6 +5131,7 @@ func (obj *StmtInclude) Output(table map[interfaces.Func]types.Value) (*interfac type StmtImport struct { Name string Alias string + TextArea } // String returns a short representation of this statement. @@ -5266,6 +5313,7 @@ type ExprBool struct { scope *interfaces.Scope // store for referencing this later V bool + TextArea } // String returns a short representation of this expression. @@ -5411,6 +5459,7 @@ type ExprStr struct { scope *interfaces.Scope // store for referencing this later V string // value of this string + TextArea } // String returns a short representation of this expression. @@ -5606,6 +5655,7 @@ type ExprInt struct { scope *interfaces.Scope // store for referencing this later V int64 + TextArea } // String returns a short representation of this expression. @@ -5749,6 +5799,7 @@ type ExprFloat struct { scope *interfaces.Scope // store for referencing this later V float64 + TextArea } // String returns a short representation of this expression. @@ -5896,6 +5947,7 @@ type ExprList struct { //Elements []*ExprListElement Elements []interfaces.Expr + TextArea } // String returns a short representation of this expression. @@ -6249,6 +6301,7 @@ type ExprMap struct { typ *types.Type KVs []*ExprMapKV + TextArea } // String returns a short representation of this expression. @@ -6734,6 +6787,7 @@ type ExprStruct struct { typ *types.Type Fields []*ExprStructField // the list (fields) are intentionally ordered! + TextArea } // String returns a short representation of this expression. @@ -7166,6 +7220,7 @@ type ExprFunc struct { // XXX: is this necessary? //V func(interfaces.Txn, []pgraph.Vertex) (pgraph.Vertex, error) + TextArea } // String returns a short representation of this expression. @@ -7910,6 +7965,7 @@ type ExprCall struct { Args []interfaces.Expr // list of args in parsed order // Var specifies whether the function being called is a lambda in a var. Var bool + TextArea } // String returns a short representation of this expression. @@ -7918,7 +7974,9 @@ func (obj *ExprCall) String() string { for _, x := range obj.Args { s = append(s, fmt.Sprintf("%s", x.String())) } - return fmt.Sprintf("call:%s(%s)", obj.Name, strings.Join(s, ", ")) + result := fmt.Sprintf("call:%s(%s)", obj.Name, strings.Join(s, ", ")) + result += fmt.Sprintf(" @ (%d %d)", obj.startLine+1, obj.startColumn+1) + return result } // Apply is a general purpose iterator method that operates on any AST node. It @@ -7965,19 +8023,11 @@ func (obj *ExprCall) Interpolate() (interfaces.Expr, error) { orig = obj.orig } - return &ExprCall{ - data: obj.data, - scope: obj.scope, - typ: obj.typ, - // XXX: Copy copies this, do we want to here as well? (or maybe - // we want to do it here, but not in Copy?) - expr: obj.expr, - orig: orig, - V: obj.V, - Name: obj.Name, - Args: args, - Var: obj.Var, - }, nil + result := *obj + result.orig = orig + result.Args = args + + return &result, nil } // Copy returns a light copy of this struct. Anything static will not be copied. @@ -8365,7 +8415,7 @@ func (obj *ExprCall) Infer() (*types.Type, []*interfaces.UnificationInvariant, e var typExpr *types.Type // out // Look at what kind of function we are calling... - callee := trueCallee(obj.expr) + callee := TrueCallee(obj.expr) exprFunc, isFn := callee.(*ExprFunc) argGen := func(x int) (string, error) { @@ -8641,6 +8691,7 @@ type ExprVar struct { typ *types.Type Name string // name of the variable + TextArea } // String returns a short representation of this expression. @@ -8904,6 +8955,7 @@ type ExprParam struct { typ *types.Type Name string // name of the parameter + TextArea } // String returns a short representation of this expression. @@ -9616,6 +9668,7 @@ type ExprIf struct { Condition interfaces.Expr ThenBranch interfaces.Expr // could be an ExprBranch ElseBranch interfaces.Expr // could be an ExprBranch + TextArea } // String returns a short representation of this expression. diff --git a/lang/ast/util.go b/lang/ast/util.go index 4c4e71237..aed2acc6e 100644 --- a/lang/ast/util.go +++ b/lang/ast/util.go @@ -327,16 +327,16 @@ func getScope(node interfaces.Expr) (*interfaces.Scope, error) { } } -// trueCallee is a helper function because ExprTopLevel and ExprSingleton are +// TrueCallee is a helper function because ExprTopLevel and ExprSingleton are // sometimes added around builtins. This makes it difficult for the type checker // to check if a particular builtin is the callee or not. This function removes // the ExprTopLevel and ExprSingleton wrappers, if they exist. -func trueCallee(apparentCallee interfaces.Expr) interfaces.Expr { +func TrueCallee(apparentCallee interfaces.Expr) interfaces.Expr { switch x := apparentCallee.(type) { case *ExprTopLevel: - return trueCallee(x.Definition) + return TrueCallee(x.Definition) case *ExprSingleton: - return trueCallee(x.Definition) + return TrueCallee(x.Definition) default: return apparentCallee } @@ -383,7 +383,7 @@ func lambdaScopeFeedback(scope *interfaces.Scope, logf func(format string, v ... names := []string{} for name, val := range scope.Variables { // XXX: Is this a valid way to filter? - if _, ok := trueCallee(val).(*ExprFunc); !ok { + if _, ok := TrueCallee(val).(*ExprFunc); !ok { continue } names = append(names, name) diff --git a/lang/gapi/gapi.go b/lang/gapi/gapi.go index 8ce3bdf48..6aca0faa3 100644 --- a/lang/gapi/gapi.go +++ b/lang/gapi/gapi.go @@ -274,6 +274,12 @@ func (obj *GAPI) Cli(info *gapi.Info) (*gapi.Deploy, error) { unificationStrategy[unification.StrategyOptimizationsKey] = strings.Join(args.UnifyOptimizations, ",") } + fmt.Println("The Interpolated Tree: %+v") + iast.Apply(func (n interfaces.Node) error { + fmt.Println(n) + return nil + }) + if !args.SkipUnify { // apply type unification unificationLogf := func(format string, v ...interface{}) { @@ -304,7 +310,8 @@ func (obj *GAPI) Cli(info *gapi.Info) (*gapi.Deploy, error) { if args.OnlyUnify { logf("type unification failed after %s", formatted) } - return nil, errwrap.Wrapf(unifyErr, "could not unify types") + callee := ast.TrueCallee(unifyErr.(*interfaces.UnificationInvariant).Expr) + return nil, errwrap.Wrapf(unifyErr, "could not unify types [[ in expression: %s ]]", callee) } if args.OnlyUnify { diff --git a/lang/interfaces/unification.go b/lang/interfaces/unification.go index 59bd854a1..b12bf1c91 100644 --- a/lang/interfaces/unification.go +++ b/lang/interfaces/unification.go @@ -48,6 +48,13 @@ type UnificationInvariant struct { // formerly the SamInvariant // Actual is one of the two types to unify. Actual *types.Type + + // An error string to pass along with this + Err string +} + +func (obj *UnificationInvariant) Error() string { + return obj.Err } // GenericCheck is the generic implementation of the Check Expr interface call. diff --git a/lang/parser/parser.y b/lang/parser/parser.y index f497b7997..442828bc9 100644 --- a/lang/parser/parser.y +++ b/lang/parser/parser.y @@ -149,14 +149,13 @@ top: prog: /* end of list */ { - posLast(yylex, yyDollar) // our pos $$.stmt = &ast.StmtProg{ Body: []interfaces.Stmt{}, } + //locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.stmt) } | prog stmt { - posLast(yylex, yyDollar) // our pos // TODO: should we just skip comments for now? //if _, ok := $2.stmt.(*ast.StmtComment); !ok { //} @@ -166,49 +165,50 @@ prog: $$.stmt = &ast.StmtProg{ Body: stmts, } + //locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.stmt) } } ; stmt: COMMENT { - posLast(yylex, yyDollar) // our pos $$.stmt = &ast.StmtComment{ Value: $1.str, } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.stmt) } | bind { - posLast(yylex, yyDollar) // our pos $$.stmt = $1.stmt + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.stmt) } | panic { - posLast(yylex, yyDollar) // our pos $$.stmt = $1.stmt + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.stmt) } | resource { - posLast(yylex, yyDollar) // our pos $$.stmt = $1.stmt + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.stmt) } | edge { - posLast(yylex, yyDollar) // our pos $$.stmt = $1.stmt + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.stmt) } | IF expr OPEN_CURLY prog CLOSE_CURLY { - posLast(yylex, yyDollar) // our pos $$.stmt = &ast.StmtIf{ Condition: $2.expr, ThenBranch: $4.stmt, //ElseBranch: nil, } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.stmt) } | IF expr OPEN_CURLY prog CLOSE_CURLY ELSE OPEN_CURLY prog CLOSE_CURLY { - posLast(yylex, yyDollar) // our pos + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.stmt) $$.stmt = &ast.StmtIf{ Condition: $2.expr, ThenBranch: $4.stmt, @@ -221,7 +221,7 @@ stmt: // `func name(, ) { }` | FUNC_IDENTIFIER IDENTIFIER OPEN_PAREN args CLOSE_PAREN OPEN_CURLY expr CLOSE_CURLY { - posLast(yylex, yyDollar) // our pos + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.stmt) $$.stmt = &ast.StmtFunc{ Name: $2.str, Func: &ast.ExprFunc{ @@ -234,7 +234,6 @@ stmt: // `func name(...) { }` | FUNC_IDENTIFIER IDENTIFIER OPEN_PAREN args CLOSE_PAREN type OPEN_CURLY expr CLOSE_CURLY { - posLast(yylex, yyDollar) // our pos fn := &ast.ExprFunc{ Args: $4.args, Return: $6.typ, // return type is known @@ -271,191 +270,192 @@ stmt: Func: fn, Type: typ, // sam says add the type here instead... } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.stmt) } // `class name { }` | CLASS_IDENTIFIER colon_identifier OPEN_CURLY prog CLOSE_CURLY { - posLast(yylex, yyDollar) // our pos $$.stmt = &ast.StmtClass{ Name: $2.str, Args: nil, Body: $4.stmt, } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.stmt) } // `class name() { }` // `class name(, ) { }` | CLASS_IDENTIFIER colon_identifier OPEN_PAREN args CLOSE_PAREN OPEN_CURLY prog CLOSE_CURLY { - posLast(yylex, yyDollar) // our pos $$.stmt = &ast.StmtClass{ Name: $2.str, Args: $4.args, Body: $7.stmt, } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.stmt) } // `include name` | INCLUDE_IDENTIFIER dotted_identifier { - posLast(yylex, yyDollar) // our pos $$.stmt = &ast.StmtInclude{ Name: $2.str, } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.stmt) } // `include name(...)` | INCLUDE_IDENTIFIER dotted_identifier OPEN_PAREN call_args CLOSE_PAREN { - posLast(yylex, yyDollar) // our pos $$.stmt = &ast.StmtInclude{ Name: $2.str, Args: $4.exprs, } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.stmt) } // `include name as foo` // TODO: should we support: `include name as *` | INCLUDE_IDENTIFIER dotted_identifier AS_IDENTIFIER IDENTIFIER { - posLast(yylex, yyDollar) // our pos $$.stmt = &ast.StmtInclude{ Name: $2.str, Alias: $4.str, } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.stmt) } // `include name(...) as foo` // TODO: should we support: `include name(...) as *` | INCLUDE_IDENTIFIER dotted_identifier OPEN_PAREN call_args CLOSE_PAREN AS_IDENTIFIER IDENTIFIER { - posLast(yylex, yyDollar) // our pos $$.stmt = &ast.StmtInclude{ Name: $2.str, Args: $4.exprs, Alias: $7.str, } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.stmt) } // `import "name"` | IMPORT_IDENTIFIER STRING { - posLast(yylex, yyDollar) // our pos $$.stmt = &ast.StmtImport{ Name: $2.str, //Alias: "", } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.stmt) } // `import "name" as alias` | IMPORT_IDENTIFIER STRING AS_IDENTIFIER IDENTIFIER { - posLast(yylex, yyDollar) // our pos $$.stmt = &ast.StmtImport{ Name: $2.str, Alias: $4.str, } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.stmt) } // `import "name" as *` | IMPORT_IDENTIFIER STRING AS_IDENTIFIER MULTIPLY { - posLast(yylex, yyDollar) // our pos $$.stmt = &ast.StmtImport{ Name: $2.str, Alias: $4.str, } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.stmt) } /* // resource bind | rbind { - posLast(yylex, yyDollar) // our pos $$.stmt = $1.stmt + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.stmt) } */ ; expr: BOOL { - posLast(yylex, yyDollar) // our pos $$.expr = &ast.ExprBool{ V: $1.bool, } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.expr) } | STRING { - posLast(yylex, yyDollar) // our pos $$.expr = &ast.ExprStr{ V: $1.str, } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.expr) } | INTEGER { - posLast(yylex, yyDollar) // our pos $$.expr = &ast.ExprInt{ V: $1.int, } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.expr) } | FLOAT { - posLast(yylex, yyDollar) // our pos $$.expr = &ast.ExprFloat{ V: $1.float, } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.expr) } | list { - posLast(yylex, yyDollar) // our pos // TODO: list could be squashed in here directly... $$.expr = $1.expr + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.expr) } | map { - posLast(yylex, yyDollar) // our pos // TODO: map could be squashed in here directly... $$.expr = $1.expr + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.expr) } | struct { - posLast(yylex, yyDollar) // our pos // TODO: struct could be squashed in here directly... $$.expr = $1.expr + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.expr) } | call { - posLast(yylex, yyDollar) // our pos // TODO: call could be squashed in here directly... $$.expr = $1.expr + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.expr) } | var { - posLast(yylex, yyDollar) // our pos // TODO: var could be squashed in here directly... $$.expr = $1.expr + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.expr) } | func { - posLast(yylex, yyDollar) // our pos // TODO: var could be squashed in here directly... $$.expr = $1.expr + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.expr) } | IF expr OPEN_CURLY expr CLOSE_CURLY ELSE OPEN_CURLY expr CLOSE_CURLY { - posLast(yylex, yyDollar) // our pos $$.expr = &ast.ExprIf{ Condition: $2.expr, ThenBranch: $4.expr, ElseBranch: $8.expr, } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.expr) } // parenthesis wrap an expression for precedence | OPEN_PAREN expr CLOSE_PAREN { - posLast(yylex, yyDollar) // our pos $$.expr = $2.expr + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.expr) } ; list: // `[42, 0, -13]` OPEN_BRACK list_elements CLOSE_BRACK { - posLast(yylex, yyDollar) // our pos $$.expr = &ast.ExprList{ Elements: $2.exprs, } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.expr) } ; list_elements: @@ -473,18 +473,18 @@ list_elements: list_element: expr COMMA { - posLast(yylex, yyDollar) // our pos $$.expr = $1.expr + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.expr) } ; map: // `{"hello" => "there", "world" => "big",}` OPEN_CURLY map_kvs CLOSE_CURLY { - posLast(yylex, yyDollar) // our pos $$.expr = &ast.ExprMap{ KVs: $2.mapKVs, } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.expr) } ; map_kvs: @@ -513,10 +513,10 @@ struct: // `struct{answer => 0, truth => false, hello => "world",}` STRUCT_IDENTIFIER OPEN_CURLY struct_fields CLOSE_CURLY { - posLast(yylex, yyDollar) // our pos $$.expr = &ast.ExprStruct{ Fields: $3.structFields, } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.expr) } ; struct_fields: @@ -546,18 +546,17 @@ call: // iter.map(...) dotted_identifier OPEN_PAREN call_args CLOSE_PAREN { - posLast(yylex, yyDollar) // our pos $$.expr = &ast.ExprCall{ Name: $1.str, Args: $3.exprs, //Var: false, // default } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.expr) } // calling a function that's stored in a variable (a lambda) // `$foo(4, "hey")` # call function value | dotted_var_identifier OPEN_PAREN call_args CLOSE_PAREN { - posLast(yylex, yyDollar) // our pos $$.expr = &ast.ExprCall{ Name: $1.str, Args: $3.exprs, @@ -565,10 +564,10 @@ call: // prefix to the Name, but I felt this was more elegant. Var: true, // lambda } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.expr) } | expr PLUS expr { - posLast(yylex, yyDollar) // our pos $$.expr = &ast.ExprCall{ Name: operators.OperatorFuncName, Args: []interfaces.Expr{ @@ -579,10 +578,10 @@ call: $3.expr, }, } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.expr) } | expr MINUS expr { - posLast(yylex, yyDollar) // our pos $$.expr = &ast.ExprCall{ Name: operators.OperatorFuncName, Args: []interfaces.Expr{ @@ -593,10 +592,10 @@ call: $3.expr, }, } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.expr) } | expr MULTIPLY expr { - posLast(yylex, yyDollar) // our pos $$.expr = &ast.ExprCall{ Name: operators.OperatorFuncName, Args: []interfaces.Expr{ @@ -607,10 +606,10 @@ call: $3.expr, }, } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.expr) } | expr DIVIDE expr { - posLast(yylex, yyDollar) // our pos $$.expr = &ast.ExprCall{ Name: operators.OperatorFuncName, Args: []interfaces.Expr{ @@ -621,10 +620,10 @@ call: $3.expr, }, } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.expr) } | expr EQ expr { - posLast(yylex, yyDollar) // our pos $$.expr = &ast.ExprCall{ Name: operators.OperatorFuncName, Args: []interfaces.Expr{ @@ -635,10 +634,10 @@ call: $3.expr, }, } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.expr) } | expr NEQ expr { - posLast(yylex, yyDollar) // our pos $$.expr = &ast.ExprCall{ Name: operators.OperatorFuncName, Args: []interfaces.Expr{ @@ -649,10 +648,10 @@ call: $3.expr, }, } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.expr) } | expr LT expr { - posLast(yylex, yyDollar) // our pos $$.expr = &ast.ExprCall{ Name: operators.OperatorFuncName, Args: []interfaces.Expr{ @@ -663,10 +662,10 @@ call: $3.expr, }, } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.expr) } | expr GT expr { - posLast(yylex, yyDollar) // our pos $$.expr = &ast.ExprCall{ Name: operators.OperatorFuncName, Args: []interfaces.Expr{ @@ -677,10 +676,10 @@ call: $3.expr, }, } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.expr) } | expr LTE expr { - posLast(yylex, yyDollar) // our pos $$.expr = &ast.ExprCall{ Name: operators.OperatorFuncName, Args: []interfaces.Expr{ @@ -691,10 +690,10 @@ call: $3.expr, }, } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.expr) } | expr GTE expr { - posLast(yylex, yyDollar) // our pos $$.expr = &ast.ExprCall{ Name: operators.OperatorFuncName, Args: []interfaces.Expr{ @@ -705,10 +704,10 @@ call: $3.expr, }, } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.expr) } | expr AND expr { - posLast(yylex, yyDollar) // our pos $$.expr = &ast.ExprCall{ Name: operators.OperatorFuncName, Args: []interfaces.Expr{ @@ -719,10 +718,10 @@ call: $3.expr, }, } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.expr) } | expr OR expr { - posLast(yylex, yyDollar) // our pos $$.expr = &ast.ExprCall{ Name: operators.OperatorFuncName, Args: []interfaces.Expr{ @@ -733,10 +732,10 @@ call: $3.expr, }, } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.expr) } | NOT expr { - posLast(yylex, yyDollar) // our pos $$.expr = &ast.ExprCall{ Name: operators.OperatorFuncName, Args: []interfaces.Expr{ @@ -746,13 +745,13 @@ call: $2.expr, }, } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.expr) } // lookup an index in a list or a key in a map // lookup($foo, $key) // `$foo[$key]` // no default specifier | expr OPEN_BRACK expr CLOSE_BRACK { - posLast(yylex, yyDollar) // our pos $$.expr = &ast.ExprCall{ Name: funcs.LookupFuncName, Args: []interfaces.Expr{ @@ -761,13 +760,13 @@ call: //$6.expr, // the default }, } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.expr) } // lookup an index in a list or a key in a map with a default // lookup_default($foo, $key, $default) // `$foo[$key] || "default"` | expr OPEN_BRACK expr CLOSE_BRACK DEFAULT expr { - posLast(yylex, yyDollar) // our pos $$.expr = &ast.ExprCall{ Name: funcs.LookupDefaultFuncName, Args: []interfaces.Expr{ @@ -776,13 +775,13 @@ call: $6.expr, // the default }, } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.expr) } // lookup a field in a struct // _struct_lookup($foo, "field") // $foo->field | expr ARROW IDENTIFIER { - posLast(yylex, yyDollar) // our pos $$.expr = &ast.ExprCall{ Name: funcs.StructLookupFuncName, Args: []interfaces.Expr{ @@ -793,13 +792,13 @@ call: //$5.expr, // the default }, } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.expr) } // lookup a field in a struct with a default // _struct_lookup_optional($foo, "field", "default") // $foo->field || "default" | expr ARROW IDENTIFIER DEFAULT expr { - posLast(yylex, yyDollar) // our pos $$.expr = &ast.ExprCall{ Name: funcs.StructLookupOptionalFuncName, Args: []interfaces.Expr{ @@ -810,10 +809,10 @@ call: $5.expr, // the default }, } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.expr) } | expr IN expr { - posLast(yylex, yyDollar) // our pos $$.expr = &ast.ExprCall{ Name: funcs.ContainsFuncName, Args: []interfaces.Expr{ @@ -821,6 +820,7 @@ call: $3.expr, }, } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.expr) } ; // list order gets us the position of the arg, but named params would work too! @@ -846,10 +846,10 @@ call_args: var: dotted_var_identifier { - posLast(yylex, yyDollar) // our pos $$.expr = &ast.ExprVar{ Name: $1.str, } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.expr) } ; func: @@ -859,22 +859,22 @@ func: // `func(, ) { }` FUNC_IDENTIFIER OPEN_PAREN args CLOSE_PAREN OPEN_CURLY expr CLOSE_CURLY { - posLast(yylex, yyDollar) // our pos $$.expr = &ast.ExprFunc{ Args: $3.args, //Return: nil, Body: $6.expr, } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.expr) } // `func(...) { }` | FUNC_IDENTIFIER OPEN_PAREN args CLOSE_PAREN type OPEN_CURLY expr CLOSE_CURLY { - posLast(yylex, yyDollar) // our pos $$.expr = &ast.ExprFunc{ Args: $3.args, Return: $5.typ, // return type is known Body: $7.expr, } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.expr) isFullyTyped := $5.typ != nil // true if set m := make(map[string]*types.Type) ord := []string{} @@ -939,17 +939,16 @@ bind: // `$s = "hey"` var_identifier EQUALS expr { - posLast(yylex, yyDollar) // our pos $$.stmt = &ast.StmtBind{ Ident: $1.str, Value: $3.expr, } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.stmt) } // `$x bool = true` // `$x int = if true { 42 } else { 13 }` | var_identifier type EQUALS expr { - posLast(yylex, yyDollar) // our pos var expr interfaces.Expr = $4.expr // XXX: We still need to do this for now it seems... if err := expr.SetType($2.typ); err != nil { @@ -961,6 +960,7 @@ bind: Value: expr, Type: $2.typ, // sam says add the type here instead... } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.stmt) } ; panic: @@ -971,7 +971,6 @@ panic: //} PANIC_IDENTIFIER OPEN_PAREN call_args CLOSE_PAREN { - posLast(yylex, yyDollar) // our pos call := &ast.ExprCall{ Name: $1.str, // the function name Args: $3.exprs, @@ -990,6 +989,7 @@ panic: ThenBranch: res, //ElseBranch: nil, } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.stmt) } ; /* TODO: do we want to include this? @@ -997,7 +997,6 @@ panic: rbind: var_identifier EQUALS resource { - posLast(yylex, yyDollar) // our pos // XXX: this kind of bind is different than the others, because // it can only really be used for send->recv stuff, eg: // foo.SomeString -> bar.SomeOtherString @@ -1005,6 +1004,7 @@ rbind: Ident: $1.str, Value: $3.stmt, } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.expr) } ; */ @@ -1012,12 +1012,12 @@ resource: // `file "/tmp/hello" { ... }` or `aws:ec2 "/tmp/hello" { ... }` colon_identifier expr OPEN_CURLY resource_body CLOSE_CURLY { - posLast(yylex, yyDollar) // our pos $$.stmt = &ast.StmtRes{ Kind: $1.str, Name: $2.expr, Contents: $4.resContents, } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.stmt) } ; resource_body: @@ -1180,16 +1180,15 @@ edge: // Test["t1"] -> Test["t2"] -> Test["t3"] # chain or pair edge_half_list { - posLast(yylex, yyDollar) // our pos $$.stmt = &ast.StmtEdge{ EdgeHalfList: $1.edgeHalfList, //Notify: false, // unused here } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.stmt) } // Test["t1"].foo_send -> Test["t2"].blah_recv # send/recv | edge_half_sendrecv ARROW edge_half_sendrecv { - posLast(yylex, yyDollar) // our pos $$.stmt = &ast.StmtEdge{ EdgeHalfList: []*ast.StmtEdgeHalf{ $1.edgeHalf, @@ -1197,6 +1196,7 @@ edge: }, //Notify: false, // unused here, it is implied (i think) } + locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.stmt) } ; edge_half_list: @@ -1486,10 +1486,22 @@ func cast(y yyLexer) *lexParseAST { return x.(*lexParseAST) } -// postLast pulls out the "last token" and does a pos with that. This is a hack! +// The posLast variant that specifies a node will store the coordinates in the +// node. +func locate(y yyLexer, first yySymType, last yySymType, node interfaces.Node) { + posLast(y, []yySymType{last}) // TODO: is it really useful to store this in the Lexer? the values are erratic and likely unhelpful + if ln, ok := node.(ast.LocalNode) ; !ok { + return + // only run Locate on nodes that look like they have not received locations yet + // otherwise the parser will come back and overwrite with faux end positions + } else if row, col := ln.GetPosition() ; row == 0 && col == 0 { + ln.Locate(first.row, first.col, last.row, last.col) + } +} + +// postLast runs pos on the last token of the current stmt/expr. func posLast(y yyLexer, dollars []yySymType) { - // pick the last token in the set matched by the parser - pos(y, dollars[len(dollars)-1]) // our pos + pos(y, dollars[len(dollars)-1]) } // cast is used to pull out the parser run-specific struct we store our AST in. diff --git a/lang/unification/fastsolver/fastsolver.go b/lang/unification/fastsolver/fastsolver.go index a2ebd0b8f..f9c82c3ae 100644 --- a/lang/unification/fastsolver/fastsolver.go +++ b/lang/unification/fastsolver/fastsolver.go @@ -78,7 +78,7 @@ type FastInvariantSolver struct { func (obj *FastInvariantSolver) Init(init *unification.Init) error { obj.Strategy = init.Strategy obj.UnifiedState = init.UnifiedState - obj.Debug = init.Debug + obj.Debug = true //init.Debug obj.Logf = init.Logf optimizations, exists := init.Strategy[unification.StrategyOptimizationsKey] @@ -135,7 +135,8 @@ func (obj *FastInvariantSolver) Solve(ctx context.Context, data *unification.Dat // Storing the Expr with this invariant is so that we // can generate this more helpful error message here. // TODO: Improve this error message! - return nil, errwrap.Wrapf(err, "unify error with: %s", x.Expr) + x.Err = errwrap.Wrapf(err, "unify error with: %s", x.Expr).Error() + return nil, x } if obj.Debug { e1, e2 := unificationUtil.Extract(x.Expect), unificationUtil.Extract(x.Actual) diff --git a/lang/unification/unification.go b/lang/unification/unification.go index 876603d7a..800a01476 100644 --- a/lang/unification/unification.go +++ b/lang/unification/unification.go @@ -71,6 +71,7 @@ type Unifier struct { // any proper literature or examples describing a well-known implementation of // this process. Improvements and polite recommendations are welcome. func (obj *Unifier) Unify(ctx context.Context) error { + obj.Debug = true if obj.AST == nil { return fmt.Errorf("the AST is nil") }