diff --git a/lang/Makefile b/lang/Makefile index 2ed32020..684bb156 100644 --- a/lang/Makefile +++ b/lang/Makefile @@ -20,23 +20,23 @@ SHELL = /usr/bin/env bash all: build -build: lexer.nn.go y.go interpolate/parse.generated.go +build: parser/lexer.nn.go parser/y.go interpolate/parse.generated.go @# recursively run make in child dir named types @$(MAKE) --quiet -C types clean: $(MAKE) --quiet -C types clean - @rm -f lexer.nn.go y.go y.output interpolate/parse.generated.go || true + @rm -f parser/lexer.nn.go parser/y.go parser/y.output interpolate/parse.generated.go || true -lexer.nn.go: lexer.nex +parser/lexer.nn.go: parser/lexer.nex @echo "Generating: lexer..." - nex -e lexer.nex - @ROOT="$$( cd "$$( dirname "$${BASH_SOURCE[0]}" )" && cd .. && pwd )" && $$ROOT/misc/header.sh 'lexer.nn.go' + nex -e -o $@ $< + @ROOT="$$( cd "$$( dirname "$${BASH_SOURCE[0]}" )" && cd .. && pwd )" && $$ROOT/misc/header.sh 'parser/lexer.nn.go' -y.go: parser.y +parser/y.go: parser/parser.y @echo "Generating: parser..." - goyacc parser.y - @ROOT="$$( cd "$$( dirname "$${BASH_SOURCE[0]}" )" && cd .. && pwd )" && $$ROOT/misc/header.sh 'y.go' + goyacc -v parser/y.output -o $@ $< + @ROOT="$$( cd "$$( dirname "$${BASH_SOURCE[0]}" )" && cd .. && pwd )" && $$ROOT/misc/header.sh 'parser/y.go' interpolate/parse.generated.go: interpolate/parse.rl @echo "Generating: interpolation..." diff --git a/lang/scope_test.go b/lang/ast/scope_test.go similarity index 99% rename from lang/scope_test.go rename to lang/ast/scope_test.go index 55870a29..6a8487e2 100644 --- a/lang/scope_test.go +++ b/lang/ast/scope_test.go @@ -17,7 +17,7 @@ // +build !root -package lang +package ast import ( "fmt" diff --git a/lang/structs.go b/lang/ast/structs.go similarity index 99% rename from lang/structs.go rename to lang/ast/structs.go index fcc42e7e..f1901ed2 100644 --- a/lang/structs.go +++ b/lang/ast/structs.go @@ -15,7 +15,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -package lang // TODO: move this into a sub package of lang/$name? +package ast import ( "bytes" @@ -3065,13 +3065,13 @@ func (obj *StmtProg) importSystemScope(name string) (*interfaces.Scope, error) { isEmpty := true // assume empty (which should cause an error) - funcs := FuncPrefixToFunctionsScope(name) // runs funcs.LookupPrefix - if len(funcs) > 0 { + functions := FuncPrefixToFunctionsScope(name) // runs funcs.LookupPrefix + if len(functions) > 0 { isEmpty = false } // perform any normal "startup" for these functions... - for _, fn := range funcs { + for _, fn := range functions { // XXX: is this the right place for this, or should it be elsewhere? // XXX: do we need a modified obj.data for this b/c it's in a scope? if err := fn.Init(obj.data); err != nil { @@ -3084,7 +3084,7 @@ func (obj *StmtProg) importSystemScope(name string) (*interfaces.Scope, error) { scope := &interfaces.Scope{ // TODO: we could use the core API for variables somehow... //Variables: make(map[string]interfaces.Expr), - Functions: funcs, // map[string]interfaces.Expr + Functions: functions, // map[string]interfaces.Expr // TODO: we could add a core API for classes too! //Classes: make(map[string]interfaces.Stmt), } @@ -3101,7 +3101,7 @@ func (obj *StmtProg) importSystemScope(name string) (*interfaces.Scope, error) { // XXX: consider using a virtual `append *` statement to combine these instead. for _, p := range paths { // we only want code from this prefix - prefix := CoreDir + name + "/" + prefix := funcs.CoreDir + name + "/" if !strings.HasPrefix(p, prefix) { continue } @@ -3126,7 +3126,7 @@ func (obj *StmtProg) importSystemScope(name string) (*interfaces.Scope, error) { reader := bytes.NewReader(b) // wrap the byte stream // now run the lexer/parser to do the import - ast, err := LexParse(reader) + ast, err := obj.data.LexParser(reader) if err != nil { return nil, errwrap.Wrapf(err, "could not generate AST from import `%s`", name) } @@ -3240,7 +3240,7 @@ func (obj *StmtProg) importScopeWithInputs(s string, scope *interfaces.Scope, pa metadata.Metadata = obj.data.Metadata // now run the lexer/parser to do the import - ast, err := LexParse(reader) + ast, err := obj.data.LexParser(reader) if err != nil { return nil, errwrap.Wrapf(err, "could not generate AST from import") } @@ -3252,15 +3252,18 @@ func (obj *StmtProg) importScopeWithInputs(s string, scope *interfaces.Scope, pa // init and validate the structure of the AST data := &interfaces.Data{ // TODO: add missing fields here if/when needed - Fs: obj.data.Fs, - FsURI: obj.data.FsURI, - Base: output.Base, // new base dir (absolute path) - Files: files, - Imports: parentVertex, // the parent vertex that imported me - Metadata: metadata, - Modules: obj.data.Modules, - Downloader: obj.data.Downloader, - //World: obj.data.World, + Fs: obj.data.Fs, + FsURI: obj.data.FsURI, + Base: output.Base, // new base dir (absolute path) + Files: files, + Imports: parentVertex, // the parent vertex that imported me + Metadata: metadata, + Modules: obj.data.Modules, + + LexParser: obj.data.LexParser, + Downloader: obj.data.Downloader, + StrInterpolater: obj.data.StrInterpolater, + //World: obj.data.World, // TODO: do we need this? //Prefix: obj.Prefix, // TODO: add a path on? Debug: obj.data.Debug, @@ -3359,7 +3362,7 @@ func (obj *StmtProg) SetScope(scope *interfaces.Scope) error { return fmt.Errorf("import `%s` already exists in this scope", imp.Name) } - result, err := ParseImportName(imp.Name) + result, err := langutil.ParseImportName(imp.Name) if err != nil { return errwrap.Wrapf(err, "import `%s` is not valid", imp.Name) } @@ -3447,16 +3450,16 @@ func (obj *StmtProg) SetScope(scope *interfaces.Scope) error { } // now collect all the functions, and group by name (if polyfunc is ok) - funcs := make(map[string][]*StmtFunc) + functions := make(map[string][]*StmtFunc) for _, x := range obj.Body { fn, ok := x.(*StmtFunc) if !ok { continue } - _, exists := funcs[fn.Name] + _, exists := functions[fn.Name] if !exists { - funcs[fn.Name] = []*StmtFunc{} // initialize + functions[fn.Name] = []*StmtFunc{} // initialize } // check for duplicates *in this scope* @@ -3464,11 +3467,11 @@ func (obj *StmtProg) SetScope(scope *interfaces.Scope) error { return fmt.Errorf("func `%s` already exists in this scope", fn.Name) } - // collect funcs (if multiple, this is a polyfunc) - funcs[fn.Name] = append(funcs[fn.Name], fn) + // collect functions (if multiple, this is a polyfunc) + functions[fn.Name] = append(functions[fn.Name], fn) } - for name, fnList := range funcs { + for name, fnList := range functions { if obj.data.Debug { // TODO: is this message ever useful? obj.data.Logf("prog: set scope: collect: (%+v -> %d): %+v (%T)", name, len(fnList), fnList[0].Func, fnList[0].Func) } @@ -4816,7 +4819,7 @@ func (obj *ExprStr) Init(data *interfaces.Data) error { // which need interpolation. If any are found, it returns a larger AST which has // a function which returns a string as its root. Otherwise it returns itself. func (obj *ExprStr) Interpolate() (interfaces.Expr, error) { - pos := &Pos{ + pos := &interfaces.Pos{ // column/line number, starting at 1 //Column: -1, // TODO //Line: -1, // TODO @@ -4825,22 +4828,27 @@ func (obj *ExprStr) Interpolate() (interfaces.Expr, error) { data := &interfaces.Data{ // TODO: add missing fields here if/when needed - Fs: obj.data.Fs, - FsURI: obj.data.FsURI, - Base: obj.data.Base, - Files: obj.data.Files, - Imports: obj.data.Imports, - Metadata: obj.data.Metadata, - Modules: obj.data.Modules, - Downloader: obj.data.Downloader, - //World: obj.data.World, + Fs: obj.data.Fs, + FsURI: obj.data.FsURI, + Base: obj.data.Base, + Files: obj.data.Files, + Imports: obj.data.Imports, + Metadata: obj.data.Metadata, + Modules: obj.data.Modules, + + LexParser: obj.data.LexParser, + Downloader: obj.data.Downloader, + StrInterpolater: obj.data.StrInterpolater, + //World: obj.data.World, // TODO: do we need this? + Prefix: obj.data.Prefix, Debug: obj.data.Debug, Logf: func(format string, v ...interface{}) { obj.data.Logf("interpolate: "+format, v...) }, } - result, err := InterpolateStr(obj.V, pos, data) + + result, err := obj.data.StrInterpolater(obj.V, pos, data) if err != nil { return nil, err } diff --git a/lang/util.go b/lang/ast/util.go similarity index 87% rename from lang/util.go rename to lang/ast/util.go index 4a22e33f..c69e46ef 100644 --- a/lang/util.go +++ b/lang/ast/util.go @@ -15,7 +15,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -package lang +package ast import ( "fmt" @@ -27,6 +27,7 @@ import ( "github.com/purpleidea/mgmt/lang/funcs/vars" "github.com/purpleidea/mgmt/lang/interfaces" "github.com/purpleidea/mgmt/lang/types" + "github.com/purpleidea/mgmt/util/errwrap" ) // FuncPrefixToFunctionsScope is a helper function to return the functions @@ -215,3 +216,29 @@ func ValueToExpr(val types.Value) (interfaces.Expr, error) { return expr, expr.SetType(val.Type()) } + +// CollectFiles collects all the files used in the AST. You will see more files +// based on how many compiling steps have run. In general, this is useful for +// collecting all the files needed to store in our file system for a deploy. +func CollectFiles(ast interfaces.Stmt) ([]string, error) { + // collect the list of files + fileList := []string{} + fn := func(node interfaces.Node) error { + // redundant check for example purposes + stmt, ok := node.(interfaces.Stmt) + if !ok { + return nil + } + body, ok := stmt.(*StmtProg) + if !ok { + return nil + } + // collect into global + fileList = append(fileList, body.importFiles...) + return nil + } + if err := ast.Apply(fn); err != nil { + return nil, errwrap.Wrapf(err, "can't retrieve paths") + } + return fileList, nil +} diff --git a/lang/funcs/funcs.go b/lang/funcs/funcs.go index 7684888f..63dcde9d 100644 --- a/lang/funcs/funcs.go +++ b/lang/funcs/funcs.go @@ -40,6 +40,9 @@ const ( // we don't want to allow this as the first or last character in a name. // NOTE: the template library will panic if it is one of: .-# ReplaceChar = "_" + + // CoreDir is the directory prefix where core bindata mcl code is added. + CoreDir = "core/" ) // registeredFuncs is a global map of all possible funcs which can be used. You diff --git a/lang/fuzz/fuzz.go b/lang/fuzz/fuzz.go index 37e7a7a9..763836f1 100644 --- a/lang/fuzz/fuzz.go +++ b/lang/fuzz/fuzz.go @@ -20,7 +20,7 @@ package fuzz import ( "bytes" - "github.com/purpleidea/mgmt/lang" + "github.com/purpleidea/mgmt/lang/parser" ) // Fuzz is repeatedly called by go-fuzz with semi-random inputs in an attempt to @@ -32,7 +32,7 @@ import ( // gives new coverage; and 0 otherwise; other values are reserved for future // use. func Fuzz(data []byte) int { - ast, err := lang.LexParse(bytes.NewReader(data)) + ast, err := parser.LexParse(bytes.NewReader(data)) if err != nil { if ast != nil { panic("ast != nil on error") diff --git a/lang/gapi/gapi.go b/lang/gapi/gapi.go index 5f4a5dff..77ff2be8 100644 --- a/lang/gapi/gapi.go +++ b/lang/gapi/gapi.go @@ -25,10 +25,13 @@ import ( "github.com/purpleidea/mgmt/gapi" "github.com/purpleidea/mgmt/lang" + "github.com/purpleidea/mgmt/lang/ast" "github.com/purpleidea/mgmt/lang/download" "github.com/purpleidea/mgmt/lang/funcs/vars" "github.com/purpleidea/mgmt/lang/inputs" "github.com/purpleidea/mgmt/lang/interfaces" + "github.com/purpleidea/mgmt/lang/interpolate" + "github.com/purpleidea/mgmt/lang/parser" "github.com/purpleidea/mgmt/lang/unification" "github.com/purpleidea/mgmt/pgraph" "github.com/purpleidea/mgmt/util" @@ -191,12 +194,12 @@ func (obj *GAPI) Cli(cliInfo *gapi.CliInfo) (*gapi.Deploy, error) { // TODO: do the paths need to be cleaned for "../" before comparison? logf("lexing/parsing...") - ast, err := lang.LexParse(bytes.NewReader(output.Main)) + xast, err := parser.LexParse(bytes.NewReader(output.Main)) if err != nil { return nil, errwrap.Wrapf(err, "could not generate AST") } if debug { - logf("behold, the AST: %+v", ast) + logf("behold, the AST: %+v", xast) } var downloader interfaces.Downloader @@ -239,16 +242,19 @@ func (obj *GAPI) Cli(cliInfo *gapi.CliInfo) (*gapi.Deploy, error) { // init and validate the structure of the AST data := &interfaces.Data{ // TODO: add missing fields here if/when needed - Fs: localFs, // the local fs! - FsURI: localFs.URI(), // TODO: is this right? - Base: output.Base, // base dir (absolute path) that this is rooted in - Files: output.Files, - Imports: importVertex, - Metadata: output.Metadata, - Modules: modules, - Downloader: downloader, + Fs: localFs, // the local fs! + FsURI: localFs.URI(), // TODO: is this right? + Base: output.Base, // base dir (absolute path) that this is rooted in + Files: output.Files, + Imports: importVertex, + Metadata: output.Metadata, + Modules: modules, + LexParser: parser.LexParse, + Downloader: downloader, + StrInterpolater: interpolate.InterpolateStr, //World: obj.World, // TODO: do we need this? + Prefix: prefix, Debug: debug, Logf: func(format string, v ...interface{}) { @@ -257,25 +263,25 @@ func (obj *GAPI) Cli(cliInfo *gapi.CliInfo) (*gapi.Deploy, error) { }, } // some of this might happen *after* interpolate in SetScope or Unify... - if err := ast.Init(data); err != nil { + if err := xast.Init(data); err != nil { return nil, errwrap.Wrapf(err, "could not init and validate AST") } logf("interpolating...") // interpolate strings and other expansionable nodes in AST - interpolated, err := ast.Interpolate() + interpolated, err := xast.Interpolate() if err != nil { return nil, errwrap.Wrapf(err, "could not interpolate AST") } variables := map[string]interfaces.Expr{ - "purpleidea": &lang.ExprStr{V: "hello world!"}, // james says hi + "purpleidea": &ast.ExprStr{V: "hello world!"}, // james says hi // TODO: change to a func when we can change hostname dynamically! - "hostname": &lang.ExprStr{V: ""}, // NOTE: empty b/c not used + "hostname": &ast.ExprStr{V: ""}, // NOTE: empty b/c not used } - consts := lang.VarPrefixToVariablesScope(vars.ConstNamespace) // strips prefix! - addback := vars.ConstNamespace + interfaces.ModuleSep // add it back... - variables, err = lang.MergeExprMaps(variables, consts, addback) + consts := ast.VarPrefixToVariablesScope(vars.ConstNamespace) // strips prefix! + addback := vars.ConstNamespace + interfaces.ModuleSep // add it back... + variables, err = ast.MergeExprMaps(variables, consts, addback) if err != nil { return nil, errwrap.Wrapf(err, "couldn't merge in consts") } @@ -284,7 +290,7 @@ func (obj *GAPI) Cli(cliInfo *gapi.CliInfo) (*gapi.Deploy, error) { scope := &interfaces.Scope{ Variables: variables, // all the built-in top-level, core functions enter here... - Functions: lang.FuncPrefixToFunctionsScope(""), // runs funcs.LookupPrefix + Functions: ast.FuncPrefixToFunctionsScope(""), // runs funcs.LookupPrefix } logf("building scope...") @@ -316,7 +322,7 @@ func (obj *GAPI) Cli(cliInfo *gapi.CliInfo) (*gapi.Deploy, error) { } // get the list of needed files (this is available after SetScope) - fileList, err := lang.CollectFiles(interpolated) + fileList, err := ast.CollectFiles(interpolated) if err != nil { return nil, errwrap.Wrapf(err, "could not collect files") } @@ -664,7 +670,7 @@ func (obj *GAPI) Get(getInfo *gapi.GetInfo) error { // TODO: do the paths need to be cleaned for "../" before comparison? logf("lexing/parsing...") - ast, err := lang.LexParse(bytes.NewReader(output.Main)) + ast, err := parser.LexParse(bytes.NewReader(output.Main)) if err != nil { return errwrap.Wrapf(err, "could not generate AST") } @@ -709,16 +715,19 @@ func (obj *GAPI) Get(getInfo *gapi.GetInfo) error { // init and validate the structure of the AST data := &interfaces.Data{ // TODO: add missing fields here if/when needed - Fs: localFs, // the local fs! - FsURI: localFs.URI(), // TODO: is this right? - Base: output.Base, // base dir (absolute path) that this is rooted in - Files: output.Files, - Imports: importVertex, - Metadata: output.Metadata, - Modules: modules, - Downloader: downloader, + Fs: localFs, // the local fs! + FsURI: localFs.URI(), // TODO: is this right? + Base: output.Base, // base dir (absolute path) that this is rooted in + Files: output.Files, + Imports: importVertex, + Metadata: output.Metadata, + Modules: modules, + LexParser: parser.LexParse, + Downloader: downloader, + StrInterpolater: interpolate.InterpolateStr, //World: obj.World, // TODO: do we need this? + Prefix: prefix, Debug: debug, Logf: func(format string, v ...interface{}) { diff --git a/lang/interfaces/ast.go b/lang/interfaces/ast.go index 6f2aebc1..a1bb55ce 100644 --- a/lang/interfaces/ast.go +++ b/lang/interfaces/ast.go @@ -19,6 +19,7 @@ package interfaces import ( "fmt" + "io" "sort" "github.com/purpleidea/mgmt/engine" @@ -186,6 +187,16 @@ type Data struct { // deploys, however that is not blocked at the level of this interface. Downloader Downloader + // LexParser is a function that needs to get passed in to run the lexer + // and parser to build the initial AST. This is passed in this way to + // avoid dependency cycles. + LexParser func(io.Reader) (Stmt, error) + + // StrInterpolater is a function that needs to get passed in to run the + // string interpolation. This is passed in this way to avoid dependency + // cycles. + StrInterpolater func(string, *Pos, *Data) (Expr, error) + //World engine.World // TODO: do we need this? // Prefix provides a unique path prefix that we can namespace in. It is diff --git a/lang/interfaces/parser.go b/lang/interfaces/parser.go new file mode 100644 index 00000000..5e608761 --- /dev/null +++ b/lang/interfaces/parser.go @@ -0,0 +1,27 @@ +// Mgmt +// Copyright (C) 2013-2021+ James Shubin and the project contributors +// Written by James Shubin 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 . + +package interfaces + +// Pos represents a position in the code. This is used by the parser and string +// interpolation. +// TODO: consider expanding with range characteristics. +type Pos struct { + Line int // line number starting at 1 + Column int // column number starting at 1 + Filename string // optional source filename, if known +} diff --git a/lang/interpolate.go b/lang/interpolate/interpolate.go similarity index 84% rename from lang/interpolate.go rename to lang/interpolate/interpolate.go index 074fab83..350e0946 100644 --- a/lang/interpolate.go +++ b/lang/interpolate/interpolate.go @@ -15,13 +15,14 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -package lang // TODO: move this into a sub package of lang/$name? +package interpolate import ( "fmt" + "github.com/purpleidea/mgmt/lang/ast" + "github.com/purpleidea/mgmt/lang/funcs" "github.com/purpleidea/mgmt/lang/interfaces" - "github.com/purpleidea/mgmt/lang/interpolate" "github.com/purpleidea/mgmt/util/errwrap" "github.com/hashicorp/hil" @@ -35,16 +36,8 @@ const ( UseHilInterpolation = false ) -// Pos represents a position in the code. -// TODO: consider expanding with range characteristics. -type Pos struct { - Line int // line number starting at 1 - Column int // column number starting at 1 - Filename string // optional source filename, if known -} - // InterpolateStr interpolates a string and returns the representative AST. -func InterpolateStr(str string, pos *Pos, data *interfaces.Data) (interfaces.Expr, error) { +func InterpolateStr(str string, pos *interfaces.Pos, data *interfaces.Data) (interfaces.Expr, error) { if data.Debug { data.Logf("interpolating: %s", str) } @@ -57,8 +50,8 @@ func InterpolateStr(str string, pos *Pos, data *interfaces.Data) (interfaces.Exp // InterpolateRagel interpolates a string and returns the representative AST. It // uses the ragel parser to perform the string interpolation. -func InterpolateRagel(str string, pos *Pos, data *interfaces.Data) (interfaces.Expr, error) { - sequence, err := interpolate.Parse(str) +func InterpolateRagel(str string, pos *interfaces.Pos, data *interfaces.Data) (interfaces.Expr, error) { + sequence, err := Parse(str) if err != nil { return nil, errwrap.Wrapf(err, "parser failed") } @@ -67,14 +60,14 @@ func InterpolateRagel(str string, pos *Pos, data *interfaces.Data) (interfaces.E for _, term := range sequence { switch t := term.(type) { - case interpolate.Literal: - expr := &ExprStr{ + case Literal: + expr := &ast.ExprStr{ V: t.Value, } exprs = append(exprs, expr) - case interpolate.Variable: - expr := &ExprVar{ + case Variable: + expr := &ast.ExprVar{ Name: t.Name, } exprs = append(exprs, expr) @@ -85,7 +78,7 @@ func InterpolateRagel(str string, pos *Pos, data *interfaces.Data) (interfaces.E // If we didn't find anything of value, we got an empty string... if len(sequence) == 0 && str == "" { // be doubly sure... - expr := &ExprStr{ + expr := &ast.ExprStr{ V: "", } exprs = append(exprs, expr) @@ -108,7 +101,7 @@ func InterpolateRagel(str string, pos *Pos, data *interfaces.Data) (interfaces.E // InterpolateHil interpolates a string and returns the representative AST. This // particular implementation uses the hashicorp hil library and syntax to do so. -func InterpolateHil(str string, pos *Pos, data *interfaces.Data) (interfaces.Expr, error) { +func InterpolateHil(str string, pos *interfaces.Pos, data *interfaces.Data) (interfaces.Expr, error) { var line, column int = -1, -1 var filename string if pos != nil { @@ -132,15 +125,19 @@ func InterpolateHil(str string, pos *Pos, data *interfaces.Data) (interfaces.Exp transformData := &interfaces.Data{ // TODO: add missing fields here if/when needed - Fs: data.Fs, - FsURI: data.FsURI, - Base: data.Base, - Files: data.Files, - Imports: data.Imports, - Metadata: data.Metadata, - Modules: data.Modules, - Downloader: data.Downloader, - //World: data.World, + Fs: data.Fs, + FsURI: data.FsURI, + Base: data.Base, + Files: data.Files, + Imports: data.Imports, + Metadata: data.Metadata, + Modules: data.Modules, + + LexParser: data.LexParser, + Downloader: data.Downloader, + StrInterpolater: data.StrInterpolater, + //World: data.World, // TODO: do we need this? + Prefix: data.Prefix, Debug: data.Debug, Logf: func(format string, v ...interface{}) { @@ -205,7 +202,7 @@ func hilTransform(root hilast.Node, data *interfaces.Data) (interfaces.Expr, err args = append(args, arg) } - return &ExprCall{ + return &ast.ExprCall{ Name: node.Func, // name Args: args, }, nil @@ -217,23 +214,23 @@ func hilTransform(root hilast.Node, data *interfaces.Data) (interfaces.Expr, err switch node.Typex { case hilast.TypeBool: - return &ExprBool{ + return &ast.ExprBool{ V: node.Value.(bool), }, nil case hilast.TypeString: - return &ExprStr{ + return &ast.ExprStr{ V: node.Value.(string), }, nil case hilast.TypeInt: - return &ExprInt{ + return &ast.ExprInt{ // node.Value is an int stored as an interface V: int64(node.Value.(int)), }, nil case hilast.TypeFloat: - return &ExprFloat{ + return &ast.ExprFloat{ V: node.Value.(float64), }, nil @@ -249,7 +246,7 @@ func hilTransform(root hilast.Node, data *interfaces.Data) (interfaces.Expr, err if data.Debug { data.Logf("got variable access type: %+v", node) } - return &ExprVar{ + return &ast.ExprVar{ Name: node.Name, }, nil @@ -284,7 +281,7 @@ func concatExprListIntoCall(exprs []interfaces.Expr) (interfaces.Expr, error) { return nil, fmt.Errorf("empty list") } - operator := &ExprStr{ + operator := &ast.ExprStr{ V: "+", // for PLUS this is a `+` character } @@ -293,11 +290,11 @@ func concatExprListIntoCall(exprs []interfaces.Expr) (interfaces.Expr, error) { } //if len(exprs) == 1 { // arg := exprs[0] - // emptyStr := &ExprStr{ + // emptyStr := &ast.ExprStr{ // V: "", // empty str // } - // return &ExprCall{ - // Name: operatorFuncName, // concatenate the two strings with + operator + // return &ast.ExprCall{ + // Name: funcs.OperatorFuncName, // concatenate the two strings with + operator // Args: []interfaces.Expr{ // operator, // operator first // arg, // string arg @@ -313,9 +310,9 @@ func concatExprListIntoCall(exprs []interfaces.Expr) (interfaces.Expr, error) { return nil, err } - return &ExprCall{ + return &ast.ExprCall{ // NOTE: if we don't set the data field we need Init() called on it! - Name: operatorFuncName, // concatenate the two strings with + operator + Name: funcs.OperatorFuncName, // concatenate the two strings with + operator Args: []interfaces.Expr{ operator, // operator first head, // string arg @@ -333,7 +330,7 @@ func simplifyExprList(exprs []interfaces.Expr) ([]interfaces.Expr, error) { for _, x := range exprs { switch v := x.(type) { - case *ExprStr: + case *ast.ExprStr: if !last { last = true result = append(result, x) @@ -342,7 +339,7 @@ func simplifyExprList(exprs []interfaces.Expr) ([]interfaces.Expr, error) { // combine! expr := result[len(result)-1] // there has to be at least one - str, ok := expr.(*ExprStr) + str, ok := expr.(*ast.ExprStr) if !ok { // programming error return nil, fmt.Errorf("unexpected type (%T)", expr) @@ -351,7 +348,7 @@ func simplifyExprList(exprs []interfaces.Expr) ([]interfaces.Expr, error) { //last = true // redundant, it's already true // ... and don't append, we've combined! - case *ExprVar: + case *ast.ExprVar: last = false // the next one can't combine with me result = append(result, x) diff --git a/lang/interpolate_test.go b/lang/interpolate/interpolate_test.go similarity index 80% rename from lang/interpolate_test.go rename to lang/interpolate/interpolate_test.go index 276a676c..bd182b04 100644 --- a/lang/interpolate_test.go +++ b/lang/interpolate/interpolate_test.go @@ -17,7 +17,7 @@ // +build !root -package lang +package interpolate import ( "fmt" @@ -25,7 +25,10 @@ import ( "strings" "testing" + "github.com/purpleidea/mgmt/lang/ast" + "github.com/purpleidea/mgmt/lang/funcs" "github.com/purpleidea/mgmt/lang/interfaces" + "github.com/purpleidea/mgmt/lang/parser" "github.com/purpleidea/mgmt/util" "github.com/davecgh/go-spew/spew" @@ -45,28 +48,28 @@ func TestInterpolate0(t *testing.T) { // names, and then run `go test -run ` with the name(s) to run. { - ast := &StmtProg{ + xast := &ast.StmtProg{ Body: []interfaces.Stmt{}, } testCases = append(testCases, test{ // 0 "nil", ``, false, - ast, + xast, }) } { - ast := &StmtProg{ + xast := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtRes{ + &ast.StmtRes{ Kind: "test", - Name: &ExprStr{ + Name: &ast.ExprStr{ V: "t1", }, - Contents: []StmtResContents{ - &StmtResField{ + Contents: []ast.StmtResContents{ + &ast.StmtResField{ Field: "stringptr", - Value: &ExprStr{ + Value: &ast.ExprStr{ V: "foo", }, }, @@ -82,33 +85,33 @@ func TestInterpolate0(t *testing.T) { } `, fail: false, - ast: ast, + ast: xast, }) } { - fieldName := &ExprCall{ - Name: operatorFuncName, + fieldName := &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ + &ast.ExprStr{ V: "+", }, - &ExprStr{ + &ast.ExprStr{ V: "foo-", }, - &ExprVar{ + &ast.ExprVar{ Name: "x", }, }, } - ast := &StmtProg{ + xast := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtRes{ + &ast.StmtRes{ Kind: "test", - Name: &ExprStr{ + Name: &ast.ExprStr{ V: "t1", }, - Contents: []StmtResContents{ - &StmtResField{ + Contents: []ast.StmtResContents{ + &ast.StmtResField{ Field: "stringptr", Value: fieldName, }, @@ -125,21 +128,21 @@ func TestInterpolate0(t *testing.T) { } `, fail: false, - ast: ast, + ast: xast, }) } { - ast := &StmtProg{ + xast := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtRes{ + &ast.StmtRes{ Kind: "test", - Name: &ExprStr{ + Name: &ast.ExprStr{ V: "t1", }, - Contents: []StmtResContents{ - &StmtResField{ + Contents: []ast.StmtResContents{ + &ast.StmtResField{ Field: "stringptr", - Value: &ExprStr{ + Value: &ast.ExprStr{ V: "${hello}", }, }, @@ -155,21 +158,21 @@ func TestInterpolate0(t *testing.T) { } `, fail: false, - ast: ast, + ast: xast, }) } { - ast := &StmtProg{ + xast := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtRes{ + &ast.StmtRes{ Kind: "test", - Name: &ExprStr{ + Name: &ast.ExprStr{ V: "t1", }, - Contents: []StmtResContents{ - &StmtResField{ + Contents: []ast.StmtResContents{ + &ast.StmtResField{ Field: "stringptr", - Value: &ExprStr{ + Value: &ast.ExprStr{ V: `\` + `$` + `{hello}`, }, }, @@ -185,7 +188,7 @@ func TestInterpolate0(t *testing.T) { } `, fail: false, - ast: ast, + ast: xast, }) } @@ -204,7 +207,7 @@ func TestInterpolate0(t *testing.T) { code, fail, exp := tc.code, tc.fail, tc.ast str := strings.NewReader(code) - ast, err := LexParse(str) + ast, err := parser.LexParse(str) if err != nil { t.Errorf("test #%d: FAIL", index) t.Errorf("test #%d: lex/parse failed with: %+v", index, err) @@ -213,6 +216,9 @@ func TestInterpolate0(t *testing.T) { t.Logf("test #%d: AST: %+v", index, ast) data := &interfaces.Data{ + // TODO: add missing fields here if/when needed + StrInterpolater: InterpolateStr, + Debug: testing.Verbose(), // set via the -test.v flag to `go test` Logf: func(format string, v ...interface{}) { t.Logf("ast: "+format, v...) @@ -296,17 +302,17 @@ func TestInterpolateBasicStmt(t *testing.T) { // }) //} { - ast := &StmtProg{ + xast := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtRes{ + &ast.StmtRes{ Kind: "test", - Name: &ExprStr{ + Name: &ast.ExprStr{ V: "t1", }, - Contents: []StmtResContents{ - &StmtResField{ + Contents: []ast.StmtResContents{ + &ast.StmtResField{ Field: "stringptr", - Value: &ExprStr{ + Value: &ast.ExprStr{ V: "foo", }, }, @@ -314,17 +320,17 @@ func TestInterpolateBasicStmt(t *testing.T) { }, }, } - exp := &StmtProg{ + exp := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtRes{ + &ast.StmtRes{ Kind: "test", - Name: &ExprStr{ + Name: &ast.ExprStr{ V: "t1", }, - Contents: []StmtResContents{ - &StmtResField{ + Contents: []ast.StmtResContents{ + &ast.StmtResField{ Field: "stringptr", - Value: &ExprStr{ + Value: &ast.ExprStr{ V: "foo", }, }, @@ -334,23 +340,23 @@ func TestInterpolateBasicStmt(t *testing.T) { } testCases = append(testCases, test{ name: "basic resource", - ast: ast, + ast: xast, fail: false, exp: exp, }) } { - ast := &StmtProg{ + xast := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtRes{ + &ast.StmtRes{ Kind: "test", - Name: &ExprStr{ + Name: &ast.ExprStr{ V: "t${blah}", }, - Contents: []StmtResContents{ - &StmtResField{ + Contents: []ast.StmtResContents{ + &ast.StmtResField{ Field: "stringptr", - Value: &ExprStr{ + Value: &ast.ExprStr{ V: "foo", }, }, @@ -358,29 +364,29 @@ func TestInterpolateBasicStmt(t *testing.T) { }, }, } - resName := &ExprCall{ - Name: operatorFuncName, + resName := &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ + &ast.ExprStr{ V: "+", }, - &ExprStr{ + &ast.ExprStr{ V: "t", }, - &ExprVar{ + &ast.ExprVar{ Name: "blah", }, }, } - exp := &StmtProg{ + exp := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtRes{ + &ast.StmtRes{ Kind: "test", Name: resName, - Contents: []StmtResContents{ - &StmtResField{ + Contents: []ast.StmtResContents{ + &ast.StmtResField{ Field: "stringptr", - Value: &ExprStr{ + Value: &ast.ExprStr{ V: "foo", }, }, @@ -390,23 +396,23 @@ func TestInterpolateBasicStmt(t *testing.T) { } testCases = append(testCases, test{ name: "expanded resource", - ast: ast, + ast: xast, fail: false, exp: exp, }) } { - ast := &StmtProg{ + xast := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtRes{ + &ast.StmtRes{ Kind: "test", - Name: &ExprStr{ + Name: &ast.ExprStr{ V: "t${42}", // incorrect type }, - Contents: []StmtResContents{ - &StmtResField{ + Contents: []ast.StmtResContents{ + &ast.StmtResField{ Field: "stringptr", - Value: &ExprStr{ + Value: &ast.ExprStr{ V: "foo", }, }, @@ -414,30 +420,30 @@ func TestInterpolateBasicStmt(t *testing.T) { }, }, } - resName := &ExprCall{ - Name: operatorFuncName, + resName := &ast.ExprCall{ + Name: funcs.OperatorFuncName, // incorrect sig for this function, and now invalid interpolation Args: []interfaces.Expr{ - &ExprStr{ + &ast.ExprStr{ V: "+", }, - &ExprStr{ + &ast.ExprStr{ V: "t", }, - &ExprInt{ + &ast.ExprInt{ V: 42, }, }, } - exp := &StmtProg{ + exp := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtRes{ + &ast.StmtRes{ Kind: "test", Name: resName, - Contents: []StmtResContents{ - &StmtResField{ + Contents: []ast.StmtResContents{ + &ast.StmtResField{ Field: "stringptr", - Value: &ExprStr{ + Value: &ast.ExprStr{ V: "foo", }, }, @@ -448,7 +454,7 @@ func TestInterpolateBasicStmt(t *testing.T) { _ = exp // historical testCases = append(testCases, test{ name: "expanded invalid resource name", - ast: ast, + ast: xast, fail: true, //exp: exp, }) @@ -466,6 +472,8 @@ func TestInterpolateBasicStmt(t *testing.T) { data := &interfaces.Data{ // TODO: add missing fields here if/when needed + StrInterpolater: InterpolateStr, + Debug: testing.Verbose(), // set via the -test.v flag to `go test` Logf: func(format string, v ...interface{}) { t.Logf("ast: "+format, v...) @@ -535,51 +543,51 @@ func TestInterpolateBasicExpr(t *testing.T) { // }) //} { - ast := &ExprStr{ + xast := &ast.ExprStr{ V: "hello", } - exp := &ExprStr{ + exp := &ast.ExprStr{ V: "hello", } testCases = append(testCases, test{ name: "basic string", - ast: ast, + ast: xast, fail: false, exp: exp, }) } { - ast := &ExprStr{ + xast := &ast.ExprStr{ V: "hello ${person_name}", } - exp := &ExprCall{ - Name: operatorFuncName, + exp := &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ + &ast.ExprStr{ V: "+", }, - &ExprStr{ + &ast.ExprStr{ V: "hello ", }, - &ExprVar{ + &ast.ExprVar{ Name: "person_name", }, }, } testCases = append(testCases, test{ name: "basic expansion", - ast: ast, + ast: xast, fail: false, exp: exp, }) } { - ast := &ExprStr{ + xast := &ast.ExprStr{ V: "hello ${x ${y} z}", } testCases = append(testCases, test{ name: "invalid expansion", - ast: ast, + ast: xast, fail: true, }) } @@ -587,31 +595,31 @@ func TestInterpolateBasicExpr(t *testing.T) { // library, but are not yet supported by our translation layer, nor do // they necessarily work or make much sense at this point in time... //{ - // ast := &ExprStr{ + // xast := &ast.ExprStr{ // V: `hello ${func("hello ${var.foo}")}`, // } // exp := nil // TODO: add this // testCases = append(testCases, test{ // name: "double expansion", - // ast: ast, + // ast: xast, // fail: false, // exp: exp, // }) //} { - ast := &ExprStr{ + xast := &ast.ExprStr{ V: "sweetie${3.14159}", // invalid } - exp := &ExprCall{ - Name: operatorFuncName, + exp := &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ + &ast.ExprStr{ V: "+", }, - &ExprStr{ + &ast.ExprStr{ V: "sweetie", }, - &ExprFloat{ + &ast.ExprFloat{ V: 3.14159, }, }, @@ -619,24 +627,24 @@ func TestInterpolateBasicExpr(t *testing.T) { _ = exp // historical testCases = append(testCases, test{ name: "float expansion", - ast: ast, + ast: xast, fail: true, }) } { - ast := &ExprStr{ + xast := &ast.ExprStr{ V: "i am: ${sys.hostname()}", } - exp := &ExprCall{ - Name: operatorFuncName, + exp := &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ + &ast.ExprStr{ V: "+", }, - &ExprStr{ + &ast.ExprStr{ V: "i am: ", }, - &ExprCall{ + &ast.ExprCall{ Name: "sys.hostname", Args: []interfaces.Expr{}, }, @@ -645,30 +653,30 @@ func TestInterpolateBasicExpr(t *testing.T) { _ = exp // historical testCases = append(testCases, test{ name: "function expansion", - ast: ast, + ast: xast, fail: true, }) } { - ast := &ExprStr{ + xast := &ast.ExprStr{ V: "i am: ${blah(21, 12.3)}", } - exp := &ExprCall{ - Name: operatorFuncName, + exp := &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ + &ast.ExprStr{ V: "+", }, - &ExprStr{ + &ast.ExprStr{ V: "i am: ", }, - &ExprCall{ + &ast.ExprCall{ Name: "blah", Args: []interfaces.Expr{ - &ExprInt{ + &ast.ExprInt{ V: 21, }, - &ExprFloat{ + &ast.ExprFloat{ V: 12.3, }, }, @@ -678,31 +686,31 @@ func TestInterpolateBasicExpr(t *testing.T) { _ = exp // historical testCases = append(testCases, test{ name: "function expansion arg", - ast: ast, + ast: xast, fail: true, }) } // FIXME: i am broken, i don't deal well with negatives for some reason //{ - // ast := &ExprStr{ + // xast := &ast.ExprStr{ // V: "i am: ${blah(21, -12.3)}", // } - // exp := &ExprCall{ - // Name: operatorFuncName, + // exp := &ast.ExprCall{ + // Name: funcs.OperatorFuncName, // Args: []interfaces.Expr{ - // &ExprStr{ + // &ast.ExprStr{ // V: "+", // }, - // &ExprStr{ + // &ast.ExprStr{ // V: "i am: ", // }, - // &ExprCall{ + // &ast.ExprCall{ // Name: "blah", // Args: []interfaces.Expr{ - // &ExprInt{ + // &ast.ExprInt{ // V: 21, // }, - // &ExprFloat{ + // &ast.ExprFloat{ // V: -12.3, // }, // }, @@ -711,58 +719,58 @@ func TestInterpolateBasicExpr(t *testing.T) { // } // testCases = append(testCases, test{ // name: "function expansion arg negative", - // ast: ast, + // ast: xast, // fail: false, // exp: exp, // }) //} // FIXME: i am broken :( //{ - // ast := &ExprStr{ + // xast := &ast.ExprStr{ // V: "sweetie${-3.14159}", // FIXME: only the negative breaks this // } - // exp := &ExprCall{ - // Name: operatorFuncName, + // exp := &ast.ExprCall{ + // Name: funcs.OperatorFuncName, // Args: []interfaces.Expr{ - // &ExprStr{ + // &ast.ExprStr{ // V: "+", // }, - // &ExprStr{ + // &ast.ExprStr{ // V: "sweetie", // }, - // &ExprFloat{ + // &ast.ExprFloat{ // V: -3.14159, // }, // }, // } // testCases = append(testCases, test{ // name: "negative float expansion", - // ast: ast, + // ast: xast, // fail: false, // exp: exp, // }) //} // FIXME: i am also broken, but less important //{ - // ast := &ExprStr{ + // xast := &ast.ExprStr{ // V: `i am: ${blah(42, "${foo}")}`, // } - // exp := &ExprCall{ - // Name: operatorFuncName, + // exp := &ast.ExprCall{ + // Name: funcs.OperatorFuncName, // Args: []interfaces.Expr{ - // &ExprStr{ + // &ast.ExprStr{ // V: "+", // }, - // &ExprStr{ + // &ast.ExprStr{ // V: "i am: ", // }, - // &ExprCall{ + // &ast.ExprCall{ // Name: "blah", // Args: []interfaces.Expr{ - // &ExprInt{ + // &ast.ExprInt{ // V: 42, // }, - // &ExprVar{ + // &ast.ExprVar{ // Name: "foo", // }, // }, @@ -771,7 +779,7 @@ func TestInterpolateBasicExpr(t *testing.T) { // } // testCases = append(testCases, test{ // name: "function expansion arg with var", - // ast: ast, + // ast: xast, // fail: false, // exp: exp, // }) @@ -789,6 +797,8 @@ func TestInterpolateBasicExpr(t *testing.T) { data := &interfaces.Data{ // TODO: add missing fields here if/when needed + StrInterpolater: InterpolateStr, + Debug: testing.Verbose(), // set via the -test.v flag to `go test` Logf: func(format string, v ...interface{}) { t.Logf("ast: "+format, v...) diff --git a/lang/interpret_test.go b/lang/interpret_test.go index d5be8c90..ba8643d8 100644 --- a/lang/interpret_test.go +++ b/lang/interpret_test.go @@ -33,11 +33,14 @@ import ( "github.com/purpleidea/mgmt/engine/graph/autoedge" "github.com/purpleidea/mgmt/engine/resources" "github.com/purpleidea/mgmt/etcd" + "github.com/purpleidea/mgmt/lang/ast" "github.com/purpleidea/mgmt/lang/funcs" "github.com/purpleidea/mgmt/lang/funcs/vars" "github.com/purpleidea/mgmt/lang/inputs" "github.com/purpleidea/mgmt/lang/interfaces" + "github.com/purpleidea/mgmt/lang/interpolate" "github.com/purpleidea/mgmt/lang/interpret" + "github.com/purpleidea/mgmt/lang/parser" "github.com/purpleidea/mgmt/lang/unification" "github.com/purpleidea/mgmt/pgraph" "github.com/purpleidea/mgmt/util" @@ -83,11 +86,11 @@ func (obj *edge) String() string { func TestAstFunc0(t *testing.T) { scope := &interfaces.Scope{ // global scope Variables: map[string]interfaces.Expr{ - "hello": &ExprStr{V: "world"}, - "answer": &ExprInt{V: 42}, + "hello": &ast.ExprStr{V: "world"}, + "answer": &ast.ExprInt{V: 42}, }, // all the built-in top-level, core functions enter here... - Functions: FuncPrefixToFunctionsScope(""), // runs funcs.LookupPrefix + Functions: ast.FuncPrefixToFunctionsScope(""), // runs funcs.LookupPrefix } type test struct { // an individual test @@ -190,7 +193,7 @@ func TestAstFunc0(t *testing.T) { } { graph, _ := pgraph.NewGraph("g") - v1, v2, v3, v4, v5 := vtex(`str("t")`), vtex(`str("+")`), vtex("int(42)"), vtex("int(13)"), vtex(fmt.Sprintf(`call:%s(str("+"), int(42), int(13))`, operatorFuncName)) + v1, v2, v3, v4, v5 := vtex(`str("t")`), vtex(`str("+")`), vtex("int(42)"), vtex("int(13)"), vtex(fmt.Sprintf(`call:%s(str("+"), int(42), int(13))`, funcs.OperatorFuncName)) graph.AddVertex(&v1, &v2, &v3, &v4, &v5) e1, e2, e3 := edge("op"), edge("a"), edge("b") graph.AddEdge(&v2, &v5, &e1) @@ -212,8 +215,8 @@ func TestAstFunc0(t *testing.T) { graph, _ := pgraph.NewGraph("g") v1, v2, v3 := vtex(`str("t")`), vtex(`str("-")`), vtex(`str("+")`) v4, v5, v6 := vtex("int(42)"), vtex("int(13)"), vtex("int(99)") - v7 := vtex(fmt.Sprintf(`call:%s(str("+"), int(42), int(13))`, operatorFuncName)) - v8 := vtex(fmt.Sprintf(`call:%s(str("-"), call:%s(str("+"), int(42), int(13)), int(99))`, operatorFuncName, operatorFuncName)) + v7 := vtex(fmt.Sprintf(`call:%s(str("+"), int(42), int(13))`, funcs.OperatorFuncName)) + v8 := vtex(fmt.Sprintf(`call:%s(str("-"), call:%s(str("+"), int(42), int(13)), int(99))`, funcs.OperatorFuncName, funcs.OperatorFuncName)) graph.AddVertex(&v1, &v2, &v3, &v4, &v5, &v6, &v7, &v8) e1, e2, e3 := edge("op"), edge("a"), edge("b") @@ -242,7 +245,7 @@ func TestAstFunc0(t *testing.T) { v1, v2 := vtex("bool(true)"), vtex(`str("t")`) v3, v4 := vtex("int(13)"), vtex("int(42)") v5, v6 := vtex("var(i)"), vtex("var(x)") - v7, v8 := vtex(`str("+")`), vtex(fmt.Sprintf(`call:%s(str("+"), int(42), var(i))`, operatorFuncName)) + v7, v8 := vtex(`str("+")`), vtex(fmt.Sprintf(`call:%s(str("+"), int(42), var(i))`, funcs.OperatorFuncName)) e1, e2, e3, e4, e5 := edge("op"), edge("a"), edge("b"), edge("var:i"), edge("var:x") graph.AddVertex(&v1, &v2, &v3, &v4, &v5, &v6, &v7, &v8) @@ -437,29 +440,31 @@ func TestAstFunc0(t *testing.T) { t.Logf("\n\ntest #%d (%s) ----------------\n\n", index, name) str := strings.NewReader(code) - ast, err := LexParse(str) + xast, err := parser.LexParse(str) if err != nil { t.Errorf("test #%d: FAIL", index) t.Errorf("test #%d: lex/parse failed with: %+v", index, err) return } - t.Logf("test #%d: AST: %+v", index, ast) + t.Logf("test #%d: AST: %+v", index, xast) data := &interfaces.Data{ // TODO: add missing fields here if/when needed + StrInterpolater: interpolate.InterpolateStr, + Debug: testing.Verbose(), // set via the -test.v flag to `go test` Logf: func(format string, v ...interface{}) { t.Logf("ast: "+format, v...) }, } // some of this might happen *after* interpolate in SetScope or Unify... - if err := ast.Init(data); err != nil { + if err := xast.Init(data); err != nil { t.Errorf("test #%d: FAIL", index) t.Errorf("test #%d: could not init and validate AST: %+v", index, err) return } - iast, err := ast.Interpolate() + iast, err := xast.Interpolate() if err != nil { t.Errorf("test #%d: FAIL", index) t.Errorf("test #%d: interpolate failed with: %+v", index, err) @@ -562,13 +567,13 @@ func TestAstFunc1(t *testing.T) { t.Logf("tests directory is: %s", dir) variables := map[string]interfaces.Expr{ - "purpleidea": &ExprStr{V: "hello world!"}, // james says hi + "purpleidea": &ast.ExprStr{V: "hello world!"}, // james says hi // TODO: change to a func when we can change hostname dynamically! - "hostname": &ExprStr{V: ""}, // NOTE: empty b/c not used + "hostname": &ast.ExprStr{V: ""}, // NOTE: empty b/c not used } - consts := VarPrefixToVariablesScope(vars.ConstNamespace) // strips prefix! - addback := vars.ConstNamespace + interfaces.ModuleSep // add it back... - variables, err = MergeExprMaps(variables, consts, addback) + consts := ast.VarPrefixToVariablesScope(vars.ConstNamespace) // strips prefix! + addback := vars.ConstNamespace + interfaces.ModuleSep // add it back... + variables, err = ast.MergeExprMaps(variables, consts, addback) if err != nil { t.Errorf("couldn't merge in consts: %+v", err) return @@ -577,7 +582,7 @@ func TestAstFunc1(t *testing.T) { scope := &interfaces.Scope{ // global scope Variables: variables, // all the built-in top-level, core functions enter here... - Functions: FuncPrefixToFunctionsScope(""), // runs funcs.LookupPrefix + Functions: ast.FuncPrefixToFunctionsScope(""), // runs funcs.LookupPrefix } type errs struct { @@ -778,7 +783,7 @@ func TestAstFunc1(t *testing.T) { logf("main:\n%s", output.Main) // debug reader := bytes.NewReader(output.Main) - ast, err := LexParse(reader) + xast, err := parser.LexParse(reader) if (!fail || !failLexParse) && err != nil { t.Errorf("test #%d: FAIL", index) t.Errorf("test #%d: lex/parse failed with: %+v", index, err) @@ -800,7 +805,7 @@ func TestAstFunc1(t *testing.T) { return } - t.Logf("test #%d: AST: %+v", index, ast) + t.Logf("test #%d: AST: %+v", index, xast) importGraph, err := pgraph.NewGraph("importGraph") if err != nil { @@ -824,13 +829,16 @@ func TestAstFunc1(t *testing.T) { Metadata: output.Metadata, Modules: "/" + interfaces.ModuleDirectory, // not really needed here afaict + LexParser: parser.LexParse, + StrInterpolater: interpolate.InterpolateStr, + Debug: testing.Verbose(), // set via the -test.v flag to `go test` Logf: func(format string, v ...interface{}) { logf("ast: "+format, v...) }, } // some of this might happen *after* interpolate in SetScope or Unify... - err = ast.Init(data) + err = xast.Init(data) if (!fail || !failInit) && err != nil { t.Errorf("test #%d: FAIL", index) t.Errorf("test #%d: could not init and validate AST: %+v", index, err) @@ -852,7 +860,7 @@ func TestAstFunc1(t *testing.T) { return } - iast, err := ast.Interpolate() + iast, err := xast.Interpolate() if err != nil { t.Errorf("test #%d: FAIL", index) t.Errorf("test #%d: interpolate failed with: %+v", index, err) @@ -1017,13 +1025,13 @@ func TestAstFunc2(t *testing.T) { t.Logf("tests directory is: %s", dir) variables := map[string]interfaces.Expr{ - "purpleidea": &ExprStr{V: "hello world!"}, // james says hi + "purpleidea": &ast.ExprStr{V: "hello world!"}, // james says hi // TODO: change to a func when we can change hostname dynamically! - "hostname": &ExprStr{V: ""}, // NOTE: empty b/c not used + "hostname": &ast.ExprStr{V: ""}, // NOTE: empty b/c not used } - consts := VarPrefixToVariablesScope(vars.ConstNamespace) // strips prefix! - addback := vars.ConstNamespace + interfaces.ModuleSep // add it back... - variables, err = MergeExprMaps(variables, consts, addback) + consts := ast.VarPrefixToVariablesScope(vars.ConstNamespace) // strips prefix! + addback := vars.ConstNamespace + interfaces.ModuleSep // add it back... + variables, err = ast.MergeExprMaps(variables, consts, addback) if err != nil { t.Errorf("couldn't merge in consts: %+v", err) return @@ -1032,7 +1040,7 @@ func TestAstFunc2(t *testing.T) { scope := &interfaces.Scope{ // global scope Variables: variables, // all the built-in top-level, core functions enter here... - Functions: FuncPrefixToFunctionsScope(""), // runs funcs.LookupPrefix + Functions: ast.FuncPrefixToFunctionsScope(""), // runs funcs.LookupPrefix } type errs struct { @@ -1274,7 +1282,7 @@ func TestAstFunc2(t *testing.T) { logf("main:\n%s", output.Main) // debug reader := bytes.NewReader(output.Main) - ast, err := LexParse(reader) + xast, err := parser.LexParse(reader) if (!fail || !failLexParse) && err != nil { t.Errorf("test #%d: FAIL", index) t.Errorf("test #%d: lex/parse failed with: %+v", index, err) @@ -1296,7 +1304,7 @@ func TestAstFunc2(t *testing.T) { return } - t.Logf("test #%d: AST: %+v", index, ast) + t.Logf("test #%d: AST: %+v", index, xast) importGraph, err := pgraph.NewGraph("importGraph") if err != nil { @@ -1320,13 +1328,16 @@ func TestAstFunc2(t *testing.T) { Metadata: output.Metadata, Modules: "/" + interfaces.ModuleDirectory, // not really needed here afaict + LexParser: parser.LexParse, + StrInterpolater: interpolate.InterpolateStr, + Debug: testing.Verbose(), // set via the -test.v flag to `go test` Logf: func(format string, v ...interface{}) { logf("ast: "+format, v...) }, } // some of this might happen *after* interpolate in SetScope or Unify... - err = ast.Init(data) + err = xast.Init(data) if (!fail || !failInit) && err != nil { t.Errorf("test #%d: FAIL", index) t.Errorf("test #%d: could not init and validate AST: %+v", index, err) @@ -1348,7 +1359,7 @@ func TestAstFunc2(t *testing.T) { return } - iast, err := ast.Interpolate() + iast, err := xast.Interpolate() if (!fail || !failInterpolate) && err != nil { t.Errorf("test #%d: FAIL", index) t.Errorf("test #%d: Interpolate failed with: %+v", index, err) @@ -1806,12 +1817,12 @@ func TestAstInterpret0(t *testing.T) { t.Logf("\n\ntest #%d (%s) ----------------\n\n", index, name) str := strings.NewReader(code) - ast, err := LexParse(str) + xast, err := parser.LexParse(str) if err != nil { t.Errorf("test #%d: lex/parse failed with: %+v", index, err) continue } - t.Logf("test #%d: AST: %+v", index, ast) + t.Logf("test #%d: AST: %+v", index, xast) data := &interfaces.Data{ // TODO: add missing fields here if/when needed @@ -1821,7 +1832,7 @@ func TestAstInterpret0(t *testing.T) { }, } // some of this might happen *after* interpolate in SetScope or Unify... - if err := ast.Init(data); err != nil { + if err := xast.Init(data); err != nil { t.Errorf("test #%d: FAIL", index) t.Errorf("test #%d: could not init and validate AST: %+v", index, err) return @@ -1831,7 +1842,7 @@ func TestAstInterpret0(t *testing.T) { // perform type unification, run the function graph engine, and // only gives you limited results... don't expect normal code to // run and produce meaningful things in this test... - graph, err := interpret.Interpret(ast) + graph, err := interpret.Interpret(xast) if !fail && err != nil { t.Errorf("test #%d: interpret failed with: %+v", index, err) diff --git a/lang/lang.go b/lang/lang.go index fececbd6..0adaf6dd 100644 --- a/lang/lang.go +++ b/lang/lang.go @@ -23,25 +23,21 @@ import ( "sync" "github.com/purpleidea/mgmt/engine" + "github.com/purpleidea/mgmt/lang/ast" "github.com/purpleidea/mgmt/lang/funcs" _ "github.com/purpleidea/mgmt/lang/funcs/core" // import so the funcs register "github.com/purpleidea/mgmt/lang/funcs/vars" "github.com/purpleidea/mgmt/lang/inputs" "github.com/purpleidea/mgmt/lang/interfaces" + "github.com/purpleidea/mgmt/lang/interpolate" "github.com/purpleidea/mgmt/lang/interpret" + "github.com/purpleidea/mgmt/lang/parser" "github.com/purpleidea/mgmt/lang/unification" "github.com/purpleidea/mgmt/pgraph" "github.com/purpleidea/mgmt/util" "github.com/purpleidea/mgmt/util/errwrap" ) -const ( - // make these available internally without requiring the import - operatorFuncName = funcs.OperatorFuncName - historyFuncName = funcs.HistoryFuncName - containsFuncName = funcs.ContainsFuncName -) - // Lang is the main language lexer/parser object. type Lang struct { Fs engine.Fs // connected fs where input dir or metadata exists @@ -117,12 +113,12 @@ func (obj *Lang) Init() error { // run the lexer/parser and build an AST obj.Logf("lexing/parsing...") // this reads an io.Reader, which might be a stream of multiple files... - ast, err := LexParse(reader) + xast, err := parser.LexParse(reader) if err != nil { return errwrap.Wrapf(err, "could not generate AST") } if obj.Debug { - obj.Logf("behold, the AST: %+v", ast) + obj.Logf("behold, the AST: %+v", xast) } importGraph, err := pgraph.NewGraph("importGraph") @@ -147,7 +143,11 @@ func (obj *Lang) Init() error { Metadata: output.Metadata, Modules: "/" + interfaces.ModuleDirectory, // do not set from env for a deploy! + LexParser: parser.LexParse, + Downloader: nil, // XXX: is this used here? + StrInterpolater: interpolate.InterpolateStr, //World: obj.World, // TODO: do we need this? + Prefix: obj.Prefix, Debug: obj.Debug, Logf: func(format string, v ...interface{}) { @@ -156,26 +156,26 @@ func (obj *Lang) Init() error { }, } // some of this might happen *after* interpolate in SetScope or Unify... - if err := ast.Init(data); err != nil { + if err := xast.Init(data); err != nil { return errwrap.Wrapf(err, "could not init and validate AST") } obj.Logf("interpolating...") // interpolate strings and other expansionable nodes in AST - interpolated, err := ast.Interpolate() + interpolated, err := xast.Interpolate() if err != nil { return errwrap.Wrapf(err, "could not interpolate AST") } obj.ast = interpolated variables := map[string]interfaces.Expr{ - "purpleidea": &ExprStr{V: "hello world!"}, // james says hi + "purpleidea": &ast.ExprStr{V: "hello world!"}, // james says hi // TODO: change to a func when we can change hostname dynamically! - "hostname": &ExprStr{V: obj.Hostname}, + "hostname": &ast.ExprStr{V: obj.Hostname}, } - consts := VarPrefixToVariablesScope(vars.ConstNamespace) // strips prefix! - addback := vars.ConstNamespace + interfaces.ModuleSep // add it back... - variables, err = MergeExprMaps(variables, consts, addback) + consts := ast.VarPrefixToVariablesScope(vars.ConstNamespace) // strips prefix! + addback := vars.ConstNamespace + interfaces.ModuleSep // add it back... + variables, err = ast.MergeExprMaps(variables, consts, addback) if err != nil { return errwrap.Wrapf(err, "couldn't merge in consts") } @@ -184,7 +184,7 @@ func (obj *Lang) Init() error { scope := &interfaces.Scope{ Variables: variables, // all the built-in top-level, core functions enter here... - Functions: FuncPrefixToFunctionsScope(""), // runs funcs.LookupPrefix + Functions: ast.FuncPrefixToFunctionsScope(""), // runs funcs.LookupPrefix } obj.Logf("building scope...") diff --git a/lang/.gitignore b/lang/parser/.gitignore similarity index 100% rename from lang/.gitignore rename to lang/parser/.gitignore diff --git a/lang/lexer.nex b/lang/parser/lexer.nex similarity index 99% rename from lang/lexer.nex rename to lang/parser/lexer.nex index b19d568d..ed737e76 100644 --- a/lang/lexer.nex +++ b/lang/parser/lexer.nex @@ -402,7 +402,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -package lang +package parser import ( "fmt" diff --git a/lang/lexparse.go b/lang/parser/lexparse.go similarity index 59% rename from lang/lexparse.go rename to lang/parser/lexparse.go index 010a16b7..41a2d5e5 100644 --- a/lang/lexparse.go +++ b/lang/parser/lexparse.go @@ -15,13 +15,12 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -package lang // TODO: move this into a sub package of lang/$name? +package parser import ( "bufio" "fmt" "io" - "net/url" "path" "sort" "strings" @@ -32,17 +31,6 @@ import ( "github.com/purpleidea/mgmt/util/errwrap" ) -const ( - // ModuleMagicPrefix is the prefix which, if found as a prefix to the - // last token in an import path, will be removed silently if there are - // remaining characters following the name. If this is the empty string - // then it will be ignored. - ModuleMagicPrefix = "mgmt-" - - // CoreDir is the directory prefix where core bindata mcl code is added. - CoreDir = "core/" -) - // These constants represent the different possible lexer/parser errors. const ( ErrLexerUnrecognized = interfaces.Error("unrecognized") @@ -236,144 +224,3 @@ func DirectoryReader(fs engine.Fs, dir string) (io.Reader, map[uint64]string, er return io.MultiReader(readers...), offsets, nil } - -// ParseImportName parses an import name and returns the default namespace name -// that should be used with it. For example, if the import name was: -// "git://example.com/purpleidea/Module-Name", this might return an alias of -// "module_name". It also returns a bunch of other data about the parsed import. -// TODO: check for invalid or unwanted special characters -func ParseImportName(name string) (*interfaces.ImportData, error) { - magicPrefix := ModuleMagicPrefix - if name == "" { - return nil, fmt.Errorf("empty name") - } - if strings.HasPrefix(name, "/") { - return nil, fmt.Errorf("absolute paths are not allowed") - } - - u, err := url.Parse(name) - if err != nil { - return nil, errwrap.Wrapf(err, "name is not a valid url") - } - if u.Path == "" { - return nil, fmt.Errorf("empty path") - } - p := u.Path - // catch bad paths like: git:////home/james/ (note the quad slash!) - // don't penalize if we have a dir with a trailing slash at the end - if s := path.Clean(u.Path); u.Path != s && u.Path != s+"/" { - // TODO: are there any cases where this is not what we want? - return nil, fmt.Errorf("dirty path, cleaned it's: `%s`", s) - } - - for strings.HasSuffix(p, "/") { // remove trailing slashes - p = p[:len(p)-len("/")] - } - - split := strings.Split(p, "/") // take last chunk if slash separated - s := split[0] - if len(split) > 1 { - s = split[len(split)-1] // pick last chunk - } - - // TODO: should we treat a special name: "purpleidea/mgmt-foo" as "foo"? - if magicPrefix != "" && strings.HasPrefix(s, magicPrefix) && len(s) > len(magicPrefix) { - s = s[len(magicPrefix):] - } - - s = strings.Replace(s, "-", "_", -1) // XXX: allow underscores in IDENTIFIER - if strings.HasPrefix(s, "_") || strings.HasSuffix(s, "_") { - return nil, fmt.Errorf("name can't begin or end with dash or underscore") - } - alias := strings.ToLower(s) - - // if this is a local import, it's a straight directory path - // if it's an fqdn import, it should contain a metadata file - - // if there's no protocol prefix, then this must be a local path - isLocal := u.Scheme == "" - // if it has a trailing slash or .mcl extension it's not a system import - isSystem := isLocal && !strings.HasSuffix(u.Path, "/") && !strings.HasSuffix(u.Path, interfaces.DotFileNameExtension) - // is it a local file? - isFile := !isSystem && isLocal && strings.HasSuffix(u.Path, interfaces.DotFileNameExtension) - xpath := u.Path // magic path - if isSystem { - xpath = "" - } - if !isLocal { - host := u.Host // host or host:port - split := strings.Split(host, ":") - if l := len(split); l == 1 || l == 2 { - host = split[0] - } else { - return nil, fmt.Errorf("incorrect number of colons (%d) in hostname", l) - } - xpath = path.Join(host, xpath) - } - if !isLocal && !strings.HasSuffix(xpath, "/") { - xpath = xpath + "/" - } - // we're a git repo with a local path instead of an fqdn over http! - // this still counts as isLocal == false, since it's still a remote - if u.Host == "" && strings.HasPrefix(u.Path, "/") { - xpath = strings.TrimPrefix(xpath, "/") // make it a relative dir - } - if strings.HasPrefix(xpath, "/") { // safety check (programming error?) - return nil, fmt.Errorf("can't parse strange import") - } - - // build a url to clone from if we're not local... - // TODO: consider adding some logic that is similar to the logic in: - // https://github.com/golang/go/blob/054640b54df68789d9df0e50575d21d9dbffe99f/src/cmd/go/internal/get/vcs.go#L972 - // so that we can more correctly figure out the correct url to clone... - xurl := "" - if !isLocal { - u.Fragment = "" - // TODO: maybe look for ?sha1=... or ?tag=... to pick a real ref - u.RawQuery = "" - u.ForceQuery = false - xurl = u.String() - } - - // if u.Path is local file like: foo/server.mcl alias should be "server" - // we should trim the alias to remove the .mcl (the dir is already gone) - if isFile && strings.HasSuffix(alias, interfaces.DotFileNameExtension) { - alias = strings.TrimSuffix(alias, interfaces.DotFileNameExtension) - } - - return &interfaces.ImportData{ - Name: name, // save the original value here - Alias: alias, - IsSystem: isSystem, - IsLocal: isLocal, - IsFile: isFile, - Path: xpath, - URL: xurl, - }, nil -} - -// CollectFiles collects all the files used in the AST. You will see more files -// based on how many compiling steps have run. In general, this is useful for -// collecting all the files needed to store in our file system for a deploy. -func CollectFiles(ast interfaces.Stmt) ([]string, error) { - // collect the list of files - fileList := []string{} - fn := func(node interfaces.Node) error { - // redundant check for example purposes - stmt, ok := node.(interfaces.Stmt) - if !ok { - return nil - } - prog, ok := stmt.(*StmtProg) - if !ok { - return nil - } - // collect into global - fileList = append(fileList, prog.importFiles...) - return nil - } - if err := ast.Apply(fn); err != nil { - return nil, errwrap.Wrapf(err, "can't retrieve paths") - } - return fileList, nil -} diff --git a/lang/lexparse_test.go b/lang/parser/lexparse_test.go similarity index 82% rename from lang/lexparse_test.go rename to lang/parser/lexparse_test.go index 620dba57..c09a3043 100644 --- a/lang/lexparse_test.go +++ b/lang/parser/lexparse_test.go @@ -17,7 +17,7 @@ // +build !root -package lang +package parser import ( "fmt" @@ -26,8 +26,11 @@ import ( "strings" "testing" + "github.com/purpleidea/mgmt/lang/ast" + "github.com/purpleidea/mgmt/lang/funcs" "github.com/purpleidea/mgmt/lang/interfaces" "github.com/purpleidea/mgmt/lang/types" + langutil "github.com/purpleidea/mgmt/lang/util" "github.com/purpleidea/mgmt/util" "github.com/davecgh/go-spew/spew" @@ -56,11 +59,11 @@ func TestLexParse0(t *testing.T) { name: "simple assignment", code: `$rewsna = -42`, fail: false, - exp: &StmtProg{ + exp: &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtBind{ + &ast.StmtBind{ Ident: "rewsna", - Value: &ExprInt{ + Value: &ast.ExprInt{ V: -42, }, }, @@ -359,11 +362,11 @@ func TestLexParse0(t *testing.T) { }) } { - exp := &StmtProg{ + exp := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtBind{ + &ast.StmtBind{ Ident: "x1", - Value: &ExprCall{ + Value: &ast.ExprCall{ Name: "foo1", Args: []interfaces.Expr{}, }, @@ -380,17 +383,17 @@ func TestLexParse0(t *testing.T) { }) } { - exp := &StmtProg{ + exp := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtBind{ + &ast.StmtBind{ Ident: "x1", - Value: &ExprCall{ + Value: &ast.ExprCall{ Name: "foo1", Args: []interfaces.Expr{ - &ExprInt{ + &ast.ExprInt{ V: 13, }, - &ExprStr{ + &ast.ExprStr{ V: "hello", }, }, @@ -408,11 +411,11 @@ func TestLexParse0(t *testing.T) { }) } { - exp := &StmtProg{ + exp := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtBind{ + &ast.StmtBind{ Ident: "x1", - Value: &ExprCall{ + Value: &ast.ExprCall{ Name: "pkg.foo1", Args: []interfaces.Expr{}, }, @@ -429,17 +432,17 @@ func TestLexParse0(t *testing.T) { }) } { - exp := &StmtProg{ + exp := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtBind{ + &ast.StmtBind{ Ident: "x1", - Value: &ExprCall{ + Value: &ast.ExprCall{ Name: "pkg.foo1", Args: []interfaces.Expr{ - &ExprBool{ + &ast.ExprBool{ V: true, }, - &ExprStr{ + &ast.ExprStr{ V: "hello", }, }, @@ -493,11 +496,11 @@ func TestLexParse0(t *testing.T) { }) } { - exp := &StmtProg{ + exp := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtBind{ + &ast.StmtBind{ Ident: "x1", - Value: &ExprVar{ + Value: &ast.ExprVar{ Name: "pkg.foo1", }, }, @@ -513,11 +516,11 @@ func TestLexParse0(t *testing.T) { }) } { - exp := &StmtProg{ + exp := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtBind{ + &ast.StmtBind{ Ident: "x1", - Value: &ExprVar{ + Value: &ast.ExprVar{ Name: "pkg.foo1.bar", }, }, @@ -569,26 +572,26 @@ func TestLexParse0(t *testing.T) { }) } { - exp := &StmtProg{ + exp := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtRes{ + &ast.StmtRes{ Kind: "test", - Name: &ExprStr{ + Name: &ast.ExprStr{ V: "t1", }, - Contents: []StmtResContents{ - &StmtResField{ + Contents: []ast.StmtResContents{ + &ast.StmtResField{ Field: "int64ptr", - Value: &ExprCall{ - Name: operatorFuncName, + Value: &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ + &ast.ExprStr{ V: "+", }, - &ExprInt{ + &ast.ExprInt{ V: 13, }, - &ExprInt{ + &ast.ExprInt{ V: 42, }, }, @@ -610,37 +613,37 @@ func TestLexParse0(t *testing.T) { }) } { - exp := &StmtProg{ + exp := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtRes{ + &ast.StmtRes{ Kind: "test", - Name: &ExprStr{ + Name: &ast.ExprStr{ V: "t1", }, - Contents: []StmtResContents{ - &StmtResField{ + Contents: []ast.StmtResContents{ + &ast.StmtResField{ Field: "float32", - Value: &ExprCall{ - Name: operatorFuncName, + Value: &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ + &ast.ExprStr{ V: "+", }, - &ExprCall{ - Name: operatorFuncName, + &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ + &ast.ExprStr{ V: "+", }, - &ExprFloat{ + &ast.ExprFloat{ V: -25.38789, }, - &ExprFloat{ + &ast.ExprFloat{ V: 32.6, }, }, }, - &ExprFloat{ + &ast.ExprFloat{ V: 13.7, }, }, @@ -662,35 +665,35 @@ func TestLexParse0(t *testing.T) { }) } { - exp := &StmtProg{ + exp := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtRes{ + &ast.StmtRes{ Kind: "test", - Name: &ExprStr{ + Name: &ast.ExprStr{ V: "t1", }, - Contents: []StmtResContents{ - &StmtResField{ + Contents: []ast.StmtResContents{ + &ast.StmtResField{ Field: "int64ptr", - Value: &ExprCall{ - Name: operatorFuncName, + Value: &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ + &ast.ExprStr{ V: "+", }, - &ExprInt{ + &ast.ExprInt{ V: 4, }, - &ExprCall{ - Name: operatorFuncName, + &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ + &ast.ExprStr{ V: "*", }, - &ExprInt{ + &ast.ExprInt{ V: 3, }, - &ExprInt{ + &ast.ExprInt{ V: 12, }, }, @@ -714,37 +717,37 @@ func TestLexParse0(t *testing.T) { }) } { - exp := &StmtProg{ + exp := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtRes{ + &ast.StmtRes{ Kind: "test", - Name: &ExprStr{ + Name: &ast.ExprStr{ V: "t1", }, - Contents: []StmtResContents{ - &StmtResField{ + Contents: []ast.StmtResContents{ + &ast.StmtResField{ Field: "int64ptr", - Value: &ExprCall{ - Name: operatorFuncName, + Value: &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ + &ast.ExprStr{ V: "+", }, - &ExprCall{ - Name: operatorFuncName, + &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ + &ast.ExprStr{ V: "*", }, - &ExprInt{ + &ast.ExprInt{ V: 3, }, - &ExprInt{ + &ast.ExprInt{ V: 12, }, }, }, - &ExprInt{ + &ast.ExprInt{ V: 4, }, }, @@ -766,35 +769,35 @@ func TestLexParse0(t *testing.T) { }) } { - exp := &StmtProg{ + exp := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtRes{ + &ast.StmtRes{ Kind: "test", - Name: &ExprStr{ + Name: &ast.ExprStr{ V: "t1", }, - Contents: []StmtResContents{ - &StmtResField{ + Contents: []ast.StmtResContents{ + &ast.StmtResField{ Field: "int64ptr", - Value: &ExprCall{ - Name: operatorFuncName, + Value: &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ + &ast.ExprStr{ V: "*", }, - &ExprInt{ + &ast.ExprInt{ V: 3, }, - &ExprCall{ - Name: operatorFuncName, + &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ + &ast.ExprStr{ V: "+", }, - &ExprInt{ + &ast.ExprInt{ V: 12, }, - &ExprInt{ + &ast.ExprInt{ V: 4, }, }, @@ -818,37 +821,37 @@ func TestLexParse0(t *testing.T) { }) } { - exp := &StmtProg{ + exp := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtRes{ + &ast.StmtRes{ Kind: "test", - Name: &ExprStr{ + Name: &ast.ExprStr{ V: "t1", }, - Contents: []StmtResContents{ - &StmtResField{ + Contents: []ast.StmtResContents{ + &ast.StmtResField{ Field: "boolptr", - Value: &ExprCall{ - Name: operatorFuncName, + Value: &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ + &ast.ExprStr{ V: ">", }, - &ExprCall{ - Name: operatorFuncName, + &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ + &ast.ExprStr{ V: "+", }, - &ExprInt{ + &ast.ExprInt{ V: 3, }, - &ExprInt{ + &ast.ExprInt{ V: 4, }, }, }, - &ExprInt{ + &ast.ExprInt{ V: 5, }, }, @@ -870,35 +873,35 @@ func TestLexParse0(t *testing.T) { }) } { - exp := &StmtProg{ + exp := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtRes{ + &ast.StmtRes{ Kind: "test", - Name: &ExprStr{ + Name: &ast.ExprStr{ V: "t1", }, - Contents: []StmtResContents{ - &StmtResField{ + Contents: []ast.StmtResContents{ + &ast.StmtResField{ Field: "boolptr", - Value: &ExprCall{ - Name: operatorFuncName, + Value: &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ + &ast.ExprStr{ V: ">", }, - &ExprInt{ + &ast.ExprInt{ V: 3, }, - &ExprCall{ - Name: operatorFuncName, + &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ + &ast.ExprStr{ V: "+", }, - &ExprInt{ + &ast.ExprInt{ V: 4, }, - &ExprInt{ + &ast.ExprInt{ V: 5, }, }, @@ -922,34 +925,34 @@ func TestLexParse0(t *testing.T) { }) } { - exp := &StmtProg{ + exp := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtRes{ + &ast.StmtRes{ Kind: "test", - Name: &ExprStr{ + Name: &ast.ExprStr{ V: "t1", }, - Contents: []StmtResContents{ - &StmtResField{ + Contents: []ast.StmtResContents{ + &ast.StmtResField{ Field: "boolptr", - Value: &ExprCall{ - Name: operatorFuncName, + Value: &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ + &ast.ExprStr{ V: ">", }, - &ExprCall{ - Name: operatorFuncName, + &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ + &ast.ExprStr{ V: "!", }, - &ExprInt{ + &ast.ExprInt{ V: 3, }, }, }, - &ExprInt{ + &ast.ExprInt{ V: 4, }, }, @@ -971,37 +974,37 @@ func TestLexParse0(t *testing.T) { }) } { - exp := &StmtProg{ + exp := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtRes{ + &ast.StmtRes{ Kind: "test", - Name: &ExprStr{ + Name: &ast.ExprStr{ V: "t1", }, - Contents: []StmtResContents{ - &StmtResField{ + Contents: []ast.StmtResContents{ + &ast.StmtResField{ Field: "boolptr", - Value: &ExprCall{ - Name: operatorFuncName, + Value: &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ + &ast.ExprStr{ V: "&&", }, - &ExprCall{ - Name: operatorFuncName, + &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ + &ast.ExprStr{ V: "<", }, - &ExprInt{ + &ast.ExprInt{ V: 7, }, - &ExprInt{ + &ast.ExprInt{ V: 4, }, }, }, - &ExprBool{ + &ast.ExprBool{ V: true, }, }, @@ -1023,48 +1026,48 @@ func TestLexParse0(t *testing.T) { }) } { - exp := &StmtProg{ + exp := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtRes{ + &ast.StmtRes{ Kind: "test", - Name: &ExprStr{ + Name: &ast.ExprStr{ V: "t1", }, - Contents: []StmtResContents{ - &StmtResField{ + Contents: []ast.StmtResContents{ + &ast.StmtResField{ Field: "int64ptr", - Value: &ExprInt{ + Value: &ast.ExprInt{ V: 42, }, }, }, }, - &StmtRes{ + &ast.StmtRes{ Kind: "test", - Name: &ExprStr{ + Name: &ast.ExprStr{ V: "t2", }, - Contents: []StmtResContents{ - &StmtResField{ + Contents: []ast.StmtResContents{ + &ast.StmtResField{ Field: "int64ptr", - Value: &ExprInt{ + Value: &ast.ExprInt{ V: 13, }, }, }, }, - &StmtEdge{ - EdgeHalfList: []*StmtEdgeHalf{ + &ast.StmtEdge{ + EdgeHalfList: []*ast.StmtEdgeHalf{ { Kind: "test", - Name: &ExprStr{ + Name: &ast.ExprStr{ V: "t1", }, SendRecv: "foosend", }, { Kind: "test", - Name: &ExprStr{ + Name: &ast.ExprStr{ V: "t2", }, SendRecv: "barrecv", @@ -1090,75 +1093,75 @@ func TestLexParse0(t *testing.T) { }) } { - exp := &StmtProg{ + exp := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtRes{ + &ast.StmtRes{ Kind: "test", - Name: &ExprStr{ + Name: &ast.ExprStr{ V: "t1", }, - Contents: []StmtResContents{ - &StmtResMeta{ + Contents: []ast.StmtResContents{ + &ast.StmtResMeta{ Property: "noop", - MetaExpr: &ExprBool{ + MetaExpr: &ast.ExprBool{ V: true, }, }, - &StmtResMeta{ + &ast.StmtResMeta{ Property: "delay", - MetaExpr: &ExprInt{ + MetaExpr: &ast.ExprInt{ V: 42, }, - Condition: &ExprBool{ + Condition: &ast.ExprBool{ V: true, }, }, }, }, - &StmtRes{ + &ast.StmtRes{ Kind: "test", - Name: &ExprStr{ + Name: &ast.ExprStr{ V: "t2", }, - Contents: []StmtResContents{ - &StmtResMeta{ + Contents: []ast.StmtResContents{ + &ast.StmtResMeta{ Property: "limit", - MetaExpr: &ExprFloat{ + MetaExpr: &ast.ExprFloat{ V: 0.45, }, }, - &StmtResMeta{ + &ast.StmtResMeta{ Property: "burst", - MetaExpr: &ExprInt{ + MetaExpr: &ast.ExprInt{ V: 4, }, }, }, }, - &StmtRes{ + &ast.StmtRes{ Kind: "test", - Name: &ExprStr{ + Name: &ast.ExprStr{ V: "t3", }, - Contents: []StmtResContents{ - &StmtResMeta{ + Contents: []ast.StmtResContents{ + &ast.StmtResMeta{ Property: "noop", - MetaExpr: &ExprBool{ + MetaExpr: &ast.ExprBool{ V: true, }, }, - &StmtResMeta{ + &ast.StmtResMeta{ Property: "meta", - MetaExpr: &ExprStruct{ - Fields: []*ExprStructField{ - {Name: "poll", Value: &ExprInt{V: 5}}, - {Name: "retry", Value: &ExprInt{V: 3}}, + MetaExpr: &ast.ExprStruct{ + Fields: []*ast.ExprStructField{ + {Name: "poll", Value: &ast.ExprInt{V: 5}}, + {Name: "retry", Value: &ast.ExprInt{V: 3}}, { Name: "sema", - Value: &ExprList{ + Value: &ast.ExprList{ Elements: []interfaces.Expr{ - &ExprStr{V: "foo:1"}, - &ExprStr{V: "bar:3"}, + &ast.ExprStr{V: "foo:1"}, + &ast.ExprStr{V: "bar:3"}, }, }, }, @@ -1217,21 +1220,21 @@ func TestLexParse0(t *testing.T) { }) } { - exp := &StmtProg{ + exp := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtClass{ + &ast.StmtClass{ Name: "c1", - Body: &StmtProg{ + Body: &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtRes{ + &ast.StmtRes{ Kind: "test", - Name: &ExprStr{ + Name: &ast.ExprStr{ V: "t1", }, - Contents: []StmtResContents{ - &StmtResField{ + Contents: []ast.StmtResContents{ + &ast.StmtResField{ Field: "stringptr", - Value: &ExprStr{ + Value: &ast.ExprStr{ V: "hello", }, }, @@ -1240,7 +1243,7 @@ func TestLexParse0(t *testing.T) { }, }, }, - &StmtInclude{ + &ast.StmtInclude{ Name: "c1", }, }, @@ -1260,21 +1263,21 @@ func TestLexParse0(t *testing.T) { }) } { - exp := &StmtProg{ + exp := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtClass{ + &ast.StmtClass{ Name: "c1", - Body: &StmtProg{ + Body: &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtRes{ + &ast.StmtRes{ Kind: "test", - Name: &ExprStr{ + Name: &ast.ExprStr{ V: "t1", }, - Contents: []StmtResContents{ - &StmtResField{ + Contents: []ast.StmtResContents{ + &ast.StmtResField{ Field: "stringptr", - Value: &ExprStr{ + Value: &ast.ExprStr{ V: "hello", }, }, @@ -1283,7 +1286,7 @@ func TestLexParse0(t *testing.T) { }, }, }, - &StmtInclude{ + &ast.StmtInclude{ Name: "pkg.c1", }, }, @@ -1305,21 +1308,21 @@ func TestLexParse0(t *testing.T) { }) } { - exp := &StmtProg{ + exp := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtClass{ + &ast.StmtClass{ Name: "c1", - Body: &StmtProg{ + Body: &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtRes{ + &ast.StmtRes{ Kind: "test", - Name: &ExprStr{ + Name: &ast.ExprStr{ V: "t1", }, - Contents: []StmtResContents{ - &StmtResField{ + Contents: []ast.StmtResContents{ + &ast.StmtResField{ Field: "stringptr", - Value: &ExprStr{ + Value: &ast.ExprStr{ V: "hello", }, }, @@ -1328,7 +1331,7 @@ func TestLexParse0(t *testing.T) { }, }, }, - &StmtInclude{ + &ast.StmtInclude{ Name: "pkg.ns.c1", }, }, @@ -1412,23 +1415,23 @@ func TestLexParse0(t *testing.T) { }) } { - exp := &StmtProg{ + exp := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtClass{ + &ast.StmtClass{ Name: "x", Args: []*interfaces.Arg{}, - Body: &StmtProg{ + Body: &ast.StmtProg{ Body: []interfaces.Stmt{}, }, }, - &StmtClass{ + &ast.StmtClass{ Name: "y1", Args: []*interfaces.Arg{}, - Body: &StmtProg{ + Body: &ast.StmtProg{ Body: []interfaces.Stmt{}, }, }, - &StmtInclude{ + &ast.StmtInclude{ Name: "z", Args: nil, }, @@ -1458,9 +1461,9 @@ func TestLexParse0(t *testing.T) { }) } { - exp := &StmtProg{ + exp := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtClass{ + &ast.StmtClass{ Name: "c1", Args: []*interfaces.Arg{ { @@ -1472,17 +1475,17 @@ func TestLexParse0(t *testing.T) { //Type: &types.Type{}, }, }, - Body: &StmtProg{ + Body: &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtRes{ + &ast.StmtRes{ Kind: "test", - Name: &ExprVar{ + Name: &ast.ExprVar{ Name: "a", }, - Contents: []StmtResContents{ - &StmtResField{ + Contents: []ast.StmtResContents{ + &ast.StmtResField{ Field: "stringptr", - Value: &ExprVar{ + Value: &ast.ExprVar{ Name: "b", }, }, @@ -1491,24 +1494,24 @@ func TestLexParse0(t *testing.T) { }, }, }, - &StmtInclude{ + &ast.StmtInclude{ Name: "c1", Args: []interfaces.Expr{ - &ExprStr{ + &ast.ExprStr{ V: "t1", }, - &ExprStr{ + &ast.ExprStr{ V: "hello", }, }, }, - &StmtInclude{ + &ast.StmtInclude{ Name: "c1", Args: []interfaces.Expr{ - &ExprStr{ + &ast.ExprStr{ V: "t2", }, - &ExprStr{ + &ast.ExprStr{ V: "world", }, }, @@ -1531,9 +1534,9 @@ func TestLexParse0(t *testing.T) { }) } { - exp := &StmtProg{ + exp := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtClass{ + &ast.StmtClass{ Name: "c1", Args: []*interfaces.Arg{ { @@ -1545,17 +1548,17 @@ func TestLexParse0(t *testing.T) { //Type: &types.Type{}, }, }, - Body: &StmtProg{ + Body: &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtRes{ + &ast.StmtRes{ Kind: "test", - Name: &ExprVar{ + Name: &ast.ExprVar{ Name: "a", }, - Contents: []StmtResContents{ - &StmtResField{ + Contents: []ast.StmtResContents{ + &ast.StmtResField{ Field: "stringptr", - Value: &ExprVar{ + Value: &ast.ExprVar{ Name: "b", }, }, @@ -1564,24 +1567,24 @@ func TestLexParse0(t *testing.T) { }, }, }, - &StmtInclude{ + &ast.StmtInclude{ Name: "c1", Args: []interfaces.Expr{ - &ExprStr{ + &ast.ExprStr{ V: "t1", }, - &ExprStr{ + &ast.ExprStr{ V: "hello", }, }, }, - &StmtInclude{ + &ast.StmtInclude{ Name: "c1", Args: []interfaces.Expr{ - &ExprStr{ + &ast.ExprStr{ V: "t2", }, - &ExprStr{ + &ast.ExprStr{ V: "world", }, }, @@ -1604,9 +1607,9 @@ func TestLexParse0(t *testing.T) { }) } { - exp := &StmtProg{ + exp := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtImport{ + &ast.StmtImport{ Name: "foo1", Alias: "", }, @@ -1622,9 +1625,9 @@ func TestLexParse0(t *testing.T) { }) } { - exp := &StmtProg{ + exp := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtImport{ + &ast.StmtImport{ Name: "foo1", Alias: "bar", }, @@ -1640,17 +1643,17 @@ func TestLexParse0(t *testing.T) { }) } { - exp := &StmtProg{ + exp := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtImport{ + &ast.StmtImport{ Name: "foo1", Alias: "", }, - &StmtImport{ + &ast.StmtImport{ Name: "foo2", Alias: "bar", }, - &StmtImport{ + &ast.StmtImport{ Name: "foo3", Alias: "", }, @@ -1668,9 +1671,9 @@ func TestLexParse0(t *testing.T) { }) } { - exp := &StmtProg{ + exp := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtImport{ + &ast.StmtImport{ Name: "foo1", Alias: "*", }, @@ -1686,24 +1689,24 @@ func TestLexParse0(t *testing.T) { }) } { - exp := &StmtProg{ + exp := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtClass{ + &ast.StmtClass{ Name: "c1", - Body: &StmtProg{ + Body: &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtImport{ + &ast.StmtImport{ Name: "foo", Alias: "bar", }, - &StmtImport{ + &ast.StmtImport{ Name: "baz", Alias: "", }, }, }, }, - &StmtInclude{ + &ast.StmtInclude{ Name: "c1", }, }, @@ -1722,13 +1725,13 @@ func TestLexParse0(t *testing.T) { }) } { - exp := &StmtProg{ + exp := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtFunc{ + &ast.StmtFunc{ Name: "f1", - Func: &ExprFunc{ + Func: &ast.ExprFunc{ Args: []*interfaces.Arg{}, - Body: &ExprInt{ + Body: &ast.ExprInt{ V: 42, }, }, @@ -1747,19 +1750,19 @@ func TestLexParse0(t *testing.T) { }) } { - fn := &ExprFunc{ + fn := &ast.ExprFunc{ Args: []*interfaces.Arg{}, Return: types.TypeInt, - Body: &ExprCall{ - Name: operatorFuncName, + Body: &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ + &ast.ExprStr{ V: "+", }, - &ExprInt{ + &ast.ExprInt{ V: 13, }, - &ExprInt{ + &ast.ExprInt{ V: 42, }, }, @@ -1769,9 +1772,9 @@ func TestLexParse0(t *testing.T) { if err := fn.SetType(types.NewType("func() int")); err != nil { t.Fatal("could not build type") } - exp := &StmtProg{ + exp := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtFunc{ + &ast.StmtFunc{ Name: "f2", Func: fn, }, @@ -1789,7 +1792,7 @@ func TestLexParse0(t *testing.T) { }) } { - fn := &ExprFunc{ + fn := &ast.ExprFunc{ Args: []*interfaces.Arg{ { Name: "a", @@ -1801,16 +1804,16 @@ func TestLexParse0(t *testing.T) { }, }, Return: types.TypeInt, - Body: &ExprCall{ - Name: operatorFuncName, + Body: &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ + &ast.ExprStr{ V: "+", }, - &ExprVar{ + &ast.ExprVar{ Name: "a", }, - &ExprVar{ + &ast.ExprVar{ Name: "b", }, }, @@ -1820,9 +1823,9 @@ func TestLexParse0(t *testing.T) { //if err := fn.SetType(types.NewType("func() int")); err != nil { // t.Fatal("could not build type") //} - exp := &StmtProg{ + exp := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtFunc{ + &ast.StmtFunc{ Name: "f3", Func: fn, }, @@ -1840,7 +1843,7 @@ func TestLexParse0(t *testing.T) { }) } { - fn := &ExprFunc{ + fn := &ast.ExprFunc{ Args: []*interfaces.Arg{ { Name: "x", @@ -1848,16 +1851,16 @@ func TestLexParse0(t *testing.T) { }, }, Return: types.TypeStr, - Body: &ExprCall{ - Name: operatorFuncName, + Body: &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ + &ast.ExprStr{ V: "+", }, - &ExprStr{ + &ast.ExprStr{ V: "hello", }, - &ExprVar{ + &ast.ExprVar{ Name: "x", }, }, @@ -1866,9 +1869,9 @@ func TestLexParse0(t *testing.T) { if err := fn.SetType(types.NewType("func(x str) str")); err != nil { t.Fatal("could not build type") } - exp := &StmtProg{ + exp := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtFunc{ + &ast.StmtFunc{ Name: "f4", Func: fn, }, @@ -1887,15 +1890,15 @@ func TestLexParse0(t *testing.T) { } { - fn := &ExprFunc{ + fn := &ast.ExprFunc{ Args: []*interfaces.Arg{}, - Body: &ExprInt{ + Body: &ast.ExprInt{ V: 42, }, } - exp := &StmtProg{ + exp := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtBind{ + &ast.StmtBind{ Ident: "fn", Value: fn, }, @@ -1914,7 +1917,7 @@ func TestLexParse0(t *testing.T) { }) } { - fn := &ExprFunc{ + fn := &ast.ExprFunc{ Args: []*interfaces.Arg{ { Name: "x", @@ -1922,16 +1925,16 @@ func TestLexParse0(t *testing.T) { }, }, Return: types.TypeStr, - Body: &ExprCall{ - Name: operatorFuncName, + Body: &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ + &ast.ExprStr{ V: "+", }, - &ExprStr{ + &ast.ExprStr{ V: "hello", }, - &ExprVar{ + &ast.ExprVar{ Name: "x", }, }, @@ -1940,9 +1943,9 @@ func TestLexParse0(t *testing.T) { if err := fn.SetType(types.NewType("func(x str) str")); err != nil { t.Fatal("could not build type") } - exp := &StmtProg{ + exp := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtBind{ + &ast.StmtBind{ Ident: "fn", Value: fn, }, @@ -1961,7 +1964,7 @@ func TestLexParse0(t *testing.T) { }) } { - fn := &ExprFunc{ + fn := &ast.ExprFunc{ Args: []*interfaces.Arg{ { Name: "x", @@ -1969,16 +1972,16 @@ func TestLexParse0(t *testing.T) { }, }, Return: types.TypeStr, - Body: &ExprCall{ - Name: operatorFuncName, + Body: &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ + &ast.ExprStr{ V: "+", }, - &ExprStr{ + &ast.ExprStr{ V: "hello", }, - &ExprVar{ + &ast.ExprVar{ Name: "x", }, }, @@ -1987,18 +1990,18 @@ func TestLexParse0(t *testing.T) { if err := fn.SetType(types.NewType("func(x str) str")); err != nil { t.Fatal("could not build type") } - exp := &StmtProg{ + exp := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtBind{ + &ast.StmtBind{ Ident: "fn", Value: fn, }, - &StmtBind{ + &ast.StmtBind{ Ident: "foo", - Value: &ExprCall{ + Value: &ast.ExprCall{ Name: "fn", Args: []interfaces.Expr{ - &ExprStr{ + &ast.ExprStr{ V: "world", }, }, @@ -2021,33 +2024,33 @@ func TestLexParse0(t *testing.T) { }) } { - exp := &StmtProg{ + exp := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtFunc{ + &ast.StmtFunc{ Name: "funcgen", // This is the outer function... - Func: &ExprFunc{ + Func: &ast.ExprFunc{ Args: []*interfaces.Arg{}, // This is the inner function... - Body: &ExprFunc{ + Body: &ast.ExprFunc{ Args: []*interfaces.Arg{}, - Body: &ExprStr{ + Body: &ast.ExprStr{ V: "hello", }, }, }, }, - &StmtBind{ + &ast.StmtBind{ Ident: "fn", - Value: &ExprCall{ + Value: &ast.ExprCall{ Name: "funcgen", Args: []interfaces.Expr{}, Var: false, }, }, - &StmtBind{ + &ast.StmtBind{ Ident: "foo", - Value: &ExprCall{ + Value: &ast.ExprCall{ Name: "fn", Args: []interfaces.Expr{}, Var: true, // comes from a var @@ -2103,7 +2106,7 @@ func TestLexParse0(t *testing.T) { t.Logf("\n\ntest #%d (%s) ----------------\n\n", index, name) str := strings.NewReader(code) - ast, err := LexParse(str) + xast, err := LexParse(str) if !fail && err != nil { t.Errorf("test #%d: lex/parse failed with: %+v", index, err) @@ -2114,7 +2117,7 @@ func TestLexParse0(t *testing.T) { return } - if !fail && ast == nil { + if !fail && xast == nil { t.Errorf("test #%d: lex/parse was nil", index) return } @@ -2122,7 +2125,7 @@ func TestLexParse0(t *testing.T) { if exp == nil { return } - if reflect.DeepEqual(ast, exp) { + if reflect.DeepEqual(xast, exp) { return } // double check because DeepEqual is different since the func exists @@ -2135,16 +2138,16 @@ func TestLexParse0(t *testing.T) { //PrintTextMarshalers: false, SkipZeroFields: true, } - diff := diffable.Compare(exp, ast) + diff := diffable.Compare(exp, xast) if diff == "" { // bonus return } t.Errorf("test #%d: AST did not match expected", index) // TODO: consider making our own recursive print function - t.Logf("test #%d: actual: \n\n%s\n", index, spew.Sdump(ast)) + t.Logf("test #%d: actual: \n\n%s\n", index, spew.Sdump(xast)) t.Logf("test #%d: expected: \n\n%s", index, spew.Sdump(exp)) - t.Logf("test #%d: actual: \n\n%s\n", index, diffable.Sprint(ast)) + t.Logf("test #%d: actual: \n\n%s\n", index, diffable.Sprint(xast)) t.Logf("test #%d: expected: \n\n%s", index, diffable.Sprint(exp)) t.Logf("test #%d: diff:\n%s", index, diff) }) @@ -2474,7 +2477,7 @@ func TestImportParsing0(t *testing.T) { fail: true, // don't allow double root slash }) - t.Logf("ModuleMagicPrefix: %s", ModuleMagicPrefix) + t.Logf("ModuleMagicPrefix: %s", langutil.ModuleMagicPrefix) names := []string{} for index, tc := range testCases { // run all the tests if util.StrInList(tc.name, names) { @@ -2485,7 +2488,7 @@ func TestImportParsing0(t *testing.T) { t.Run(fmt.Sprintf("test #%d (%s)", index, tc.name), func(t *testing.T) { name, fail, alias, isSystem, isLocal, isFile, path, url := tc.name, tc.fail, tc.alias, tc.isSystem, tc.isLocal, tc.isFile, tc.path, tc.url - output, err := ParseImportName(name) + output, err := langutil.ParseImportName(name) if !fail && err != nil { t.Errorf("test #%d: FAIL", index) t.Errorf("test #%d: ParseImportName failed with: %+v", index, err) diff --git a/lang/parser.y b/lang/parser/parser.y similarity index 88% rename from lang/parser.y rename to lang/parser/parser.y index 24d446d1..5ed2acbd 100644 --- a/lang/parser.y +++ b/lang/parser/parser.y @@ -16,12 +16,14 @@ // along with this program. If not, see . %{ -package lang +package parser import ( "fmt" "strings" + "github.com/purpleidea/mgmt/lang/ast" + "github.com/purpleidea/mgmt/lang/funcs" "github.com/purpleidea/mgmt/lang/interfaces" "github.com/purpleidea/mgmt/lang/types" "github.com/purpleidea/mgmt/util" @@ -56,22 +58,22 @@ func init() { exprs []interfaces.Expr expr interfaces.Expr - mapKVs []*ExprMapKV - mapKV *ExprMapKV + mapKVs []*ast.ExprMapKV + mapKV *ast.ExprMapKV - structFields []*ExprStructField - structField *ExprStructField + structFields []*ast.ExprStructField + structField *ast.ExprStructField args []*interfaces.Arg arg *interfaces.Arg - resContents []StmtResContents // interface - resField *StmtResField - resEdge *StmtResEdge - resMeta *StmtResMeta + resContents []ast.StmtResContents // interface + resField *ast.StmtResField + resEdge *ast.StmtResEdge + resMeta *ast.StmtResMeta - edgeHalfList []*StmtEdgeHalf - edgeHalf *StmtEdgeHalf + edgeHalfList []*ast.StmtEdgeHalf + edgeHalf *ast.StmtEdgeHalf } %token OPEN_CURLY CLOSE_CURLY @@ -133,7 +135,7 @@ prog: /* end of list */ { posLast(yylex, yyDollar) // our pos - $$.stmt = &StmtProg{ + $$.stmt = &ast.StmtProg{ Body: []interfaces.Stmt{}, } } @@ -141,12 +143,12 @@ prog: { posLast(yylex, yyDollar) // our pos // TODO: should we just skip comments for now? - //if _, ok := $2.stmt.(*StmtComment); !ok { + //if _, ok := $2.stmt.(*ast.StmtComment); !ok { //} - if stmt, ok := $1.stmt.(*StmtProg); ok { + if stmt, ok := $1.stmt.(*ast.StmtProg); ok { stmts := stmt.Body stmts = append(stmts, $2.stmt) - $$.stmt = &StmtProg{ + $$.stmt = &ast.StmtProg{ Body: stmts, } } @@ -156,7 +158,7 @@ stmt: COMMENT { posLast(yylex, yyDollar) // our pos - $$.stmt = &StmtComment{ + $$.stmt = &ast.StmtComment{ Value: $1.str, } } @@ -178,7 +180,7 @@ stmt: | IF expr OPEN_CURLY prog CLOSE_CURLY { posLast(yylex, yyDollar) // our pos - $$.stmt = &StmtIf{ + $$.stmt = &ast.StmtIf{ Condition: $2.expr, ThenBranch: $4.stmt, //ElseBranch: nil, @@ -187,7 +189,7 @@ stmt: | IF expr OPEN_CURLY prog CLOSE_CURLY ELSE OPEN_CURLY prog CLOSE_CURLY { posLast(yylex, yyDollar) // our pos - $$.stmt = &StmtIf{ + $$.stmt = &ast.StmtIf{ Condition: $2.expr, ThenBranch: $4.stmt, ElseBranch: $8.stmt, @@ -200,9 +202,9 @@ stmt: | FUNC_IDENTIFIER IDENTIFIER OPEN_PAREN args CLOSE_PAREN OPEN_CURLY expr CLOSE_CURLY { posLast(yylex, yyDollar) // our pos - $$.stmt = &StmtFunc{ + $$.stmt = &ast.StmtFunc{ Name: $2.str, - Func: &ExprFunc{ + Func: &ast.ExprFunc{ Args: $4.args, //Return: nil, Body: $7.expr, @@ -213,7 +215,7 @@ stmt: | FUNC_IDENTIFIER IDENTIFIER OPEN_PAREN args CLOSE_PAREN type OPEN_CURLY expr CLOSE_CURLY { posLast(yylex, yyDollar) // our pos - fn := &ExprFunc{ + fn := &ast.ExprFunc{ Args: $4.args, Return: $6.typ, // return type is known Body: $8.expr, @@ -242,7 +244,7 @@ stmt: yylex.Error(fmt.Sprintf("%s: %+v", ErrParseSetType, err)) } } - $$.stmt = &StmtFunc{ + $$.stmt = &ast.StmtFunc{ Name: $2.str, Func: fn, } @@ -251,7 +253,7 @@ stmt: | CLASS_IDENTIFIER IDENTIFIER OPEN_CURLY prog CLOSE_CURLY { posLast(yylex, yyDollar) // our pos - $$.stmt = &StmtClass{ + $$.stmt = &ast.StmtClass{ Name: $2.str, Args: nil, Body: $4.stmt, @@ -262,7 +264,7 @@ stmt: | CLASS_IDENTIFIER IDENTIFIER OPEN_PAREN args CLOSE_PAREN OPEN_CURLY prog CLOSE_CURLY { posLast(yylex, yyDollar) // our pos - $$.stmt = &StmtClass{ + $$.stmt = &ast.StmtClass{ Name: $2.str, Args: $4.args, Body: $7.stmt, @@ -272,7 +274,7 @@ stmt: | INCLUDE_IDENTIFIER dotted_identifier { posLast(yylex, yyDollar) // our pos - $$.stmt = &StmtInclude{ + $$.stmt = &ast.StmtInclude{ Name: $2.str, } } @@ -280,7 +282,7 @@ stmt: | INCLUDE_IDENTIFIER dotted_identifier OPEN_PAREN call_args CLOSE_PAREN { posLast(yylex, yyDollar) // our pos - $$.stmt = &StmtInclude{ + $$.stmt = &ast.StmtInclude{ Name: $2.str, Args: $4.exprs, } @@ -289,7 +291,7 @@ stmt: | IMPORT_IDENTIFIER STRING { posLast(yylex, yyDollar) // our pos - $$.stmt = &StmtImport{ + $$.stmt = &ast.StmtImport{ Name: $2.str, //Alias: "", } @@ -298,7 +300,7 @@ stmt: | IMPORT_IDENTIFIER STRING AS_IDENTIFIER IDENTIFIER { posLast(yylex, yyDollar) // our pos - $$.stmt = &StmtImport{ + $$.stmt = &ast.StmtImport{ Name: $2.str, Alias: $4.str, } @@ -307,7 +309,7 @@ stmt: | IMPORT_IDENTIFIER STRING AS_IDENTIFIER MULTIPLY { posLast(yylex, yyDollar) // our pos - $$.stmt = &StmtImport{ + $$.stmt = &ast.StmtImport{ Name: $2.str, Alias: $4.str, } @@ -325,28 +327,28 @@ expr: BOOL { posLast(yylex, yyDollar) // our pos - $$.expr = &ExprBool{ + $$.expr = &ast.ExprBool{ V: $1.bool, } } | STRING { posLast(yylex, yyDollar) // our pos - $$.expr = &ExprStr{ + $$.expr = &ast.ExprStr{ V: $1.str, } } | INTEGER { posLast(yylex, yyDollar) // our pos - $$.expr = &ExprInt{ + $$.expr = &ast.ExprInt{ V: $1.int, } } | FLOAT { posLast(yylex, yyDollar) // our pos - $$.expr = &ExprFloat{ + $$.expr = &ast.ExprFloat{ V: $1.float, } } @@ -389,7 +391,7 @@ expr: | IF expr OPEN_CURLY expr CLOSE_CURLY ELSE OPEN_CURLY expr CLOSE_CURLY { posLast(yylex, yyDollar) // our pos - $$.expr = &ExprIf{ + $$.expr = &ast.ExprIf{ Condition: $2.expr, ThenBranch: $4.expr, ElseBranch: $8.expr, @@ -407,7 +409,7 @@ list: OPEN_BRACK list_elements CLOSE_BRACK { posLast(yylex, yyDollar) // our pos - $$.expr = &ExprList{ + $$.expr = &ast.ExprList{ Elements: $2.exprs, } } @@ -436,7 +438,7 @@ map: OPEN_CURLY map_kvs CLOSE_CURLY { posLast(yylex, yyDollar) // our pos - $$.expr = &ExprMap{ + $$.expr = &ast.ExprMap{ KVs: $2.mapKVs, } } @@ -445,7 +447,7 @@ map_kvs: /* end of list */ { posLast(yylex, yyDollar) // our pos - $$.mapKVs = []*ExprMapKV{} + $$.mapKVs = []*ast.ExprMapKV{} } | map_kvs map_kv { @@ -457,7 +459,7 @@ map_kv: expr ROCKET expr COMMA { posLast(yylex, yyDollar) // our pos - $$.mapKV = &ExprMapKV{ + $$.mapKV = &ast.ExprMapKV{ Key: $1.expr, Val: $3.expr, } @@ -468,7 +470,7 @@ struct: STRUCT_IDENTIFIER OPEN_CURLY struct_fields CLOSE_CURLY { posLast(yylex, yyDollar) // our pos - $$.expr = &ExprStruct{ + $$.expr = &ast.ExprStruct{ Fields: $3.structFields, } } @@ -477,7 +479,7 @@ struct_fields: /* end of list */ { posLast(yylex, yyDollar) // our pos - $$.structFields = []*ExprStructField{} + $$.structFields = []*ast.ExprStructField{} } | struct_fields struct_field { @@ -489,7 +491,7 @@ struct_field: IDENTIFIER ROCKET expr COMMA { posLast(yylex, yyDollar) // our pos - $$.structField = &ExprStructField{ + $$.structField = &ast.ExprStructField{ Name: $1.str, Value: $3.expr, } @@ -500,7 +502,7 @@ call: dotted_identifier OPEN_PAREN call_args CLOSE_PAREN { posLast(yylex, yyDollar) // our pos - $$.expr = &ExprCall{ + $$.expr = &ast.ExprCall{ Name: $1.str, Args: $3.exprs, //Var: false, // default @@ -511,7 +513,7 @@ call: | VAR_IDENTIFIER OPEN_PAREN call_args CLOSE_PAREN { posLast(yylex, yyDollar) // our pos - $$.expr = &ExprCall{ + $$.expr = &ast.ExprCall{ Name: $1.str, Args: $3.exprs, // Instead of `Var: true`, we could have added a `$` @@ -522,11 +524,11 @@ call: | expr PLUS expr { posLast(yylex, yyDollar) // our pos - $$.expr = &ExprCall{ - Name: operatorFuncName, + $$.expr = &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ // operator first - V: $2.str, // for PLUS this is a `+` character + &ast.ExprStr{ // operator first + V: $2.str, // for PLUS this is a `+` character }, $1.expr, $3.expr, @@ -536,10 +538,10 @@ call: | expr MINUS expr { posLast(yylex, yyDollar) // our pos - $$.expr = &ExprCall{ - Name: operatorFuncName, + $$.expr = &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ // operator first + &ast.ExprStr{ // operator first V: $2.str, }, $1.expr, @@ -550,10 +552,10 @@ call: | expr MULTIPLY expr { posLast(yylex, yyDollar) // our pos - $$.expr = &ExprCall{ - Name: operatorFuncName, + $$.expr = &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ // operator first + &ast.ExprStr{ // operator first V: $2.str, }, $1.expr, @@ -564,10 +566,10 @@ call: | expr DIVIDE expr { posLast(yylex, yyDollar) // our pos - $$.expr = &ExprCall{ - Name: operatorFuncName, + $$.expr = &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ // operator first + &ast.ExprStr{ // operator first V: $2.str, }, $1.expr, @@ -578,10 +580,10 @@ call: | expr EQ expr { posLast(yylex, yyDollar) // our pos - $$.expr = &ExprCall{ - Name: operatorFuncName, + $$.expr = &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ // operator first + &ast.ExprStr{ // operator first V: $2.str, }, $1.expr, @@ -592,10 +594,10 @@ call: | expr NEQ expr { posLast(yylex, yyDollar) // our pos - $$.expr = &ExprCall{ - Name: operatorFuncName, + $$.expr = &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ // operator first + &ast.ExprStr{ // operator first V: $2.str, }, $1.expr, @@ -606,10 +608,10 @@ call: | expr LT expr { posLast(yylex, yyDollar) // our pos - $$.expr = &ExprCall{ - Name: operatorFuncName, + $$.expr = &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ // operator first + &ast.ExprStr{ // operator first V: $2.str, }, $1.expr, @@ -620,10 +622,10 @@ call: | expr GT expr { posLast(yylex, yyDollar) // our pos - $$.expr = &ExprCall{ - Name: operatorFuncName, + $$.expr = &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ // operator first + &ast.ExprStr{ // operator first V: $2.str, }, $1.expr, @@ -634,10 +636,10 @@ call: | expr LTE expr { posLast(yylex, yyDollar) // our pos - $$.expr = &ExprCall{ - Name: operatorFuncName, + $$.expr = &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ // operator first + &ast.ExprStr{ // operator first V: $2.str, }, $1.expr, @@ -648,10 +650,10 @@ call: | expr GTE expr { posLast(yylex, yyDollar) // our pos - $$.expr = &ExprCall{ - Name: operatorFuncName, + $$.expr = &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ // operator first + &ast.ExprStr{ // operator first V: $2.str, }, $1.expr, @@ -662,10 +664,10 @@ call: | expr AND expr { posLast(yylex, yyDollar) // our pos - $$.expr = &ExprCall{ - Name: operatorFuncName, + $$.expr = &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ // operator first + &ast.ExprStr{ // operator first V: $2.str, }, $1.expr, @@ -676,10 +678,10 @@ call: | expr OR expr { posLast(yylex, yyDollar) // our pos - $$.expr = &ExprCall{ - Name: operatorFuncName, + $$.expr = &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ // operator first + &ast.ExprStr{ // operator first V: $2.str, }, $1.expr, @@ -690,10 +692,10 @@ call: | NOT expr { posLast(yylex, yyDollar) // our pos - $$.expr = &ExprCall{ - Name: operatorFuncName, + $$.expr = &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ // operator first + &ast.ExprStr{ // operator first V: $1.str, }, $2.expr, @@ -704,13 +706,13 @@ call: // get the N-th historical value, eg: $foo{3} is equivalent to: history($foo, 3) { posLast(yylex, yyDollar) // our pos - $$.expr = &ExprCall{ - Name: historyFuncName, + $$.expr = &ast.ExprCall{ + Name: funcs.HistoryFuncName, Args: []interfaces.Expr{ - &ExprVar{ + &ast.ExprVar{ Name: $1.str, }, - &ExprInt{ + &ast.ExprInt{ V: $1.int, }, }, @@ -722,13 +724,13 @@ call: //| dotted_var_identifier OPEN_CURLY INTEGER CLOSE_CURLY // { // posLast(yylex, yyDollar) // our pos -// $$.expr = &ExprCall{ -// Name: historyFuncName, +// $$.expr = &ast.ExprCall{ +// Name: funcs.HistoryFuncName, // Args: []interfaces.Expr{ -// &ExprVar{ +// &ast.ExprVar{ // Name: $1.str, // }, -// &ExprInt{ +// &ast.ExprInt{ // V: $3.int, // }, // }, @@ -737,8 +739,8 @@ call: | expr IN expr { posLast(yylex, yyDollar) // our pos - $$.expr = &ExprCall{ - Name: containsFuncName, + $$.expr = &ast.ExprCall{ + Name: funcs.ContainsFuncName, Args: []interfaces.Expr{ $1.expr, $3.expr, @@ -770,7 +772,7 @@ var: dotted_var_identifier { posLast(yylex, yyDollar) // our pos - $$.expr = &ExprVar{ + $$.expr = &ast.ExprVar{ Name: $1.str, } } @@ -783,7 +785,7 @@ func: FUNC_IDENTIFIER OPEN_PAREN args CLOSE_PAREN OPEN_CURLY expr CLOSE_CURLY { posLast(yylex, yyDollar) // our pos - $$.expr = &ExprFunc{ + $$.expr = &ast.ExprFunc{ Args: $3.args, //Return: nil, Body: $6.expr, @@ -793,7 +795,7 @@ func: | FUNC_IDENTIFIER OPEN_PAREN args CLOSE_PAREN type OPEN_CURLY expr CLOSE_CURLY { posLast(yylex, yyDollar) // our pos - $$.expr = &ExprFunc{ + $$.expr = &ast.ExprFunc{ Args: $3.args, Return: $5.typ, // return type is known Body: $7.expr, @@ -863,7 +865,7 @@ bind: VAR_IDENTIFIER EQUALS expr { posLast(yylex, yyDollar) // our pos - $$.stmt = &StmtBind{ + $$.stmt = &ast.StmtBind{ Ident: $1.str, Value: $3.expr, } @@ -878,7 +880,7 @@ bind: // this will ultimately cause a parser error to occur... yylex.Error(fmt.Sprintf("%s: %+v", ErrParseSetType, err)) } - $$.stmt = &StmtBind{ + $$.stmt = &ast.StmtBind{ Ident: $1.str, Value: expr, } @@ -893,7 +895,7 @@ rbind: // 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 - $$.expr = &StmtBind{ + $$.expr = &ast.StmtBind{ Ident: $1.str, Value: $3.stmt, } @@ -905,7 +907,7 @@ resource: RES_IDENTIFIER expr OPEN_CURLY resource_body CLOSE_CURLY { posLast(yylex, yyDollar) // our pos - $$.stmt = &StmtRes{ + $$.stmt = &ast.StmtRes{ Kind: $1.str, Name: $2.expr, Contents: $4.resContents, @@ -916,7 +918,7 @@ resource: | IDENTIFIER expr OPEN_CURLY resource_body CLOSE_CURLY { posLast(yylex, yyDollar) // our pos - $$.stmt = &StmtRes{ + $$.stmt = &ast.StmtRes{ Kind: $1.str, Name: $2.expr, Contents: $4.resContents, @@ -927,7 +929,7 @@ resource_body: /* end of list */ { posLast(yylex, yyDollar) // our pos - $$.resContents = []StmtResContents{} + $$.resContents = []ast.StmtResContents{} } | resource_body resource_field { @@ -974,7 +976,7 @@ resource_field: IDENTIFIER ROCKET expr COMMA { posLast(yylex, yyDollar) // our pos - $$.resField = &StmtResField{ + $$.resField = &ast.StmtResField{ Field: $1.str, Value: $3.expr, } @@ -985,7 +987,7 @@ conditional_resource_field: IDENTIFIER ROCKET expr ELVIS expr COMMA { posLast(yylex, yyDollar) // our pos - $$.resField = &StmtResField{ + $$.resField = &ast.StmtResField{ Field: $1.str, Value: $5.expr, Condition: $3.expr, @@ -997,7 +999,7 @@ resource_edge: CAPITALIZED_IDENTIFIER ROCKET edge_half COMMA { posLast(yylex, yyDollar) // our pos - $$.resEdge = &StmtResEdge{ + $$.resEdge = &ast.StmtResEdge{ Property: $1.str, EdgeHalf: $3.edgeHalf, } @@ -1008,7 +1010,7 @@ conditional_resource_edge: CAPITALIZED_IDENTIFIER ROCKET expr ELVIS edge_half COMMA { posLast(yylex, yyDollar) // our pos - $$.resEdge = &StmtResEdge{ + $$.resEdge = &ast.StmtResEdge{ Property: $1.str, EdgeHalf: $5.edgeHalf, Condition: $3.expr, @@ -1020,11 +1022,11 @@ resource_meta: CAPITALIZED_IDENTIFIER COLON IDENTIFIER ROCKET expr COMMA { posLast(yylex, yyDollar) // our pos - if strings.ToLower($1.str) != strings.ToLower(MetaField) { + if strings.ToLower($1.str) != strings.ToLower(ast.MetaField) { // this will ultimately cause a parser error to occur... yylex.Error(fmt.Sprintf("%s: %s", ErrParseResFieldInvalid, $1.str)) } - $$.resMeta = &StmtResMeta{ + $$.resMeta = &ast.StmtResMeta{ Property: $3.str, MetaExpr: $5.expr, } @@ -1035,11 +1037,11 @@ conditional_resource_meta: CAPITALIZED_IDENTIFIER COLON IDENTIFIER ROCKET expr ELVIS expr COMMA { posLast(yylex, yyDollar) // our pos - if strings.ToLower($1.str) != strings.ToLower(MetaField) { + if strings.ToLower($1.str) != strings.ToLower(ast.MetaField) { // this will ultimately cause a parser error to occur... yylex.Error(fmt.Sprintf("%s: %s", ErrParseResFieldInvalid, $1.str)) } - $$.resMeta = &StmtResMeta{ + $$.resMeta = &ast.StmtResMeta{ Property: $3.str, MetaExpr: $7.expr, Condition: $5.expr, @@ -1051,11 +1053,11 @@ resource_meta_struct: CAPITALIZED_IDENTIFIER ROCKET expr COMMA { posLast(yylex, yyDollar) // our pos - if strings.ToLower($1.str) != strings.ToLower(MetaField) { + if strings.ToLower($1.str) != strings.ToLower(ast.MetaField) { // this will ultimately cause a parser error to occur... yylex.Error(fmt.Sprintf("%s: %s", ErrParseResFieldInvalid, $1.str)) } - $$.resMeta = &StmtResMeta{ + $$.resMeta = &ast.StmtResMeta{ Property: $1.str, MetaExpr: $3.expr, } @@ -1066,11 +1068,11 @@ conditional_resource_meta_struct: CAPITALIZED_IDENTIFIER ROCKET expr ELVIS expr COMMA { posLast(yylex, yyDollar) // our pos - if strings.ToLower($1.str) != strings.ToLower(MetaField) { + if strings.ToLower($1.str) != strings.ToLower(ast.MetaField) { // this will ultimately cause a parser error to occur... yylex.Error(fmt.Sprintf("%s: %s", ErrParseResFieldInvalid, $1.str)) } - $$.resMeta = &StmtResMeta{ + $$.resMeta = &ast.StmtResMeta{ Property: $1.str, MetaExpr: $5.expr, Condition: $3.expr, @@ -1084,7 +1086,7 @@ edge: edge_half_list { posLast(yylex, yyDollar) // our pos - $$.stmt = &StmtEdge{ + $$.stmt = &ast.StmtEdge{ EdgeHalfList: $1.edgeHalfList, //Notify: false, // unused here } @@ -1093,8 +1095,8 @@ edge: | edge_half_sendrecv ARROW edge_half_sendrecv { posLast(yylex, yyDollar) // our pos - $$.stmt = &StmtEdge{ - EdgeHalfList: []*StmtEdgeHalf{ + $$.stmt = &ast.StmtEdge{ + EdgeHalfList: []*ast.StmtEdgeHalf{ $1.edgeHalf, $3.edgeHalf, }, @@ -1106,7 +1108,7 @@ edge_half_list: edge_half { posLast(yylex, yyDollar) // our pos - $$.edgeHalfList = []*StmtEdgeHalf{$1.edgeHalf} + $$.edgeHalfList = []*ast.StmtEdgeHalf{$1.edgeHalf} } | edge_half_list ARROW edge_half { @@ -1119,7 +1121,7 @@ edge_half: capitalized_res_identifier OPEN_BRACK expr CLOSE_BRACK { posLast(yylex, yyDollar) // our pos - $$.edgeHalf = &StmtEdgeHalf{ + $$.edgeHalf = &ast.StmtEdgeHalf{ Kind: $1.str, Name: $3.expr, //SendRecv: "", // unused @@ -1131,7 +1133,7 @@ edge_half_sendrecv: capitalized_res_identifier OPEN_BRACK expr CLOSE_BRACK DOT IDENTIFIER { posLast(yylex, yyDollar) // our pos - $$.edgeHalf = &StmtEdgeHalf{ + $$.edgeHalf = &ast.StmtEdgeHalf{ Kind: $1.str, Name: $3.expr, SendRecv: $6.str, diff --git a/lang/unification_test.go b/lang/unification_test.go index c047fcca..88e47402 100644 --- a/lang/unification_test.go +++ b/lang/unification_test.go @@ -17,13 +17,15 @@ // +build !root -package lang +package lang // XXX: move this to the unification package import ( "fmt" "strings" "testing" + "github.com/purpleidea/mgmt/lang/ast" + "github.com/purpleidea/mgmt/lang/funcs" "github.com/purpleidea/mgmt/lang/funcs/vars" "github.com/purpleidea/mgmt/lang/interfaces" "github.com/purpleidea/mgmt/lang/types" @@ -52,14 +54,14 @@ func TestUnification1(t *testing.T) { // }) //} { - expr := &ExprStr{V: "hello"} - stmt := &StmtProg{ + expr := &ast.ExprStr{V: "hello"} + stmt := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtRes{ + &ast.StmtRes{ Kind: "test", - Name: &ExprStr{V: "t1"}, - Contents: []StmtResContents{ - &StmtResField{ + Name: &ast.ExprStr{V: "t1"}, + Contents: []ast.StmtResContents{ + &ast.StmtResField{ Field: "str", Value: expr, }, @@ -77,23 +79,23 @@ func TestUnification1(t *testing.T) { }) } { - v1 := &ExprStr{} - v2 := &ExprStr{} - v3 := &ExprStr{} - expr := &ExprList{ + v1 := &ast.ExprStr{} + v2 := &ast.ExprStr{} + v3 := &ast.ExprStr{} + expr := &ast.ExprList{ Elements: []interfaces.Expr{ v1, v2, v3, }, } - stmt := &StmtProg{ + stmt := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtRes{ + &ast.StmtRes{ Kind: "test", - Name: &ExprStr{V: "test"}, - Contents: []StmtResContents{ - &StmtResField{ + Name: &ast.ExprStr{V: "test"}, + Contents: []ast.StmtResContents{ + &ast.StmtResField{ Field: "slicestring", Value: expr, }, @@ -114,26 +116,26 @@ func TestUnification1(t *testing.T) { }) } { - k1 := &ExprInt{} - k2 := &ExprInt{} - k3 := &ExprInt{} - v1 := &ExprFloat{} - v2 := &ExprFloat{} - v3 := &ExprFloat{} - expr := &ExprMap{ - KVs: []*ExprMapKV{ + k1 := &ast.ExprInt{} + k2 := &ast.ExprInt{} + k3 := &ast.ExprInt{} + v1 := &ast.ExprFloat{} + v2 := &ast.ExprFloat{} + v3 := &ast.ExprFloat{} + expr := &ast.ExprMap{ + KVs: []*ast.ExprMapKV{ {Key: k1, Val: v1}, {Key: k2, Val: v2}, {Key: k3, Val: v3}, }, } - stmt := &StmtProg{ + stmt := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtRes{ + &ast.StmtRes{ Kind: "test", - Name: &ExprStr{V: "test"}, - Contents: []StmtResContents{ - &StmtResField{ + Name: &ast.ExprStr{V: "test"}, + Contents: []ast.StmtResContents{ + &ast.StmtResField{ Field: "mapintfloat", Value: expr, }, @@ -157,25 +159,25 @@ func TestUnification1(t *testing.T) { }) } { - b := &ExprBool{} - s := &ExprStr{} - i := &ExprInt{} - f := &ExprFloat{} - expr := &ExprStruct{ - Fields: []*ExprStructField{ + b := &ast.ExprBool{} + s := &ast.ExprStr{} + i := &ast.ExprInt{} + f := &ast.ExprFloat{} + expr := &ast.ExprStruct{ + Fields: []*ast.ExprStructField{ {Name: "somebool", Value: b}, {Name: "somestr", Value: s}, {Name: "someint", Value: i}, {Name: "somefloat", Value: f}, }, } - stmt := &StmtProg{ + stmt := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtRes{ + &ast.StmtRes{ Kind: "test", - Name: &ExprStr{V: "test"}, - Contents: []StmtResContents{ - &StmtResField{ + Name: &ast.ExprStr{V: "test"}, + Contents: []ast.StmtResContents{ + &ast.StmtResField{ Field: "mixedstruct", Value: expr, }, @@ -200,30 +202,30 @@ func TestUnification1(t *testing.T) { // test "n1" { // int64ptr => 13 + 42, //} - expr := &ExprCall{ - Name: operatorFuncName, + expr := &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ + &ast.ExprStr{ V: "+", }, - &ExprInt{ + &ast.ExprInt{ V: 13, }, - &ExprInt{ + &ast.ExprInt{ V: 42, }, }, } - stmt := &StmtProg{ + stmt := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtRes{ + &ast.StmtRes{ Kind: "test", - Name: &ExprStr{ + Name: &ast.ExprStr{ V: "n1", }, - Contents: []StmtResContents{ - &StmtResField{ + Contents: []ast.StmtResContents{ + &ast.StmtResField{ Field: "int64ptr", Value: expr, // func }, @@ -244,41 +246,41 @@ func TestUnification1(t *testing.T) { //test "n1" { // int64ptr => 13 + 42 - 4, //} - innerFunc := &ExprCall{ - Name: operatorFuncName, + innerFunc := &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ + &ast.ExprStr{ V: "-", }, - &ExprInt{ + &ast.ExprInt{ V: 42, }, - &ExprInt{ + &ast.ExprInt{ V: 4, }, }, } - expr := &ExprCall{ - Name: operatorFuncName, + expr := &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ + &ast.ExprStr{ V: "+", }, - &ExprInt{ + &ast.ExprInt{ V: 13, }, innerFunc, // nested func, can we unify? }, } - stmt := &StmtProg{ + stmt := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtRes{ + &ast.StmtRes{ Kind: "test", - Name: &ExprStr{ + Name: &ast.ExprStr{ V: "n1", }, - Contents: []StmtResContents{ - &StmtResField{ + Contents: []ast.StmtResContents{ + &ast.StmtResField{ Field: "int64ptr", Value: expr, }, @@ -300,41 +302,41 @@ func TestUnification1(t *testing.T) { //test "n1" { // float32 => -25.38789 + 32.6 + 13.7, //} - innerFunc := &ExprCall{ - Name: operatorFuncName, + innerFunc := &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ + &ast.ExprStr{ V: "+", }, - &ExprFloat{ + &ast.ExprFloat{ V: 32.6, }, - &ExprFloat{ + &ast.ExprFloat{ V: 13.7, }, }, } - expr := &ExprCall{ - Name: operatorFuncName, + expr := &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ + &ast.ExprStr{ V: "+", }, - &ExprFloat{ + &ast.ExprFloat{ V: -25.38789, }, innerFunc, // nested func, can we unify? }, } - stmt := &StmtProg{ + stmt := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtRes{ + &ast.StmtRes{ Kind: "test", - Name: &ExprStr{ + Name: &ast.ExprStr{ V: "n1", }, - Contents: []StmtResContents{ - &StmtResField{ + Contents: []ast.StmtResContents{ + &ast.StmtResField{ Field: "float32", Value: expr, }, @@ -357,35 +359,35 @@ func TestUnification1(t *testing.T) { //test "t1" { // int64 => $x, //} - innerFunc := &ExprCall{ - Name: operatorFuncName, + innerFunc := &ast.ExprCall{ + Name: funcs.OperatorFuncName, Args: []interfaces.Expr{ - &ExprStr{ + &ast.ExprStr{ V: "-", }, - &ExprInt{ + &ast.ExprInt{ V: 42, }, - &ExprInt{ + &ast.ExprInt{ V: 13, }, }, } - stmt := &StmtProg{ + stmt := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtBind{ + &ast.StmtBind{ Ident: "x", Value: innerFunc, }, - &StmtRes{ + &ast.StmtRes{ Kind: "test", - Name: &ExprStr{ + Name: &ast.ExprStr{ V: "t1", }, - Contents: []StmtResContents{ - &StmtResField{ + Contents: []ast.StmtResContents{ + &ast.StmtResField{ Field: "int64", - Value: &ExprVar{ + Value: &ast.ExprVar{ Name: "x", }, }, @@ -407,32 +409,32 @@ func TestUnification1(t *testing.T) { //test "t1" { // anotherstr => $x, //} - innerFunc := &ExprCall{ + innerFunc := &ast.ExprCall{ Name: "template", Args: []interfaces.Expr{ - &ExprStr{ + &ast.ExprStr{ V: "hello", }, - &ExprInt{ + &ast.ExprInt{ V: 42, }, }, } - stmt := &StmtProg{ + stmt := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtBind{ + &ast.StmtBind{ Ident: "x", Value: innerFunc, }, - &StmtRes{ + &ast.StmtRes{ Kind: "test", - Name: &ExprStr{ + Name: &ast.ExprStr{ V: "t1", }, - Contents: []StmtResContents{ - &StmtResField{ + Contents: []ast.StmtResContents{ + &ast.StmtResField{ Field: "anotherstr", - Value: &ExprVar{ + Value: &ast.ExprVar{ Name: "x", }, }, @@ -455,38 +457,38 @@ func TestUnification1(t *testing.T) { //test "t1" { // anotherstr => $x, //} - innerFunc := &ExprCall{ + innerFunc := &ast.ExprCall{ Name: "template", Args: []interfaces.Expr{ - &ExprStr{ + &ast.ExprStr{ V: "hello", // whatever... }, - &ExprVar{ + &ast.ExprVar{ Name: "v", }, }, } - stmt := &StmtProg{ + stmt := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtBind{ + &ast.StmtBind{ Ident: "v", - Value: &ExprInt{ + Value: &ast.ExprInt{ V: 42, }, }, - &StmtBind{ + &ast.StmtBind{ Ident: "x", Value: innerFunc, }, - &StmtRes{ + &ast.StmtRes{ Kind: "test", - Name: &ExprStr{ + Name: &ast.ExprStr{ V: "t1", }, - Contents: []StmtResContents{ - &StmtResField{ + Contents: []ast.StmtResContents{ + &ast.StmtResField{ Field: "anotherstr", - Value: &ExprVar{ + Value: &ast.ExprVar{ Name: "x", }, }, @@ -508,20 +510,20 @@ func TestUnification1(t *testing.T) { //test "t1" { // stringptr => datetime.now(), # bad (str vs. int) //} - expr := &ExprCall{ + expr := &ast.ExprCall{ Name: "datetime.now", Args: []interfaces.Expr{}, } - stmt := &StmtProg{ + stmt := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtImport{ + &ast.StmtImport{ Name: "datetime", }, - &StmtRes{ + &ast.StmtRes{ Kind: "test", - Name: &ExprStr{V: "t1"}, - Contents: []StmtResContents{ - &StmtResField{ + Name: &ast.ExprStr{V: "t1"}, + Contents: []ast.StmtResContents{ + &ast.StmtResField{ Field: "stringptr", Value: expr, }, @@ -540,27 +542,27 @@ func TestUnification1(t *testing.T) { //test "t1" { // stringptr => sys.getenv("GOPATH", "bug"), # bad (two args vs. one) //} - expr := &ExprCall{ + expr := &ast.ExprCall{ Name: "sys.getenv", Args: []interfaces.Expr{ - &ExprStr{ + &ast.ExprStr{ V: "GOPATH", }, - &ExprStr{ + &ast.ExprStr{ V: "bug", }, }, } - stmt := &StmtProg{ + stmt := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtImport{ + &ast.StmtImport{ Name: "sys", }, - &StmtRes{ + &ast.StmtRes{ Kind: "test", - Name: &ExprStr{V: "t1"}, - Contents: []StmtResContents{ - &StmtResField{ + Name: &ast.ExprStr{V: "t1"}, + Contents: []ast.StmtResContents{ + &ast.StmtResField{ Field: "stringptr", Value: expr, }, @@ -580,27 +582,27 @@ func TestUnification1(t *testing.T) { // //test "t1" { // // stringptr => fmt.printf("hello %s and %s", "one"), # bad // //} - // expr := &ExprCall{ + // expr := &ast.ExprCall{ // Name: "fmt.printf", // Args: []interfaces.Expr{ - // &ExprStr{ + // &ast.ExprStr{ // V: "hello %s and %s", // }, - // &ExprStr{ + // &ast.ExprStr{ // V: "one", // }, // }, // } - // stmt := &StmtProg{ + // stmt := &ast.StmtProg{ // Body: []interfaces.Stmt{ - // &StmtImport{ + // &ast.StmtImport{ // Name: "fmt", // }, - // &StmtRes{ + // &ast.StmtRes{ // Kind: "test", - // Name: &ExprStr{V: "t1"}, - // Contents: []StmtResContents{ - // &StmtResField{ + // Name: &ast.ExprStr{V: "t1"}, + // Contents: []ast.StmtResContents{ + // &ast.StmtResField{ // Field: "stringptr", // Value: expr, // }, @@ -619,33 +621,33 @@ func TestUnification1(t *testing.T) { // //test "t1" { // // stringptr => fmt.printf("hello %s and %s", "one", "two", "three"), # bad // //} - // expr := &ExprCall{ + // expr := &ast.ExprCall{ // Name: "fmt.printf", // Args: []interfaces.Expr{ - // &ExprStr{ + // &ast.ExprStr{ // V: "hello %s and %s", // }, - // &ExprStr{ + // &ast.ExprStr{ // V: "one", // }, - // &ExprStr{ + // &ast.ExprStr{ // V: "two", // }, - // &ExprStr{ + // &ast.ExprStr{ // V: "three", // }, // }, // } - // stmt := &StmtProg{ + // stmt := &ast.StmtProg{ // Body: []interfaces.Stmt{ - // &StmtImport{ + // &ast.StmtImport{ // Name: "fmt", // }, - // &StmtRes{ + // &ast.StmtRes{ // Kind: "test", - // Name: &ExprStr{V: "t1"}, - // Contents: []StmtResContents{ - // &StmtResField{ + // Name: &ast.ExprStr{V: "t1"}, + // Contents: []ast.StmtResContents{ + // &ast.StmtResField{ // Field: "stringptr", // Value: expr, // }, @@ -664,30 +666,30 @@ func TestUnification1(t *testing.T) { //test "t1" { // stringptr => fmt.printf("hello %s and %s", "one", "two"), //} - expr := &ExprCall{ + expr := &ast.ExprCall{ Name: "fmt.printf", Args: []interfaces.Expr{ - &ExprStr{ + &ast.ExprStr{ V: "hello %s and %s", }, - &ExprStr{ + &ast.ExprStr{ V: "one", }, - &ExprStr{ + &ast.ExprStr{ V: "two", }, }, } - stmt := &StmtProg{ + stmt := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtImport{ + &ast.StmtImport{ Name: "fmt", }, - &StmtRes{ + &ast.StmtRes{ Kind: "test", - Name: &ExprStr{V: "t1"}, - Contents: []StmtResContents{ - &StmtResField{ + Name: &ast.ExprStr{V: "t1"}, + Contents: []ast.StmtResContents{ + &ast.StmtResField{ Field: "stringptr", Value: expr, }, @@ -714,37 +716,37 @@ func TestUnification1(t *testing.T) { //test "t1" { // stringptr => fmt.printf("hello %s", $x), //} - cond := &ExprIf{ - Condition: &ExprBool{V: true}, - ThenBranch: &ExprInt{V: 42}, - ElseBranch: &ExprInt{V: 13}, + cond := &ast.ExprIf{ + Condition: &ast.ExprBool{V: true}, + ThenBranch: &ast.ExprInt{V: 42}, + ElseBranch: &ast.ExprInt{V: 13}, } cond.SetType(types.TypeStr) // should fail unification - expr := &ExprCall{ + expr := &ast.ExprCall{ Name: "fmt.printf", Args: []interfaces.Expr{ - &ExprStr{ + &ast.ExprStr{ V: "hello %s", }, - &ExprVar{ + &ast.ExprVar{ Name: "x", // the var }, }, } - stmt := &StmtProg{ + stmt := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtImport{ + &ast.StmtImport{ Name: "fmt", }, - &StmtBind{ + &ast.StmtBind{ Ident: "x", // the var Value: cond, }, - &StmtRes{ + &ast.StmtRes{ Kind: "test", - Name: &ExprStr{V: "t1"}, - Contents: []StmtResContents{ - &StmtResField{ + Name: &ast.ExprStr{V: "t1"}, + Contents: []ast.StmtResContents{ + &ast.StmtResField{ Field: "anotherstr", Value: expr, }, @@ -766,38 +768,38 @@ func TestUnification1(t *testing.T) { //test "t1" { // stringptr => fmt.printf("hello %s", $x), //} - wvar := &ExprBool{V: true} - xvar := &ExprVar{Name: "w"} + wvar := &ast.ExprBool{V: true} + xvar := &ast.ExprVar{Name: "w"} xvar.SetType(types.TypeStr) // should fail unification - expr := &ExprCall{ + expr := &ast.ExprCall{ Name: "fmt.printf", Args: []interfaces.Expr{ - &ExprStr{ + &ast.ExprStr{ V: "hello %s", }, - &ExprVar{ + &ast.ExprVar{ Name: "x", // the var }, }, } - stmt := &StmtProg{ + stmt := &ast.StmtProg{ Body: []interfaces.Stmt{ - &StmtImport{ + &ast.StmtImport{ Name: "fmt", }, - &StmtBind{ + &ast.StmtBind{ Ident: "w", Value: wvar, }, - &StmtBind{ + &ast.StmtBind{ Ident: "x", // the var Value: xvar, }, - &StmtRes{ + &ast.StmtRes{ Kind: "test", - Name: &ExprStr{V: "t1"}, - Contents: []StmtResContents{ - &StmtResField{ + Name: &ast.ExprStr{V: "t1"}, + Contents: []ast.StmtResContents{ + &ast.StmtResField{ Field: "anotherstr", Value: expr, }, @@ -825,16 +827,16 @@ func TestUnification1(t *testing.T) { } names = append(names, tc.name) t.Run(fmt.Sprintf("test #%d (%s)", index, tc.name), func(t *testing.T) { - ast, fail, expect, experr, experrstr := tc.ast, tc.fail, tc.expect, tc.experr, tc.experrstr + xast, fail, expect, experr, experrstr := tc.ast, tc.fail, tc.expect, tc.experr, tc.experrstr //str := strings.NewReader(code) - //ast, err := LexParse(str) + //xast, err := parser.LexParse(str) //if err != nil { // t.Errorf("test #%d: lex/parse failed with: %+v", index, err) // return //} // TODO: print out the AST's so that we can see the types - t.Logf("\n\ntest #%d: AST (before): %+v\n", index, ast) + t.Logf("\n\ntest #%d: AST (before): %+v\n", index, xast) data := &interfaces.Data{ // TODO: add missing fields here if/when needed @@ -844,7 +846,7 @@ func TestUnification1(t *testing.T) { }, } // some of this might happen *after* interpolate in SetScope or Unify... - if err := ast.Init(data); err != nil { + if err := xast.Init(data); err != nil { t.Errorf("test #%d: FAIL", index) t.Errorf("test #%d: could not init and validate AST: %+v", index, err) return @@ -860,13 +862,13 @@ func TestUnification1(t *testing.T) { //t.Logf("test #%d: astInterpolated: %+v", index, astInterpolated) variables := map[string]interfaces.Expr{ - "purpleidea": &ExprStr{V: "hello world!"}, // james says hi - //"hostname": &ExprStr{V: obj.Hostname}, + "purpleidea": &ast.ExprStr{V: "hello world!"}, // james says hi + //"hostname": &ast.ExprStr{V: obj.Hostname}, } - consts := VarPrefixToVariablesScope(vars.ConstNamespace) // strips prefix! - addback := vars.ConstNamespace + interfaces.ModuleSep // add it back... + consts := ast.VarPrefixToVariablesScope(vars.ConstNamespace) // strips prefix! + addback := vars.ConstNamespace + interfaces.ModuleSep // add it back... var err error - variables, err = MergeExprMaps(variables, consts, addback) + variables, err = ast.MergeExprMaps(variables, consts, addback) if err != nil { t.Errorf("test #%d: FAIL", index) t.Errorf("test #%d: couldn't merge in consts: %+v", index, err) @@ -877,10 +879,10 @@ func TestUnification1(t *testing.T) { scope := &interfaces.Scope{ Variables: variables, // all the built-in top-level, core functions enter here... - Functions: FuncPrefixToFunctionsScope(""), // runs funcs.LookupPrefix + Functions: ast.FuncPrefixToFunctionsScope(""), // runs funcs.LookupPrefix } // propagate the scope down through the AST... - if err := ast.SetScope(scope); err != nil { + if err := xast.SetScope(scope); err != nil { t.Errorf("test #%d: FAIL", index) t.Errorf("test #%d: set scope failed with: %+v", index, err) return @@ -891,7 +893,7 @@ func TestUnification1(t *testing.T) { t.Logf(fmt.Sprintf("test #%d", index)+": unification: "+format, v...) } unifier := &unification.Unifier{ - AST: ast, + AST: xast, Solver: unification.SimpleInvariantSolverLogger(logf), Debug: testing.Verbose(), Logf: logf, @@ -899,7 +901,7 @@ func TestUnification1(t *testing.T) { err = unifier.Unify() // TODO: print out the AST's so that we can see the types - t.Logf("\n\ntest #%d: AST (after): %+v\n", index, ast) + t.Logf("\n\ntest #%d: AST (after): %+v\n", index, xast) if !fail && err != nil { t.Errorf("test #%d: FAIL", index) diff --git a/lang/util/util.go b/lang/util/util.go index 2c899ce3..5aa577e1 100644 --- a/lang/util/util.go +++ b/lang/util/util.go @@ -19,13 +19,24 @@ package util import ( "fmt" + "net/url" + "path" "regexp" "strings" + "github.com/purpleidea/mgmt/lang/interfaces" "github.com/purpleidea/mgmt/lang/types" "github.com/purpleidea/mgmt/util/errwrap" ) +const ( + // ModuleMagicPrefix is the prefix which, if found as a prefix to the + // last token in an import path, will be removed silently if there are + // remaining characters following the name. If this is the empty string + // then it will be ignored. + ModuleMagicPrefix = "mgmt-" +) + // HasDuplicateTypes returns an error if the list of types is not unique. func HasDuplicateTypes(typs []*types.Type) error { // FIXME: do this comparison in < O(n^2) ? @@ -127,3 +138,118 @@ func ValidateVarName(name string) error { return nil } + +// ParseImportName parses an import name and returns the default namespace name +// that should be used with it. For example, if the import name was: +// "git://example.com/purpleidea/Module-Name", this might return an alias of +// "module_name". It also returns a bunch of other data about the parsed import. +// TODO: check for invalid or unwanted special characters +func ParseImportName(name string) (*interfaces.ImportData, error) { + magicPrefix := ModuleMagicPrefix + if name == "" { + return nil, fmt.Errorf("empty name") + } + if strings.HasPrefix(name, "/") { + return nil, fmt.Errorf("absolute paths are not allowed") + } + + u, err := url.Parse(name) + if err != nil { + return nil, errwrap.Wrapf(err, "name is not a valid url") + } + if u.Path == "" { + return nil, fmt.Errorf("empty path") + } + p := u.Path + // catch bad paths like: git:////home/james/ (note the quad slash!) + // don't penalize if we have a dir with a trailing slash at the end + if s := path.Clean(u.Path); u.Path != s && u.Path != s+"/" { + // TODO: are there any cases where this is not what we want? + return nil, fmt.Errorf("dirty path, cleaned it's: `%s`", s) + } + + for strings.HasSuffix(p, "/") { // remove trailing slashes + p = p[:len(p)-len("/")] + } + + split := strings.Split(p, "/") // take last chunk if slash separated + s := split[0] + if len(split) > 1 { + s = split[len(split)-1] // pick last chunk + } + + // TODO: should we treat a special name: "purpleidea/mgmt-foo" as "foo"? + if magicPrefix != "" && strings.HasPrefix(s, magicPrefix) && len(s) > len(magicPrefix) { + s = s[len(magicPrefix):] + } + + s = strings.Replace(s, "-", "_", -1) // XXX: allow underscores in IDENTIFIER + if strings.HasPrefix(s, "_") || strings.HasSuffix(s, "_") { + return nil, fmt.Errorf("name can't begin or end with dash or underscore") + } + alias := strings.ToLower(s) + + // if this is a local import, it's a straight directory path + // if it's an fqdn import, it should contain a metadata file + + // if there's no protocol prefix, then this must be a local path + isLocal := u.Scheme == "" + // if it has a trailing slash or .mcl extension it's not a system import + isSystem := isLocal && !strings.HasSuffix(u.Path, "/") && !strings.HasSuffix(u.Path, interfaces.DotFileNameExtension) + // is it a local file? + isFile := !isSystem && isLocal && strings.HasSuffix(u.Path, interfaces.DotFileNameExtension) + xpath := u.Path // magic path + if isSystem { + xpath = "" + } + if !isLocal { + host := u.Host // host or host:port + split := strings.Split(host, ":") + if l := len(split); l == 1 || l == 2 { + host = split[0] + } else { + return nil, fmt.Errorf("incorrect number of colons (%d) in hostname", l) + } + xpath = path.Join(host, xpath) + } + if !isLocal && !strings.HasSuffix(xpath, "/") { + xpath = xpath + "/" + } + // we're a git repo with a local path instead of an fqdn over http! + // this still counts as isLocal == false, since it's still a remote + if u.Host == "" && strings.HasPrefix(u.Path, "/") { + xpath = strings.TrimPrefix(xpath, "/") // make it a relative dir + } + if strings.HasPrefix(xpath, "/") { // safety check (programming error?) + return nil, fmt.Errorf("can't parse strange import") + } + + // build a url to clone from if we're not local... + // TODO: consider adding some logic that is similar to the logic in: + // https://github.com/golang/go/blob/054640b54df68789d9df0e50575d21d9dbffe99f/src/cmd/go/internal/get/vcs.go#L972 + // so that we can more correctly figure out the correct url to clone... + xurl := "" + if !isLocal { + u.Fragment = "" + // TODO: maybe look for ?sha1=... or ?tag=... to pick a real ref + u.RawQuery = "" + u.ForceQuery = false + xurl = u.String() + } + + // if u.Path is local file like: foo/server.mcl alias should be "server" + // we should trim the alias to remove the .mcl (the dir is already gone) + if isFile && strings.HasSuffix(alias, interfaces.DotFileNameExtension) { + alias = strings.TrimSuffix(alias, interfaces.DotFileNameExtension) + } + + return &interfaces.ImportData{ + Name: name, // save the original value here + Alias: alias, + IsSystem: isSystem, + IsLocal: isLocal, + IsFile: isFile, + Path: xpath, + URL: xurl, + }, nil +} diff --git a/test/test-golint.sh b/test/test-golint.sh index 9d0c8554..a7d4e22d 100755 --- a/test/test-golint.sh +++ b/test/test-golint.sh @@ -27,7 +27,7 @@ if [ "$COMMITS" != "" ] && [ "$COMMITS" -gt "1" ]; then fi # find all go files, exluding temporary directories and generated files -LINT=$(find * -maxdepth 9 -iname '*.go' -not -path 'old/*' -not -path 'tmp/*' -not -path 'bindata/*' -not -path 'lang/y.go' -not -path 'lang/lexer.nn.go' -not -path 'lang/interpolate/parse.generated.go' -not -path 'lang/types/kind_stringer.go' -not -path 'vendor/*' -exec golint {} \;) # current golint output +LINT=$(find * -maxdepth 9 -iname '*.go' -not -path 'old/*' -not -path 'tmp/*' -not -path 'bindata/*' -not -path 'lang/parser/y.go' -not -path 'lang/parser/lexer.nn.go' -not -path 'lang/interpolate/parse.generated.go' -not -path 'lang/types/kind_stringer.go' -not -path 'vendor/*' -exec golint {} \;) # current golint output COUNT=`echo -e "$LINT" | wc -l` # number of golint problems in current branch [ "$LINT" = "" ] && echo PASS && exit # everything is "perfect" @@ -55,7 +55,7 @@ while read -r line; do done <<< "$NUMSTAT1" # three < is the secret to putting a variable into read git checkout "$PREVIOUS" &>/dev/null # previous commit -LINT1=$(find * -maxdepth 9 -iname '*.go' -not -path 'old/*' -not -path 'tmp/*' -not -path 'bindata/*' -not -path 'lang/y.go' -not -path 'lang/lexer.nn.go' -not -path 'vendor/*' -exec golint {} \;) +LINT1=$(find * -maxdepth 9 -iname '*.go' -not -path 'old/*' -not -path 'tmp/*' -not -path 'bindata/*' -not -path 'lang/parser/y.go' -not -path 'lang/parser/lexer.nn.go' -not -path 'vendor/*' -exec golint {} \;) COUNT1=`echo -e "$LINT1" | wc -l` # number of golint problems in older branch # clean up diff --git a/test/test-gometalinter.sh b/test/test-gometalinter.sh index 3abd8faf..4f8dfb67 100755 --- a/test/test-gometalinter.sh +++ b/test/test-gometalinter.sh @@ -45,8 +45,8 @@ gml="$gml --enable=misspell" # exclude generated files: # TODO: at least until https://github.com/alecthomas/gometalinter/issues/270 -gml="$gml --exclude=lang/lexer.nn.go" -gml="$gml --exclude=lang/y.go" +gml="$gml --exclude=lang/parser/lexer.nn.go" +gml="$gml --exclude=lang/parser/y.go" gml="$gml --exclude=bindata/bindata.go" gml="$gml --exclude=lang/types/kind_stringer.go" gml="$gml --exclude=lang/interpolate/parse.generated.go" diff --git a/test/test-govet.sh b/test/test-govet.sh index a79456b6..ce92235c 100755 --- a/test/test-govet.sh +++ b/test/test-govet.sh @@ -109,7 +109,7 @@ function reflowed-comments() { return 0 fi - if [ "$1" = './lang/lexer.nn.go' ]; then + if [ "$1" = './lang/parser/lexer.nn.go' ]; then return 0 fi