From cd1bb91555aa3b41c35d3985445bd0b4ecf74980 Mon Sep 17 00:00:00 2001 From: Greg Shuflin Date: Mon, 1 Nov 2021 00:14:15 -0700 Subject: [PATCH] Add control flow types --- schala-lang/language/src/ast/mod.rs | 8 +++++ schala-lang/language/src/ast/visitor.rs | 3 ++ schala-lang/language/src/ast/visualize.rs | 16 +++++++-- schala-lang/language/src/parsing/mod.rs | 18 +++++++++- schala-lang/language/src/parsing/test.rs | 35 +++++++++++++++++++ schala-lang/language/src/reduced_ir/mod.rs | 16 ++++++++- schala-lang/language/src/reduced_ir/types.rs | 3 ++ schala-lang/language/src/tokenizing.rs | 6 ++-- .../language/src/tree_walk_eval/evaluator.rs | 3 ++ 9 files changed, 102 insertions(+), 6 deletions(-) diff --git a/schala-lang/language/src/ast/mod.rs b/schala-lang/language/src/ast/mod.rs index c8e53de..83eda7a 100644 --- a/schala-lang/language/src/ast/mod.rs +++ b/schala-lang/language/src/ast/mod.rs @@ -54,6 +54,14 @@ pub enum StatementKind { Declaration(Declaration), Import(ImportSpecifier), Module(ModuleSpecifier), + Flow(FlowControl), +} + +#[derive(Debug, Clone, PartialEq)] +pub enum FlowControl { + Continue, + Break, + Return(Option), } #[derive(Debug, Clone, PartialEq, Default)] diff --git a/schala-lang/language/src/ast/visitor.rs b/schala-lang/language/src/ast/visitor.rs index 0dbee8b..70f90ca 100644 --- a/schala-lang/language/src/ast/visitor.rs +++ b/schala-lang/language/src/ast/visitor.rs @@ -47,6 +47,9 @@ pub fn walk_block(v: &mut V, block: &Block) { if let Recursion::Continue = v.module(module_spec) { walk_block(v, &module_spec.contents); }, + Flow(ref _flow_control) => { + //TODO do something with flow control in Visitor + } } } } diff --git a/schala-lang/language/src/ast/visualize.rs b/schala-lang/language/src/ast/visualize.rs index a0866cc..1268a28 100644 --- a/schala-lang/language/src/ast/visualize.rs +++ b/schala-lang/language/src/ast/visualize.rs @@ -1,7 +1,9 @@ #![allow(clippy::single_char_add_str)] +use std::fmt::Write; + use super::{ - Block, Declaration, Expression, ExpressionKind, ImportSpecifier, InvocationArgument, ModuleSpecifier, - Signature, Statement, StatementKind, AST, + Block, Declaration, Expression, ExpressionKind, FlowControl, ImportSpecifier, InvocationArgument, + ModuleSpecifier, Signature, Statement, StatementKind, AST, }; const LEVEL: usize = 2; @@ -35,6 +37,7 @@ fn render_statement(stmt: &Statement, indent: usize, buf: &mut String) { Declaration(ref decl) => render_declaration(decl, indent, buf), Import(ref spec) => render_import(spec, indent, buf), Module(ref spec) => render_module(spec, indent, buf), + Flow(ref flow_control) => render_flow_control(flow_control, indent, buf), } } @@ -191,6 +194,15 @@ fn render_module(_expr: &ModuleSpecifier, _indent: usize, buf: &mut String) { buf.push_str("(Module )"); } +fn render_flow_control(flow: &FlowControl, _indent: usize, buf: &mut String) { + use FlowControl::*; + match flow { + Return(ref _expr) => write!(buf, "return ").unwrap(), + Break => write!(buf, "break").unwrap(), + Continue => write!(buf, "continue").unwrap(), + } +} + #[cfg(test)] mod test { use super::render_ast; diff --git a/schala-lang/language/src/parsing/mod.rs b/schala-lang/language/src/parsing/mod.rs index 24912ae..4222189 100644 --- a/schala-lang/language/src/parsing/mod.rs +++ b/schala-lang/language/src/parsing/mod.rs @@ -15,7 +15,7 @@ //! ```text //! program := (statement delimiter)* EOF //! delimiter := NEWLINE | ";" -//! statement := expression | declaration | import | module +//! statement := expression | declaration | import | module | flow //! block := "{" (statement delimiter)* "}" //! declaration := type_declaration | func_declaration | binding_declaration | impl_declaration //! ``` @@ -401,12 +401,28 @@ impl Parser { Keyword(Impl) => self.impl_declaration().map(StatementKind::Declaration), Keyword(Import) => self.import_declaration().map(StatementKind::Import), Keyword(Module) => self.module_declaration().map(StatementKind::Module), + Keyword(Continue) | Keyword(Return) | Keyword(Break) => + self.flow_control().map(StatementKind::Flow), _ => self.expression().map(StatementKind::Expression), }?; let id = self.id_store.fresh(); Ok(Statement { kind, id, location: tok.location }) } + #[recursive_descent_method] + fn flow_control(&mut self) -> ParseResult { + let tok = self.token_handler.next(); + Ok(match tok.get_kind() { + Keyword(Continue) => FlowControl::Continue, + Keyword(Break) => FlowControl::Break, + Keyword(Return) => match self.token_handler.peek_kind() { + Semicolon | Newline => FlowControl::Return(None), + _ => FlowControl::Return(Some(self.expression()?)), + }, + _ => unreachable!(), + }) + } + #[recursive_descent_method] fn annotation(&mut self) -> ParseResult { expect!(self, AtSign); diff --git a/schala-lang/language/src/parsing/test.rs b/schala-lang/language/src/parsing/test.rs index fc94514..8b4e74c 100644 --- a/schala-lang/language/src/parsing/test.rs +++ b/schala-lang/language/src/parsing/test.rs @@ -1215,3 +1215,38 @@ if (45, "panda", false, 2.2) { ) }; } + +#[test] +fn flow_control() { + use ExpressionKind::*; + + // This is an incorrect program, but shoudl parse correctly. + let source = r#" + fn test() { + let a = 10; + break; + continue; + return; + return 10; + }"#; + + assert_ast!( + source, + vec![fn_decl( + Signature { name: rc("test"), operator: false, type_anno: None, params: vec![] }, + vec![ + decl(Declaration::Binding { + name: rc("a"), + constant: true, + type_anno: None, + expr: expr(NatLiteral(10)) + }), + stmt(StatementKind::Flow(FlowControl::Break)), + stmt(StatementKind::Flow(FlowControl::Continue)), + stmt(StatementKind::Flow(FlowControl::Return(None))), + stmt(StatementKind::Flow(FlowControl::Return(Some(expr(NatLiteral(10)))))), + ] + .into() + )] + ); +} diff --git a/schala-lang/language/src/reduced_ir/mod.rs b/schala-lang/language/src/reduced_ir/mod.rs index 6c882c9..9f74e0d 100644 --- a/schala-lang/language/src/reduced_ir/mod.rs +++ b/schala-lang/language/src/reduced_ir/mod.rs @@ -80,6 +80,9 @@ impl<'a, 'b> Reducer<'a, 'b> { ast::StatementKind::Module(_modspec) => { //TODO handle modules } + ast::StatementKind::Flow(..) => { + //TODO this should be an error + } } } @@ -100,7 +103,18 @@ impl<'a, 'b> Reducer<'a, 'b> { _ => None, }, - _ => None, + ast::StatementKind::Module(_) | ast::StatementKind::Import(_) => { + //TODO need to handle function-internal modules, imports + None + } + ast::StatementKind::Flow(ast::FlowControl::Return(expr)) => + if let Some(expr) = expr { + Some(Statement::Return(self.expression(expr))) + } else { + Some(Statement::Return(Expression::unit())) + }, + ast::StatementKind::Flow(ast::FlowControl::Break) => Some(Statement::Break), + ast::StatementKind::Flow(ast::FlowControl::Continue) => Some(Statement::Continue), } } diff --git a/schala-lang/language/src/reduced_ir/types.rs b/schala-lang/language/src/reduced_ir/types.rs index d803c5b..2b79be4 100644 --- a/schala-lang/language/src/reduced_ir/types.rs +++ b/schala-lang/language/src/reduced_ir/types.rs @@ -42,6 +42,9 @@ impl ReducedIR { pub enum Statement { Expression(Expression), Binding { id: DefId, constant: bool, expr: Expression }, + Return(Expression), + Continue, + Break, } #[derive(Debug, Clone)] diff --git a/schala-lang/language/src/tokenizing.rs b/schala-lang/language/src/tokenizing.rs index 4ed0dc0..6326a11 100644 --- a/schala-lang/language/src/tokenizing.rs +++ b/schala-lang/language/src/tokenizing.rs @@ -86,11 +86,12 @@ pub enum Kw { Func, For, While, - Const, Let, In, Mut, Return, + Continue, + Break, Alias, Type, SelfType, @@ -115,11 +116,12 @@ impl TryFrom<&str> for Kw { "fn" => Kw::Func, "for" => Kw::For, "while" => Kw::While, - "const" => Kw::Const, "let" => Kw::Let, "in" => Kw::In, "mut" => Kw::Mut, "return" => Kw::Return, + "break" => Kw::Break, + "continue" => Kw::Continue, "alias" => Kw::Alias, "type" => Kw::Type, "Self" => Kw::SelfType, diff --git a/schala-lang/language/src/tree_walk_eval/evaluator.rs b/schala-lang/language/src/tree_walk_eval/evaluator.rs index 734a79f..da453a6 100644 --- a/schala-lang/language/src/tree_walk_eval/evaluator.rs +++ b/schala-lang/language/src/tree_walk_eval/evaluator.rs @@ -58,6 +58,9 @@ impl<'a, 'b> Evaluator<'a, 'b> { let evaluated = self.expression(expr)?; Ok(Some(evaluated.into())) } + Statement::Return(expr) => unimplemented!(), + Statement::Break => unimplemented!(), + Statement::Continue => unimplemented!(), } }