This commit is contained in:
Greg Shuflin 2021-11-19 00:40:10 -08:00
parent ec51659452
commit 721a499384
2 changed files with 137 additions and 15 deletions

View File

@ -300,6 +300,16 @@ fn expression_no_struct(input: Span) -> ParseResult<Expression> {
})(input)
}
fn expr_or_block(input: Span) -> ParseResult<Block> {
let (input, pos) = position(input)?;
let id = fresh_id(&input);
let location = pos.location_offset().into();
alt((
block,
map(expression, move |expr| Statement { id, location, kind: StatementKind::Expression(expr) }.into()),
))(input)
}
fn type_anno(input: Span) -> ParseResult<TypeIdentifier> {
preceded(tok(char(':')), type_identifier)(input)
}
@ -444,11 +454,13 @@ fn primary_expr(allow_struct: bool) -> impl FnMut(Span) -> ParseResult<Expressio
}
fn primary_expr_no_struct(input: Span) -> ParseResult<ExpressionKind> {
println!("PRIM EXPR: {}", input.fragment());
context(
"primary-expr-no-struct",
alt((
while_expr,
for_expr,
if_expr,
list_expr,
paren_expr,
string_literal,
@ -482,10 +494,119 @@ fn while_expr(input: Span) -> ParseResult<ExpressionKind> {
})(input)
}
fn if_expr(input: Span) -> ParseResult<ExpressionKind> {
fn else_case(input: Span) -> ParseResult<Option<Block>> {
opt(preceded(kw("else"), expr_or_block))(input)
}
fn cond_block(input: Span) -> ParseResult<IfExpressionBody> {
map(block_template(cond_arm), IfExpressionBody::CondList)(input)
}
fn cond_arm(input: Span) -> ParseResult<ConditionArm> {
let condition = map(preceded(kw("is"), pattern), Condition::Pattern);
let condition_guard = opt(preceded(kw("if"), expression));
alt((
map(preceded(kw("else"), expr_or_block), |body| ConditionArm {
condition: Condition::Else,
guard: None,
body,
}),
map(
tuple((condition, condition_guard, kw("then"), expr_or_block)),
|(condition, guard, _, body)| ConditionArm { condition, guard, body },
),
))(input)
}
fn simple_pattern_match(input: Span) -> ParseResult<IfExpressionBody> {
map(
tuple((preceded(kw("is"), pattern), preceded(kw("then"), pair(expr_or_block, else_case)))),
|(pattern, (then_case, else_case))| IfExpressionBody::SimplePatternMatch {
pattern,
then_case,
else_case,
},
)(input)
}
fn simple_conditional(input: Span) -> ParseResult<IfExpressionBody> {
map(preceded(kw("then"), pair(expr_or_block, else_case)), |(then_case, else_case)| {
IfExpressionBody::SimpleConditional { then_case, else_case }
})(input)
}
fn if_expr_body(input: Span) -> ParseResult<IfExpressionBody> {
alt((cond_block, simple_pattern_match, simple_conditional))(input)
}
map(preceded(kw("if"), pair(opt(expression_no_struct), if_expr_body)), |(discriminator, body)| {
ExpressionKind::IfExpression { discriminator: discriminator.map(Box::new), body: Box::new(body) }
})(input)
}
fn pattern(input: Span) -> ParseResult<Pattern> {
alt((
map(
delimited(tok(char('(')), separated_list1(tok(char(',')), pattern), tok(char(')'))),
Pattern::TuplePattern,
),
simple_pattern,
))(input)
}
fn simple_pattern(input: Span) -> ParseResult<Pattern> {
fn record_pattern_entry(input: Span) -> ParseResult<(Rc<String>, Pattern)> {
let id = fresh_id(&input);
alt((
map(separated_pair(identifier, tok(char(':')), pattern), |(ident, pat)| {
(rc_string(ident.fragment()), pat)
}),
map(identifier, move |ident| {
let qn = QualifiedName { id, components: vec![rc_string(ident.fragment())] };
(rc_string(ident.fragment()), Pattern::VarOrName(qn))
}),
))(input)
}
alt((
pattern_literal,
map(
pair(
qualified_identifier,
delimited(tok(char('(')), separated_list0(tok(char(',')), pattern), tok(char(')'))),
),
|(qn, members)| Pattern::TupleStruct(qn, members),
),
map(
pair(
qualified_identifier,
delimited(
tok(char('{')),
separated_list0(tok(char(',')), record_pattern_entry),
tok(char('}')),
),
),
|(qn, members)| Pattern::Record(qn, members),
),
map(qualified_identifier, Pattern::VarOrName),
))(input)
}
fn pattern_literal(input: Span) -> ParseResult<Pattern> {
alt((
value(Pattern::Ignored, kw("_")),
value(Pattern::Literal(PatternLiteral::BoolPattern(true)), kw("true")),
value(Pattern::Literal(PatternLiteral::BoolPattern(false)), kw("false")),
map(tok(bare_string_literal), |s| Pattern::Literal(PatternLiteral::StringPattern(Rc::new(s)))),
map(pair(opt(tok(char('-'))), alt((float_literal, number_literal))), |(sign, num)| {
Pattern::Literal(PatternLiteral::NumPattern { neg: sign.is_some(), num })
}),
))(input)
}
fn for_expr(input: Span) -> ParseResult<ExpressionKind> {
println!("IN FOR EXPR: {}", input.fragment());
fn for_enumerators(input: Span) -> ParseResult<Vec<Enumerator>> {
println!("NEUMS: {}", input.fragment());
alt((
delimited(tok(char('{')), separated_list0(tok(char(',')), enumerator), tok(char('}'))),
map(enumerator, |enumerator| vec![enumerator]),
@ -543,6 +664,7 @@ fn string_literal(input: Span) -> ParseResult<ExpressionKind> {
}
fn bare_string_literal(input: Span) -> ParseResult<String> {
println!("BARE STRING: {}", input.fragment());
let string_escape_transforms =
alt((value("\\", tag("\\")), value("\"", tag("\"")), value("\n", tag("n")), value("\t", tag("t"))));
alt((

View File

@ -134,7 +134,7 @@ macro_rules! assert_expr {
macro_rules! assert_expr_comb {
($input:expr, $correct:expr) => {
let mut parser = Parser::new();
let expr = parser.expression_comb($input);
let expr = parser.expression_comb($input.trim_start());
if expr.is_err() {
println!("Expression parse error: {}", expr.unwrap_err().msg);
panic!();
@ -1097,7 +1097,7 @@ fn imports() {
#[test]
fn if_exprs() {
use ExpressionKind::*;
assert_expr!(
assert_expr_comb!(
"if a() then { tuah(); }",
expr(IfExpression {
discriminator: Some(bx(expr(Call { f: bx(expr(Value(qn!(a)))), arguments: vec![] }))),
@ -1108,7 +1108,8 @@ fn if_exprs() {
})
);
assert_expr!(
//TODO add tests for named expressions
assert_expr_comb!(
"if a then b else c",
expr(IfExpression {
discriminator: Some(bx(expr(Value(qn!(a))))),
@ -1119,9 +1120,8 @@ fn if_exprs() {
})
);
assert_expr!(
r#"
if true then {
assert_expr_comb!(
r#"if true then {
let a = 10
b
} else {
@ -1151,7 +1151,7 @@ fn pattern_matching() {
use ExpressionKind::*;
for item in ["if x is Some(a) then { 4 } else { 9 }", "if x is Some(a) then 4 else 9"] {
assert_expr!(
assert_expr_comb!(
item,
expr(IfExpression {
discriminator: Some(bx(expr(Value(qn!(x))))),
@ -1164,7 +1164,7 @@ fn pattern_matching() {
);
}
assert_expr!(
assert_expr_comb!(
"if x is Something { a, b: x } then { 4 } else { 9 }",
expr(IfExpression {
discriminator: Some(bx(expr(Value(qn!(x))))),
@ -1179,7 +1179,7 @@ fn pattern_matching() {
})
);
assert_expr!(
assert_expr_comb!(
"if x is -1 then 1 else 2",
expr(IfExpression {
discriminator: Some(bx(expr(Value(qn!(x))))),
@ -1191,7 +1191,7 @@ fn pattern_matching() {
})
);
assert_expr!(
assert_expr_comb!(
"if x is true then 1 else 2",
expr(IfExpression {
discriminator: Some(bx(expr(Value(qn!(x))))),
@ -1203,7 +1203,7 @@ fn pattern_matching() {
})
);
assert_expr!(
assert_expr_comb!(
"if x { is 1 then 5; else 20 }",
expr(IfExpression {
discriminator: Some(bx(expr(Value(qn!(x))))),
@ -1225,7 +1225,7 @@ fn pattern_matching() {
})
);
assert_expr!(
assert_expr_comb!(
r#"if x is "gnosticism" then 1 else 2"#,
expr(IfExpression {
discriminator: Some(bx(expr(Value(qn!(x))))),
@ -1237,7 +1237,7 @@ fn pattern_matching() {
})
);
assert_expr! {
assert_expr_comb! {
r#"
if (45, "panda", false, 2.2) {
is (49, "pablo", _, 28.4) then "no"