diff --git a/schala-lang/src/parsing/combinator.rs b/schala-lang/src/parsing/combinator.rs index f708d8e..a44c133 100644 --- a/schala-lang/src/parsing/combinator.rs +++ b/schala-lang/src/parsing/combinator.rs @@ -79,6 +79,22 @@ fn statement_delimiter(input: Span) -> ParseResult<()> { tok(alt((value((), line_ending), value((), char(';')))))(input) } +pub fn program(input: Span) -> ParseResult { + let id = fresh_id(&input); + //TODO `rest` should be empty + let (rest, statements) = context("AST", + map( + tuple(( + many0(statement_delimiter), + separated_list0(statement_delimiter, statement), + many0(statement_delimiter), + )), |(_, items, _)| items.into()) + )(input)?; + + let ast = AST { id, statements }; + Ok((rest, ast)) +} + pub fn block(input: Span) -> ParseResult { context( "block", @@ -97,16 +113,33 @@ pub fn block(input: Span) -> ParseResult { fn statement(input: Span) -> ParseResult { let (input, pos) = position(input)?; - let pos: usize = pos.location_offset(); + let location = pos.location_offset().into(); let id = fresh_id(&input); - context( + let (rest, kind) = context( "Parsing-statement", - map(expression, move |expr| Statement { - id, - location: pos.into(), - kind: StatementKind::Expression(expr), - }), - )(input) + alt(( + map(expression, StatementKind::Expression), + map(declaration, StatementKind::Declaration), + )) + )(input)?; + Ok((rest, Statement { id, location, kind })) +} + +fn declaration(input: Span) -> ParseResult { + alt((binding, module))(input) +} + +fn binding(input: Span) -> ParseResult { + let parser = + tuple((kw("let"), opt(kw("mut")), tok(identifier), opt(type_anno), tok(char('=')), expression)); + map(parser, |(_, maybe_mut, ident, type_anno, _, expr)| + Declaration::Binding { name: rc_string(ident.fragment()), constant: maybe_mut.is_none(), + type_anno, expr })(input) +} + +fn module(input: Span) -> ParseResult { + map(tuple((kw("module"), tok(identifier), block)), + |(_, name, items)| Declaration::Module { name: rc_string(name.fragment()), items })(input) } pub fn expression(input: Span) -> ParseResult { diff --git a/schala-lang/src/parsing/mod.rs b/schala-lang/src/parsing/mod.rs index 8cc2365..72debf0 100644 --- a/schala-lang/src/parsing/mod.rs +++ b/schala-lang/src/parsing/mod.rs @@ -28,6 +28,12 @@ impl Parser { peg_parser::schala_parser::program(input, self).map_err(ParseError::from_peg) } + pub(crate) fn parse_comb(&mut self, input: &str) -> Result { + let id_store: IdStore = IdStore::new(); + let span = Span::new_extra(input, Rc::new(RefCell::new(id_store))); + combinator::program(span).map_err(|err| convert_err(input, err)).map(|(_, output)| output) + } + #[cfg(test)] fn expression(&mut self, input: &str) -> Result { peg_parser::schala_parser::expression(input, self).map_err(ParseError::from_peg) diff --git a/schala-lang/src/parsing/test.rs b/schala-lang/src/parsing/test.rs index fa4f9be..0e6e08b 100644 --- a/schala-lang/src/parsing/test.rs +++ b/schala-lang/src/parsing/test.rs @@ -98,6 +98,20 @@ macro_rules! assert_ast { }; } + +macro_rules! assert_ast_comb { + ($input:expr, $statements:expr) => { + let mut parser = Parser::new(); + let ast = parser.parse_comb($input); + let expected = AST { id: Default::default(), statements: $statements.into() }; + if ast.is_err() { + println!("Parse error: {}", ast.unwrap_err().msg); + panic!(); + } + assert_eq!(ast.unwrap(), expected); + }; +} + macro_rules! assert_fail { ($input:expr, $failure:expr) => { let mut parser = Parser::new(); @@ -180,7 +194,7 @@ fn binexps() { use StatementKind::Expression; assert_expr_comb!("0xf_f+1", binop("+", expr(NatLiteral(255)), expr(NatLiteral(1)))); - assert_ast!( + assert_ast_comb!( "3; 4; 4.3", vec![ stmt(Expression(expr(NatLiteral(3)))), @@ -1315,7 +1329,7 @@ fn blocks() { let mut parser = Parser::new(); for case in cases.iter() { - let block = parser.block(case); + let block = parser.block_comb(case); assert_eq!(block.unwrap(), vec![exst(Value(qn!(a)))].into()); } @@ -1397,7 +1411,7 @@ fn comments() { assert_fail_expr!(source, binop("+", expr(NatLiteral(1)), expr(NatLiteral(2)))); let source = "5//no man\n"; - assert_ast!(source, vec![exst(NatLiteral(5))]); + assert_ast_comb!(source, vec![exst(NatLiteral(5))]); } //TODO support backtick operators like this