Skip to content

evaluator: Add index and dot expression evaluation#39

Merged
juliaogris merged 6 commits intomasterfrom
eval-index
Oct 19, 2022
Merged

evaluator: Add index and dot expression evaluation#39
juliaogris merged 6 commits intomasterfrom
eval-index

Conversation

@juliaogris
Copy link
Member

@juliaogris juliaogris commented Oct 18, 2022

Add index and dot expression evaluation in evaluator, similar to binary
expression evaluation.

Change Array.Elements and Map.Order to pointer. This is another preparatory
step for proper array and map manipulation so that two variables may
reference the same array or map and if one gets updated the other one will
get updated too.

Rework scoped Value updates such that instead of replacing the value in the
scope on assignment with scope.set(name, newValue), we now update the Value
field with scope.get(name).Set(newValue)


The first three commits are covered by PRs #37 and #38 .

@github-actions
Copy link

github-actions bot commented Oct 18, 2022

firebase-deployment: https://evy-lang--39-zvdfxh68.web.app (0237bfa)

`print 1 (len "abc") 2`: "print(1, len('abc'), 2)",
`print (len "abc") 2`: "print(len('abc'), 2)",
`print (len "abc") (len "x")`: "print(len('abc'), len('x'))",
`print s[1]`: "print((s[1]))",
Copy link
Member Author

Choose a reason for hiding this comment

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

This was a nil panic before

`
m := {name: "Greta"}
s := name
print m[s]`: "line 3 column 6: unknown variable name 'name'",
Copy link
Member Author

Choose a reason for hiding this comment

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

nil panic before fix.

Copy link
Member

@camh- camh- left a comment

Choose a reason for hiding this comment

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

🍃 LGTM. A few comments, but feel free to fix, commit and merge.

return val
}
if decl.Type() == parser.ANY_TYPE {
val = &Any{Val: val}
Copy link
Member

Choose a reason for hiding this comment

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

If e.Eval(...) above returns an Any, should you just use val as is?

Copy link
Member Author

Choose a reason for hiding this comment

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

If I remove these added lines I get a test failures:

--- FAIL: TestAssignmentAny (0.00s)
    evaluator_test.go:174: want != got
        "false 0\n0 0\n🦊 0\n"
        "false 0\nfalse 0\nfalse 0\n"

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 zero value for any is false; the inferred decl might by anything, but in either case it does not return the wrapped any type it seems but the bare type.

Copy link
Member Author

Choose a reason for hiding this comment

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

If you search for &Any{.... it doesn't seem to be created anywhere in the evaluator.go file.

Copy link
Member Author

Choose a reason for hiding this comment

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

OH! I think you are suggesting I should just add it to the main switch.

Copy link
Member Author

Choose a reason for hiding this comment

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

good idea.

Copy link
Member Author

Choose a reason for hiding this comment

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

hmmm. not sure actually

Copy link
Member Author

Choose a reason for hiding this comment

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

add a parseAnyLiteral ?
The wrapped value could hold an error so we want to surface that I think.

Copy link
Member Author

Choose a reason for hiding this comment

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

also: there is no AST node for Any...
I think I need to leave this as is.

Copy link
Member

Choose a reason for hiding this comment

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

I just meant if eval could return an any will you end up wrapping an any in an any?


func (m *Map) Set(v Value) {
if m2, ok := v.(*Map); ok {
m.Pairs = m2.Pairs
Copy link
Member

Choose a reason for hiding this comment

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

Just *m = *m2 should do i think (instead of assigning individual elements) - and you could do that for all the Set() methods if you like

Copy link
Member Author

Choose a reason for hiding this comment

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

ok, done.

case *Map:
index, ok := index.(*String)
if !ok {
return newError("expected string for map index, found " + index.String())
Copy link
Member

Choose a reason for hiding this comment

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

hmmm, isn't index here an empty string or something from two lines above? Shadowing vars is confusing. I was also confused about left which I thought was a Node, but later realised it was shadowed by the type switch. I guess its ok, I'm not sure what other name to give it that makes sense.

But, shouldn't you be printing the type of the incorrect map index? as in "expected , got "?

Edit: checked the spec, and indeed index will be an empty string in this error message.

Copy link
Member Author

Choose a reason for hiding this comment

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

ok, I've renamed this to strIndex and also renamed left := left.(type) to l := left.(type).
I've added an extra fixup commit on top doing the rename from the previous evalBinaryExpr function for consitency.

"print m.name": "Greta",
`print m["name"]`: "Greta",
`s := "name"
print m[s]`: "Greta",
Copy link
Member

Choose a reason for hiding this comment

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

Should there be a test case for looking up a key not in the map? I can't remember what the semantics of that is? Returns zero value like Go? (as an aside, what's the zero value of any? doesn't seem to be in the spec)

ok, I see from below value.go that a bad index is an error. leaving this comment for the any question.

🍍

Copy link
Member Author

Choose a reason for hiding this comment

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

ok, good point. I've added a test case. Will add more when implementing has and del functions.

I wonder if I should return the zero value....

Copy link
Member Author

Choose a reason for hiding this comment

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

sorry, no good at not answering.

Fix IndexExpression.Type() for indexed string, e.g. "abc"[1]. We
indiscriminately returned left.Type().Sub, which is only correct for
arrays. For strings the indexed type should also be string.
Add missing early return on index expression parse error. This resulted
in a "dereferenced nil pointer" panic at run time.
Rework scoped Value updates such that instead of replacing the value in
the scope on assignment with scope.set(name, newValue), we now update
the Value field with:

	val := scope.get(name)
	val.set(newVal)

This is a preparatory step for index and dot expressions which also
update the "internals" of a Value.

As a fall-out of this step we had to extend the Value interface with a
`Set(Val)` method. We also had to remove the bool constants TRUE and
FALSE, as the internals of these constants got updated. `scope.getScope
(name)` is now not needed any longer and removed. Additionally
introduce an `Any` type that implements Value and wraps a value so that
we can update an any value from one type to another, e.g. Bool to
String.
Change Array.Elements and Map.Order to pointer. This is another
preparatory step for proper array and map manipulation so that two
variables may reference the same array or map and if one gets updated
the other one will get updated too.

In go maps are references and natively support this property, however
slices may get allocated new memory and are not guaranteed to keep the
same reference and appended.
@juliaogris juliaogris force-pushed the eval-index branch 2 times, most recently from ba173e9 to f27370f Compare October 19, 2022 22:53
Add index and dot expression evaluation in evaluator, similar to binary
expression evaluation.
Rename local variable in evalBinaryExpr for consistency with new
evalIndexExpr: avoid variable shadowing when type casting in switch
statement.
@juliaogris juliaogris merged commit f8b2aca into master Oct 19, 2022
@juliaogris juliaogris deleted the eval-index branch October 19, 2022 23:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants