Skip to content
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
3 changes: 2 additions & 1 deletion src/v2/extdeps/languages/c.dag
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ import v2.std.grammar {
grammar_relation_tokens_node
}
import v2.std.compilers.target_model {
target_bind_let_separator_present,
IntLiteralUnwired,
InfixToken,
canonical_operation_op_add,
Expand Down Expand Up @@ -1098,7 +1099,7 @@ fn c_value_expression_projection() -> TargetValueExpressionProjection {
let_form: TargetBindLetShape {
let_token: ^c_token_unwired_let,
assign_token: ^c_token_unwired_assign,
in_token: ^c_token_semicolon
in_token: target_bind_let_separator_present(token: ^c_token_semicolon)
},
loop_form: TargetLoopShape { loop_token: ^c_token_unwired_loop },
record_construct_form: TargetRecordConstructShape {
Expand Down
13 changes: 9 additions & 4 deletions src/v2/extdeps/languages/dag.dag
Original file line number Diff line number Diff line change
Expand Up @@ -238,8 +238,8 @@ data dag_pick_false_source_text: String = "fn one_or_two() -> Int { if false the
data dag_loop_source_text: String = "fn one() -> Int { loop 1 }"
data dag_loop_wrong_value_source_text: String = "fn one() -> Int { loop 2 }"

data dag_bind_source_text: String = "fn one() -> Int { let x = 1 in x }"
data dag_bind_wrong_value_source_text: String = "fn one() -> Int { let x = 2 in x }"
data dag_bind_source_text: String = "fn one() -> Int { { let x = (1) { x } } }"
data dag_bind_wrong_value_source_text: String = "fn one() -> Int { { let x = (2) { x } } }"

data dag_match_source_text: String = "fn one_or_two() -> Int { match true { true => 1, false => 2 } }"

Expand Down Expand Up @@ -332,12 +332,17 @@ fn dag_bind_concrete_tokens() -> List<ConcreteSyntaxToken> {
FixedToken { token_class: ^dag_token_arrow },
BoundToken { token_class: ^dag_token_ident, binding: ^dag_binding_type_int },
FixedToken { token_class: ^dag_token_lbrace },
FixedToken { token_class: ^dag_token_lbrace },
FixedToken { token_class: ^dag_token_kw_let },
BoundToken { token_class: ^dag_token_ident, binding: ^dag_binding_bind_x },
FixedToken { token_class: ^dag_token_eq },
FixedToken { token_class: ^dag_token_lparen },
BoundToken { token_class: ^dag_token_ident, binding: ^dag_binding_pick_lit_one },
FixedToken { token_class: ^dag_token_kw_in },
FixedToken { token_class: ^dag_token_rparen },
FixedToken { token_class: ^dag_token_lbrace },
BoundToken { token_class: ^dag_token_ident, binding: ^dag_binding_bind_x },
FixedToken { token_class: ^dag_token_rbrace },
FixedToken { token_class: ^dag_token_rbrace },
FixedToken { token_class: ^dag_token_rbrace }
]
}
Expand Down Expand Up @@ -5828,7 +5833,7 @@ fn dag_value_expression_projection() -> TargetValueExpressionProjection {
let_form: TargetBindLetShape {
let_token: ^dag_token_kw_let,
assign_token: ^dag_token_eq,
in_token: ^dag_token_kw_in
in_token: Absent
},
loop_form: TargetLoopShape {
loop_token: ^dag_token_kw_loop
Expand Down
3 changes: 2 additions & 1 deletion src/v2/extdeps/languages/rust.dag
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ import v2.std.compilers.coproduct_variant_shape {
coproduct_all_variants_nullary
}
import v2.std.compilers.target_model {
target_bind_let_separator_present,
IntLiteralDecimal,
TargetCapabilityKey,
ProducedDeclSupport,
Expand Down Expand Up @@ -1204,7 +1205,7 @@ fn rust_value_expression_projection() -> TargetValueExpressionProjection {
let_form: TargetBindLetShape {
let_token: ^rust_token_kw_let,
assign_token: ^rust_token_equal,
in_token: ^rust_token_semicolon
in_token: target_bind_let_separator_present(token: ^rust_token_semicolon)
},
loop_form: TargetLoopShape { loop_token: ^rust_token_unwired_loop },
record_construct_form: TargetRecordConstructShape {
Expand Down
3 changes: 2 additions & 1 deletion src/v2/extdeps/languages/typescript.dag
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ import v2.std.grounding {
}
import v2.std.node_query { find_named_child }
import v2.std.compilers.target_model {
target_bind_let_separator_present,
IntLiteralUnwired,
ProducedDeclParamSegment,
ProducedDeclRenderRows,
Expand Down Expand Up @@ -656,7 +657,7 @@ fn ts_value_expression_projection_full(
let_form: TargetBindLetShape {
let_token: ^ts_token_kw_let,
assign_token: ^ts_token_eq,
in_token: ^ts_token_unwired_bind_in
in_token: target_bind_let_separator_present(token: ^ts_token_unwired_bind_in)
},
loop_form: TargetLoopShape {
loop_token: ^ts_token_unwired_loop
Expand Down
90 changes: 86 additions & 4 deletions src/v2/std/compilers/target_model.dag
Original file line number Diff line number Diff line change
Expand Up @@ -1824,12 +1824,38 @@ type TargetConditionalShape {
delimiting: ConditionalDelimiting
}

// THE TOKEN BETWEEN A BINDING AND ITS BODY, WHEN THE TARGET HAS ONE. `let k = v in b` (`in`) and the
// statement-sequenced C/Rust/TypeScript forms (`;`) name it. A target whose let is a STATEMENT and
// has no separator token (dag: statement `let` only, `let x = e in body` dropped by the 2026-10-02
// zero-ambiguity decisions) declares Absent, and a core Bind is emitted as a block that holds the
// binding as its first statement and the body as its last: `{ let k = (v) { b } }`
// (bind_let_block_scoped_tokens). The value is parenthesised and the body braced so that no
// continuation of the value can absorb the body -- a call suffix, an operator or a brace literal
// cannot follow a closed group into a block -- whatever the value and body are; the outer braces give
// the binding a statement position wherever the Bind stands, an expression position included.
type TargetBindLetShape {
let_token: Symbol
assign_token: Symbol
in_token: Symbol
in_token: Optional<Symbol>
}

// Absent is carried in the projection bundle as its own atom, never as a missing field: a bundle that
// lost the field still refuses at decode rather than reading as "no separator".
fn target_bind_let_separator_atom(in_token: Optional<Symbol>) -> Symbol {
match in_token {
Present { value: t } => t
Absent => ^target_bind_let_no_separator
}
}

fn target_bind_let_separator_of_atom(atom: Symbol) -> Optional<Symbol> {
if atom == ^target_bind_let_no_separator { target_bind_let_separator_absent() } else { target_bind_let_separator_present(token: atom) }
}

fn target_bind_let_separator_absent() -> Optional<Symbol> { Absent }

fn target_bind_let_separator_present(token: Symbol) -> Optional<Symbol> { Present { value: token } }

type TargetLoopShape {
loop_token: Symbol
}
Expand Down Expand Up @@ -3011,7 +3037,7 @@ fn decode_bind_let_shape_bundle(bundle: Node) -> Outcome<TargetBindLetShape> {
TargetBindLetShape {
let_token: let_token,
assign_token: assign_token,
in_token: in_token
in_token: target_bind_let_separator_of_atom(atom: in_token)
}
)
}
Expand Down Expand Up @@ -6491,6 +6517,62 @@ fn bind_let_value_producing_tokens(
key_tokens: List<ConcreteSyntaxToken>,
value_tokens: List<ConcreteSyntaxToken>,
body_tokens: List<ConcreteSyntaxToken>
) -> List<ConcreteSyntaxToken> {
match projection.let_form.in_token {
Present { value: in_token } =>
bind_let_separated_tokens(projection: projection, in_token: in_token, key_tokens: key_tokens, value_tokens: value_tokens, body_tokens: body_tokens)
Absent =>
bind_let_block_scoped_tokens(projection: projection, key_tokens: key_tokens, value_tokens: value_tokens, body_tokens: body_tokens)
}
}

// `{ let k = ( v ) { b } }`, with the target's own block and group delimiters (closure body and
// primitive apply). See TargetBindLetShape for why each delimiter is there.
fn bind_let_block_scoped_tokens(
projection: TargetValueExpressionProjection,
key_tokens: List<ConcreteSyntaxToken>,
value_tokens: List<ConcreteSyntaxToken>,
body_tokens: List<ConcreteSyntaxToken>
) -> List<ConcreteSyntaxToken> {
list_append(
left: list_append(
left: list_append(
left: list_append(
left: list_append(
left: [
FixedToken { token_class: projection.closure_form.body_open },
FixedToken { token_class: projection.let_form.let_token }
],
right: key_tokens
),
right: [
FixedToken { token_class: projection.let_form.assign_token },
FixedToken { token_class: projection.primitive_apply_form.open }
]
),
right: value_tokens
),
right: [
FixedToken { token_class: projection.primitive_apply_form.close },
FixedToken { token_class: projection.closure_form.body_open }
]
),
right: list_append(
left: body_tokens,
right: [
FixedToken { token_class: projection.closure_form.body_close },
FixedToken { token_class: projection.closure_form.body_close }
]
)
)
}

fn bind_let_separated_tokens(
projection: TargetValueExpressionProjection,
in_token: Symbol,
key_tokens: List<ConcreteSyntaxToken>,
value_tokens: List<ConcreteSyntaxToken>,
body_tokens: List<ConcreteSyntaxToken>
) -> List<ConcreteSyntaxToken> {
list_append(
left: list_append(
Expand All @@ -6509,7 +6591,7 @@ fn bind_let_value_producing_tokens(
right: value_tokens
),
right: [
FixedToken { token_class: projection.let_form.in_token }
FixedToken { token_class: in_token }
]
),
right: body_tokens
Expand Down Expand Up @@ -8956,7 +9038,7 @@ fn value_expr_projection_bundle_node(projection: TargetValueExpressionProjection
target_model_named_edge(
name: ^target_value_expr_field_in_token,
target: target_model_type_atom_node(
identity: projection.let_form.in_token
identity: target_bind_let_separator_atom(in_token: projection.let_form.in_token)
)
)
]
Expand Down
Loading