Skip to content

Commit 1497f0e

Browse files
committed
unified: Declare exposed fields on supertypes
Supertypes in ast_nodes.yml can now have a list of fields to expose in the generated QL class. Callable.parameter is then exposed, which was previously not exposed because some callables cannot have parameters, but it's useful to have the getter available anyway.
1 parent 9e935d8 commit 1497f0e

9 files changed

Lines changed: 423 additions & 148 deletions

File tree

‎shared/tree-sitter-extractor/src/extractor/mod.rs‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -904,7 +904,8 @@ impl<'a> Visitor<'a> {
904904
if tp == single_type {
905905
return true;
906906
}
907-
if let EntryKind::Union { members } = &self.schema.get(single_type).unwrap().kind
907+
if let EntryKind::Union { members, .. } =
908+
&self.schema.get(single_type).unwrap().kind
908909
&& self.type_matches_set(tp, members)
909910
{
910911
return true;
@@ -926,7 +927,7 @@ impl<'a> Visitor<'a> {
926927
return true;
927928
}
928929
for other in types.iter() {
929-
if let EntryKind::Union { members } = &self.schema.get(other).unwrap().kind
930+
if let EntryKind::Union { members, .. } = &self.schema.get(other).unwrap().kind
930931
&& self.type_matches_set(tp, members)
931932
{
932933
return true;

‎shared/tree-sitter-extractor/src/generator/mod.rs‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,9 @@ fn convert_nodes(
399399
.collect();
400400
for node in nodes.values() {
401401
match &node.kind {
402-
node_types::EntryKind::Union { members: n_members } => {
402+
node_types::EntryKind::Union {
403+
members: n_members, ..
404+
} => {
403405
// It's a tree-sitter supertype node, for which we create a union
404406
// type.
405407
let members: Set<&str> = n_members

‎shared/tree-sitter-extractor/src/generator/ql_gen.rs‎

Lines changed: 133 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -799,7 +799,7 @@ fn compute_direct_supertypes(
799799
) -> std::collections::BTreeMap<node_types::TypeName, BTreeSet<&str>> {
800800
let mut supertypes = std::collections::BTreeMap::new();
801801
for node in nodes.values() {
802-
if let node_types::EntryKind::Union { members } = &node.kind {
802+
if let node_types::EntryKind::Union { members, .. } = &node.kind {
803803
for member in members {
804804
supertypes
805805
.entry(member.clone())
@@ -841,12 +841,9 @@ fn same_predicate_signature(a: &ql::Predicate, b: &ql::Predicate) -> bool {
841841
a.name == b.name && a.return_type == b.return_type && a.formal_parameters == b.formal_parameters
842842
}
843843

844-
/// Computes, for each tree-sitter supertype (union) node, the list of
845-
/// predicates that are guaranteed to be defined identically (in terms of
846-
/// name, return type, and formal parameters, though not necessarily body) by
847-
/// every one of its members. These are the predicates that can be hoisted to
848-
/// an `abstract` predicate on the union's class, with the corresponding
849-
/// predicates on its members becoming `override`s.
844+
/// Computes the predicates explicitly exposed by a node. For a table these are
845+
/// its field predicates; for a union they are the predicates declared by the
846+
/// fields on that supertype.
850847
///
851848
/// The result for a given node is memoized in `cache` (keyed by its QL class
852849
/// name), and also used to answer the query for any other node that
@@ -869,24 +866,8 @@ fn compute_exposed_predicates<'a, 'b>(
869866
Some(node_types::EntryKind::Table { .. }) => {
870867
field_predicates.get(type_name).cloned().unwrap_or_default()
871868
}
872-
Some(node_types::EntryKind::Union { members }) => {
873-
let mut members = members.iter();
874-
let mut common = match members.next() {
875-
Some(first) => {
876-
compute_exposed_predicates(first, nodes, field_predicates, cache).clone()
877-
}
878-
None => Vec::new(),
879-
};
880-
for member in members {
881-
let member_predicates =
882-
compute_exposed_predicates(member, nodes, field_predicates, cache);
883-
common.retain(|predicate| {
884-
member_predicates
885-
.iter()
886-
.any(|other| same_predicate_signature(predicate, other))
887-
});
888-
}
889-
common
869+
Some(node_types::EntryKind::Union { .. }) => {
870+
field_predicates.get(type_name).cloned().unwrap_or_default()
890871
}
891872
Some(node_types::EntryKind::Token { .. }) | None => Vec::new(),
892873
};
@@ -930,25 +911,24 @@ pub fn convert_nodes(nodes: &node_types::NodeTypeMap) -> Vec<ql::TopLevel<'_>> {
930911
}
931912
}
932913

933-
// First, compute the field-getter predicates (and the expressions used by
934-
// `getAFieldOrChild`) for every table node, without yet knowing whether
935-
// any of them will need to be marked `override`. These are needed both
936-
// to build the final classes below, and to figure out which fields are
937-
// shared identically by all the members of a supertype.
914+
// First, compute field-getter predicates for tables and the explicitly
915+
// declared field predicates for supertypes.
938916
let mut field_predicates: BTreeMap<&node_types::TypeName, Vec<ql::Predicate<'_>>> =
939917
BTreeMap::new();
940918
let mut get_child_exprs: BTreeMap<&node_types::TypeName, Vec<ql::Expression<'_>>> =
941919
BTreeMap::new();
942920
for (type_name, node) in nodes {
943-
if let node_types::EntryKind::Table {
944-
name: main_table_name,
945-
fields,
946-
} = &node.kind
947-
{
948-
if fields.is_empty() {
949-
panic!("Encountered node '{}' with no fields", type_name.kind);
921+
let (main_table_name, fields, has_storage) = match &node.kind {
922+
node_types::EntryKind::Table { name, fields } => (name.as_str(), fields, true),
923+
node_types::EntryKind::Union { fields, .. } => {
924+
(node.dbscheme_name.as_str(), fields, false)
950925
}
951-
926+
node_types::EntryKind::Token { .. } => continue,
927+
};
928+
if has_storage && fields.is_empty() {
929+
panic!("Encountered node '{}' with no fields", type_name.kind);
930+
}
931+
if !fields.is_empty() {
952932
// Count how many columns there will be in the main table. There
953933
// will be one for the id, plus one for each field that's stored
954934
// as a column.
@@ -969,20 +949,18 @@ pub fn convert_nodes(nodes: &node_types::NodeTypeMap) -> Vec<ql::TopLevel<'_>> {
969949
nodes,
970950
);
971951
predicates.extend(get_preds);
972-
if let Some(get_child_expr) = get_child_expr {
952+
if has_storage && let Some(get_child_expr) = get_child_expr {
973953
exprs.push(get_child_expr)
974954
}
975955
}
976956
field_predicates.insert(type_name, predicates);
977-
get_child_exprs.insert(type_name, exprs);
957+
if has_storage {
958+
get_child_exprs.insert(type_name, exprs);
959+
}
978960
}
979961
}
980962

981-
// Next, for every supertype (union) node, compute the predicates that are
982-
// guaranteed to be defined identically (in name, return type, and formal
983-
// parameters) by every one of its members. Such predicates can be hoisted
984-
// to an `abstract` predicate on the supertype's class, with the
985-
// corresponding predicates on its members becoming `override`s.
963+
// Next, collect the predicates explicitly exposed by every supertype.
986964
let mut exposed_predicates: BTreeMap<&str, Vec<ql::Predicate<'_>>> = BTreeMap::new();
987965
for (type_name, node) in nodes {
988966
if let node_types::EntryKind::Union { .. } = &node.kind {
@@ -1017,10 +995,10 @@ pub fn convert_nodes(nodes: &node_types::NodeTypeMap) -> Vec<ql::TopLevel<'_>> {
1017995
}));
1018996
}
1019997
}
1020-
node_types::EntryKind::Union { members: _ } => {
998+
node_types::EntryKind::Union { .. } => {
1021999
// It's a tree-sitter supertype node, so we're wrapping a dbscheme
1022-
// union type. Any predicate that's identically defined by every
1023-
// member becomes an `abstract` predicate here.
1000+
// union type. Fields declared on the supertype become abstract
1001+
// predicates here.
10241002
let predicates = exposed_predicates
10251003
.get(node.ql_class_name.as_str())
10261004
.cloned()
@@ -1215,3 +1193,110 @@ pub fn create_print_ast_module(nodes: &node_types::NodeTypeMap) -> ql::TopLevel<
12151193
overlay: None,
12161194
})
12171195
}
1196+
1197+
#[cfg(test)]
1198+
mod tests {
1199+
use super::*;
1200+
1201+
#[test]
1202+
fn supertype_exposes_only_declared_fields() {
1203+
let node_types = r#"[
1204+
{
1205+
"type": "container",
1206+
"named": true,
1207+
"subtypes": [
1208+
{ "type": "alpha", "named": true },
1209+
{ "type": "beta", "named": true }
1210+
],
1211+
"fields": {
1212+
"item": {
1213+
"multiple": true,
1214+
"required": false,
1215+
"types": [{ "type": "item", "named": true }]
1216+
}
1217+
}
1218+
},
1219+
{
1220+
"type": "alpha",
1221+
"named": true,
1222+
"fields": {
1223+
"hidden": {
1224+
"multiple": false,
1225+
"required": true,
1226+
"types": [{ "type": "item", "named": true }]
1227+
},
1228+
"item": {
1229+
"multiple": true,
1230+
"required": false,
1231+
"types": [{ "type": "item", "named": true }]
1232+
}
1233+
}
1234+
},
1235+
{
1236+
"type": "beta",
1237+
"named": true,
1238+
"fields": {
1239+
"hidden": {
1240+
"multiple": false,
1241+
"required": true,
1242+
"types": [{ "type": "item", "named": true }]
1243+
},
1244+
"item": {
1245+
"multiple": true,
1246+
"required": false,
1247+
"types": [{ "type": "item", "named": true }]
1248+
}
1249+
}
1250+
},
1251+
{ "type": "item", "named": true, "fields": {} }
1252+
]"#;
1253+
let nodes = node_types::read_node_types_str("test", node_types).unwrap();
1254+
let classes = convert_nodes(&nodes);
1255+
1256+
let container = classes
1257+
.iter()
1258+
.find_map(|top_level| match top_level {
1259+
ql::TopLevel::Class(class) if class.name == "Container" => Some(class),
1260+
_ => None,
1261+
})
1262+
.unwrap();
1263+
assert_eq!(
1264+
container
1265+
.predicates
1266+
.iter()
1267+
.map(|predicate| predicate.name)
1268+
.collect::<BTreeSet<_>>(),
1269+
BTreeSet::from(["getAnItem", "getItem"]),
1270+
);
1271+
assert!(
1272+
container
1273+
.predicates
1274+
.iter()
1275+
.all(|predicate| predicate.body.is_none() && !predicate.is_final)
1276+
);
1277+
1278+
let alpha = classes
1279+
.iter()
1280+
.find_map(|top_level| match top_level {
1281+
ql::TopLevel::Class(class) if class.name == "Alpha" => Some(class),
1282+
_ => None,
1283+
})
1284+
.unwrap();
1285+
assert!(
1286+
alpha
1287+
.predicates
1288+
.iter()
1289+
.find(|predicate| predicate.name == "getItem")
1290+
.unwrap()
1291+
.overridden
1292+
);
1293+
assert!(
1294+
!alpha
1295+
.predicates
1296+
.iter()
1297+
.find(|predicate| predicate.name == "getHidden")
1298+
.unwrap()
1299+
.overridden
1300+
);
1301+
}
1302+
}

‎shared/tree-sitter-extractor/src/node_types.rs‎

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,17 @@ pub struct Entry {
1717

1818
#[derive(Debug)]
1919
pub enum EntryKind {
20-
Union { members: Set<TypeName> },
21-
Table { name: String, fields: Vec<Field> },
22-
Token { kind_id: usize },
20+
Union {
21+
members: Set<TypeName>,
22+
fields: Vec<Field>,
23+
},
24+
Table {
25+
name: String,
26+
fields: Vec<Field>,
27+
},
28+
Token {
29+
kind_id: usize,
30+
},
2331
}
2432

2533
#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq)]
@@ -135,16 +143,39 @@ pub fn convert_nodes(prefix: &str, nodes: &[NodeInfo]) -> NodeTypeMap {
135143
if !subtypes.is_empty() {
136144
// It's a tree-sitter supertype node, for which we create a union
137145
// type.
146+
let type_name = TypeName {
147+
kind: node.kind.clone(),
148+
named: node.named,
149+
};
150+
let mut fields = Vec::new();
151+
for (field_name, field_info) in &node.fields {
152+
add_field(
153+
prefix,
154+
&type_name,
155+
Some(field_name.to_string()),
156+
field_info,
157+
&mut fields,
158+
&token_kinds,
159+
);
160+
}
161+
if let Some(children) = &node.children {
162+
add_field(
163+
prefix,
164+
&type_name,
165+
None,
166+
children,
167+
&mut fields,
168+
&token_kinds,
169+
);
170+
}
138171
entries.insert(
139-
TypeName {
140-
kind: node.kind.clone(),
141-
named: node.named,
142-
},
172+
type_name,
143173
Entry {
144174
dbscheme_name,
145175
ql_class_name,
146176
kind: EntryKind::Union {
147177
members: convert_types(subtypes),
178+
fields,
148179
},
149180
},
150181
);

0 commit comments

Comments
 (0)