From 7e2b95593ff091e49480e661424b1639a9af4665 Mon Sep 17 00:00:00 2001 From: Greg Shuflin Date: Thu, 18 Nov 2021 21:02:33 -0800 Subject: [PATCH] Named structs --- schala-lang/src/parsing/combinator.rs | 178 +++++++++++++++----------- schala-lang/src/parsing/test.rs | 6 +- 2 files changed, 107 insertions(+), 77 deletions(-) diff --git a/schala-lang/src/parsing/combinator.rs b/schala-lang/src/parsing/combinator.rs index 2a54ee6..1d031f9 100644 --- a/schala-lang/src/parsing/combinator.rs +++ b/schala-lang/src/parsing/combinator.rs @@ -4,16 +4,14 @@ use nom::{ branch::alt, bytes::complete::{escaped_transform, tag, take_till, take_while}, character::{ - complete::{ - alpha1, alphanumeric0, char, line_ending, none_of, not_line_ending, one_of, space0, space1, - }, + complete::{alpha1, alphanumeric0, char, line_ending, none_of, not_line_ending, one_of, space1}, is_alphanumeric, }, - combinator::{map, not, opt, peek, recognize, value}, + combinator::{cond, flat_map, map, not, opt, peek, recognize, value}, error::{context, ParseError, VerboseError}, - multi::{fold_many1, many0, many1, separated_list0, separated_list1}, - sequence::{delimited, pair, preceded, tuple}, - Err, IResult, Parser, + multi::{many0, many1, separated_list0, separated_list1}, + sequence::{delimited, pair, preceded, separated_pair, tuple}, + IResult, Parser, }; use nom_locate::{position, LocatedSpan}; @@ -99,15 +97,16 @@ pub fn program(input: Span) -> ParseResult { Ok((rest, ast)) } -fn block_template<'a, O>(input_parser: impl Parser, O, VerboseError>>) -> impl FnMut(Span<'a>) -> -IResult, Vec, VerboseError>> { +fn block_template<'a, O>( + input_parser: impl Parser, O, VerboseError>>, +) -> impl FnMut(Span<'a>) -> IResult, Vec, VerboseError>> { map( delimited( tok(char('{')), tuple(( - many0(statement_delimiter), - separated_list0(statement_delimiter, input_parser), - many0(statement_delimiter), + many0(statement_delimiter), + separated_list0(statement_delimiter, input_parser), + many0(statement_delimiter), )), tok(char('}')), ), @@ -286,9 +285,20 @@ fn module(input: Span) -> ParseResult { pub fn expression(input: Span) -> ParseResult { let id = fresh_id(&input); - map(pair(expression_kind, opt(type_anno)), move |(kind, type_anno)| Expression { id, type_anno, kind })( - input, - ) + map(pair(expression_kind(true), opt(type_anno)), move |(kind, type_anno)| Expression { + id, + type_anno, + kind, + })(input) +} + +fn expression_no_struct(input: Span) -> ParseResult { + let id = fresh_id(&input); + map(pair(expression_kind(false), opt(type_anno)), move |(kind, type_anno)| Expression { + id, + type_anno, + kind, + })(input) } fn type_anno(input: Span) -> ParseResult { @@ -316,23 +326,22 @@ fn type_params(input: Span) -> ParseResult> { delimited(tok(char('<')), separated_list1(tok(char(',')), type_identifier), tok(char('>')))(input) } -pub fn expression_kind(input: Span) -> ParseResult { - context("expression-kind", precedence_expr)(input) +pub fn expression_kind(allow_struct: bool) -> impl FnMut(Span) -> ParseResult { + move |input: Span| context("expression-kind", precedence_expr(allow_struct))(input) } -fn precedence_expr(input: Span) -> ParseResult { - let handle = input.extra.clone(); - map( - pair(prefix_expr, many0(precedence_continuation)), - move |(first, rest): (ExpressionKind, Vec<(BinOp, ExpressionKind)>)| { - let mut handle_ref = handle.borrow_mut(); - BinopSequence { first, rest }.do_precedence(&mut handle_ref) - }, - )(input) -} - -fn precedence_continuation(input: Span) -> ParseResult<(BinOp, ExpressionKind)> { - pair(operator, prefix_expr)(input) +fn precedence_expr(allow_struct: bool) -> impl FnMut(Span) -> ParseResult { + move |input: Span| { + let handle = input.extra.clone(); + let precedence_continuation = pair(operator, prefix_expr(allow_struct)); + map( + pair(prefix_expr(allow_struct), many0(precedence_continuation)), + move |(first, rest): (ExpressionKind, Vec<(BinOp, ExpressionKind)>)| { + let mut handle_ref = handle.borrow_mut(); + BinopSequence { first, rest }.do_precedence(&mut handle_ref) + }, + )(input) + } } fn operator(input: Span) -> ParseResult { @@ -346,19 +355,21 @@ fn prefix_op(input: Span) -> ParseResult { tok(map(recognize(one_of("+-!")), |sigil: Span| PrefixOp::from_sigil(sigil.fragment())))(input) } -fn prefix_expr(input: Span) -> ParseResult { - let handle = input.extra.clone(); - context( - "prefix-expr", - map(pair(opt(prefix_op), extended_expr), move |(prefix, expr)| { - if let Some(prefix) = prefix { - let expr = Expression::new(fresh_id_rc(&handle), expr); - ExpressionKind::PrefixExp(prefix, Box::new(expr)) - } else { - expr - } - }), - )(input) +fn prefix_expr(allow_struct: bool) -> impl FnMut(Span) -> ParseResult { + move |input: Span| { + let handle = input.extra.clone(); + context( + "prefix-expr", + map(pair(opt(prefix_op), extended_expr(allow_struct)), move |(prefix, expr)| { + if let Some(prefix) = prefix { + let expr = Expression::new(fresh_id_rc(&handle), expr); + ExpressionKind::PrefixExp(prefix, Box::new(expr)) + } else { + expr + } + }), + )(input) + } } #[derive(Debug)] @@ -368,25 +379,27 @@ enum ExtendedPart<'a> { Accessor(&'a str), } -fn extended_expr(input: Span) -> ParseResult { - let (s, (primary, parts)) = - context("extended-expr", pair(primary_expr, many0(extended_expr_part)))(input)?; +fn extended_expr(allow_struct: bool) -> impl FnMut(Span) -> ParseResult { + move |input: Span| { + let (s, (primary, parts)) = + context("extended-expr", pair(primary_expr(allow_struct), many0(extended_expr_part)))(input)?; - let mut expression = Expression::new(fresh_id(&s), primary); - for part in parts.into_iter() { - let kind = match part { - ExtendedPart::Index(indexers) => - ExpressionKind::Index { indexee: Box::new(expression), indexers }, - ExtendedPart::Call(arguments) => ExpressionKind::Call { f: Box::new(expression), arguments }, - ExtendedPart::Accessor(name) => { - let name = rc_string(name); - ExpressionKind::Access { name, expr: Box::new(expression) } - } - }; - expression = Expression::new(fresh_id(&s), kind); + let mut expression = Expression::new(fresh_id(&s), primary); + for part in parts.into_iter() { + let kind = match part { + ExtendedPart::Index(indexers) => + ExpressionKind::Index { indexee: Box::new(expression), indexers }, + ExtendedPart::Call(arguments) => ExpressionKind::Call { f: Box::new(expression), arguments }, + ExtendedPart::Accessor(name) => { + let name = rc_string(name); + ExpressionKind::Access { name, expr: Box::new(expression) } + } + }; + expression = Expression::new(fresh_id(&s), kind); + } + + Ok((s, expression.kind)) } - - Ok((s, expression.kind)) } fn extended_expr_part(input: Span) -> ParseResult { @@ -420,13 +433,14 @@ fn invocation_argument(input: Span) -> ParseResult { ))(input) } -fn primary_expr(input: Span) -> ParseResult { - context( - "primary-expr", - alt(( - primary_expr_no_struct, - )), - )(input) +fn primary_expr(allow_struct: bool) -> impl FnMut(Span) -> ParseResult { + move |input: Span| { + if allow_struct { + context("primary-expr", alt((named_struct, primary_expr_no_struct)))(input) + } else { + context("primary-expr", primary_expr_no_struct)(input) + } + } } fn primary_expr_no_struct(input: Span) -> ParseResult { @@ -445,12 +459,25 @@ fn primary_expr_no_struct(input: Span) -> ParseResult { )(input) } +fn named_struct(input: Span) -> ParseResult { + map(pair(qualified_identifier, record_block), |(name, fields)| ExpressionKind::NamedStruct { + name, + fields, + })(input) +} + +//TODO support anonymous structs and Elm-style update syntax for structs +fn record_block(input: Span) -> ParseResult, Expression)>> { + let record_entry = + separated_pair(map(tok(identifier), |span| rc_string(span.fragment())), tok(char(':')), expression); + + delimited(tok(char('{')), separated_list0(tok(char(',')), record_entry), tok(char('}')))(input) +} + fn while_expr(input: Span) -> ParseResult { let id = fresh_id(&input); - map(preceded(kw("while"), pair(opt(expression), block)), - move |(condition, body)| ExpressionKind::WhileExpression { - condition: condition.map(Box::new), - body, + map(preceded(kw("while"), pair(opt(expression_no_struct), block)), move |(condition, body)| { + ExpressionKind::WhileExpression { condition: condition.map(Box::new), body } })(input) } @@ -695,12 +722,15 @@ mod test { #[test] fn combinator_test2() { for s in [" 15", " 0b1111", " 1_5_", "0XF__", "0Xf"].iter() { - assert_eq!(span!(expression_kind, s).unwrap().1, ExpressionKind::NatLiteral(15)); + assert_eq!(span!(expression_kind(true), s).unwrap().1, ExpressionKind::NatLiteral(15)); } - assert_eq!(span!(expression_kind, " /*gay*/ true").unwrap().1, ExpressionKind::BoolLiteral(true)); assert_eq!( - span!(expression_kind, " /*yolo*/ barnaby").unwrap().1, + span!(expression_kind(true), " /*gay*/ true").unwrap().1, + ExpressionKind::BoolLiteral(true) + ); + assert_eq!( + span!(expression_kind(true), " /*yolo*/ barnaby").unwrap().1, ExpressionKind::Value(qn!(barnaby)) ); } diff --git a/schala-lang/src/parsing/test.rs b/schala-lang/src/parsing/test.rs index 3139fa8..03ec6dc 100644 --- a/schala-lang/src/parsing/test.rs +++ b/schala-lang/src/parsing/test.rs @@ -318,14 +318,14 @@ fn identifiers() { #[test] fn named_struct() { use ExpressionKind::*; - assert_expr!( + assert_expr_comb!( "Pandas { a: x + y }", expr(NamedStruct { name: qn!(Pandas), fields: vec![(rc("a"), binop("+", expr(Value(qn!(x))), expr(Value(qn!(y)))))] }) ); - assert_expr!( + assert_expr_comb!( "Trousers { a:1, b:800 }", expr(NamedStruct { name: qn!(Trousers), @@ -375,7 +375,7 @@ fn index() { fn while_expression() { use ExpressionKind::*; - // assert_expr_comb!("while { }", expr(WhileExpression { condition: None, body: Block::default() })); + // assert_expr_comb!("while { }", expr(WhileExpression { condition: None, body: Block::default() })); assert_expr_comb!( "while a == b { }", expr(WhileExpression {