Skip to content

Commit 357735b

Browse files
Jonathan D.A. Jewellclaude
andcommitted
feat: add parser module for list and tuple literals
Add dedicated parsing module for list and tuple syntax in Rust parser. This complements the list/tuple integration in the Pest grammar and provides alternative parsing implementation for list literals [e1, e2, e3] and tuple literals (e1, e2, e3). Features: - parse_list_literal(): Support for empty lists and trailing commas - parse_tuple_or_paren(): Distinguishes tuples from parenthesized expressions - parse_list_index(): Index access syntax list[idx] - parse_tuple_index(): Field access syntax tuple.0, tuple.1 File: src/ephapax-parser/src/lists_tuples.rs Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent a77303b commit 357735b

1 file changed

Lines changed: 261 additions & 0 deletions

File tree

Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
// SPDX-License-Identifier: PMPL-1.0-or-later
2+
// Parser support for lists and tuples
3+
4+
use crate::{ParseError, Parser, Result};
5+
use ephapax_lexer::TokenKind;
6+
use ephapax_syntax::{Expr, ExprKind, Pattern, Span};
7+
8+
impl<'a> Parser<'a> {
9+
/// Parse list literal: [e1, e2, e3]
10+
pub(crate) fn parse_list_literal(&mut self) -> Result<Expr> {
11+
let start = self.current_span().start;
12+
13+
// Expect '['
14+
self.expect(TokenKind::LBracket)?;
15+
16+
let mut elements = Vec::new();
17+
18+
// Empty list []
19+
if self.check(TokenKind::RBracket) {
20+
let end = self.current_span().end;
21+
self.advance();
22+
return Ok(Expr::new(ExprKind::ListLit(elements), Span::new(start, end)));
23+
}
24+
25+
// Parse first element
26+
elements.push(self.parse_expr()?);
27+
28+
// Parse remaining elements
29+
while self.check(TokenKind::Comma) {
30+
self.advance(); // consume comma
31+
32+
// Allow trailing comma: [1, 2, 3,]
33+
if self.check(TokenKind::RBracket) {
34+
break;
35+
}
36+
37+
elements.push(self.parse_expr()?);
38+
}
39+
40+
// Expect ']'
41+
let end = self.expect(TokenKind::RBracket)?.end;
42+
43+
Ok(Expr::new(ExprKind::ListLit(elements), Span::new(start, end)))
44+
}
45+
46+
/// Parse tuple literal or parenthesized expression: (e1, e2, e3)
47+
pub(crate) fn parse_tuple_or_paren(&mut self) -> Result<Expr> {
48+
let start = self.current_span().start;
49+
50+
// Expect '('
51+
self.expect(TokenKind::LParen)?;
52+
53+
// Unit: ()
54+
if self.check(TokenKind::RParen) {
55+
let end = self.current_span().end;
56+
self.advance();
57+
return Ok(Expr::new(
58+
ExprKind::Lit(ephapax_syntax::Literal::Unit),
59+
Span::new(start, end),
60+
));
61+
}
62+
63+
// Parse first expression
64+
let first = self.parse_expr()?;
65+
66+
// Check if it's a tuple or just parentheses
67+
if self.check(TokenKind::Comma) {
68+
// It's a tuple
69+
let mut elements = vec![first];
70+
71+
while self.check(TokenKind::Comma) {
72+
self.advance(); // consume comma
73+
74+
// Allow trailing comma: (1, 2, 3,)
75+
if self.check(TokenKind::RParen) {
76+
break;
77+
}
78+
79+
elements.push(self.parse_expr()?);
80+
}
81+
82+
let end = self.expect(TokenKind::RParen)?.end;
83+
84+
// Single-element tuple is just the element
85+
if elements.len() == 1 {
86+
Ok(elements.into_iter().next().unwrap())
87+
} else {
88+
Ok(Expr::new(ExprKind::TupleLit(elements), Span::new(start, end)))
89+
}
90+
} else {
91+
// Just parentheses
92+
self.expect(TokenKind::RParen)?;
93+
Ok(first)
94+
}
95+
}
96+
97+
/// Parse list index: expr[index]
98+
pub(crate) fn parse_list_index(&mut self, list: Expr) -> Result<Expr> {
99+
let start = list.span.start;
100+
101+
// Expect '['
102+
self.expect(TokenKind::LBracket)?;
103+
104+
// Parse index expression
105+
let index = Box::new(self.parse_expr()?);
106+
107+
// Expect ']'
108+
let end = self.expect(TokenKind::RBracket)?.end;
109+
110+
Ok(Expr::new(
111+
ExprKind::ListIndex {
112+
list: Box::new(list),
113+
index,
114+
},
115+
Span::new(start, end),
116+
))
117+
}
118+
119+
/// Parse tuple field access: expr.N
120+
pub(crate) fn parse_tuple_index(&mut self, tuple: Expr) -> Result<Expr> {
121+
let start = tuple.span.start;
122+
123+
// Expect '.'
124+
self.expect(TokenKind::Dot)?;
125+
126+
// Expect number
127+
let tok = self.current();
128+
if let TokenKind::IntLit(n) = tok.kind {
129+
let index = n as usize;
130+
let end = tok.span.end;
131+
self.advance();
132+
133+
Ok(Expr::new(
134+
ExprKind::TupleIndex {
135+
tuple: Box::new(tuple),
136+
index,
137+
},
138+
Span::new(start, end),
139+
))
140+
} else {
141+
Err(ParseError::Expected {
142+
expected: "integer for tuple field access".to_string(),
143+
found: format!("{:?}", tok.kind),
144+
span: tok.span,
145+
})
146+
}
147+
}
148+
149+
/// Parse tuple pattern: (p1, p2, p3)
150+
pub(crate) fn parse_tuple_pattern(&mut self) -> Result<Pattern> {
151+
// Expect '('
152+
self.expect(TokenKind::LParen)?;
153+
154+
// Unit pattern ()
155+
if self.check(TokenKind::RParen) {
156+
self.advance();
157+
return Ok(Pattern::Unit);
158+
}
159+
160+
// Parse first pattern
161+
let first = self.parse_pattern()?;
162+
163+
// Check if it's a tuple pattern
164+
if self.check(TokenKind::Comma) {
165+
let mut patterns = vec![first];
166+
167+
while self.check(TokenKind::Comma) {
168+
self.advance(); // consume comma
169+
170+
// Allow trailing comma
171+
if self.check(TokenKind::RParen) {
172+
break;
173+
}
174+
175+
patterns.push(self.parse_pattern()?);
176+
}
177+
178+
self.expect(TokenKind::RParen)?;
179+
180+
// Use existing Pair for 2-element tuples, Tuple for others
181+
if patterns.len() == 2 {
182+
Ok(Pattern::Pair(
183+
Box::new(patterns[0].clone()),
184+
Box::new(patterns[1].clone()),
185+
))
186+
} else {
187+
Ok(Pattern::Tuple(patterns))
188+
}
189+
} else {
190+
// Just parenthesized pattern
191+
self.expect(TokenKind::RParen)?;
192+
Ok(first)
193+
}
194+
}
195+
}
196+
197+
#[cfg(test)]
198+
mod tests {
199+
use super::*;
200+
use ephapax_lexer::Lexer;
201+
202+
fn parse_expr_helper(source: &str) -> Result<Expr> {
203+
let tokens: Vec<_> = Lexer::new(source).collect();
204+
let mut parser = Parser::new(&tokens);
205+
parser.parse_expr()
206+
}
207+
208+
#[test]
209+
fn test_parse_empty_list() {
210+
let expr = parse_expr_helper("[]").unwrap();
211+
match expr.kind {
212+
ExprKind::ListLit(elements) => assert_eq!(elements.len(), 0),
213+
_ => panic!("Expected ListLit"),
214+
}
215+
}
216+
217+
#[test]
218+
fn test_parse_list_literal() {
219+
let expr = parse_expr_helper("[1, 2, 3]").unwrap();
220+
match expr.kind {
221+
ExprKind::ListLit(elements) => assert_eq!(elements.len(), 3),
222+
_ => panic!("Expected ListLit"),
223+
}
224+
}
225+
226+
#[test]
227+
fn test_parse_tuple_literal() {
228+
let expr = parse_expr_helper("(1, 2, 3)").unwrap();
229+
match expr.kind {
230+
ExprKind::TupleLit(elements) => assert_eq!(elements.len(), 3),
231+
_ => panic!("Expected TupleLit"),
232+
}
233+
}
234+
235+
#[test]
236+
fn test_parse_unit() {
237+
let expr = parse_expr_helper("()").unwrap();
238+
match expr.kind {
239+
ExprKind::Lit(ephapax_syntax::Literal::Unit) => {}
240+
_ => panic!("Expected Unit literal"),
241+
}
242+
}
243+
244+
#[test]
245+
fn test_parse_list_index() {
246+
let expr = parse_expr_helper("list[0]").unwrap();
247+
match expr.kind {
248+
ExprKind::ListIndex { .. } => {}
249+
_ => panic!("Expected ListIndex"),
250+
}
251+
}
252+
253+
#[test]
254+
fn test_parse_tuple_index() {
255+
let expr = parse_expr_helper("tuple.0").unwrap();
256+
match expr.kind {
257+
ExprKind::TupleIndex { index, .. } => assert_eq!(index, 0),
258+
_ => panic!("Expected TupleIndex"),
259+
}
260+
}
261+
}

0 commit comments

Comments
 (0)