lang: ast, interfaces: Move textarea to a common package
We're going to use it everywhere. We also make it more forgiving in the meanwhile while we're porting things over.
This commit is contained in:
@@ -183,7 +183,8 @@ var (
|
|||||||
// StmtBind is a representation of an assignment, which binds a variable to an
|
// StmtBind is a representation of an assignment, which binds a variable to an
|
||||||
// expression.
|
// expression.
|
||||||
type StmtBind struct {
|
type StmtBind struct {
|
||||||
Textarea
|
interfaces.Textarea
|
||||||
|
|
||||||
data *interfaces.Data
|
data *interfaces.Data
|
||||||
|
|
||||||
Ident string
|
Ident string
|
||||||
@@ -378,7 +379,8 @@ func (obj *StmtBind) Output(map[interfaces.Func]types.Value) (*interfaces.Output
|
|||||||
// TODO: Consider expanding Name to have this return a list of Res's in the
|
// TODO: Consider expanding Name to have this return a list of Res's in the
|
||||||
// Output function if it is a map[name]struct{}, or even a map[[]name]struct{}.
|
// Output function if it is a map[name]struct{}, or even a map[[]name]struct{}.
|
||||||
type StmtRes struct {
|
type StmtRes struct {
|
||||||
Textarea
|
interfaces.Textarea
|
||||||
|
|
||||||
data *interfaces.Data
|
data *interfaces.Data
|
||||||
|
|
||||||
Kind string // kind of resource, eg: pkg, file, svc, etc...
|
Kind string // kind of resource, eg: pkg, file, svc, etc...
|
||||||
@@ -1594,7 +1596,8 @@ type StmtResContents interface {
|
|||||||
// StmtResField represents a single field in the parsed resource representation.
|
// StmtResField represents a single field in the parsed resource representation.
|
||||||
// This does not satisfy the Stmt interface.
|
// This does not satisfy the Stmt interface.
|
||||||
type StmtResField struct {
|
type StmtResField struct {
|
||||||
Textarea
|
interfaces.Textarea
|
||||||
|
|
||||||
data *interfaces.Data
|
data *interfaces.Data
|
||||||
|
|
||||||
Field string
|
Field string
|
||||||
@@ -1879,7 +1882,8 @@ func (obj *StmtResField) Graph(env *interfaces.Env) (*pgraph.Graph, error) {
|
|||||||
// StmtResEdge represents a single edge property in the parsed resource
|
// StmtResEdge represents a single edge property in the parsed resource
|
||||||
// representation. This does not satisfy the Stmt interface.
|
// representation. This does not satisfy the Stmt interface.
|
||||||
type StmtResEdge struct {
|
type StmtResEdge struct {
|
||||||
Textarea
|
interfaces.Textarea
|
||||||
|
|
||||||
data *interfaces.Data
|
data *interfaces.Data
|
||||||
|
|
||||||
Property string // TODO: iota constant instead?
|
Property string // TODO: iota constant instead?
|
||||||
@@ -2129,7 +2133,8 @@ func (obj *StmtResEdge) Graph(env *interfaces.Env) (*pgraph.Graph, error) {
|
|||||||
// correspond to the particular meta parameter specified. This does not satisfy
|
// correspond to the particular meta parameter specified. This does not satisfy
|
||||||
// the Stmt interface.
|
// the Stmt interface.
|
||||||
type StmtResMeta struct {
|
type StmtResMeta struct {
|
||||||
Textarea
|
interfaces.Textarea
|
||||||
|
|
||||||
data *interfaces.Data
|
data *interfaces.Data
|
||||||
|
|
||||||
Property string // TODO: iota constant instead?
|
Property string // TODO: iota constant instead?
|
||||||
@@ -2683,7 +2688,8 @@ func (obj *StmtResCollect) Graph(env *interfaces.Env) (*pgraph.Graph, error) {
|
|||||||
// names are compatible and listed. In this case of Send/Recv, only lists of
|
// names are compatible and listed. In this case of Send/Recv, only lists of
|
||||||
// length two are legal.
|
// length two are legal.
|
||||||
type StmtEdge struct {
|
type StmtEdge struct {
|
||||||
Textarea
|
interfaces.Textarea
|
||||||
|
|
||||||
data *interfaces.Data
|
data *interfaces.Data
|
||||||
|
|
||||||
EdgeHalfList []*StmtEdgeHalf // represents a chain of edges
|
EdgeHalfList []*StmtEdgeHalf // represents a chain of edges
|
||||||
@@ -3032,7 +3038,8 @@ func (obj *StmtEdge) Output(table map[interfaces.Func]types.Value) (*interfaces.
|
|||||||
// is assumed that a list of strings should be expected. More mechanisms to
|
// is assumed that a list of strings should be expected. More mechanisms to
|
||||||
// determine if the value is static may be added over time.
|
// determine if the value is static may be added over time.
|
||||||
type StmtEdgeHalf struct {
|
type StmtEdgeHalf struct {
|
||||||
Textarea
|
interfaces.Textarea
|
||||||
|
|
||||||
data *interfaces.Data
|
data *interfaces.Data
|
||||||
|
|
||||||
Kind string // kind of resource, eg: pkg, file, svc, etc...
|
Kind string // kind of resource, eg: pkg, file, svc, etc...
|
||||||
@@ -3203,7 +3210,8 @@ func (obj *StmtEdgeHalf) Graph(env *interfaces.Env) (*pgraph.Graph, error) {
|
|||||||
// optional, it is the else branch, although this struct allows either to be
|
// optional, it is the else branch, although this struct allows either to be
|
||||||
// optional, even if it is not commonly used.
|
// optional, even if it is not commonly used.
|
||||||
type StmtIf struct {
|
type StmtIf struct {
|
||||||
Textarea
|
interfaces.Textarea
|
||||||
|
|
||||||
data *interfaces.Data
|
data *interfaces.Data
|
||||||
|
|
||||||
Condition interfaces.Expr
|
Condition interfaces.Expr
|
||||||
@@ -3569,7 +3577,8 @@ func (obj *StmtIf) Output(table map[interfaces.Func]types.Value) (*interfaces.Ou
|
|||||||
|
|
||||||
// StmtFor represents an iteration over a list. The body contains statements.
|
// StmtFor represents an iteration over a list. The body contains statements.
|
||||||
type StmtFor struct {
|
type StmtFor struct {
|
||||||
Textarea
|
interfaces.Textarea
|
||||||
|
|
||||||
data *interfaces.Data
|
data *interfaces.Data
|
||||||
scope *interfaces.Scope // store for referencing this later
|
scope *interfaces.Scope // store for referencing this later
|
||||||
|
|
||||||
@@ -4077,7 +4086,8 @@ func (obj *StmtFor) Output(table map[interfaces.Func]types.Value) (*interfaces.O
|
|||||||
|
|
||||||
// StmtForKV represents an iteration over a map. The body contains statements.
|
// StmtForKV represents an iteration over a map. The body contains statements.
|
||||||
type StmtForKV struct {
|
type StmtForKV struct {
|
||||||
Textarea
|
interfaces.Textarea
|
||||||
|
|
||||||
data *interfaces.Data
|
data *interfaces.Data
|
||||||
scope *interfaces.Scope // store for referencing this later
|
scope *interfaces.Scope // store for referencing this later
|
||||||
|
|
||||||
@@ -4583,7 +4593,8 @@ func (obj *StmtForKV) Output(table map[interfaces.Func]types.Value) (*interfaces
|
|||||||
// the bind statement's are correctly applied in this scope, and irrespective of
|
// the bind statement's are correctly applied in this scope, and irrespective of
|
||||||
// their order of definition.
|
// their order of definition.
|
||||||
type StmtProg struct {
|
type StmtProg struct {
|
||||||
Textarea
|
interfaces.Textarea
|
||||||
|
|
||||||
data *interfaces.Data
|
data *interfaces.Data
|
||||||
scope *interfaces.Scope // store for use by imports
|
scope *interfaces.Scope // store for use by imports
|
||||||
|
|
||||||
@@ -6177,7 +6188,8 @@ func (obj *StmtProg) IsModuleUnsafe() error { // TODO: rename this function?
|
|||||||
// the supplied function in the current scope and irrespective of the order of
|
// the supplied function in the current scope and irrespective of the order of
|
||||||
// definition.
|
// definition.
|
||||||
type StmtFunc struct {
|
type StmtFunc struct {
|
||||||
Textarea
|
interfaces.Textarea
|
||||||
|
|
||||||
data *interfaces.Data
|
data *interfaces.Data
|
||||||
|
|
||||||
Name string
|
Name string
|
||||||
@@ -6391,7 +6403,8 @@ func (obj *StmtFunc) Output(map[interfaces.Func]types.Value) (*interfaces.Output
|
|||||||
// TODO: We don't currently support defining polymorphic classes (eg: different
|
// TODO: We don't currently support defining polymorphic classes (eg: different
|
||||||
// signatures for the same class name) but it might be something to consider.
|
// signatures for the same class name) but it might be something to consider.
|
||||||
type StmtClass struct {
|
type StmtClass struct {
|
||||||
Textarea
|
interfaces.Textarea
|
||||||
|
|
||||||
data *interfaces.Data
|
data *interfaces.Data
|
||||||
scope *interfaces.Scope // store for referencing this later
|
scope *interfaces.Scope // store for referencing this later
|
||||||
|
|
||||||
@@ -6601,7 +6614,8 @@ func (obj *StmtClass) Output(table map[interfaces.Func]types.Value) (*interfaces
|
|||||||
// to call a class except that it produces output instead of a value. Most of
|
// to call a class except that it produces output instead of a value. Most of
|
||||||
// the interesting logic for classes happens here or in StmtProg.
|
// the interesting logic for classes happens here or in StmtProg.
|
||||||
type StmtInclude struct {
|
type StmtInclude struct {
|
||||||
Textarea
|
interfaces.Textarea
|
||||||
|
|
||||||
data *interfaces.Data
|
data *interfaces.Data
|
||||||
scope *interfaces.Scope
|
scope *interfaces.Scope
|
||||||
|
|
||||||
@@ -7099,7 +7113,8 @@ func (obj *StmtInclude) Output(table map[interfaces.Func]types.Value) (*interfac
|
|||||||
// file. As with any statement, it produces output, but that output is empty. To
|
// file. As with any statement, it produces output, but that output is empty. To
|
||||||
// benefit from its inclusion, reference the scope definitions you want.
|
// benefit from its inclusion, reference the scope definitions you want.
|
||||||
type StmtImport struct {
|
type StmtImport struct {
|
||||||
Textarea
|
interfaces.Textarea
|
||||||
|
|
||||||
data *interfaces.Data
|
data *interfaces.Data
|
||||||
|
|
||||||
Name string
|
Name string
|
||||||
@@ -7208,7 +7223,7 @@ func (obj *StmtImport) Output(map[interfaces.Func]types.Value) (*interfaces.Outp
|
|||||||
// formatting) but so that they can exist anywhere in the code. Currently these
|
// formatting) but so that they can exist anywhere in the code. Currently these
|
||||||
// are dropped by the lexer.
|
// are dropped by the lexer.
|
||||||
type StmtComment struct {
|
type StmtComment struct {
|
||||||
Textarea
|
interfaces.Textarea
|
||||||
|
|
||||||
Value string
|
Value string
|
||||||
}
|
}
|
||||||
@@ -7290,7 +7305,8 @@ func (obj *StmtComment) Output(map[interfaces.Func]types.Value) (*interfaces.Out
|
|||||||
|
|
||||||
// ExprBool is a representation of a boolean.
|
// ExprBool is a representation of a boolean.
|
||||||
type ExprBool struct {
|
type ExprBool struct {
|
||||||
Textarea
|
interfaces.Textarea
|
||||||
|
|
||||||
data *interfaces.Data
|
data *interfaces.Data
|
||||||
scope *interfaces.Scope // store for referencing this later
|
scope *interfaces.Scope // store for referencing this later
|
||||||
|
|
||||||
@@ -7443,7 +7459,8 @@ func (obj *ExprBool) Value() (types.Value, error) {
|
|||||||
|
|
||||||
// ExprStr is a representation of a string.
|
// ExprStr is a representation of a string.
|
||||||
type ExprStr struct {
|
type ExprStr struct {
|
||||||
Textarea
|
interfaces.Textarea
|
||||||
|
|
||||||
data *interfaces.Data
|
data *interfaces.Data
|
||||||
scope *interfaces.Scope // store for referencing this later
|
scope *interfaces.Scope // store for referencing this later
|
||||||
|
|
||||||
@@ -7645,7 +7662,8 @@ func (obj *ExprStr) Value() (types.Value, error) {
|
|||||||
|
|
||||||
// ExprInt is a representation of an int.
|
// ExprInt is a representation of an int.
|
||||||
type ExprInt struct {
|
type ExprInt struct {
|
||||||
Textarea
|
interfaces.Textarea
|
||||||
|
|
||||||
data *interfaces.Data
|
data *interfaces.Data
|
||||||
scope *interfaces.Scope // store for referencing this later
|
scope *interfaces.Scope // store for referencing this later
|
||||||
|
|
||||||
@@ -7797,7 +7815,8 @@ func (obj *ExprInt) Value() (types.Value, error) {
|
|||||||
|
|
||||||
// ExprFloat is a representation of a float.
|
// ExprFloat is a representation of a float.
|
||||||
type ExprFloat struct {
|
type ExprFloat struct {
|
||||||
Textarea
|
interfaces.Textarea
|
||||||
|
|
||||||
data *interfaces.Data
|
data *interfaces.Data
|
||||||
scope *interfaces.Scope // store for referencing this later
|
scope *interfaces.Scope // store for referencing this later
|
||||||
|
|
||||||
@@ -7951,7 +7970,8 @@ func (obj *ExprFloat) Value() (types.Value, error) {
|
|||||||
|
|
||||||
// ExprList is a representation of a list.
|
// ExprList is a representation of a list.
|
||||||
type ExprList struct {
|
type ExprList struct {
|
||||||
Textarea
|
interfaces.Textarea
|
||||||
|
|
||||||
data *interfaces.Data
|
data *interfaces.Data
|
||||||
scope *interfaces.Scope // store for referencing this later
|
scope *interfaces.Scope // store for referencing this later
|
||||||
typ *types.Type
|
typ *types.Type
|
||||||
@@ -8315,7 +8335,8 @@ func (obj *ExprList) Value() (types.Value, error) {
|
|||||||
|
|
||||||
// ExprMap is a representation of a (dictionary) map.
|
// ExprMap is a representation of a (dictionary) map.
|
||||||
type ExprMap struct {
|
type ExprMap struct {
|
||||||
Textarea
|
interfaces.Textarea
|
||||||
|
|
||||||
data *interfaces.Data
|
data *interfaces.Data
|
||||||
scope *interfaces.Scope // store for referencing this later
|
scope *interfaces.Scope // store for referencing this later
|
||||||
typ *types.Type
|
typ *types.Type
|
||||||
@@ -8804,7 +8825,7 @@ func (obj *ExprMap) Value() (types.Value, error) {
|
|||||||
// ExprMapKV represents a key and value pair in a (dictionary) map. This does
|
// ExprMapKV represents a key and value pair in a (dictionary) map. This does
|
||||||
// not satisfy the Expr interface.
|
// not satisfy the Expr interface.
|
||||||
type ExprMapKV struct {
|
type ExprMapKV struct {
|
||||||
Textarea
|
interfaces.Textarea
|
||||||
|
|
||||||
Key interfaces.Expr // keys can be strings, int's, etc...
|
Key interfaces.Expr // keys can be strings, int's, etc...
|
||||||
Val interfaces.Expr
|
Val interfaces.Expr
|
||||||
@@ -8812,7 +8833,8 @@ type ExprMapKV struct {
|
|||||||
|
|
||||||
// ExprStruct is a representation of a struct.
|
// ExprStruct is a representation of a struct.
|
||||||
type ExprStruct struct {
|
type ExprStruct struct {
|
||||||
Textarea
|
interfaces.Textarea
|
||||||
|
|
||||||
data *interfaces.Data
|
data *interfaces.Data
|
||||||
scope *interfaces.Scope // store for referencing this later
|
scope *interfaces.Scope // store for referencing this later
|
||||||
typ *types.Type
|
typ *types.Type
|
||||||
@@ -9208,7 +9230,7 @@ func (obj *ExprStruct) Value() (types.Value, error) {
|
|||||||
// ExprStructField represents a name value pair in a struct field. This does not
|
// ExprStructField represents a name value pair in a struct field. This does not
|
||||||
// satisfy the Expr interface.
|
// satisfy the Expr interface.
|
||||||
type ExprStructField struct {
|
type ExprStructField struct {
|
||||||
Textarea
|
interfaces.Textarea
|
||||||
|
|
||||||
Name string
|
Name string
|
||||||
Value interfaces.Expr
|
Value interfaces.Expr
|
||||||
@@ -9224,7 +9246,8 @@ type ExprStructField struct {
|
|||||||
// 4. A pure built-in function (set Values to a singleton)
|
// 4. A pure built-in function (set Values to a singleton)
|
||||||
// 5. A pure polymorphic built-in function (set Values to a list)
|
// 5. A pure polymorphic built-in function (set Values to a list)
|
||||||
type ExprFunc struct {
|
type ExprFunc struct {
|
||||||
Textarea
|
interfaces.Textarea
|
||||||
|
|
||||||
data *interfaces.Data
|
data *interfaces.Data
|
||||||
scope *interfaces.Scope // store for referencing this later
|
scope *interfaces.Scope // store for referencing this later
|
||||||
typ *types.Type
|
typ *types.Type
|
||||||
@@ -10139,7 +10162,8 @@ func (obj *ExprFunc) Value() (types.Value, error) {
|
|||||||
// declaration or implementation of a new function value. This struct has an
|
// declaration or implementation of a new function value. This struct has an
|
||||||
// analogous symmetry with ExprVar.
|
// analogous symmetry with ExprVar.
|
||||||
type ExprCall struct {
|
type ExprCall struct {
|
||||||
Textarea
|
interfaces.Textarea
|
||||||
|
|
||||||
data *interfaces.Data
|
data *interfaces.Data
|
||||||
scope *interfaces.Scope // store for referencing this later
|
scope *interfaces.Scope // store for referencing this later
|
||||||
typ *types.Type
|
typ *types.Type
|
||||||
@@ -11118,7 +11142,8 @@ func (obj *ExprCall) Value() (types.Value, error) {
|
|||||||
// ExprVar is a representation of a variable lookup. It returns the expression
|
// ExprVar is a representation of a variable lookup. It returns the expression
|
||||||
// that that variable refers to.
|
// that that variable refers to.
|
||||||
type ExprVar struct {
|
type ExprVar struct {
|
||||||
Textarea
|
interfaces.Textarea
|
||||||
|
|
||||||
data *interfaces.Data
|
data *interfaces.Data
|
||||||
scope *interfaces.Scope // store for referencing this later
|
scope *interfaces.Scope // store for referencing this later
|
||||||
typ *types.Type
|
typ *types.Type
|
||||||
@@ -12322,7 +12347,8 @@ func (obj *ExprSingleton) Value() (types.Value, error) {
|
|||||||
// returns a value. As a result, it has a type. This is different from a StmtIf,
|
// returns a value. As a result, it has a type. This is different from a StmtIf,
|
||||||
// which does not need to have both branches, and which does not return a value.
|
// which does not need to have both branches, and which does not return a value.
|
||||||
type ExprIf struct {
|
type ExprIf struct {
|
||||||
Textarea
|
interfaces.Textarea
|
||||||
|
|
||||||
data *interfaces.Data
|
data *interfaces.Data
|
||||||
scope *interfaces.Scope // store for referencing this later
|
scope *interfaces.Scope // store for referencing this later
|
||||||
typ *types.Type
|
typ *types.Type
|
||||||
|
|||||||
155
lang/ast/util.go
155
lang/ast/util.go
@@ -31,7 +31,6 @@ package ast
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -41,7 +40,6 @@ import (
|
|||||||
"github.com/purpleidea/mgmt/lang/funcs/vars"
|
"github.com/purpleidea/mgmt/lang/funcs/vars"
|
||||||
"github.com/purpleidea/mgmt/lang/interfaces"
|
"github.com/purpleidea/mgmt/lang/interfaces"
|
||||||
"github.com/purpleidea/mgmt/lang/types"
|
"github.com/purpleidea/mgmt/lang/types"
|
||||||
"github.com/purpleidea/mgmt/util"
|
|
||||||
"github.com/purpleidea/mgmt/util/errwrap"
|
"github.com/purpleidea/mgmt/util/errwrap"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -589,156 +587,3 @@ func highlightHelper(node interfaces.Node, logf func(format string, v ...interfa
|
|||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Textarea stores the coordinates of a statement or expression in the form of a
|
|
||||||
// starting line/column and ending line/column.
|
|
||||||
type Textarea struct {
|
|
||||||
// debug represents if we're running in debug mode or not.
|
|
||||||
debug bool
|
|
||||||
|
|
||||||
// logf is a logger which should be used.
|
|
||||||
logf func(format string, v ...interface{})
|
|
||||||
|
|
||||||
// sf is the SourceFinder function implementation that maps a filename
|
|
||||||
// to the source.
|
|
||||||
sf interfaces.SourceFinderFunc
|
|
||||||
|
|
||||||
// path is the full path/filename where this text area exists.
|
|
||||||
path string
|
|
||||||
|
|
||||||
// This data is zero-based. (Eg: first line of file is 0)
|
|
||||||
startLine int // first
|
|
||||||
startColumn int // left
|
|
||||||
endLine int // last
|
|
||||||
endColumn int // right
|
|
||||||
|
|
||||||
isSet bool
|
|
||||||
|
|
||||||
// Bug5819 works around issue https://github.com/golang/go/issues/5819
|
|
||||||
Bug5819 interface{} // XXX: workaround
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup is used during AST initialization in order to store in each AST node
|
|
||||||
// the name of the source file from which it was generated.
|
|
||||||
func (obj *Textarea) Setup(data *interfaces.Data) {
|
|
||||||
obj.debug = data.Debug
|
|
||||||
obj.logf = data.Logf
|
|
||||||
obj.sf = data.SourceFinder
|
|
||||||
obj.path = data.AbsFilename()
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsSet returns if the position was already set with Locate already.
|
|
||||||
func (obj *Textarea) IsSet() bool {
|
|
||||||
return obj.isSet
|
|
||||||
}
|
|
||||||
|
|
||||||
// Locate is used by the parser to store the token positions in AST nodes. The
|
|
||||||
// path will be filled during AST node initialization usually, because the
|
|
||||||
// parser does not know the name of the file it is processing.
|
|
||||||
func (obj *Textarea) Locate(line int, col int, endline int, endcol int) {
|
|
||||||
obj.startLine = line
|
|
||||||
obj.startColumn = col
|
|
||||||
obj.endLine = endline
|
|
||||||
obj.endColumn = endcol
|
|
||||||
obj.isSet = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pos returns the starting line/column of an AST node.
|
|
||||||
func (obj *Textarea) Pos() (int, int) {
|
|
||||||
return obj.startLine, obj.startColumn
|
|
||||||
}
|
|
||||||
|
|
||||||
// End returns the end line/column of an AST node.
|
|
||||||
func (obj *Textarea) End() (int, int) {
|
|
||||||
return obj.endLine, obj.endColumn
|
|
||||||
}
|
|
||||||
|
|
||||||
// Path returns the name of the source file that holds the code for an AST node.
|
|
||||||
func (obj *Textarea) Path() string {
|
|
||||||
return obj.path
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filename returns the printable filename that we'd like to display. It tries
|
|
||||||
// to return a relative version if possible.
|
|
||||||
func (obj *Textarea) Filename() string {
|
|
||||||
if obj.path == "" {
|
|
||||||
return "<unknown>" // TODO: should this be <stdin> ?
|
|
||||||
}
|
|
||||||
|
|
||||||
wd, _ := os.Getwd() // ignore error since "" would just pass through
|
|
||||||
wd += "/" // it's a dir
|
|
||||||
if s, err := util.RemoveBasePath(obj.path, wd); err == nil {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj.path
|
|
||||||
}
|
|
||||||
|
|
||||||
// Byline gives a succinct representation of the Textarea, but is useful only in
|
|
||||||
// debugging. In order to generate pretty error messages, see HighlightText.
|
|
||||||
func (obj *Textarea) Byline() string {
|
|
||||||
// We convert to 1-based for user display.
|
|
||||||
return fmt.Sprintf("%s @ %d:%d-%d:%d", obj.Filename(), obj.startLine+1, obj.startColumn+1, obj.endLine+1, obj.endColumn+1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// HighlightText generates a generic description that just visually indicates
|
|
||||||
// part of the line described by a Textarea. If the coordinates that are passed
|
|
||||||
// span multiple lines, don't show those lines, but just a description of the
|
|
||||||
// area. If it can't generate a valid snippet, then it returns the empty string.
|
|
||||||
func (obj *Textarea) HighlightText() string {
|
|
||||||
b, err := obj.sf(obj.path) // source finder!
|
|
||||||
if err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
contents := string(b)
|
|
||||||
|
|
||||||
result := &strings.Builder{}
|
|
||||||
|
|
||||||
result.WriteString(obj.Byline())
|
|
||||||
|
|
||||||
lines := strings.Split(contents, "\n")
|
|
||||||
if len(lines) < obj.endLine-1 {
|
|
||||||
// XXX: out of bounds?
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
result.WriteString("\n\n")
|
|
||||||
|
|
||||||
if obj.startLine == obj.endLine {
|
|
||||||
line := lines[obj.startLine] + "\n"
|
|
||||||
text := strings.TrimLeft(line, " \t")
|
|
||||||
indent := strings.TrimSuffix(line, text)
|
|
||||||
offset := len(indent)
|
|
||||||
|
|
||||||
result.WriteString(line)
|
|
||||||
result.WriteString(indent)
|
|
||||||
result.WriteString(strings.Repeat(" ", obj.startColumn-offset))
|
|
||||||
// TODO: add on the width of the second element as well
|
|
||||||
result.WriteString(strings.Repeat("^", obj.endColumn-obj.startColumn+1))
|
|
||||||
result.WriteString("\n")
|
|
||||||
|
|
||||||
return result.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
line := lines[obj.startLine] + "\n"
|
|
||||||
text := strings.TrimLeft(line, " \t")
|
|
||||||
indent := strings.TrimSuffix(line, text)
|
|
||||||
offset := len(indent)
|
|
||||||
|
|
||||||
result.WriteString(line)
|
|
||||||
result.WriteString(indent)
|
|
||||||
result.WriteString(strings.Repeat(" ", obj.startColumn-offset))
|
|
||||||
result.WriteString("^ from here ...\n")
|
|
||||||
|
|
||||||
line = lines[obj.endLine] + "\n"
|
|
||||||
text = strings.TrimLeft(line, " \t")
|
|
||||||
indent = strings.TrimSuffix(line, text)
|
|
||||||
offset = len(indent)
|
|
||||||
|
|
||||||
result.WriteString(line)
|
|
||||||
result.WriteString(indent)
|
|
||||||
result.WriteString(strings.Repeat(" ", obj.startColumn-offset))
|
|
||||||
result.WriteString("^ ... to here\n")
|
|
||||||
|
|
||||||
return result.String()
|
|
||||||
}
|
|
||||||
|
|||||||
198
lang/interfaces/textarea.go
Normal file
198
lang/interfaces/textarea.go
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
// Mgmt
|
||||||
|
// Copyright (C) James Shubin and the project contributors
|
||||||
|
// Written by James Shubin <james@shubin.ca> and the project contributors
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
//
|
||||||
|
// Additional permission under GNU GPL version 3 section 7
|
||||||
|
//
|
||||||
|
// If you modify this program, or any covered work, by linking or combining it
|
||||||
|
// with embedded mcl code and modules (and that the embedded mcl code and
|
||||||
|
// modules which link with this program, contain a copy of their source code in
|
||||||
|
// the authoritative form) containing parts covered by the terms of any other
|
||||||
|
// license, the licensors of this program grant you additional permission to
|
||||||
|
// convey the resulting work. Furthermore, the licensors of this program grant
|
||||||
|
// the original author, James Shubin, additional permission to update this
|
||||||
|
// additional permission if he deems it necessary to achieve the goals of this
|
||||||
|
// additional permission.
|
||||||
|
|
||||||
|
package interfaces
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/purpleidea/mgmt/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Textarea stores the coordinates of a statement or expression in the form of a
|
||||||
|
// starting line/column and ending line/column.
|
||||||
|
type Textarea struct {
|
||||||
|
// debug represents if we're running in debug mode or not.
|
||||||
|
debug bool
|
||||||
|
|
||||||
|
// logf is a logger which should be used.
|
||||||
|
logf func(format string, v ...interface{})
|
||||||
|
|
||||||
|
// sf is the SourceFinder function implementation that maps a filename
|
||||||
|
// to the source.
|
||||||
|
sf SourceFinderFunc
|
||||||
|
|
||||||
|
// path is the full path/filename where this text area exists.
|
||||||
|
path string
|
||||||
|
|
||||||
|
// This data is zero-based. (Eg: first line of file is 0)
|
||||||
|
startLine int // first
|
||||||
|
startColumn int // left
|
||||||
|
endLine int // last
|
||||||
|
endColumn int // right
|
||||||
|
|
||||||
|
isSet bool
|
||||||
|
|
||||||
|
// Bug5819 works around issue https://github.com/golang/go/issues/5819
|
||||||
|
Bug5819 interface{} // XXX: workaround
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup is used during AST initialization in order to store in each AST node
|
||||||
|
// the name of the source file from which it was generated.
|
||||||
|
func (obj *Textarea) Setup(data *Data) {
|
||||||
|
obj.debug = data.Debug
|
||||||
|
obj.logf = data.Logf
|
||||||
|
obj.sf = data.SourceFinder
|
||||||
|
obj.path = data.AbsFilename()
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSet returns if the position was already set with Locate already.
|
||||||
|
func (obj *Textarea) IsSet() bool {
|
||||||
|
return obj.isSet
|
||||||
|
}
|
||||||
|
|
||||||
|
// Locate is used by the parser to store the token positions in AST nodes. The
|
||||||
|
// path will be filled during AST node initialization usually, because the
|
||||||
|
// parser does not know the name of the file it is processing.
|
||||||
|
func (obj *Textarea) Locate(line int, col int, endline int, endcol int) {
|
||||||
|
obj.startLine = line
|
||||||
|
obj.startColumn = col
|
||||||
|
obj.endLine = endline
|
||||||
|
obj.endColumn = endcol
|
||||||
|
obj.isSet = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pos returns the starting line/column of an AST node.
|
||||||
|
func (obj *Textarea) Pos() (int, int) {
|
||||||
|
return obj.startLine, obj.startColumn
|
||||||
|
}
|
||||||
|
|
||||||
|
// End returns the end line/column of an AST node.
|
||||||
|
func (obj *Textarea) End() (int, int) {
|
||||||
|
return obj.endLine, obj.endColumn
|
||||||
|
}
|
||||||
|
|
||||||
|
// Path returns the name of the source file that holds the code for an AST node.
|
||||||
|
func (obj *Textarea) Path() string {
|
||||||
|
return obj.path
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filename returns the printable filename that we'd like to display. It tries
|
||||||
|
// to return a relative version if possible.
|
||||||
|
func (obj *Textarea) Filename() string {
|
||||||
|
if obj.path == "" {
|
||||||
|
return "<unknown>" // TODO: should this be <stdin> ?
|
||||||
|
}
|
||||||
|
|
||||||
|
wd, _ := os.Getwd() // ignore error since "" would just pass through
|
||||||
|
wd += "/" // it's a dir
|
||||||
|
if s, err := util.RemoveBasePath(obj.path, wd); err == nil {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj.path
|
||||||
|
}
|
||||||
|
|
||||||
|
// Byline gives a succinct representation of the Textarea, but is useful only in
|
||||||
|
// debugging. In order to generate pretty error messages, see HighlightText.
|
||||||
|
func (obj *Textarea) Byline() string {
|
||||||
|
// We convert to 1-based for user display.
|
||||||
|
return fmt.Sprintf("%s @ %d:%d-%d:%d", obj.Filename(), obj.startLine+1, obj.startColumn+1, obj.endLine+1, obj.endColumn+1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HighlightText generates a generic description that just visually indicates
|
||||||
|
// part of the line described by a Textarea. If the coordinates that are passed
|
||||||
|
// span multiple lines, don't show those lines, but just a description of the
|
||||||
|
// area. If it can't generate a valid snippet, then it returns the empty string.
|
||||||
|
func (obj *Textarea) HighlightText() string {
|
||||||
|
if obj.sf == nil {
|
||||||
|
// XXX: when all functions are ported over to use ast.Textarea,
|
||||||
|
// then uncomment this return and add in the panic below.
|
||||||
|
return "" // XXX: temporary
|
||||||
|
// programming error
|
||||||
|
//panic("nil SourceFinderFunc")
|
||||||
|
}
|
||||||
|
b, err := obj.sf(obj.path) // source finder!
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
contents := string(b)
|
||||||
|
|
||||||
|
result := &strings.Builder{}
|
||||||
|
|
||||||
|
result.WriteString(obj.Byline())
|
||||||
|
|
||||||
|
lines := strings.Split(contents, "\n")
|
||||||
|
if len(lines) < obj.endLine-1 {
|
||||||
|
// XXX: out of bounds?
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
result.WriteString("\n\n")
|
||||||
|
|
||||||
|
if obj.startLine == obj.endLine {
|
||||||
|
line := lines[obj.startLine] + "\n"
|
||||||
|
text := strings.TrimLeft(line, " \t")
|
||||||
|
indent := strings.TrimSuffix(line, text)
|
||||||
|
offset := len(indent)
|
||||||
|
|
||||||
|
result.WriteString(line)
|
||||||
|
result.WriteString(indent)
|
||||||
|
result.WriteString(strings.Repeat(" ", obj.startColumn-offset))
|
||||||
|
// TODO: add on the width of the second element as well
|
||||||
|
result.WriteString(strings.Repeat("^", obj.endColumn-obj.startColumn+1))
|
||||||
|
result.WriteString("\n")
|
||||||
|
|
||||||
|
return result.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
line := lines[obj.startLine] + "\n"
|
||||||
|
text := strings.TrimLeft(line, " \t")
|
||||||
|
indent := strings.TrimSuffix(line, text)
|
||||||
|
offset := len(indent)
|
||||||
|
|
||||||
|
result.WriteString(line)
|
||||||
|
result.WriteString(indent)
|
||||||
|
result.WriteString(strings.Repeat(" ", obj.startColumn-offset))
|
||||||
|
result.WriteString("^ from here ...\n")
|
||||||
|
|
||||||
|
line = lines[obj.endLine] + "\n"
|
||||||
|
text = strings.TrimLeft(line, " \t")
|
||||||
|
indent = strings.TrimSuffix(line, text)
|
||||||
|
offset = len(indent)
|
||||||
|
|
||||||
|
result.WriteString(line)
|
||||||
|
result.WriteString(indent)
|
||||||
|
result.WriteString(strings.Repeat(" ", obj.startColumn-offset))
|
||||||
|
result.WriteString("^ ... to here\n")
|
||||||
|
|
||||||
|
return result.String()
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user