Repository navigation
feat: add Node.ident with parser assignment and emitter defaults #408
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -376,6 +376,7 @@ type DeclaredFuncEnv { | |
|
|
||
| type Node { | ||
| name: String | ||
| ident: Int | ||
| span: SourceSpan | ||
| ident_span: SourceSpan? | ||
| // Fractal composition -- operations, capabilities, items are all children | ||
|
|
@@ -1262,7 +1263,7 @@ fn transport_env(t: Node, source_indices: Map<String, NewlineIndex>) -> List<Nod | |
|
|
||
| fn map_children(node: Node, transform: fn(Node) -> Node) -> Node { | ||
| Node { | ||
| name: node.name, span: node.span, ident_span: node.ident_span, | ||
| name: node.name, ident: node.ident, span: node.span, ident_span: node.ident_span, | ||
| children: node.children |> map(child => transform(child)), | ||
| connective: node.connective, | ||
| params: node.params, inferred: node.inferred, | ||
|
|
@@ -1653,10 +1654,16 @@ fn merge_intern_tables(tables: List<InternTable>) -> InternTable { | |
| ) | ||
| } | ||
|
|
||
| fn pre_intern_tokens(tokens: List<Token>, table: InternTable) -> InternTable { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Invariant violation: |
||
| fold(tokens, init: table, f: (t, tok) => | ||
| if tok.text != "" { intern(table: t, s: tok.text).table } else { t } | ||
| ) | ||
| } | ||
|
|
||
| fn with_optional_cardinality(n: Node) -> Node { | ||
| Node { name: n.name, span: n.span, ident_span: n.ident_span, children: n.children, connective: n.connective, params: n.params, inferred: n.inferred, return_cardinality: CardOptional, uses: n.uses, body: n.body, transport: n.transport, properties: n.properties, type_annotation: n.type_annotation, is_self_recursive: n.is_self_recursive, has_non_tail_self_call: n.has_non_tail_self_call, match_pattern: n.match_pattern, expr_data: n.expr_data } | ||
| Node { name: n.name, ident: n.ident, span: n.span, ident_span: n.ident_span, children: n.children, connective: n.connective, params: n.params, inferred: n.inferred, return_cardinality: CardOptional, uses: n.uses, body: n.body, transport: n.transport, properties: n.properties, type_annotation: n.type_annotation, is_self_recursive: n.is_self_recursive, has_non_tail_self_call: n.has_non_tail_self_call, match_pattern: n.match_pattern, expr_data: n.expr_data } | ||
| } | ||
|
|
||
| fn with_required_cardinality(n: Node) -> Node { | ||
| Node { name: n.name, span: n.span, ident_span: n.ident_span, children: n.children, connective: n.connective, params: n.params, inferred: n.inferred, return_cardinality: Required, uses: n.uses, body: n.body, transport: n.transport, properties: n.properties, type_annotation: n.type_annotation, is_self_recursive: n.is_self_recursive, has_non_tail_self_call: n.has_non_tail_self_call, match_pattern: n.match_pattern, expr_data: n.expr_data } | ||
| Node { name: n.name, ident: n.ident, span: n.span, ident_span: n.ident_span, children: n.children, connective: n.connective, params: n.params, inferred: n.inferred, return_cardinality: Required, uses: n.uses, body: n.body, transport: n.transport, properties: n.properties, type_annotation: n.type_annotation, is_self_recursive: n.is_self_recursive, has_non_tail_self_call: n.has_non_tail_self_call, match_pattern: n.match_pattern, expr_data: n.expr_data } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3554,7 +3554,43 @@ fn emit_typed_record_lit(type_name: String?, fields: List<Node>, parent_enum: St | |
| } else { val_str } | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Invariant violation: emit_typed_record_lit now completes missing struct fields using EmitTypeSummary plus rust_zero_value string matches on type names, conflicting with INVARIANTS.md “No case enumeration for open sets,” “Heuristics indicate lost structure,” and “No fallbacks that fabricate” (silent completion of literals without IR witnesses).
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Invariant violation: emit_typed_record_lit now completes missing struct fields using EmitTypeSummary plus rust_zero_value branches on string type names, conflicting with INVARIANTS.md “No case enumeration for open sets,” “Heuristics indicate lost structure,” and “No fallbacks that fabricate” when literals lack explicit field witnesses. |
||
| concat(" ", emit_ident(name: f_name, target: Rust), ": ", field_val, ",") | ||
| ) | ||
| let all_field_strs = field_strs | ||
| // Fill default values for struct fields not explicitly set in .dag source. | ||
| // Only emits defaults if ALL missing fields have known zero values — this avoids | ||
| // false positives from cross-module type name collisions in the summary registry. | ||
| let provided_names = fields |> map(f => field_init_node_name_at(n: f, source_indices: scope.type_env.source_indices)) | ||
| let provided_set = fold(provided_names, init: empty_map(), f: (acc, n) => map_insert(acc, n, true)) | ||
| let summary = lookup_emit_type_summary(emit_info: emit_info, type_name: tn) | ||
| let default_strs = match summary { | ||
| Some { value: s } => | ||
| let fs = s.field_summaries | ||
| let ftm = s.field_type_map | ||
| let missing = map_keys(fs) |> filter(k => !emit_map_has(m: provided_set, key: k)) | ||
| let all_defaultable = missing |> all(fname => | ||
| let has_zv = match map_get(ftm, fname) { | ||
| Some { value: ft } => match rust_zero_value(type_name: ft) { Some { value: _ } => true, None => false } | ||
| None => false | ||
| } | ||
| has_zv || is_optional_struct_field(emit_info: emit_info, struct_name: tn, field_name: fname) | ||
| ) | ||
| if all_defaultable { | ||
| missing |> flat_map(fname => | ||
| if is_optional_struct_field(emit_info: emit_info, struct_name: tn, field_name: fname) { | ||
| [concat(" ", emit_ident(name: fname, target: Rust), ": None,")] | ||
| } else { | ||
| match map_get(ftm, fname) { | ||
| Some { value: ft } => | ||
| match rust_zero_value(type_name: ft) { | ||
| Some { value: zv } => [concat(" ", emit_ident(name: fname, target: Rust), ": ", zv, ",")] | ||
| None => [] | ||
| } | ||
| None => [] | ||
| } | ||
| } | ||
| ) | ||
| } else { [] } | ||
| None => [] | ||
| } | ||
| let all_field_strs = concat(field_strs, default_strs) | ||
| let fields_str = all_field_strs |> join(separator: "\n") | ||
| let raw = concat(display_tn, " {\n", fields_str, "\n}") | ||
| // Rc wrapping is the caller's responsibility (data_decl_body, emit_typed_expr, etc.). | ||
|
|
@@ -3564,6 +3600,14 @@ fn emit_typed_record_lit(type_name: String?, fields: List<Node>, parent_enum: St | |
| } | ||
| } | ||
|
|
||
| // Zero-value for a Rust type, used to fill struct fields not set in .dag source. | ||
| // Returns none if the type has no obvious zero-value (caller must set it explicitly). | ||
| fn rust_zero_value(type_name: String) -> String? { | ||
| if type_name == "Int" { Some { value: "0" } } | ||
| else if type_name == "Bool" { Some { value: "false" } } | ||
| else if type_name == "String" { Some { value: concat("\"\"", ".to_string()") } } | ||
| else { none } | ||
| } | ||
|
|
||
| fn emit_typed_bin_op(op: BinOp, algebra_field: AlgebraFieldKind?, left: Node, right: Node, registry: Map<String, ItemInfo>, scope: InferScope, depth: Int, shared_types: Map<String, Bool>, emit_info: EmitGraphInfo) -> String { | ||
| let l_str = emit_typed_expr(texpr: left, registry: registry, scope: scope, depth: depth, shared_types: shared_types, emit_info: emit_info, fuel: 1024) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -71,6 +71,7 @@ pub fn type_variable_node(id: String) -> Rc<Node> { | |
| has_non_tail_self_call: false, | ||
| match_pattern: None, | ||
| expr_data: Rc::new(ExprData::NoExprData), | ||
| ident: 0, | ||
| }) | ||
| } | ||
|
|
||
|
|
@@ -3634,7 +3635,43 @@ let field_val = if needs_wrap.clone() { | |
| }; | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Invariant violation: The same missing-field synthesis and rust_zero_value policy are hand-maintained in stage0 alongside 05_emit_rust.dag, violating INVARIANTS.md “No duplicate representations” / “No parallel implementations” for bootstrap-generated Rust versus the .dag source of truth.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Invariant violation: The same default-filling block and rust_zero_value live in hand-edited stage0 in parallel with 05_emit_rust.dag, violating INVARIANTS.md “No duplicate representations” / “No parallel implementations” until regeneration is the only write path. |
||
| v2_rt::concat(v2_rt::concat(v2_rt::concat(v2_rt::concat(" ".to_string(), emit_ident(f_name.clone(), RenderTarget::Rust)), ": ".to_string()), field_val.clone()), ",".to_string()) | ||
| }); } __result }); | ||
| let all_field_strs = field_strs; | ||
| let provided_names = Rc::new({ let mut __result = Vec::new(); for f in fields.clone().iter().cloned() { __result.push(field_init_node_name_at(f.clone(), scope.type_env.clone().source_indices.clone())); } __result }); | ||
| let provided_set = provided_names.iter().cloned().fold(v2_rt::rc_empty_map::<bool>(), |acc: Rc<HashMap<String, bool>>, n: String| v2_rt::rc_map_insert(acc.clone(), n.clone(), true)); | ||
| let summary = lookup_emit_type_summary(emit_info.clone(), tn.clone()); | ||
| let default_strs = match summary { | ||
| Some(s) => { | ||
| let fs = s.field_summaries.clone(); | ||
| let ftm = s.field_type_map.clone(); | ||
| let missing = Rc::new({ let mut __result = Vec::new(); for k in Rc::new(v2_rt::map_keys(&fs)).iter().cloned() { if !emit_map_has(provided_set.clone(), k.clone()) { __result.push(k); } } __result }); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This default-filling path derives Useful? React with 👍 / 👎. |
||
| let all_defaultable = { let mut __all = true; for fname in missing.clone().iter().cloned() { if !({ | ||
| let has_zv = match v2_rt::map_get(&ftm, fname.clone()) { | ||
| Some(ft) => match rust_zero_value(&ft) { | ||
| Some(_) => true, | ||
| None => false, | ||
| }, | ||
| None => false, | ||
| }; | ||
| (has_zv.clone() || is_optional_struct_field(emit_info.clone(), tn.clone(), fname.clone())) | ||
| }) { __all = false; break; } } __all }; | ||
| if all_defaultable { | ||
| Rc::new({ let mut __result = Vec::new(); for fname in missing.clone().iter().cloned() { __result.extend((*if is_optional_struct_field(emit_info.clone(), tn.clone(), fname.clone()) { | ||
| Rc::new(vec![v2_rt::concat(v2_rt::concat(" ".to_string(), emit_ident(fname.clone(), RenderTarget::Rust)), ": None,".to_string())]) | ||
| } else { | ||
| match v2_rt::map_get(&ftm, fname.clone()) { | ||
| Some(ft) => match rust_zero_value(&ft) { | ||
| Some(zv) => Rc::new(vec![v2_rt::concat(v2_rt::concat(v2_rt::concat(v2_rt::concat(" ".to_string(), emit_ident(fname.clone(), RenderTarget::Rust)), ": ".to_string()), zv.clone()), ",".to_string())]), | ||
| None => Rc::new(vec![]), | ||
| }, | ||
| None => Rc::new(vec![]), | ||
| } | ||
| }).iter().cloned()); } __result }) | ||
| } else { | ||
| Rc::new(vec![]) | ||
| } | ||
| }, | ||
| None => Rc::new(vec![]), | ||
| }; | ||
| let all_field_strs = v2_rt::concat(field_strs, default_strs); | ||
| let fields_str = all_field_strs.join(&"\n".to_string()); | ||
| let raw = v2_rt::concat(v2_rt::concat(v2_rt::concat(display_tn, " {\n".to_string()), fields_str), "\n}".to_string()); | ||
| raw | ||
|
|
@@ -3646,6 +3683,22 @@ raw | |
| } | ||
| } | ||
|
|
||
| pub fn rust_zero_value(type_name: &String) -> Option<String> { | ||
| if (type_name.clone().as_str() == "Int".to_string().as_str()) { | ||
| Some("0".to_string()) | ||
| } else { | ||
| if (type_name.clone().as_str() == "Bool".to_string().as_str()) { | ||
| Some("false".to_string()) | ||
| } else { | ||
| if (type_name.clone().as_str() == "String".to_string().as_str()) { | ||
| Some(v2_rt::concat("\"\"".to_string(), ".to_string()".to_string())) | ||
| } else { | ||
| None | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| pub fn emit_typed_bin_op(op: &BinOp, algebra_field: Option<AlgebraFieldKind>, left: &Rc<Node>, right: &Rc<Node>, registry: &Rc<HashMap<String, Rc<ItemInfo>>>, scope: &Rc<InferScope>, depth: i64, shared_types: &Rc<HashMap<String, bool>>, emit_info: &Rc<EmitGraphInfo>) -> String { | ||
| { | ||
| let l_str = emit_typed_expr(left.clone(), registry.clone(), &scope, depth.clone(), shared_types.clone(), emit_info.clone(), 1024); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Invariant violation: Making
Node.identa totalIntadds a second authority for authored identity and leaves no structural way to represent “no identity,” violating INVARIANTS.md “No duplicate representations” and “No fallbacks that fabricate.”