Skip to content

Commit

Permalink
V622-017: Fix env grouping with non-default metadata.
Browse files Browse the repository at this point in the history
The routine for grouping lexical envs would previously always flatten grouped
envs. However, this process would discard the `default_md` field of the nested
grouped envs.
The implementation now decides to flatten grouped envs only if the resulting
env behavior is the same.
  • Loading branch information
Roldak committed Sep 1, 2022
1 parent 540f2d3 commit a638fac
Show file tree
Hide file tree
Showing 7 changed files with 166 additions and 12 deletions.
24 changes: 12 additions & 12 deletions langkit/support/langkit_support-lexical_envs_impl.adb
Original file line number Diff line number Diff line change
Expand Up @@ -1635,18 +1635,18 @@ package body Langkit_Support.Lexical_Envs_Impl is

procedure Append_Envs (E : Lexical_Env) is
begin
case E.Kind is
-- Flatten grouped envs
when Grouped =>
for C of Unwrap (E).Grouped_Envs.all loop
Append_Envs (C);
end loop;
when others =>
if not Already_Has (E) then
Inc_Ref (E);
V.Append (E);
end if;
end case;
-- Flatten grouped envs only when we don't lose the `default_md`
-- field of a nested grouped env.
if E.Kind in Grouped
and then Unwrap (E).Default_Md in Empty_Metadata | With_Md
then
for C of Unwrap (E).Grouped_Envs.all loop
Append_Envs (C);
end loop;
elsif not Already_Has (E) then
Inc_Ref (E);
V.Append (E);
end if;
end Append_Envs;
begin
if Envs'Length = 0 then
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import lexer_example

@with_lexer(foo_lexer)
grammar foo_grammar {
@main_rule main_rule <- list+(decl)
decl <- Decl(Name(@identifier) "(" list*(ref) ")")
ref <- Ref(Name(@identifier))
}

@abstract class FooNode implements Node[FooNode] {

@memoized fun env_with_md(foo_node: FooNode, bar_node: FooNode): LexicalEnv[FooNode] =
{
val md1 = Metadata(foo_node=foo_node, bar_node=null);
val md2 = Metadata(foo_node=null, bar_node=bar_node);

[[node.node_env()].env_group()].env_group()
}

@export fun get_with_md(name: Symbol, foo_node: FooNode, bar_node: FooNode): FooNode =
self.env_with_md(foo_node, bar_node).get_first(name)

@export fun get_foo_metadata(): FooNode = self.info.md.foo_node

@export fun get_bar_metadata(): FooNode = self.info.md.bar_node
}

class Decl : FooNode {
@parse_field name: Name
@parse_field refs: ASTList[FooNode, Ref]
}

class Name : FooNode implements TokenNode {
}

class Ref : FooNode {
@parse_field name: Name
}
24 changes: 24 additions & 0 deletions testsuite/tests/lexical_envs/grouped_env_default_md/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import sys

import libfoolang


print('main.py: Running...')

ctx = libfoolang.AnalysisContext()
u = ctx.get_from_file('main.txt')
if u.diagnostics:
for d in u.diagnostics:
print(d)
sys.exit(1)

decl_list = u.root
foo = decl_list[0]
bar = decl_list[1]

baz = decl_list.p_get_with_md("baz", foo, bar)
print(baz.f_name.text + " has the following metadata:")
print(" - foo_node: " + baz.p_get_foo_metadata.f_name.text)
print(" - bar_node: " + baz.p_get_bar_metadata.f_name.text)

print('main.py: Done.')
7 changes: 7 additions & 0 deletions testsuite/tests/lexical_envs/grouped_env_default_md/main.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
foo(
bar
baz
)

bar()
baz()
6 changes: 6 additions & 0 deletions testsuite/tests/lexical_envs/grouped_env_default_md/test.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
main.py: Running...
baz has the following metadata:
- foo_node: foo
- bar_node: bar
main.py: Done.
Done
78 changes: 78 additions & 0 deletions testsuite/tests/lexical_envs/grouped_env_default_md/test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
"""
Test that nested grouped envs with non-null default metadata behave as
expected.
"""

from langkit.dsl import ASTNode, Field, Struct, T, UserField, env_metadata
from langkit.envs import EnvSpec, add_env, add_to_env_kv
from langkit.expressions import Entity, No, Self, Var, langkit_property

from utils import build_and_run


@env_metadata
class Metadata(Struct):
foo_node = UserField(T.FooNode)
bar_node = UserField(T.FooNode)


class FooNode(ASTNode):
@langkit_property(memoized=True)
def env_with_md(foo_node=T.FooNode, bar_node=T.FooNode):
md1 = Var(Metadata.new(
foo_node=foo_node,
bar_node=No(T.FooNode)
))
md2 = Var(Metadata.new(
foo_node=No(T.FooNode),
bar_node=bar_node
))
return Self.node_env.singleton.env_group(
with_md=md1
).singleton.env_group(
with_md=md2
)

@langkit_property(return_type=T.FooNode.entity, public=True)
def get_with_md(
name=T.Symbol,
foo_node=T.FooNode,
bar_node=T.FooNode
):
return Entity.env_with_md(foo_node, bar_node).get_first(name)

@langkit_property(return_type=T.FooNode, public=True)
def get_foo_metadata():
return Entity.info.md.foo_node

@langkit_property(return_type=T.FooNode, public=True)
def get_bar_metadata():
return Entity.info.md.bar_node


class Name(FooNode):
token_node = True


class Decl(FooNode):
name = Field()
refs = Field()

env_spec = EnvSpec(
add_to_env_kv(
key=Self.name.symbol, value=Self
),
add_env()
)


class Ref(FooNode):
name = Field()

env_spec = EnvSpec(add_to_env_kv(
key=Self.name.symbol, value=Self
))


build_and_run(lkt_file='expected_concrete_syntax.lkt', py_script='main.py')
print('Done')
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
driver: python

0 comments on commit a638fac

Please sign in to comment.