lang: Split lang package out into many subpackages

This is a giant refactor to split the giant lang package into many
subpackages. The most difficult piece was figuring out how to extract
the extra ast structs into their own package, because they needed to
call two functions which also needed to import the ast.

The solution was to separate out those functions into their own
packages, and to pass them into the ast at the root when they're needed,
and to let the relevant ast portions call a handle.

This isn't terribly ugly because we already had a giant data struct
woven through the ast.

The bad part is rebasing any WIP work on top of this.
This commit is contained in:
James Shubin
2021-10-21 03:35:31 -04:00
parent 8ae47bd490
commit 23b5a4729f
23 changed files with 1212 additions and 1129 deletions

View File

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