Skip to content

Commit 31b5865

Browse files
committed
feat(cst): create CstType
- used in function signatures - may represent a named type (`i8`) or a tuple type (`(i8, bool)`) - `ast_gen` updated to enforce `CstType::Named` variant when lowering
1 parent 073aee8 commit 31b5865

18 files changed

Lines changed: 290 additions & 142 deletions

src/ir/cst.rs

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ pub use self::{
44
expression::{BinaryOperation, UnaryOperation, *},
55
function::*,
66
statement::*,
7+
ty::*,
78
util::*,
89
};
910

@@ -70,7 +71,7 @@ mod function {
7071
#[expect(dead_code, reason = "token field")]
7172
pub tok_colon: tok::Colon,
7273
/// Type of the parameter.
73-
pub ty: tok::Ident,
74+
pub ty: CstType,
7475
}
7576

7677
/// Return type for a function declaration.
@@ -83,7 +84,7 @@ mod function {
8384
#[expect(dead_code, reason = "token field")]
8485
pub tok_thin_arrow: tok::ThinArrow,
8586
/// Return type.
86-
pub ty: tok::Ident,
87+
pub ty: CstType,
8788
}
8889
}
8990

@@ -98,9 +99,45 @@ pub struct Block {
9899
pub tok_r_brace: tok::RBrace,
99100
}
100101

101-
mod statement {
102-
use crate::enum_conversion;
102+
/// Tuple of items. Used for both a tuple of [`Expression`], and a tuple of [`CstType`].
103+
#[derive(Clone, Debug)]
104+
pub struct Tuple<T> {
105+
#[expect(dead_code, reason = "token field")]
106+
pub tok_l_parenthesis: tok::LParenthesis,
107+
pub items: PunctuatedList<T, tok::Comma>,
108+
#[expect(dead_code, reason = "token field")]
109+
pub tok_r_parenthesis: tok::RParenthesis,
110+
}
111+
112+
mod ty {
113+
use super::*;
114+
115+
/// A type, such as for parameters or variable declarations.
116+
#[derive(Clone, Debug)]
117+
pub enum CstType {
118+
/// A named type represented with a single [`tok::Ident`], such as `i8`.
119+
Named(tok::Ident),
120+
/// A tuple type, composed of many inner [`CstType`]s.
121+
Tuple(Tuple<CstType>),
122+
}
123+
124+
impl CstType {
125+
pub fn as_named(&self) -> &tok::Ident {
126+
match self {
127+
Self::Named(ident) => ident,
128+
_ => panic!(),
129+
}
130+
}
131+
}
103132

133+
enum_conversion! {
134+
[CstType]
135+
Named: tok::Ident,
136+
Tuple: Tuple<CstType>,
137+
}
138+
}
139+
140+
mod statement {
104141
use super::*;
105142

106143
/// A statement present within a [`Block`].
@@ -166,8 +203,6 @@ mod statement {
166203
}
167204

168205
mod expression {
169-
use crate::enum_conversion;
170-
171206
use super::*;
172207

173208
/// All possible expressions.
@@ -184,7 +219,7 @@ mod expression {
184219
Call(Call),
185220
Block(Block),
186221
Variable(Variable),
187-
Tuple(Tuple),
222+
Tuple(Tuple<Expression>),
188223
}
189224

190225
/// Assignment.
@@ -392,16 +427,6 @@ mod expression {
392427
pub variable: tok::Ident,
393428
}
394429

395-
/// Tuple expression.
396-
#[derive(Clone, Debug)]
397-
pub struct Tuple {
398-
#[expect(dead_code, reason = "token field")]
399-
pub tok_l_parenthesis: tok::LParenthesis,
400-
pub values: PunctuatedList<Expression, tok::Comma>,
401-
#[expect(dead_code, reason = "token field")]
402-
pub tok_r_parenthesis: tok::RParenthesis,
403-
}
404-
405430
enum_conversion! {
406431
[Expression]
407432
Assign: Assign,
@@ -414,7 +439,7 @@ mod expression {
414439
Call: Call,
415440
Block: Block,
416441
Variable: Variable,
417-
Tuple: Tuple,
442+
Tuple: Tuple<Expression>,
418443
}
419444
}
420445

src/passes/ast_gen.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,13 @@ impl<'ctx> AstGen<'ctx> {
4444
.iter_items()
4545
.map(|parameter| FunctionParameter {
4646
name: self.ctx.strings.intern(&parameter.name.0),
47-
ty: self.ctx.strings.intern(&parameter.ty.0),
47+
ty: self.ctx.strings.intern(&parameter.ty.as_named().0),
4848
})
4949
.collect(),
5050
return_ty: function
5151
.return_ty
5252
.as_ref()
53-
.map(|ty| self.ctx.strings.intern(&ty.ty.0)),
53+
.map(|ty| self.ctx.strings.intern(&ty.ty.as_named().0)),
5454
body: self.lower_block(&function.body),
5555
};
5656
self.ast.function_declarations.insert(function_declaration);

src/passes/cst_gen.rs

Lines changed: 114 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ mod function {
5555
.next_if()
5656
.map(|tok_thin_arrow| cst::FunctionReturnType {
5757
tok_thin_arrow,
58-
ty: lexer.expect().unwrap(),
58+
ty: cst::CstType::parse(lexer),
5959
}),
6060
body: cst::Block::parse(lexer),
6161
}
@@ -67,7 +67,7 @@ mod function {
6767
Self {
6868
name: lexer.expect().unwrap(),
6969
tok_colon: lexer.expect().unwrap(),
70-
ty: lexer.expect().unwrap(),
70+
ty: cst::CstType::parse(lexer),
7171
}
7272
}
7373
}
@@ -95,6 +95,68 @@ impl Parse for cst::Block {
9595
}
9696
}
9797

98+
impl<T> cst::Tuple<T>
99+
where
100+
T: Parse,
101+
{
102+
/// Parse a tuple from an existing [`tok::LParenthesis`] and an optional first item with
103+
/// it's [`tok::Comma`]. If an item isn't provided, it will still attempt to be parsed.
104+
///
105+
/// The [`tok::Comma`] must be provided with an item to ensure that single-item tuples are
106+
/// only accepted if they were terminated with a [`tok::Comma`].
107+
fn parse_with_parts(
108+
tok_l_parenthesis: tok::LParenthesis,
109+
first_item: Option<(T, tok::Comma)>,
110+
lexer: &mut Lexer,
111+
) -> Self {
112+
Self {
113+
tok_l_parenthesis,
114+
items: {
115+
// Parse out remaining values.
116+
let mut values = cst::PunctuatedList::parse_while(lexer, |tok| {
117+
!matches!(tok, Tok::RParenthesis)
118+
});
119+
120+
// Prepend the provided first expression.
121+
if let Some((item, comma)) = first_item {
122+
values.items.insert(0, item);
123+
values.punctuation.insert(0, comma);
124+
}
125+
126+
assert!(
127+
values.items.len() != 1 || values.has_trailing(),
128+
"if tuple is of length 1, it must end in a trailing comma"
129+
);
130+
131+
values
132+
},
133+
tok_r_parenthesis: lexer.expect().unwrap(),
134+
}
135+
}
136+
}
137+
impl<T> Parse for cst::Tuple<T>
138+
where
139+
T: Parse,
140+
{
141+
fn parse(lexer: &mut Lexer<'_>) -> Self {
142+
Self::parse_with_parts(lexer.expect().unwrap(), None, lexer)
143+
}
144+
}
145+
146+
mod ty {
147+
use super::*;
148+
149+
impl Parse for cst::CstType {
150+
fn parse(lexer: &mut Lexer<'_>) -> Self {
151+
match lexer.peek() {
152+
Tok::Ident(_) => lexer.expect::<tok::Ident>().unwrap().into(),
153+
Tok::LParenthesis => cst::Tuple::parse(lexer).into(),
154+
tok => panic!("unexpected tok when parsing type: {tok}"),
155+
}
156+
}
157+
}
158+
}
159+
98160
mod statement {
99161
use super::*;
100162

@@ -461,50 +523,6 @@ mod expression {
461523
}
462524
}
463525
}
464-
465-
impl cst::Tuple {
466-
/// Parse a tuple from an existing [`tok::LParenthesis`] and an optional first
467-
/// [`cst::Expression`] with it's [`tok::Comma`]. If the expression isn't provided, it will
468-
/// still attempt to be parsed.
469-
///
470-
/// The [`tok::Comma`] must be provided with the [`cst::Expression`] to ensure that
471-
/// single-expression tuples are only accepted if they were terminated with a
472-
/// [`tok::Comma`].
473-
fn parse_with_parts(
474-
tok_l_parenthesis: tok::LParenthesis,
475-
first_expression: Option<(cst::Expression, tok::Comma)>,
476-
lexer: &mut Lexer,
477-
) -> Self {
478-
Self {
479-
tok_l_parenthesis,
480-
values: {
481-
// Parse out remaining values.
482-
let mut values = cst::PunctuatedList::parse_while(lexer, |tok| {
483-
!matches!(tok, Tok::RParenthesis)
484-
});
485-
486-
// Prepend the provided first expression.
487-
if let Some((expression, comma)) = first_expression {
488-
values.items.insert(0, expression);
489-
values.punctuation.insert(0, comma);
490-
}
491-
492-
assert!(
493-
values.items.len() != 1 || values.has_trailing(),
494-
"if tuple is of length 1, it must end in a trailing comma"
495-
);
496-
497-
values
498-
},
499-
tok_r_parenthesis: lexer.expect().unwrap(),
500-
}
501-
}
502-
}
503-
impl Parse for cst::Tuple {
504-
fn parse(lexer: &mut Lexer<'_>) -> Self {
505-
Self::parse_with_parts(lexer.expect().unwrap(), None, lexer)
506-
}
507-
}
508526
}
509527

510528
mod util {
@@ -551,6 +569,56 @@ mod test {
551569
assert_eq!(lexer.next(), Tok::Eof)
552570
}
553571

572+
mod tuple {
573+
use super::*;
574+
575+
#[rstest]
576+
#[case("tuple_empty", "()")]
577+
#[case("tuple_single_item_trailing_comma", "(1,)")]
578+
#[case("tuple_many_items", "(1, 2, 3)")]
579+
#[case("tuple_many_items_trailing_comma", "(1, 2, 3,)")]
580+
fn tuple(#[case] name: &str, #[case] source: &str) {
581+
test_with_lexer(source, |lexer| {
582+
let tuple = cst::Tuple::<cst::Literal>::parse(lexer);
583+
assert_debug_snapshot!(name, tuple, source);
584+
});
585+
}
586+
587+
#[rstest]
588+
#[should_panic]
589+
#[case::single_item_no_comma("(1)")]
590+
fn tuple_failure(#[case] source: &str) {
591+
test_with_lexer(source, |lexer| {
592+
cst::Tuple::<cst::Literal>::parse(lexer);
593+
});
594+
}
595+
}
596+
597+
mod ty {
598+
use super::*;
599+
600+
#[rstest]
601+
#[case("named_ident", "i8")]
602+
#[case("tuple_empty", "()")]
603+
#[case("tuple_single", "(i8,)")]
604+
#[case("tuple_many", "(i8, bool, u8)")]
605+
fn ty(#[case] name: &str, #[case] source: &str) {
606+
test_with_lexer(source, |lexer| {
607+
let ty = cst::CstType::parse(lexer);
608+
assert_debug_snapshot!(name, ty, source);
609+
});
610+
}
611+
612+
#[rstest]
613+
#[should_panic]
614+
#[case::tuple_no_trailing_comma("(i8)")]
615+
fn ty_failure(#[case] source: &str) {
616+
test_with_lexer(source, |lexer| {
617+
cst::CstType::parse(lexer);
618+
});
619+
}
620+
}
621+
554622
mod expression {
555623
use super::*;
556624

@@ -688,27 +756,6 @@ mod test {
688756
assert_debug_snapshot!(name, variable, source);
689757
});
690758
}
691-
692-
#[rstest]
693-
#[case("tuple_empty", "()")]
694-
#[case("tuple_single_item_trailing_comma", "(1,)")]
695-
#[case("tuple_many_items", "(1, 2, 3)")]
696-
#[case("tuple_many_items_trailing_comma", "(1, 2, 3,)")]
697-
fn tuple(#[case] name: &str, #[case] source: &str) {
698-
test_with_lexer(source, |lexer| {
699-
let tuple = cst::Tuple::parse(lexer);
700-
assert_debug_snapshot!(name, tuple, source);
701-
});
702-
}
703-
704-
#[rstest]
705-
#[should_panic]
706-
#[case::single_item_no_comma("(1)")]
707-
fn tuple_failure(#[case] source: &str) {
708-
test_with_lexer(source, |lexer| {
709-
cst::Tuple::parse(lexer);
710-
});
711-
}
712759
}
713760

714761
#[rstest]

src/passes/snapshots/lumina2__passes__cst_gen__test__expression__expression_tuple_empty.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ expression: ()
55
Tuple(
66
Tuple {
77
tok_l_parenthesis: LParenthesis,
8-
values: PunctuatedList {
8+
items: PunctuatedList {
99
items: [],
1010
punctuation: [],
1111
},

src/passes/snapshots/lumina2__passes__cst_gen__test__expression__expression_tuple_many_values.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ expression: "(1,2,3)"
55
Tuple(
66
Tuple {
77
tok_l_parenthesis: LParenthesis,
8-
values: PunctuatedList {
8+
items: PunctuatedList {
99
items: [
1010
Literal(
1111
Integer(

src/passes/snapshots/lumina2__passes__cst_gen__test__expression__expression_tuple_many_values_trailing.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ expression: "(1,2,3,)"
55
Tuple(
66
Tuple {
77
tok_l_parenthesis: LParenthesis,
8-
values: PunctuatedList {
8+
items: PunctuatedList {
99
items: [
1010
Literal(
1111
Integer(

src/passes/snapshots/lumina2__passes__cst_gen__test__expression__expression_tuple_single_value.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ expression: "(1,)"
55
Tuple(
66
Tuple {
77
tok_l_parenthesis: LParenthesis,
8-
values: PunctuatedList {
8+
items: PunctuatedList {
99
items: [
1010
Literal(
1111
Integer(

0 commit comments

Comments
 (0)