Merge pull request #664 from aiken-lang/KtorZ/error-todo-parser
This commit is contained in:
		
						commit
						bb01ddd7b5
					
				|  | @ -540,7 +540,7 @@ pub const DEFAULT_TODO_STR: &str = "aiken::todo"; | |||
| pub const DEFAULT_ERROR_STR: &str = "aiken::error"; | ||||
| 
 | ||||
| impl UntypedExpr { | ||||
|     pub fn todo(location: Span, reason: Option<Self>) -> Self { | ||||
|     pub fn todo(reason: Option<Self>, location: Span) -> Self { | ||||
|         UntypedExpr::Trace { | ||||
|             location, | ||||
|             kind: TraceKind::Todo, | ||||
|  | @ -552,7 +552,7 @@ impl UntypedExpr { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn error(location: Span, reason: Option<Self>) -> Self { | ||||
|     pub fn error(reason: Option<Self>, location: Span) -> Self { | ||||
|         UntypedExpr::Trace { | ||||
|             location, | ||||
|             kind: TraceKind::Error, | ||||
|  |  | |||
|  | @ -27,7 +27,7 @@ pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError | |||
|             |((((public, name), (arguments, args_span)), return_annotation), body), span| { | ||||
|                 ast::UntypedDefinition::Fn(ast::Function { | ||||
|                     arguments, | ||||
|                     body: body.unwrap_or_else(|| UntypedExpr::todo(span, None)), | ||||
|                     body: body.unwrap_or_else(|| UntypedExpr::todo(None, span)), | ||||
|                     doc: None, | ||||
|                     location: ast::Span { | ||||
|                         start: span.start, | ||||
|  |  | |||
|  | @ -23,7 +23,7 @@ pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError | |||
|         .map_with_span(|(((fail, name), span_end), body), span| { | ||||
|             ast::UntypedDefinition::Test(ast::Function { | ||||
|                 arguments: vec![], | ||||
|                 body: body.unwrap_or_else(|| UntypedExpr::todo(span, None)), | ||||
|                 body: body.unwrap_or_else(|| UntypedExpr::todo(None, span)), | ||||
|                 doc: None, | ||||
|                 location: span_end, | ||||
|                 end_position: span.end - 1, | ||||
|  |  | |||
|  | @ -0,0 +1,65 @@ | |||
| use chumsky::prelude::*; | ||||
| 
 | ||||
| use crate::{ | ||||
|     expr::UntypedExpr, | ||||
|     parser::{ | ||||
|         error::ParseError, | ||||
|         expr::{block::parser as block, string}, | ||||
|         token::Token, | ||||
|     }, | ||||
| }; | ||||
| 
 | ||||
| pub fn parser( | ||||
|     sequence: Recursive<'_, Token, UntypedExpr, ParseError>, | ||||
| ) -> impl Parser<Token, UntypedExpr, Error = ParseError> + '_ { | ||||
|     let message = || choice((string::hybrid(), block(sequence.clone()))); | ||||
|     choice(( | ||||
|         just(Token::Todo) | ||||
|             .ignore_then(message().or_not()) | ||||
|             .map_with_span(UntypedExpr::todo), | ||||
|         just(Token::ErrorTerm) | ||||
|             .ignore_then(message().or_not()) | ||||
|             .map_with_span(UntypedExpr::error), | ||||
|     )) | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use crate::assert_expr; | ||||
| 
 | ||||
|     #[test] | ||||
|     fn error_basic() { | ||||
|         assert_expr!( | ||||
|             r#" | ||||
|             error @"foo" | ||||
|             "#
 | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn error_sugar() { | ||||
|         assert_expr!( | ||||
|             r#" | ||||
|             error "foo" | ||||
|             "#
 | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn todo_basic() { | ||||
|         assert_expr!( | ||||
|             r#" | ||||
|             todo @"foo" | ||||
|             "#
 | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn todo_sugar() { | ||||
|         assert_expr!( | ||||
|             r#" | ||||
|             todo "foo" | ||||
|             "#
 | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|  | @ -7,6 +7,7 @@ pub mod assignment; | |||
| mod block; | ||||
| pub(crate) mod bytearray; | ||||
| mod chained; | ||||
| mod error_todo; | ||||
| mod if_else; | ||||
| mod int; | ||||
| mod list; | ||||
|  | @ -22,6 +23,7 @@ pub use anonymous_function::parser as anonymous_function; | |||
| pub use block::parser as block; | ||||
| pub use bytearray::parser as bytearray; | ||||
| pub use chained::parser as chained; | ||||
| pub use error_todo::parser as error_todo; | ||||
| pub use if_else::parser as if_else; | ||||
| pub use int::parser as int; | ||||
| pub use list::parser as list; | ||||
|  | @ -40,6 +42,17 @@ pub fn parser( | |||
|     sequence: Recursive<'_, Token, UntypedExpr, ParseError>, | ||||
| ) -> impl Parser<Token, UntypedExpr, Error = ParseError> + '_ { | ||||
|     recursive(|expression| { | ||||
|         choice(( | ||||
|             error_todo(sequence.clone()), | ||||
|             pure_expression(sequence, expression), | ||||
|         )) | ||||
|     }) | ||||
| } | ||||
| 
 | ||||
| pub fn pure_expression<'a>( | ||||
|     sequence: Recursive<'a, Token, UntypedExpr, ParseError>, | ||||
|     expression: Recursive<'a, Token, UntypedExpr, ParseError>, | ||||
| ) -> impl Parser<Token, UntypedExpr, Error = ParseError> + 'a { | ||||
|     let chained_debugged = chained(sequence, expression) | ||||
|         .then(just(Token::Question).or_not()) | ||||
|         .map_with_span(|(value, token), location| match token { | ||||
|  | @ -190,7 +203,6 @@ pub fn parser( | |||
|                 one_liner: pipe != Token::NewLinePipe, | ||||
|             } | ||||
|         }) | ||||
|     }) | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
|  |  | |||
|  | @ -3,33 +3,27 @@ use chumsky::prelude::*; | |||
| use crate::{ | ||||
|     ast::TraceKind, | ||||
|     expr::UntypedExpr, | ||||
|     parser::{error::ParseError, token::Token}, | ||||
|     parser::{ | ||||
|         error::ParseError, | ||||
|         expr::{block::parser as block, string}, | ||||
|         token::Token, | ||||
|     }, | ||||
| }; | ||||
| 
 | ||||
| pub fn parser() -> impl Parser<Token, UntypedExpr, Error = ParseError> { | ||||
|     recursive(|expression| { | ||||
|     recursive(|sequence| { | ||||
|         choice(( | ||||
|             just(Token::Trace) | ||||
|                 .ignore_then(super::parser(expression.clone())) | ||||
|                 .then(expression.clone()) | ||||
|                 .ignore_then(choice((string::hybrid(), block(sequence.clone())))) | ||||
|                 .then(sequence.clone()) | ||||
|                 .map_with_span(|(text, then_), span| UntypedExpr::Trace { | ||||
|                     kind: TraceKind::Trace, | ||||
|                     location: span, | ||||
|                     then: Box::new(then_), | ||||
|                     text: Box::new(super::string::flexible(text)), | ||||
|                     text: Box::new(text), | ||||
|                 }), | ||||
|             just(Token::ErrorTerm) | ||||
|                 .ignore_then(super::parser(expression.clone()).or_not()) | ||||
|                 .map_with_span(|reason, span| { | ||||
|                     UntypedExpr::error(span, reason.map(super::string::flexible)) | ||||
|                 }), | ||||
|             just(Token::Todo) | ||||
|                 .ignore_then(super::parser(expression.clone()).or_not()) | ||||
|                 .map_with_span(|reason, span| { | ||||
|                     UntypedExpr::todo(span, reason.map(super::string::flexible)) | ||||
|                 }), | ||||
|             super::parser(expression.clone()) | ||||
|                 .then(expression.repeated()) | ||||
|             super::parser(sequence.clone()) | ||||
|                 .then(sequence.repeated()) | ||||
|                 .foldl(|current, next| current.append_in_sequence(next)), | ||||
|         )) | ||||
|     }) | ||||
|  |  | |||
|  | @ -0,0 +1,15 @@ | |||
| --- | ||||
| source: crates/aiken-lang/src/parser/expr/error_todo.rs | ||||
| description: "Code:\n\nerror @\"foo\"\n" | ||||
| --- | ||||
| Trace { | ||||
|     kind: Error, | ||||
|     location: 0..12, | ||||
|     then: ErrorTerm { | ||||
|         location: 0..12, | ||||
|     }, | ||||
|     text: String { | ||||
|         location: 6..12, | ||||
|         value: "foo", | ||||
|     }, | ||||
| } | ||||
|  | @ -0,0 +1,15 @@ | |||
| --- | ||||
| source: crates/aiken-lang/src/parser/expr/error_todo.rs | ||||
| description: "Code:\n\nerror \"foo\"\n" | ||||
| --- | ||||
| Trace { | ||||
|     kind: Error, | ||||
|     location: 0..11, | ||||
|     then: ErrorTerm { | ||||
|         location: 0..11, | ||||
|     }, | ||||
|     text: String { | ||||
|         location: 6..11, | ||||
|         value: "foo", | ||||
|     }, | ||||
| } | ||||
|  | @ -0,0 +1,15 @@ | |||
| --- | ||||
| source: crates/aiken-lang/src/parser/expr/error_todo.rs | ||||
| description: "Code:\n\ntodo @\"foo\"\n" | ||||
| --- | ||||
| Trace { | ||||
|     kind: Todo, | ||||
|     location: 0..11, | ||||
|     then: ErrorTerm { | ||||
|         location: 0..11, | ||||
|     }, | ||||
|     text: String { | ||||
|         location: 5..11, | ||||
|         value: "foo", | ||||
|     }, | ||||
| } | ||||
|  | @ -0,0 +1,15 @@ | |||
| --- | ||||
| source: crates/aiken-lang/src/parser/expr/error_todo.rs | ||||
| description: "Code:\n\ntodo \"foo\"\n" | ||||
| --- | ||||
| Trace { | ||||
|     kind: Todo, | ||||
|     location: 0..10, | ||||
|     then: ErrorTerm { | ||||
|         location: 0..10, | ||||
|     }, | ||||
|     text: String { | ||||
|         location: 5..10, | ||||
|         value: "foo", | ||||
|     }, | ||||
| } | ||||
|  | @ -1,9 +1,11 @@ | |||
| use chumsky::prelude::*; | ||||
| 
 | ||||
| use crate::{ | ||||
|     ast, | ||||
|     expr::UntypedExpr, | ||||
|     parser::{error::ParseError, literal::string::parser as string, token::Token}, | ||||
|     parser::{ | ||||
|         error::ParseError, literal::bytearray::utf8_string, literal::string::parser as string, | ||||
|         token::Token, | ||||
|     }, | ||||
| }; | ||||
| 
 | ||||
| pub fn parser() -> impl Parser<Token, UntypedExpr, Error = ParseError> { | ||||
|  | @ -13,23 +15,15 @@ pub fn parser() -> impl Parser<Token, UntypedExpr, Error = ParseError> { | |||
|     }) | ||||
| } | ||||
| 
 | ||||
| /// Interpret bytearray string literals written as utf-8 strings, as strings.
 | ||||
| ///
 | ||||
| /// This is mostly convenient so that todo & error works with either @"..." or plain "...".
 | ||||
| /// In this particular context, there's actually no ambiguity about the right-hand-side, so
 | ||||
| /// we can provide this syntactic sugar.
 | ||||
| pub fn flexible(expr: UntypedExpr) -> UntypedExpr { | ||||
|     match expr { | ||||
|         UntypedExpr::ByteArray { | ||||
|             preferred_format: ast::ByteArrayFormatPreference::Utf8String, | ||||
|             bytes, | ||||
|             location, | ||||
|         } => UntypedExpr::String { | ||||
|             location, | ||||
|             value: String::from_utf8(bytes).unwrap(), | ||||
|         }, | ||||
|         _ => expr, | ||||
|     } | ||||
| pub fn hybrid() -> impl Parser<Token, UntypedExpr, Error = ParseError> { | ||||
|     choice(( | ||||
|         string(), | ||||
|         utf8_string().map(|(_, bytes)| String::from_utf8(bytes).unwrap()), | ||||
|     )) | ||||
|     .map_with_span(|value, span| UntypedExpr::String { | ||||
|         location: span, | ||||
|         value, | ||||
|     }) | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
|  |  | |||
|  | @ -4,13 +4,13 @@ use vec1::vec1; | |||
| use crate::{ | ||||
|     ast, | ||||
|     expr::UntypedExpr, | ||||
|     parser::{error::ParseError, expr::string::flexible, pattern, token::Token}, | ||||
|     parser::{error::ParseError, pattern, token::Token}, | ||||
| }; | ||||
| 
 | ||||
| use super::guard; | ||||
| 
 | ||||
| pub fn parser( | ||||
|     r: Recursive<'_, Token, UntypedExpr, ParseError>, | ||||
|     expression: Recursive<'_, Token, UntypedExpr, ParseError>, | ||||
| ) -> impl Parser<Token, ast::UntypedClause, Error = ParseError> + '_ { | ||||
|     pattern() | ||||
|         .then(just(Token::Vbar).ignore_then(pattern()).repeated().or_not()) | ||||
|  | @ -27,23 +27,7 @@ pub fn parser( | |||
|                 }), | ||||
|         ))) | ||||
|         // TODO: add hint "Did you mean to wrap a multi line clause in curly braces?"
 | ||||
|         .then(choice(( | ||||
|             r.clone(), | ||||
|             just(Token::Todo) | ||||
|                 .ignore_then( | ||||
|                     r.clone() | ||||
|                         .then_ignore(one_of(Token::RArrow).not().rewind()) | ||||
|                         .or_not(), | ||||
|                 ) | ||||
|                 .map_with_span(|reason, span| UntypedExpr::todo(span, reason.map(flexible))), | ||||
|             just(Token::ErrorTerm) | ||||
|                 .ignore_then( | ||||
|                     r.clone() | ||||
|                         .then_ignore(just(Token::RArrow).not().rewind()) | ||||
|                         .or_not(), | ||||
|                 ) | ||||
|                 .map_with_span(|reason, span| UntypedExpr::error(span, reason.map(flexible))), | ||||
|         ))) | ||||
|         .then(expression) | ||||
|         .map_with_span( | ||||
|             |(((pattern, alternative_patterns_opt), guard), then), span| { | ||||
|                 let mut patterns = vec1![pattern]; | ||||
|  | @ -57,3 +41,43 @@ pub fn parser( | |||
|             }, | ||||
|         ) | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use crate::assert_expr; | ||||
| 
 | ||||
|     #[test] | ||||
|     fn todo_clause() { | ||||
|         assert_expr!( | ||||
|             r#" | ||||
|             when val is { | ||||
|               Bar1{..} -> True | ||||
|               Bar2{..} -> todo @"unimplemented" | ||||
|             } | ||||
|             "#
 | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn error_single_clause_no_message() { | ||||
|         assert_expr!( | ||||
|             r#" | ||||
|             when val is { | ||||
|               Bar1{..} -> error | ||||
|             } | ||||
|             "#
 | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn todo_double_clause_no_message() { | ||||
|         assert_expr!( | ||||
|             r#" | ||||
|             when val is { | ||||
|               Bar1{..} -> todo | ||||
|               Bar2{..} -> todo | ||||
|             } | ||||
|             "#
 | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -0,0 +1,40 @@ | |||
| --- | ||||
| source: crates/aiken-lang/src/parser/expr/when/clause.rs | ||||
| description: "Code:\n\nwhen val is {\n  Bar1{..} -> error\n}\n" | ||||
| --- | ||||
| When { | ||||
|     location: 0..35, | ||||
|     subject: Var { | ||||
|         location: 5..8, | ||||
|         name: "val", | ||||
|     }, | ||||
|     clauses: [ | ||||
|         UntypedClause { | ||||
|             location: 16..33, | ||||
|             patterns: [ | ||||
|                 Constructor { | ||||
|                     is_record: true, | ||||
|                     location: 16..24, | ||||
|                     name: "Bar1", | ||||
|                     arguments: [], | ||||
|                     module: None, | ||||
|                     constructor: (), | ||||
|                     with_spread: true, | ||||
|                     tipo: (), | ||||
|                 }, | ||||
|             ], | ||||
|             guard: None, | ||||
|             then: Trace { | ||||
|                 kind: Error, | ||||
|                 location: 28..33, | ||||
|                 then: ErrorTerm { | ||||
|                     location: 28..33, | ||||
|                 }, | ||||
|                 text: String { | ||||
|                     location: 28..33, | ||||
|                     value: "aiken::error", | ||||
|                 }, | ||||
|             }, | ||||
|         }, | ||||
|     ], | ||||
| } | ||||
|  | @ -0,0 +1,60 @@ | |||
| --- | ||||
| source: crates/aiken-lang/src/parser/expr/when/clause.rs | ||||
| description: "Code:\n\nwhen val is {\n  Bar1{..} -> True\n  Bar2{..} -> todo @\"unimplemented\"\n}\n" | ||||
| --- | ||||
| When { | ||||
|     location: 0..70, | ||||
|     subject: Var { | ||||
|         location: 5..8, | ||||
|         name: "val", | ||||
|     }, | ||||
|     clauses: [ | ||||
|         UntypedClause { | ||||
|             location: 16..32, | ||||
|             patterns: [ | ||||
|                 Constructor { | ||||
|                     is_record: true, | ||||
|                     location: 16..24, | ||||
|                     name: "Bar1", | ||||
|                     arguments: [], | ||||
|                     module: None, | ||||
|                     constructor: (), | ||||
|                     with_spread: true, | ||||
|                     tipo: (), | ||||
|                 }, | ||||
|             ], | ||||
|             guard: None, | ||||
|             then: Var { | ||||
|                 location: 28..32, | ||||
|                 name: "True", | ||||
|             }, | ||||
|         }, | ||||
|         UntypedClause { | ||||
|             location: 35..68, | ||||
|             patterns: [ | ||||
|                 Constructor { | ||||
|                     is_record: true, | ||||
|                     location: 35..43, | ||||
|                     name: "Bar2", | ||||
|                     arguments: [], | ||||
|                     module: None, | ||||
|                     constructor: (), | ||||
|                     with_spread: true, | ||||
|                     tipo: (), | ||||
|                 }, | ||||
|             ], | ||||
|             guard: None, | ||||
|             then: Trace { | ||||
|                 kind: Todo, | ||||
|                 location: 47..68, | ||||
|                 then: ErrorTerm { | ||||
|                     location: 47..68, | ||||
|                 }, | ||||
|                 text: String { | ||||
|                     location: 52..68, | ||||
|                     value: "unimplemented", | ||||
|                 }, | ||||
|             }, | ||||
|         }, | ||||
|     ], | ||||
| } | ||||
|  | @ -0,0 +1,67 @@ | |||
| --- | ||||
| source: crates/aiken-lang/src/parser/expr/when/clause.rs | ||||
| description: "Code:\n\nwhen val is {\n  Bar1{..} -> todo\n  Bar2{..} -> todo\n}\n" | ||||
| --- | ||||
| When { | ||||
|     location: 0..53, | ||||
|     subject: Var { | ||||
|         location: 5..8, | ||||
|         name: "val", | ||||
|     }, | ||||
|     clauses: [ | ||||
|         UntypedClause { | ||||
|             location: 16..32, | ||||
|             patterns: [ | ||||
|                 Constructor { | ||||
|                     is_record: true, | ||||
|                     location: 16..24, | ||||
|                     name: "Bar1", | ||||
|                     arguments: [], | ||||
|                     module: None, | ||||
|                     constructor: (), | ||||
|                     with_spread: true, | ||||
|                     tipo: (), | ||||
|                 }, | ||||
|             ], | ||||
|             guard: None, | ||||
|             then: Trace { | ||||
|                 kind: Todo, | ||||
|                 location: 28..32, | ||||
|                 then: ErrorTerm { | ||||
|                     location: 28..32, | ||||
|                 }, | ||||
|                 text: String { | ||||
|                     location: 28..32, | ||||
|                     value: "aiken::todo", | ||||
|                 }, | ||||
|             }, | ||||
|         }, | ||||
|         UntypedClause { | ||||
|             location: 35..51, | ||||
|             patterns: [ | ||||
|                 Constructor { | ||||
|                     is_record: true, | ||||
|                     location: 35..43, | ||||
|                     name: "Bar2", | ||||
|                     arguments: [], | ||||
|                     module: None, | ||||
|                     constructor: (), | ||||
|                     with_spread: true, | ||||
|                     tipo: (), | ||||
|                 }, | ||||
|             ], | ||||
|             guard: None, | ||||
|             then: Trace { | ||||
|                 kind: Todo, | ||||
|                 location: 47..51, | ||||
|                 then: ErrorTerm { | ||||
|                     location: 47..51, | ||||
|                 }, | ||||
|                 text: String { | ||||
|                     location: 47..51, | ||||
|                     value: "aiken::todo", | ||||
|                 }, | ||||
|             }, | ||||
|         }, | ||||
|     ], | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	 Matthias Benkort
						Matthias Benkort