Skip to content

Commit c655749

Browse files
committed
Update AST to remove use of special identifier
1 parent cb92c59 commit c655749

File tree

5 files changed

+80
-52
lines changed

5 files changed

+80
-52
lines changed

quiver-compiler/src/ast.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ pub enum TupleName {
7878
Literal(String),
7979
/// Inherit name from variable like a[..., y: 2]
8080
Identifier(String),
81+
/// Inherit name from ripple like ~[..., y: 2]
82+
Ripple,
8183
/// Unnamed tuple like [x: 1]
8284
None,
8385
}
@@ -88,10 +90,20 @@ pub struct Tuple {
8890
pub fields: Vec<TupleField>,
8991
}
9092

93+
#[derive(Debug, Clone, PartialEq)]
94+
pub enum SpreadSource {
95+
/// Bare spread (...) - will be filled in based on context
96+
Chained,
97+
/// Identifier spread (...identifier)
98+
Identifier(String),
99+
/// Ripple spread - spreads the rippled value
100+
Ripple,
101+
}
102+
91103
#[derive(Debug, Clone, PartialEq)]
92104
pub enum FieldValue {
93105
Chain(Chain),
94-
Spread(Option<String>),
106+
Spread(SpreadSource),
95107
}
96108

97109
#[derive(Debug, Clone, PartialEq)]

quiver-compiler/src/compiler.rs

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -428,34 +428,33 @@ impl<'a> Compiler<'a> {
428428
let resolved_tuple_name = match tuple_name {
429429
ast::TupleName::Literal(name) => Some(name),
430430
ast::TupleName::None => None,
431+
ast::TupleName::Ripple => {
432+
// Ripple spread: ~[..., fields]
433+
// Get the ripple type from ripple_context
434+
ripple_context.and_then(|ctx| {
435+
let ripple_type = &ctx.value_type;
436+
if let Type::Tuple(type_id) = ripple_type {
437+
self.program
438+
.lookup_type(type_id)
439+
.and_then(|type_info| type_info.name.clone())
440+
} else {
441+
None
442+
}
443+
})
444+
}
431445
ast::TupleName::Identifier(id) => {
432-
if id == "~" {
433-
// Ripple spread: ~[..., fields]
434-
// Get the ripple type from ripple_context
435-
ripple_context.and_then(|ctx| {
436-
let ripple_type = &ctx.value_type;
437-
if let Type::Tuple(type_id) = ripple_type {
446+
// Identifier spread: identifier[..., fields]
447+
// Look up the variable's type
448+
self.lookup_variable(&self.scopes, &id, &[])
449+
.and_then(|(var_type, _)| {
450+
if let Type::Tuple(type_id) = var_type {
438451
self.program
439-
.lookup_type(type_id)
452+
.lookup_type(&type_id)
440453
.and_then(|type_info| type_info.name.clone())
441454
} else {
442455
None
443456
}
444457
})
445-
} else {
446-
// Identifier spread: identifier[..., fields]
447-
// Look up the variable's type
448-
self.lookup_variable(&self.scopes, &id, &[])
449-
.and_then(|(var_type, _)| {
450-
if let Type::Tuple(type_id) = var_type {
451-
self.program
452-
.lookup_type(&type_id)
453-
.and_then(|type_info| type_info.name.clone())
454-
} else {
455-
None
456-
}
457-
})
458-
}
459458
}
460459
};
461460

@@ -499,10 +498,12 @@ impl<'a> Compiler<'a> {
499498
let resolved_name = match tuple_name {
500499
ast::TupleName::Literal(name) => Some(name),
501500
ast::TupleName::None => None,
502-
ast::TupleName::Identifier(_) => {
503-
// Parser enforces that identifier syntax requires spreads,
501+
ast::TupleName::Identifier(_) | ast::TupleName::Ripple => {
502+
// Parser enforces that identifier/ripple syntax requires spreads,
504503
// so this path only executes when there are spreads (handled above)
505-
unreachable!("TupleName::Identifier without spreads should be rejected by parser")
504+
unreachable!(
505+
"TupleName::Identifier/Ripple without spreads should be rejected by parser"
506+
)
506507
}
507508
};
508509
let type_id = self.program.register_type(resolved_name, field_types);

quiver-compiler/src/compiler/spread.rs

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@ pub fn compile_tuple_with_spread(
1313
fields: Vec<ast::TupleField>,
1414
ripple_context: Option<&RippleContext>,
1515
) -> Result<Type, Error> {
16-
let has_chained_spread = fields.iter().any(|f| match &f.value {
17-
ast::FieldValue::Spread(None) => true,
18-
ast::FieldValue::Spread(Some(id)) if id == "~" => true,
19-
_ => false,
16+
let has_chained_spread = fields.iter().any(|f| {
17+
matches!(
18+
&f.value,
19+
ast::FieldValue::Spread(ast::SpreadSource::Chained | ast::SpreadSource::Ripple)
20+
)
2021
});
2122

2223
if has_chained_spread && ripple_context.is_none() {
@@ -75,10 +76,10 @@ pub fn compile_tuple_with_spread(
7576
});
7677
stack_size += 1;
7778
}
78-
ast::FieldValue::Spread(identifier) => {
79+
ast::FieldValue::Spread(spread_source) => {
7980
// Get the type of the value to spread
80-
let spread_type = if let Some(id) = identifier {
81-
if id == "~" {
81+
let spread_type = match spread_source {
82+
ast::SpreadSource::Ripple => {
8283
// Spread the ripple value (~)
8384
let ctx = ripple_context.ok_or_else(|| {
8485
Error::FeatureUnsupported(
@@ -89,7 +90,8 @@ pub fn compile_tuple_with_spread(
8990
.codegen
9091
.add_instruction(Instruction::Pick(ctx.stack_offset + stack_size));
9192
ctx.value_type.clone()
92-
} else {
93+
}
94+
ast::SpreadSource::Identifier(id) => {
9395
// Spread a variable
9496
let (var_type, var_index) = compiler
9597
.lookup_variable(&compiler.scopes, id, &[])
@@ -99,17 +101,18 @@ pub fn compile_tuple_with_spread(
99101
.add_instruction(Instruction::Load(var_index));
100102
var_type
101103
}
102-
} else {
103-
// Spread the chained value (...)
104-
let ctx = ripple_context.ok_or_else(|| {
105-
Error::FeatureUnsupported(
106-
"Chained spread (...) requires a piped value".to_string(),
107-
)
108-
})?;
109-
compiler
110-
.codegen
111-
.add_instruction(Instruction::Pick(ctx.stack_offset + stack_size));
112-
ctx.value_type.clone()
104+
ast::SpreadSource::Chained => {
105+
// Spread the chained value (...)
106+
let ctx = ripple_context.ok_or_else(|| {
107+
Error::FeatureUnsupported(
108+
"Chained spread (...) requires a piped value".to_string(),
109+
)
110+
})?;
111+
compiler
112+
.codegen
113+
.add_instruction(Instruction::Pick(ctx.stack_offset + stack_size));
114+
ctx.value_type.clone()
115+
}
113116
};
114117

115118
compiled_values.push(CompiledValue::Spread {

quiver-compiler/src/compiler/typing.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,12 @@ fn resolve_tuple_name(
173173
match name {
174174
ast::TupleName::Literal(s) => Ok(Some(s)),
175175
ast::TupleName::None => Ok(None),
176+
ast::TupleName::Ripple => {
177+
// Ripple is only valid in value context, not type context
178+
Err(Error::FeatureUnsupported(
179+
"Ripple (~) cannot be used in type definitions".to_string(),
180+
))
181+
}
176182
ast::TupleName::Identifier(identifier) => {
177183
// Look up the type alias to get its tuple name
178184
let (type_params, type_def) = type_aliases
@@ -416,6 +422,7 @@ fn resolve_ast_type_impl(
416422
// Determine the final tuple name based on the name mode:
417423
// - TupleName::Literal -> use the literal name
418424
// - TupleName::Identifier -> use the variant name from spread
425+
// - TupleName::Ripple -> error (ripple only valid in value context)
419426
// - TupleName::None -> use None (unnamed)
420427
let final_name = match &tuple.name {
421428
ast::TupleName::Literal(_) | ast::TupleName::None => {
@@ -425,6 +432,11 @@ fn resolve_ast_type_impl(
425432
// Use variant name from spread (inherits from source)
426433
variant_name
427434
}
435+
ast::TupleName::Ripple => {
436+
return Err(Error::FeatureUnsupported(
437+
"Ripple (~) cannot be used in type definitions".to_string(),
438+
));
439+
}
428440
};
429441

430442
let type_id = program.register_type(final_name, fields);

quiver-compiler/src/parser.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -663,12 +663,12 @@ fn tuple_field(input: &str) -> IResult<&str, TupleField> {
663663
// Spread with identifier: ...identifier
664664
map(preceded(tag("..."), identifier), |id| TupleField {
665665
name: None,
666-
value: FieldValue::Spread(Some(id)),
666+
value: FieldValue::Spread(SpreadSource::Identifier(id)),
667667
}),
668668
// Spread chained value: ...
669669
map(tag("..."), |_| TupleField {
670670
name: None,
671-
value: FieldValue::Spread(None),
671+
value: FieldValue::Spread(SpreadSource::Chained),
672672
}),
673673
// Unnamed chain: chain
674674
map(chain, |chain_value| TupleField {
@@ -711,8 +711,9 @@ fn tuple_term(input: &str) -> IResult<&str, Tuple> {
711711
// Transform chained spread (...) into identifier spread (...identifier)
712712
// This allows a[..., y: 2] to mean "spread a and add y"
713713
for field in &mut fields {
714-
if matches!(field.value, FieldValue::Spread(None)) {
715-
field.value = FieldValue::Spread(Some(name.clone()));
714+
if matches!(field.value, FieldValue::Spread(SpreadSource::Chained)) {
715+
field.value =
716+
FieldValue::Spread(SpreadSource::Identifier(name.clone()));
716717
}
717718
}
718719
(name, fields)
@@ -739,10 +740,9 @@ fn tuple_term(input: &str) -> IResult<&str, Tuple> {
739740
),
740741
|mut fields| {
741742
// Transform chained spread (...) to reference the ripple
742-
// We use "~" as a special marker that the compiler will recognize
743743
for field in &mut fields {
744-
if matches!(field.value, FieldValue::Spread(None)) {
745-
field.value = FieldValue::Spread(Some("~".to_string()));
744+
if matches!(field.value, FieldValue::Spread(SpreadSource::Chained)) {
745+
field.value = FieldValue::Spread(SpreadSource::Ripple);
746746
}
747747
}
748748
fields
@@ -755,7 +755,7 @@ fn tuple_term(input: &str) -> IResult<&str, Tuple> {
755755
},
756756
),
757757
|fields| Tuple {
758-
name: TupleName::Identifier("~".to_string()), // Special marker for ripple
758+
name: TupleName::Ripple,
759759
fields,
760760
},
761761
),

0 commit comments

Comments
 (0)