@@ -213,22 +213,48 @@ fn expr<'sc>() -> impl Parser<Token<'sc>, ast::Expr, Error = ParseError<'sc>> +
213213 . delimited_by ( just ( Token :: ParenOpen ) , just ( Token :: ParenClose ) ) ;
214214
215215 let call = ident ( )
216- . then ( args)
216+ . then ( args. clone ( ) )
217217 . map ( |( name, args) | ast:: Expr :: Call { name, args } ) ;
218218
219+ let tuple = args. map ( ast:: Expr :: Tuple ) ;
220+
219221 let atom = choice ( (
220222 immediate ( ) . map ( ast:: Expr :: Immediate ) ,
221223 unary_op ( expr. clone ( ) ) ,
222224 code_block_expr ( expr. clone ( ) ) . map ( ast:: Expr :: Block ) ,
223225 if_expr ( expr. clone ( ) ) ,
224226 call,
227+ tuple,
225228 ident ( ) . map ( ast:: Expr :: Ident ) ,
226229 ) ) ;
227230
228- comparison_op ( additive_op ( multiplicative_op ( atom) ) )
231+ comparison_op ( additive_op ( multiplicative_op ( tuple_index ( atom) ) ) )
229232 } )
230233}
231234
235+ fn tuple_index < ' sc , P > (
236+ parser : P ,
237+ ) -> impl Parser < Token < ' sc > , ast:: Expr , Error = ParseError < ' sc > > + Clone
238+ where
239+ P : Parser < Token < ' sc > , ast:: Expr , Error = ParseError < ' sc > > + Clone ,
240+ {
241+ // This extracts a `usize` index. Fails for everything else (therefore, `t.0.0` is not
242+ // supported - but `t.0 .0` is fine).
243+ let index = filter_map ( |span, token| match token {
244+ Token :: IntLiteral ( num_str) => num_str
245+ . parse :: < usize > ( )
246+ . map_err ( |_| ParseError :: InvalidIntegerForTupleIndex { span, index : token } ) ,
247+ _ => Err ( ParseError :: InvalidTupleIndex { span, index : token } ) ,
248+ } ) ;
249+
250+ parser
251+ . then ( just ( Token :: Dot ) . ignore_then ( index) . repeated ( ) )
252+ . foldl ( |expr, index| ast:: Expr :: TupleIndex {
253+ tuple : Box :: new ( expr) ,
254+ index,
255+ } )
256+ }
257+
232258fn multiplicative_op < ' sc , P > (
233259 parser : P ,
234260) -> impl Parser < Token < ' sc > , ast:: Expr , Error = ParseError < ' sc > > + Clone
@@ -321,12 +347,20 @@ fn ident<'sc>() -> impl Parser<Token<'sc>, ast::Ident, Error = ParseError<'sc>>
321347}
322348
323349fn type_ < ' sc > ( ) -> impl Parser < Token < ' sc > , ast:: Type , Error = ParseError < ' sc > > + Clone {
324- choice ( (
325- just ( Token :: Real ) . to ( ast:: Type :: Real ) ,
326- just ( Token :: Int ) . to ( ast:: Type :: Int ) ,
327- just ( Token :: Bool ) . to ( ast:: Type :: Bool ) ,
328- just ( Token :: String ) . to ( ast:: Type :: String ) ,
329- ) )
350+ recursive ( |type_| {
351+ let tuple = type_
352+ . separated_by ( just ( Token :: Comma ) )
353+ . allow_trailing ( )
354+ . delimited_by ( just ( Token :: ParenOpen ) , just ( Token :: ParenClose ) ) ;
355+
356+ choice ( (
357+ just ( Token :: Real ) . to ( ast:: Type :: Real ) ,
358+ just ( Token :: Int ) . to ( ast:: Type :: Int ) ,
359+ just ( Token :: Bool ) . to ( ast:: Type :: Bool ) ,
360+ just ( Token :: String ) . to ( ast:: Type :: String ) ,
361+ tuple. map ( ast:: Type :: Tuple ) ,
362+ ) )
363+ } )
330364}
331365
332366fn immediate < ' sc > ( ) -> impl Parser < Token < ' sc > , ast:: Immediate , Error = ParseError < ' sc > > + Clone {
@@ -373,6 +407,25 @@ fn check(actual: &str, expect: expect_test::Expect) {
373407 expect. assert_eq ( actual) ;
374408}
375409
410+ #[ test]
411+ fn types ( ) {
412+ check ( & run_parser ! ( type_( ) , "int" ) , expect_test:: expect![ "Int" ] ) ;
413+ check ( & run_parser ! ( type_( ) , "real" ) , expect_test:: expect![ "Real" ] ) ;
414+ check ( & run_parser ! ( type_( ) , "bool" ) , expect_test:: expect![ "Bool" ] ) ;
415+ check (
416+ & run_parser ! ( type_( ) , "string" ) ,
417+ expect_test:: expect![ "String" ] ,
418+ ) ;
419+ check (
420+ & run_parser ! ( type_( ) , "(int, real, string)" ) ,
421+ expect_test:: expect![ "Tuple([Int, Real, String])" ] ,
422+ ) ;
423+ check (
424+ & run_parser ! ( type_( ) , "(int, (real, int), string)" ) ,
425+ expect_test:: expect![ "Tuple([Int, Tuple([Real, Int]), String])" ] ,
426+ ) ;
427+ }
428+
376429#[ test]
377430fn let_decls ( ) {
378431 check (
@@ -839,7 +892,7 @@ fn code_blocks() {
839892 check (
840893 & format ! ( "{:?}" , run_parser!( let_decl( expr( ) ) , "let x = {};" ) ) ,
841894 expect_test:: expect![ [
842- r#""@9..10: found \"}\" but expected \"!\", \"+\", \"-\", \"{\", \"if\", \"var\", \"let\", or \"constraint\"\n""#
895+ r#""@9..10: found \"}\" but expected \"!\", \"+\", \"-\", \"{\", \"(\", \" if\", \"var\", \"let\", or \"constraint\"\n""#
843896 ] ] ,
844897 ) ;
845898}
@@ -881,6 +934,93 @@ fn if_exprs() {
881934 ) ;
882935}
883936
937+ #[ test]
938+ fn tuple_expressions ( ) {
939+ check (
940+ & run_parser ! ( expr( ) , r#"(0,)"# ) ,
941+ expect_test:: expect![ "Tuple([Immediate(Int(0))])" ] ,
942+ ) ;
943+
944+ check (
945+ & run_parser ! ( expr( ) , r#"(0, 1.0, "foo")"# ) ,
946+ expect_test:: expect![ [
947+ r#"Tuple([Immediate(Int(0)), Immediate(Real(1.0)), Immediate(String("foo"))])"#
948+ ] ] ,
949+ ) ;
950+
951+ check (
952+ & run_parser ! ( expr( ) , r#"(0, (1.0, "bar"), "foo")"# ) ,
953+ expect_test:: expect![ [
954+ r#"Tuple([Immediate(Int(0)), Tuple([Immediate(Real(1.0)), Immediate(String("bar"))]), Immediate(String("foo"))])"#
955+ ] ] ,
956+ ) ;
957+
958+ check (
959+ & run_parser ! ( expr( ) , r#"( { 42 }, if cond { 2 } else { 3 }, foo() )"# ) ,
960+ expect_test:: expect![ [
961+ r#"Tuple([Block(Block { statements: [], final_expr: Immediate(Int(42)) }), If(IfExpr { condition: Ident(Ident("cond")), then_block: Block { statements: [], final_expr: Immediate(Int(2)) }, else_block: Block { statements: [], final_expr: Immediate(Int(3)) } }), Call { name: Ident("foo"), args: [] }])"#
962+ ] ] ,
963+ ) ;
964+
965+ check (
966+ & run_parser ! ( expr( ) , r#"t.0 + t.9999999"# ) ,
967+ expect_test:: expect![ [
968+ r#"BinaryOp { op: Add, lhs: TupleIndex { tuple: Ident(Ident("t")), index: 0 }, rhs: TupleIndex { tuple: Ident(Ident("t")), index: 9999999 } }"#
969+ ] ] ,
970+ ) ;
971+
972+ check (
973+ & run_parser ! ( expr( ) , r#"(0, 1).0"# ) ,
974+ expect_test:: expect![
975+ "TupleIndex { tuple: Tuple([Immediate(Int(0)), Immediate(Int(1))]), index: 0 }"
976+ ] ,
977+ ) ;
978+
979+ check (
980+ & run_parser ! ( expr( ) , r#"t.0 .0"# ) ,
981+ expect_test:: expect![ [
982+ r#"TupleIndex { tuple: TupleIndex { tuple: Ident(Ident("t")), index: 0 }, index: 0 }"#
983+ ] ] ,
984+ ) ;
985+
986+ check (
987+ & run_parser ! ( expr( ) , r#"foo().0"# ) ,
988+ expect_test:: expect![ [
989+ r#"TupleIndex { tuple: Call { name: Ident("foo"), args: [] }, index: 0 }"#
990+ ] ] ,
991+ ) ;
992+
993+ check (
994+ & run_parser ! ( expr( ) , r#"{ (0, 0) }.0"# ) ,
995+ expect_test:: expect![ "TupleIndex { tuple: Block(Block { statements: [], final_expr: Tuple([Immediate(Int(0)), Immediate(Int(0))]) }), index: 0 }" ] ,
996+ ) ;
997+
998+ check (
999+ & run_parser ! ( expr( ) , r#"if true { (0, 0) } else { (0, 0) }.0"# ) ,
1000+ expect_test:: expect![ "TupleIndex { tuple: If(IfExpr { condition: Immediate(Bool(true)), then_block: Block { statements: [], final_expr: Tuple([Immediate(Int(0)), Immediate(Int(0))]) }, else_block: Block { statements: [], final_expr: Tuple([Immediate(Int(0)), Immediate(Int(0))]) } }), index: 0 }" ] ,
1001+ ) ;
1002+
1003+ // This parses because `1 + 2` is an expression, but it should fail in semantic analysis.
1004+ check (
1005+ & run_parser ! ( expr( ) , "1 + 2 .3" ) ,
1006+ expect_test:: expect![ "BinaryOp { op: Add, lhs: Immediate(Int(1)), rhs: TupleIndex { tuple: Immediate(Int(2)), index: 3 } }" ] ,
1007+ ) ;
1008+
1009+ check (
1010+ & run_parser ! ( let_decl( expr( ) ) , "let x = t.0xa;" ) ,
1011+ expect_test:: expect![ [ r#"
1012+ @10..13: Invalid integer value "0xa" for tuple index
1013+ "# ] ] ,
1014+ ) ;
1015+
1016+ check (
1017+ & run_parser ! ( let_decl( expr( ) ) , "let x = t.xx;" ) ,
1018+ expect_test:: expect![ [ r#"
1019+ @10..12: Invalid value "xx" for tuple index
1020+ "# ] ] ,
1021+ ) ;
1022+ }
1023+
8841024#[ test]
8851025fn basic_program ( ) {
8861026 let src = r#"
@@ -907,7 +1047,7 @@ fn with_errors() {
9071047 check (
9081048 & run_parser ! ( yurt_program( ) , "let low_val: bad = 1.23" ) ,
9091049 expect_test:: expect![ [ r#"
910- @13..16: found "bad" but expected "real", "int", "bool", or "string"
1050+ @13..16: found "bad" but expected "(", " real", "int", "bool", or "string"
9111051 "# ] ] ,
9121052 ) ;
9131053}
@@ -924,7 +1064,7 @@ fn fn_errors() {
9241064 check (
9251065 & run_parser ! ( yurt_program( ) , "fn foo() -> real {}" ) ,
9261066 expect_test:: expect![ [ r#"
927- @18..19: found "}" but expected "!", "+", "-", "{", "if", "var", "let", or "constraint"
1067+ @18..19: found "}" but expected "!", "+", "-", "{", "(", " if", "var", "let", or "constraint"
9281068 "# ] ] ,
9291069 ) ;
9301070}
0 commit comments