lang: Plumb data and unification strategy through the lang struct

This adds some plumbing to pass values into the lang struct.
This commit is contained in:
James Shubin
2024-03-30 17:25:20 -04:00
parent cede7e5ac0
commit ddf1be653e
7 changed files with 73 additions and 16 deletions

View File

@@ -70,6 +70,9 @@ func init() {
type GAPI struct {
InputURI string // input URI of code file system to run
// Data is some additional data for the lang struct.
Data *lang.Data
lang *lang.Lang // lang struct
wgRun *sync.WaitGroup
ctx context.Context
@@ -261,6 +264,11 @@ func (obj *GAPI) Cli(info *gapi.Info) (*gapi.Deploy, error) {
return nil, nil // success!
}
unificationStrategy := make(map[string]string)
if name := args.UnifySolver; name != nil && *name != "" {
unificationStrategy[unification.StrategyNameKey] = *name
}
if !args.SkipUnify {
// apply type unification
unificationLogf := func(format string, v ...interface{}) {
@@ -275,10 +283,11 @@ func (obj *GAPI) Cli(info *gapi.Info) (*gapi.Deploy, error) {
return nil, errwrap.Wrapf(err, "could not get default solver")
}
unifier := &unification.Unifier{
AST: iast,
Solver: solver,
Debug: debug,
Logf: unificationLogf,
AST: iast,
Solver: solver,
Strategy: unificationStrategy,
Debug: debug,
Logf: unificationLogf,
}
startTime := time.Now()
unifyErr := unifier.Unify(context.TODO())
@@ -411,7 +420,10 @@ func (obj *GAPI) Cli(info *gapi.Info) (*gapi.Deploy, error) {
Sema: info.Flags.Sema,
GAPI: &GAPI{
InputURI: fs.URI(),
// TODO: add properties here...
Data: &lang.Data{
UnificationStrategy: unificationStrategy,
// TODO: add properties here...
},
},
}, nil
}
@@ -451,6 +463,7 @@ func (obj *GAPI) LangInit(ctx context.Context) error {
Fs: fs,
FsURI: obj.InputURI,
Input: input,
Data: obj.Data,
Hostname: obj.data.Hostname,
Local: obj.data.Local,

View File

@@ -64,6 +64,18 @@ const (
EngineStartupStatsTimeout = 10
)
// Data is some data that is passed into the Lang struct. It is presented here
// as a single struct with room for multiple fields so that it can be changed or
// extended in the future without having to re-plumb through all the fields it
// contains
type Data struct {
// UnificationStrategy is a hack to tune unification performance until
// we have an overall cleaner unification algorithm in place.
UnificationStrategy map[string]string
// TODO: Add other fields here if necessary.
}
// Lang is the main language lexer/parser object.
type Lang struct {
Fs engine.Fs // connected fs where input dir or metadata exists
@@ -79,6 +91,9 @@ type Lang struct {
// run the raw string as mcl code.
Input string
// Data is some additional data for the lang struct.
Data *Data
Hostname string
Local *local.API
World engine.World
@@ -101,6 +116,12 @@ type Lang struct {
// watching them, *before* we pull their values, that way we'll know if they
// changed from the values we wanted.
func (obj *Lang) Init(ctx context.Context) error {
if obj.Data == nil {
return fmt.Errorf("lang struct was not built properly")
}
if obj.Data.UnificationStrategy == nil {
return fmt.Errorf("lang struct was not built properly")
}
if obj.Debug {
obj.Logf("input: %s", obj.Input)
tree, err := util.FsTree(obj.Fs, "/") // should look like gapi
@@ -227,15 +248,20 @@ func (obj *Lang) Init(ctx context.Context) error {
}
obj.Logf("running type unification...")
solver, err := unification.LookupDefault()
if err != nil {
var solver unification.Solver
if name, exists := obj.Data.UnificationStrategy["solver"]; exists && name != "" {
if solver, err = unification.Lookup(name); err != nil {
return errwrap.Wrapf(err, "could not get solver: %s", name)
}
} else if solver, err = unification.LookupDefault(); err != nil {
return errwrap.Wrapf(err, "could not get default solver")
}
unifier := &unification.Unifier{
AST: obj.ast,
Solver: solver,
Debug: obj.Debug,
Logf: logf,
AST: obj.ast,
Solver: solver,
Strategy: obj.Data.UnificationStrategy,
Debug: obj.Debug,
Logf: logf,
}
timing = time.Now()
// NOTE: This is the "real" Unify that runs. (This is not for deploy.)

View File

@@ -137,7 +137,10 @@ func runInterpret(t *testing.T, code string) (_ *pgraph.Graph, reterr error) {
lang := &Lang{
Fs: fs,
Input: "/" + interfaces.MetadataFilename, // start path in fs
Debug: testing.Verbose(), // set via the -test.v flag to `go test`
Data: &Data{
UnificationStrategy: make(map[string]string), // empty
},
Debug: testing.Verbose(), // set via the -test.v flag to `go test`
Logf: logf,
}
if err := lang.Init(ctx); err != nil {

View File

@@ -42,11 +42,18 @@ const (
// ErrAmbiguous means we couldn't find a solution, but we weren't
// inconsistent.
ErrAmbiguous = interfaces.Error("can't unify, no equalities were consumed, we're ambiguous")
// StrategyNameKey is the string key used when choosing a solver name.
StrategyNameKey = "name"
)
// Init contains some handles that are used to initialize every solver. Each
// individual solver can choose to omit using some of the fields.
type Init struct {
// Strategy is a hack to tune unification performance until we have an
// overall cleaner unification algorithm in place.
Strategy map[string]string
Debug bool
Logf func(format string, v ...interface{})
}

View File

@@ -75,6 +75,8 @@ type SimpleInvariantSolver struct {
// Init contains some handles that are used to initialize the solver.
func (obj *SimpleInvariantSolver) Init(init *unification.Init) error {
obj.Strategy = init.Strategy
obj.Debug = init.Debug
obj.Logf = init.Logf

View File

@@ -48,6 +48,10 @@ type Unifier struct {
// Solver is the solver algorithm implementation to use.
Solver Solver
// Strategy is a hack to tune unification performance until we have an
// overall cleaner unification algorithm in place.
Strategy map[string]string
Debug bool
Logf func(format string, v ...interface{})
}
@@ -76,8 +80,9 @@ func (obj *Unifier) Unify(ctx context.Context) error {
}
init := &Init{
Logf: obj.Logf,
Debug: obj.Debug,
Strategy: obj.Strategy,
Logf: obj.Logf,
Debug: obj.Debug,
}
if err := obj.Solver.Init(init); err != nil {
return err