Closed scizzorz closed 4 years ago
The PR is updated with some more behavioral changes. Notably:
Moldfile
, it now has a reference to the owning Mold
and can check for a variable's existence. This breaks the interface boundary a bit, but it avoids weird ordering issues that showed up during the initial approach. I think it's the most effective way to handle this.
The implementation was a little trickier than I intended because the behavior was a little trickier than I expected. The current details for handling variable assignments are roughly as follows:
Mold
is created as the main execution context with a variable map containing a few special Mold variables likeMOLD_ROOT
andMOLD_DIR
Moldfile
is created from a source file and collects all of the top-level variable assignments into its own variable mapRecipe
in aMoldfile
collects all of its internal variable assignments into its own variable mapMold
opens aMoldfile
, such as the initial file or during an import, all of theMoldfile
's contents (recipes, variables, etc) are merged into theMold
, overriding any existing values.Recipe
is executed, the recipe's contents (working dir, variables, etc) are merged into a copy of the theMold
's contents, overriding any existing values.Recipe
's execution variable map and fall back to the OS environment.So, in short, the variable priority is roughly:
Recipe
variables >Moldfile
variables > environment variables. Because of this ordering, it's not possible for Mold to provide a default value for a variable that can be overridden by the environment; any default values will have to be handled in the script rather than in Mold.The open issue #128 is intended to address this, and I was expecting that I could simply parse the default assignment and apply it if the variable map and environment turned up empty. Because of the multi-layered variable system (
Mold
,Moldfile
, andRecipe
), my initial assumptions don't work out well without breaking some boundaries: aMoldfile
would need to inspect the variable map of theMold
that's opening it, and aRecipe
would need to inspect the variable map of theMoldfile
that defines it, etc. Unfortunately, I don't think that's feasible and would significantly impact the interfaces between those concepts.The approach I've taken here is to add a secondary variable map,
defaults
, for all three structures. This variable map takes the opposite prioritization approach: the first definition takes priority, rather than the last. That is:$foo
would evaluate to"two"
here (last assignment takes priority), while:$foo
would evaluate to"one"
here (first assignment takes priority).Following the way that
Recipe
s are treated as happening "after" theMoldfile
, allowing their variables override the file's, but respecting the inversion of priority for default variables, the file's defaults override the recipe's defaults. Default values are appended to the priority list after environment variables, making the final priority list look something like this:Recipe
variables >Moldfile
variables > environment variables >Moldfile
defaults >Recipe
defaults.