Skip to content
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

Update behavior of augmented null coalescing operator #3614

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 24 additions & 22 deletions M2/Macaulay2/d/evaluate.d
Original file line number Diff line number Diff line change
Expand Up @@ -1236,16 +1236,37 @@ tryEval(c:Code):Expr := (
else tryEvalSuccess = true);
p);

nullify(c:Code):Expr := (
e := tryEval(c);
if tryEvalSuccess
then (
when e
is Nothing do e
else (
f := lookup(Class(e), QuestionQuestionS);
if f == nullE then e
else applyEE(f, e)))
else nullE);

nullCoalescion(lhs:Code,rhs:Code):Expr := (
e := nullify(lhs);
when e
is Nothing do eval(rhs)
else e);
setup(QuestionQuestionS, nullify, nullCoalescion);

augmentedAssignmentFun(x:augmentedAssignmentCode):Expr := (
when lookup(x.oper.word, augmentedAssignmentOperatorTable)
is null do buildErrorPacket("unknown augmented assignment operator")
is s:Symbol do (
-- evaluate the left-hand side first
lexpr := nullE;
if s.word.name === "??" -- null coalescion; ignore errors
if s.word.name === "??" -- x ??= y is treated like x ?? (x = y)
then (
e := tryEval(x.lhs);
if tryEvalSuccess then lexpr = e)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a little confused. Where is the actual assignment to lexpr happening now?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need it anymore when the left-hand side is non-null since we return right away. And when it's null, we've already assigned lexpr as nullE a couple lines earlier.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still confused. Where does the x = y in x ?? (x = y) is happening? I thought here lexpr was x.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The assignment happens in one of several places, depending on what the code looks like.

lexpr is the evaluated left-hand side. But we still have the original unevaluated Code object, which we need to figure out what type of assignment we'll end up doing (global, local, assigning an element of a list/hash table, etc.). Here's the basic gist:

  • Go ahead and evaluate the left hand side (lexpr) and stuff it in an evaluatedCode object (left). We also check for errors here and whether someone has installed an augmented assignment method for this operator for the class of lexpr. If so, call that and return the result.
  • Now go back and look at the original unevaluated Code object to figure out how to assign things. But instead of using this Code object, we swap it out with left to avoid re-evaluating the left-hand side.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, that makes sense.

e := nullify(x.lhs);
when e
is Nothing do nothing
else return e)
else lexpr = eval(x.lhs);
left := evaluatedCode(lexpr, dummyPosition);
when left.expr is e:Error do return Expr(e) else nothing;
Expand Down Expand Up @@ -2091,25 +2112,6 @@ export notFun(a:Expr):Expr := if a == True then False else if a == False then Tr
-- to evaluate methods in hashtables.dd before it is defined.
applyEEEpointer = applyEEE;

nullify(c:Code):Expr := (
e := tryEval(c);
if tryEvalSuccess
then (
when e
is Nothing do e
else (
f := lookup(Class(e), QuestionQuestionS);
if f == nullE then e
else applyEE(f, e)))
else nullE);

nullCoalescion(lhs:Code,rhs:Code):Expr := (
e := nullify(lhs);
when e
is Nothing do eval(rhs)
else e);
setup(QuestionQuestionS, nullify, nullCoalescion);

-- Local Variables:
-- compile-command: "echo \"make: Entering directory \\`$M2BUILDDIR/Macaulay2/d'\" && make -C $M2BUILDDIR/Macaulay2/d evaluate.o "
-- End:
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,10 @@ doc ///
The null coalescing operator can be combined with
@TO "augmented assignment"@ as a shortcut for
@M2CODE "if x === null then x = y"@ and
@M2CODE "if not x#?i then x#i = y"@.
@M2CODE "if not x#?i then x#i = y"@. Note that it behaves
slightly differently than other augmented assignment operators,
as @CODE "x ??= y"@ is treated like @CODE "x ?? (x = y)"@ rather
than @CODE "x = x ?? y"@.
Example
x = null
x ??= 2
Expand Down
4 changes: 4 additions & 0 deletions M2/Macaulay2/tests/normal/augmented-assignment.m2
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,7 @@ assert BinaryOperation(symbol ===, y ?? x, y)
assert BinaryOperation(symbol ===, x ?? y, y)
x ??= y
assert BinaryOperation(symbol ===, x, y)

-- issue #3612
h = new HashTable from { symbol cache => new CacheTable };
h.cache ??= new CacheTable
Loading