diff --git a/schala-lang/language/src/parser.rs b/schala-lang/language/src/parser.rs index 5d4fbe4..aef1122 100644 --- a/schala-lang/language/src/parser.rs +++ b/schala-lang/language/src/parser.rs @@ -6,7 +6,7 @@ use std::str::FromStr; use nom::IResult; use nom::character::complete::{one_of, space0, alphanumeric0}; use nom::bytes::complete::{tag, take, take_while, take_while1, take_until}; -use nom::combinator::{cut, map, map_res, value, opt, verify}; +use nom::combinator::{cut, cond, map, map_res, value, opt, verify}; use nom::multi::{separated_list, separated_nonempty_list, many1, many0}; use nom::error::{context, ParseError, VerboseError}; use nom::branch::alt; @@ -51,10 +51,15 @@ fn identifier(text: &str) -> ParseResult> { } const OPERATOR_CHARS: &'static str = "~`!@#$%^&*-+=<>?/|"; -fn parse_binop(text: &str) -> ParseResult { - let (text, op): (_, Vec) = context("Binop", many1(one_of(OPERATOR_CHARS)))(text)?; - let sigil: String = op.into_iter().collect(); - Ok((text, BinOp::from_sigil(&sigil))) +fn operator(text: &str) -> ParseResult> { + many1(one_of(OPERATOR_CHARS))(text) +} + +fn binop(text: &str) -> ParseResult { + context("Binop", map( + operator, + |op| BinOp::from_sigil(&op.into_iter().collect::()) + ))(text) } fn bool_literal(text: &str) -> ParseResult { @@ -68,7 +73,7 @@ fn bool_literal(text: &str) -> ParseResult { fn number_literal(text: &str) -> ParseResult { let num_lit = many1(alt(( map(one_of("1234567890"), |s: char| Some(s)), - map(nom::character::complete::char('_'), |_| None) + value(None, nom::character::complete::char('_')), ))); let (text, n) = map_res(num_lit, @@ -143,10 +148,10 @@ fn primary_expr(text: &str) -> ParseResult { // primary := literal | paren_expr | if_expr | for_expr | while_expr | identifier_expr | lambda_expr | anonymous_struct | list_expr alt(( + if_expr, literal, paren_expr, identifier_expr, - //if_expr, ))(text) } @@ -160,19 +165,21 @@ fn invocation_argument(text: &str) -> ParseResult { ))(text) } -/* fn if_expr(text: &str) -> ParseResult { - let p = preceded(tag("if"), pair(discriminator, if_expr_body)); + let p = preceded(tag("if"), pair(ws(discriminator), ws(if_expr_body))); map(p, |(discriminator, body)| { - - })(text) - - let expr = IfExpression { - discriminator: Option>, - body: Box, - }; + let discriminator = discriminator.map(Box::new); + let body = Box::new(body); + ExpressionKind::IfExpression { discriminator, body } + }) (text) +} + +fn discriminator(text: &str) -> ParseResult> { + use nom::combinator::verify; + cond(text.chars().next().map(|c| c != '{').unwrap_or(true), + expression + )(text) } -*/ fn if_expr_body(text: &str) -> ParseResult { alt(( @@ -244,7 +251,38 @@ fn pattern_literal(text: &str) -> ParseResult { } fn cond_block(text: &str) -> ParseResult { - unimplemented!() + use nom::character::complete::char; + //TODO maybe change this bit of syntax + let comma_or_delimitor = alt((value((), char(',')), statement_sep)); + let p = delimited(char('{'), + separated_nonempty_list(comma_or_delimitor, cond_arm), + char('}')); + map(p, IfExpressionBody::CondList)(text) +} + +fn cond_arm(text: &str) -> ParseResult { + let variant_1 = map( + tuple((condition, guard, tag("then"), expr_or_block)), + |(condition, guard, _, body)| ConditionArm { condition, guard, body } + ); + let variant_2 = map( + preceded(tag("else"), expr_or_block), + |body| ConditionArm { condition: Condition::Else, guard: None, body } + ); + alt((variant_1, variant_2))(text) +} + +fn condition(text: &str) -> ParseResult { + alt(( + map(preceded(tag("is"), pattern), Condition::Pattern), + map(tuple((binop, expression)), |(op, expr)| + Condition::TruncatedOp(op, expr)), + map(expression, Condition::Expression), + ))(text) +} + +fn guard(text: &str) -> ParseResult> { + opt(preceded(tag("if"), expression))(text) } fn expr_or_block(text: &str) -> ParseResult { @@ -294,7 +332,7 @@ fn precedence_expr(text: &str) -> ParseResult { let (mut outer_rest, mut lhs) = prefix_expr(input)?; loop { let (rest, _) = space0(outer_rest)?; - let (rest, maybe_binop) = opt(parse_binop)(rest)?; + let (rest, maybe_binop) = opt(binop)(rest)?; let (new_precedence, binop) = match maybe_binop { Some(binop) => (binop.precedence(), binop), None => break,