-
Notifications
You must be signed in to change notification settings - Fork 176
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Migrating away from with()
so we can move to ESM
#967
Comments
"Find all operands" seems a necessary step for all PL compilers (parsers). Can you steal someone else's algorithm? Is there something that makes it unusually difficult for Mavo? |
Indeed; and the AST format is standardized. I wondered about this too, someone (either @DmitrySharabin or a UROP/MEng) just needs to do the research |
I have found a couple of articles that might be helpful, including the one from Douglas Crockford—the creator of JSLint: |
From a quick skim, not sure these help in extracting operands? Can you point me to the relevant place? |
I created a couple issues with detailed steps towards getting to this (having @adamjanicki2 in mind) and added them to a tasklist at the end of the first post. Eventually we'd need to move to a project, but that's probably fine for now. @adamjanicki2 may end up finding an existing algorithm for this instead, but if not, these steps are almost guaranteed to get us there. We should probably eventually release these helpers as a separate library for working with expression ASTs, since there's practically nothing Mavo-specific there. |
Attempt 1 for taking an expression, finding all variables, filtering variables and functions, and rewriting them all to have import * as parents from "./parents.js";
import variables from "./variables.js";
import serialize from "./serialize.js";
import jsep from "jsep";
function addMemberPrefix(node, prefix) {
if (node.type === "Identifier") {
node.type = "MemberExpression";
node.computed = false;
node.object = {type: "Identifier", name: prefix};
node.property = {type: "Identifier", name: node.name};
delete node.name;
}
}
const expr = "a.b + func(a, b)";
const ast = jsep(expr);
parents.setAll(ast);
const identifiers = variables(ast);
const vars = identifiers.filter(node => node !== node.parent?.callee);
const functions = identifiers.filter(node => node === node.parent?.callee);
for (const variable of vars) {
addMemberPrefix(variable, "$data");
}
for (const func of functions) {
addMemberPrefix(func, "$fn");
}
console.log(serialize(ast)); // logs: $data.a.b + $fn.func($data.a, $data.b) |
@LeaVerou thoughts on this? |
@adamjanicki2 Sorry, I assumed you were still iterating. Just checking As a simple example off the top of my head, you could have |
Can you explain this a bit more? What does function call to the host environment mean?
So this case is properly handled by the code I pasted above, for example, the example you provided, |
E.g. functions like |
Ok makes sense, I see what you mean. So instead of the naive approach I have above, what if we check for membership in ...
for (const variable of vars) {
const prefix = globalThis[variable.name] ? "$global" : "$data";
addMemberPrefix(variable, prefix);
}
for (const func of functions) {
const prefix = globalThis[func.name] ? "$global" : "$fn";
addMemberPrefix(func, prefix);
}
... Then an expression like |
Yes, we could do this for now. |
Would having a function for "concatenating" member expressions (like above) be useful to include in vastly, or should this be implemented in Mavo? |
Yes, I think this could absolutely be more broadly useful! The only downside of handling this in vastly is that iit may need to handle more use cases than what Mavo needs. |
How about we implement this in mavo for now so we can move forward with removing |
Sounds great. |
The primary reason Mavo is not ESM yet is that we use
with()
to evaluate expressions:ESM enforces strict mode, which does not allow
with()
.Refactoring our code so that it doesn't use
with()
will not only allow us to move to ESM, but will also make expression evaluation faster (though it's hard to tell how much).This is not a simple undertaking, but I do think it's possible. We're already rewriting function calls to be called on
$fn
(in the past we usedwith()
for that too), we need to do something similar for data as well. Something like this:$data
(e.g.foo + bar.baz
would become$data.foo + $data.bar.baz
)$data
right under $fnto something like:
let $data = data ?? Mavo.Data.stub;Number 1 is the tricky bit. I've had a lot of false starts trying to write an algorithm to correctly extract all operands from an expression. It seems deceptively simple, but is not if you take all the corner cases into account. Perhaps we need a structured effort:
Expression
objects and prints an array in the console. Or even one that appends them to a JSON file, so all we need to do is login (if we're not already logged in), press the button and chill.foo + bar
anda * b
, these have the same structure, they're bothBinaryExpression
nodes with aleft
andright
that areIdentifier
nodes)Rewriting operands to be chained on
$data
will also make it easier to maybe one day rewrite Mavo to use another reactivity engine under the hood, which will resolve the source of a great chunk of bugs.PS: We'd need to release a new stable version before any of this work. We need to fix 3 regressions for that to happen, none of which seem particularly hard.
Tasks
Mavo.Script.getVariables(ast)
function to extract expression variables #975where
andby
#1002$data.
#976The text was updated successfully, but these errors were encountered: