diff --git a/axc/foo.axs b/axc/foo.axs new file mode 100644 index 0000000..fa072f4 --- /dev/null +++ b/axc/foo.axs @@ -0,0 +1 @@ +def a (x: String) = 2 + a::b["xyz"] + fn x y -> x + y + {a : b}; diff --git a/axc/src/parser.rs b/axc/src/parser.rs index 37360b8..055e462 100644 --- a/axc/src/parser.rs +++ b/axc/src/parser.rs @@ -3,7 +3,7 @@ use std::{error::Error, fmt::Display}; use chumsky::{ - prelude::{choice, end, just, none_of, one_of, Simple}, + prelude::{choice, end, just, none_of, one_of, todo, Simple}, recursive::recursive, text::{ident, int, keyword, whitespace}, Parser, @@ -331,11 +331,22 @@ fn parse_class_def<'a>( } fn parse_expression<'a>(m: &'a ParserMeta) -> impl Parser> + 'a { - (0..=10) - .rev() - .fold(parse_unary(m, parse_literal(m)).boxed(), |p, precedence| { + recursive(|full_expr| { + let lambda = parse_lambda_expr(m, full_expr.clone()); + let record = parse_record_expr(m, full_expr.clone()); + + let base = choice((parse_literal(m), parse_var_ref_expr(m))); + let subscript = parse_subscript_expr(m, base); + let term = choice((lambda, record, subscript)); + + let unary = parse_unary(m, term); + + let binary = (0..=10).rev().fold(unary.boxed(), |p, precedence| { parse_binary(m, precedence, p).boxed() - }) + }); + + binary + }) } fn parse_unary( @@ -435,6 +446,79 @@ fn parse_binary<'a>( }) } +fn parse_record_expr( + _m: &ParserMeta, + base: impl Parser> + Clone, +) -> impl Parser> + Clone { + ident() + .then_ignore(whitespace()) + .then_ignore(just(':')) + .then_ignore(whitespace()) + .then(base) + .separated_by(just(',').then(whitespace())) + .allow_trailing() + .delimited_by(just('{').then(whitespace()), just('}').then(whitespace())) + .map(Expr::Record) +} + +fn parse_lambda_expr( + m: &ParserMeta, + base: impl Parser> + Clone, +) -> impl Parser> + Clone { + keyword("fn") + .then(whitespace()) + .ignore_then(parse_pattern(m).repeated()) + .then_ignore(just("->").then(whitespace())) + .then(base) + .map(|(arguments, result)| Expr::Lambda { + arguments, + result: Box::new(result), + }) +} + +fn parse_subscript_expr( + _m: &ParserMeta, + base: impl Parser> + Clone, +) -> impl Parser> + Clone { + enum SubscriptKind { + Dot, + Bracket, + } + + base.clone() + .then( + choice(( + just('.') + .ignore_then(whitespace()) + .ignore_then(base.clone()) + .map(|e| (SubscriptKind::Dot, e)), + base.clone() + .delimited_by(just('[').then(whitespace()), just(']').then(whitespace())) + .map(|e| (SubscriptKind::Bracket, e)), + )) + .repeated(), + ) + .map(|(l, subscripts): (Expr, Vec<(SubscriptKind, Expr)>)| { + subscripts.into_iter().fold(l, |l, (kind, r)| match kind { + SubscriptKind::Dot => Expr::DotSubscript { + value: Box::new(l), + subscript: Box::new(r), + }, + SubscriptKind::Bracket => Expr::BracketSubscript { + value: Box::new(l), + subscript: Box::new(r), + }, + }) + }) +} + +fn parse_var_ref_expr(m: &ParserMeta) -> impl Parser> + Clone { + ident() + .then_ignore(whitespace()) + .separated_by(just("::").then(whitespace())) + .map(Expr::VariableReference) +} + fn parse_literal(_m: &ParserMeta) -> impl Parser> + Clone { let string_char = none_of("\"\\").or(just('\\').ignore_then(choice(( just('n').to('\n'), @@ -540,7 +624,7 @@ fn parse_record_type( .map(Type::Record) } -fn parse_pattern(m: &ParserMeta) -> impl Parser> { +fn parse_pattern(m: &ParserMeta) -> impl Parser> + Clone { recursive(|rec| { choice(( parse_ignore_pattern(m), diff --git a/axc/src/syntax.rs b/axc/src/syntax.rs index 90e71da..27f0db5 100644 --- a/axc/src/syntax.rs +++ b/axc/src/syntax.rs @@ -143,10 +143,7 @@ pub enum Expr { }, /// Record initialization, e.g., `{ pointer: xyz, length: 12 }`. - Record { - /// The elements of the record. - elements: Vec<(String, Expr)>, - }, + Record(Vec<(String, Expr)>), /// Anonymous functions, e.g., `fn x -> x + 1`. Lambda { @@ -158,16 +155,13 @@ pub enum Expr { result: Box, }, - /// Variable references, possibly namespaced, e.g., `foo::bar::baz`. - VariableReference(Vec), - /// Dot subscripts, e.g., `foo.bar`. DotSubscript { /// The left side of the subscript. value: Box, - /// The right side of the subscript; this is only allowed to be a single word. - subscript: String, + /// The right side of the subscript; semantically, this is only allowed to be a single word. + subscript: Box, }, /// Bracket subscripts, e.g., `foo[bar]`. @@ -179,6 +173,9 @@ pub enum Expr { subscript: Box, }, + /// Variable references, possibly namespaced, e.g., `foo::bar::baz`. + VariableReference(Vec), + /// Literal tokens, e.g., strings and numbers. Literal(Literal), }