From 3866a2e9f2293a736308a58fd856cca2625fa25d Mon Sep 17 00:00:00 2001 From: John Date: Mon, 16 Oct 2023 22:36:26 -0500 Subject: [PATCH] libconlang: Define the AST (TODO: clean up the AST) --- libconlang/src/lib.rs | 833 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 830 insertions(+), 3 deletions(-) diff --git a/libconlang/src/lib.rs b/libconlang/src/lib.rs index b358068..d0c65f4 100644 --- a/libconlang/src/lib.rs +++ b/libconlang/src/lib.rs @@ -1,6 +1,6 @@ -//! Conlang is an expression-based programming language +//! Conlang is an expression-based programming language with similarities to Rust #![warn(clippy::all)] - +#![feature(decl_macro)] pub mod token { //! Stores a component of a file as a type and span use std::ops::Range; @@ -155,7 +155,834 @@ pub mod token { } pub mod ast { - //! Stores functions, data structure definitions, etc. + //! # The Abstract Syntax Tree + //! Contains definitions of AST Nodes, to be derived by a [parser](super::parser). + //! + //! Also contains a [Visitor](visitor::Visitor) trait for visiting nodes + //! + //! ## Syntax + //! ```ignore + //! Start := expression::Expr + //! Identifier := IDENTIFIER + //! Literal := STRING | CHAR | FLOAT | INT | TRUE | FALSE + //! ``` + //! See [literal] and [expression] for more details. + + pub mod preamble { + //! Common imports for working with the [ast](super) + pub use super::{ + expression::{ + self, control, + math::{self, operator}, + }, + literal, + visitor::{Visitor, Walk}, + Identifier, Start, + }; + } + + mod visitor { + use super::{ + expression::{control::*, math::*, Block, *}, + literal::*, + *, + }; + /// [Walk] is the lexical inverse of [Visitor] + /// + /// # Examples + /// ```rust,ignore + /// ``` + pub trait Walk + ?Sized, R> { + /// + fn walk(&self, visitor: &mut T) -> R; + } + pub mod walker { + use super::*; + macro_rules! impl_walk { + ($($T:ty => $f:ident),*$(,)?) => { + $(impl, R> Walk for $T { + fn walk(&self, visitor: &mut T) -> R { + visitor.$f(self) + } + })* + }; + } + impl_walk! { + // ast + Start => visit, + // grouped expr + Block => visit_block, + Group => visit_group, + // Identifier + Identifier => visit_identifier, + // ast::literal + &str => visit_string_literal, + char => visit_char_literal, + bool => visit_bool_literal, + u128 => visit_int_literal, + Float => visit_float_literal, + // ast::math + Ignore => visit_ignore, + Assign => visit_assign, + Compare => visit_compare, + Logic => visit_logic, + Bitwise => visit_bitwise, + Shift => visit_shift, + Term => visit_term, + Factor => visit_factor, + Unary => visit_unary, + // ast::math::operator + operator::Ignore => visit_ignore_op, + operator::Compare => visit_compare_op, + operator::Assign => visit_assign_op, + operator::Logic => visit_logic_op, + operator::Bitwise => visit_bitwise_op, + operator::Shift => visit_shift_op, + operator::Term => visit_term_op, + operator::Factor => visit_factor_op, + operator::Unary => visit_unary_op, + // ast::control::Branch + While => visit_while, + If => visit_if, + For => visit_for, + Else => visit_else, + // ast::control::Flow + Continue => visit_continue, + Return =>visit_return, + Break => visit_break, + } + impl + ?Sized, R> Walk for Expr { + fn walk(&self, visitor: &mut T) -> R { + match self { + Expr::Flow(f) => visitor.visit_control_flow(f), + Expr::Ignore(i) => visitor.visit_ignore(i), + } + } + } + impl + ?Sized, R> Walk for Final { + fn walk(&self, visitor: &mut T) -> R { + match self { + Final::Identifier(i) => visitor.visit_identifier(i), + Final::Literal(l) => visitor.visit_literal(l), + Final::Block(b) => visitor.visit_block(b), + Final::Group(g) => visitor.visit_group(g), + Final::Branch(b) => visitor.visit_branch_expr(b), + } + } + } + impl + ?Sized, R> Walk for Literal { + fn walk(&self, visitor: &mut T) -> R { + match self { + Literal::String(s) => visitor.visit_string_literal(s), + Literal::Char(c) => visitor.visit_char_literal(c), + Literal::Bool(b) => visitor.visit_bool_literal(b), + Literal::Float(f) => visitor.visit_float_literal(f), + Literal::Int(i) => visitor.visit_int_literal(i), + } + } + } + impl + ?Sized, R> Walk for Branch { + fn walk(&self, visitor: &mut T) -> R { + match self { + Branch::While(w) => visitor.visit_while(w), + Branch::If(i) => visitor.visit_if(i), + Branch::For(f) => visitor.visit_for(f), + } + } + } + impl + ?Sized, R> Walk for Flow { + fn walk(&self, visitor: &mut T) -> R { + match self { + Flow::Continue(c) => visitor.visit_continue(c), + Flow::Return(r) => visitor.visit_return(r), + Flow::Break(b) => visitor.visit_break(b), + } + } + } + } + + pub trait Visitor { + /// Visit the start of an AST + fn visit(&mut self, start: &Start) -> R { + self.visit_expr(&start.0) + } + + /// Visit an [Expression](Expr) + fn visit_expr(&mut self, expr: &Expr) -> R { + expr.walk(self) + } + // Block expression + /// Visit a [Block] expression + fn visit_block(&mut self, expr: &Block) -> R { + self.visit_expr(&expr.expr) + } + /// Visit a [Group] expression + fn visit_group(&mut self, expr: &Group) -> R { + self.visit_expr(&expr.expr) + } + + // Math expression + /// Visit an [Ignore] expression + fn visit_ignore(&mut self, expr: &Ignore) -> R; + /// Visit an [Assign] expression + fn visit_assign(&mut self, expr: &Assign) -> R; + /// Visit a [Compare] expression + fn visit_compare(&mut self, expr: &Compare) -> R; + /// Visit a [Logic] expression + fn visit_logic(&mut self, expr: &Logic) -> R; + /// Visit a [Bitwise] expression + fn visit_bitwise(&mut self, expr: &Bitwise) -> R; + /// Visit a [Shift] expression + fn visit_shift(&mut self, expr: &Shift) -> R; + /// Visit a [Term] expression + fn visit_term(&mut self, expr: &Term) -> R; + /// Visit a [Factor] expression + fn visit_factor(&mut self, expr: &Factor) -> R; + /// Visit a [Unary] expression + fn visit_unary(&mut self, expr: &Unary) -> R; + /// Visit a [Final] expression + /// + /// [Final] := [Identifier] | [Literal] | [Block] | [Branch] + fn visit_final(&mut self, expr: &Final) -> R { + expr.walk(self) + } + // Math operators + /// Visit an [Ignore] [operator](operator::Ignore) + fn visit_ignore_op(&mut self, op: &operator::Ignore) -> R; + /// Visit a [Compare] [operator](operator::Compare) + fn visit_compare_op(&mut self, op: &operator::Compare) -> R; + /// Visit an [Assign] [operator](operator::Assign) + fn visit_assign_op(&mut self, op: &operator::Assign) -> R; + /// Visit a [Logic] [operator](operator::Logic) + fn visit_logic_op(&mut self, op: &operator::Logic) -> R; + /// Visit a [Bitwise] [operator](operator::Bitwise) + fn visit_bitwise_op(&mut self, op: &operator::Bitwise) -> R; + /// Visit a [Shift] [operator](operator::Shift) + fn visit_shift_op(&mut self, op: &operator::Shift) -> R; + /// Visit a [Term] [operator](operator::Term) + fn visit_term_op(&mut self, op: &operator::Term) -> R; + /// Visit a [Factor] [operator](operator::Factor) + fn visit_factor_op(&mut self, op: &operator::Factor) -> R; + /// Visit a [Unary] [operator](operator::Unary) + fn visit_unary_op(&mut self, op: &operator::Unary) -> R; + + /// Visit a [Branch] expression. + /// + /// [Branch] := [While] | [If] | [For] + fn visit_branch_expr(&mut self, expr: &Branch) -> R { + expr.walk(self) + } + /// Visit an [If] expression + fn visit_if(&mut self, expr: &If) -> R; + /// Visit a [While] loop expression + fn visit_while(&mut self, expr: &While) -> R; + /// Visit a [For] loop expression + fn visit_for(&mut self, expr: &For) -> R; + /// Visit an [Else] expression + fn visit_else(&mut self, expr: &Else) -> R; + /// Visit a [Control Flow](control::Flow) expression + /// + /// [`Flow`] := [`Continue`] | [`Return`] | [`Break`] + fn visit_control_flow(&mut self, expr: &control::Flow) -> R { + expr.walk(self) + } + /// Visit a [Continue] expression + fn visit_continue(&mut self, expr: &Continue) -> R; + /// Visit a [Break] expression + fn visit_break(&mut self, expr: &Break) -> R; + /// Visit a [Return] expression + fn visit_return(&mut self, expr: &Return) -> R; + + // final symbols + /// Visit an [Identifier] + fn visit_identifier(&mut self, ident: &Identifier) -> R; + /// Visit a [Literal] + /// + /// [Literal] := [String] | [char] | [bool] | [Float] | [Int] + fn visit_literal(&mut self, literal: &Literal) -> R { + literal.walk(self) + } + /// Visit a [string](str) literal + fn visit_string_literal(&mut self, string: &str) -> R; + /// Visit a [character](char) literal + fn visit_char_literal(&mut self, char: &char) -> R; + /// Visit a [boolean](bool) literal + fn visit_bool_literal(&mut self, bool: &bool) -> R; + /// Visit a [floating point](Float) literal + fn visit_float_literal(&mut self, float: &Float) -> R; + /// Visit an [integer](Int) literal + fn visit_int_literal(&mut self, int: &u128) -> R; + } + } + /// Marks the root of a tree + /// # Syntax + /// [`Start`] := [`expression::Expr`] + #[derive(Clone, Debug)] + pub struct Start(pub expression::Expr); + + /// An Identifier stores the name of an item + /// # Syntax + /// [`Identifier`] := [`IDENTIFIER`](crate::token::Type::Identifier) + #[derive(Clone, Debug, Hash)] + pub struct Identifier(pub String); + + pub mod todo { + //! temporary storage for pending expression work. \ + //! when an item is in progress, remove it from todo. + //! + //! # General TODOs: + //! - [ ] Implement support for storing items in the AST + //! - [ ] Implement paths + //! - [ ] Implement functions + //! - [ ] Implement structs + //! - [ ] Implement enums + //! - [ ] Implement implementation + //! - [ ] Store token spans in AST + pub mod path { + //! Path support + //! - [ ] Add namespace syntax (i.e. `::crate::foo::bar` | `foo::bar::Baz` | + //! `foo::bar::*`) + //! + //! Path resolution will be vital to the implementation of structs, enums, impl blocks, + //! traits, modules, etc. + } + pub mod function { + //! Function support + //! - [ ] Add function declaration expression (returns a function) + //! - [ ] Add function call expression + } + + pub mod structure { + //! Struct support + //! - [ ] Add struct declaration expression (returns a struct declaration) + //! - [ ] Add struct value expression (returns a struct value) + //! - [ ] Add struct update syntax (yippee!!) + } + + pub mod enumeration { + //! Enum support + //! - [ ] Add enum declaration expression (returns an enum declaration) + //! - [ ] Add enum value expression (returns an enum value) + } + + pub mod implementation { + //! Impl block support + //! - [ ] Add impl block expression? Statement? + //! - [ ] Add member function call expression + } + } + + pub mod literal { + //! # Literal Expressions + //! Evaluate to the literal they contain + //! # Syntax + //! ```ignore + //! Literal := String | Char | Float | Int + //! String := STRING + //! Float := FLOAT + //! Char := CHARACTER + //! Bool := TRUE | FALSE + //! Int := INTEGER + //! ``` + + /// Represents a literal value + /// # Syntax + /// [`Literal`] := [`String`] | [`char`] | [`bool`] | [`Float`] | [`Int`] + #[derive(Clone, Debug)] + pub enum Literal { + /// Represents a literal string value + /// # Syntax + /// [`Literal::String`] := [`STRING`](crate::token::Type::String) + String(String), + /// Represents a literal [char] value + /// # Syntax + /// [`Literal::Char`] := [`CHARACTER`](crate::token::Type::Character) + Char(char), + /// Represents a literal [bool] value + /// # Syntax + /// [`Literal::Bool`] := + /// [`TRUE`](crate::token::Keyword::True) + /// | [`FALSE`](crate::token::Keyword::False) + Bool(bool), + /// Represents a literal float value + /// # Syntax + /// [`Float`] := [`FLOAT`](crate::token::Type::Float) + Float(Float), + /// Represents a literal integer value + /// # Syntax + /// [`Int`] := [`INTEGER`](crate::token::Type::Integer) + Int(u128), + } + + /// Represents a literal float value + /// # Syntax + /// [`Float`] := [`FLOAT`](crate::token::Type::Float) + #[derive(Clone, Debug)] + pub struct Float { + pub sign: bool, + pub exponent: i32, + pub mantissa: u64, + } + } + + pub mod expression { + //! # Expressions + //! + //! The [expression] is the backbone of Conlang: everything is an expression. + //! + //! ## Grammar + //! Higher number = higher precedence. + //! + //! | # | Node | Function + //! |----|------------------:|:---------------------------------------------- + //! | 0 | [`Expr`]| Contains an expression + //! | 1 | [`control::Flow`]| Unconditional branches (`return`, `break`, `continue`) + //! | 2 | [`math::Ignore`]| Ignores the preceding sub-expression's result + //! | 3 | [`math::Assign`]| Assignment + //! | 4 | [`math::Compare`]| Value Comparison + //! | 5 | [`math::Logic`]| Boolean And, Or, Xor + //! | 6 | [`math::Bitwise`]| Bitwise And, Or, Xor + //! | 7 | [`math::Shift`]| Shift Left/Right + //! | 8 | [`math::Term`]| Add, Subtract + //! | 9 | [`math::Factor`]| Multiply, Divide, Remainder + //! | 10 | [`math::Unary`]| Unary Dereference, Reference, Negate, Not + //! | 11 |[`control::Branch`]| Conditional branches (`if`, `while`, `for`), `else` + //! | 12 | [`Group`]| Group expressions `(` [Expr] `)` + //! | 12 | [`Block`]| Block expressions `{` [Expr] `}` + //! | 12 | [`Final`]| Contains an [Identifier], [Literal](literal::Literal), [Block], or [Branch](control::Branch) + //! + //! ## Syntax + //! ```ignore + //! Expr := control::Flow | math::Ignore + //! Block := '{' Expr '}' + //! Group := '(' Expr ')' + //! Final := Identifier | Literal | Block | control::Branch + //! ``` + //! See [control] and [math] for their respective production rules. + + #[allow(unused_imports)] + use super::*; + + /// Contains an expression + /// + /// # Syntax + /// [`Expr`] := [`control::Flow`] | [`math::Ignore`] + #[derive(Clone, Debug)] + pub enum Expr { + Flow(control::Flow), + Ignore(math::Ignore), + } + + /// A [Final] Expression is the expression with the highest precedence (i.e. the deepest + /// derivation) + /// # Syntax + /// [`Final`] := + /// [`IDENTIFIER`](Identifier) + /// | [`Literal`](literal::Literal) + /// | [`Block`] + /// | [`Branch`](control::Branch) + #[derive(Clone, Debug)] + pub enum Final { + Identifier(Identifier), + Literal(literal::Literal), + Block(Block), + Group(Group), + Branch(control::Branch), + } + + /// Contains a Block Expression + /// # Syntax + /// [`Block`] := `'{'` [`Expr`] `'}'` + #[derive(Clone, Debug)] + pub struct Block { + pub expr: Box, + } + + /// Contains a Parenthesized Expression + /// # Syntax + /// [`Group`] := `'('` [`Expr`] `')'` + #[derive(Clone, Debug)] + pub struct Group { + pub expr: Box, + } + + pub mod math { + //! # Arithmetic and Logical Expressions + //! + //! ## Precedence Order + //! Operator associativity is always left-to-right among members of the same group + //! + //! | # | Name | Operators | Associativity + //! |---|----------:|:--------------------------------------|--------------- + // | | TODO: Try | `?` | + //! | 1 | [Unary] | `*` `&` `-` `!` | Right + //! | 2 | [Factor] | `*` `/` `%` | Left to Right + //! | 3 | [Term] | `+` `-` | Left to Right + //! | 4 | [Shift] | `<<` `>>` | Left to Right + //! | 5 | [Bitwise] | `&` | | Left to Right + //! | 6 | [Logic] | `&&` || `^^` | Left to Right + //! | 7 | [Compare] | `<` `<=` `==` `!=` `>=` `>` | Left to Right + #![doc = concat!( //| | + r" | 8 | [Assign] |", r"`*=`, `/=`, `%=`, `+=`, `-=`, ",//| + /* | | |*/ r"`&=`, |=, ", //| + /* | | |*/ r"`^=`, `<<=`, `>>=`", r"| Right to Left")] + //! | 9 | [Ignore] | `;` | + //! + //! + //! + //! ## Syntax + //! ```ignore + //! Ignore := Assign (CompareOp Assign )* + //! Assign := Compare (IgnoreOp Compare)* + //! Compare := Logic (AssignOp Logic )* + //! Logic := Bitwise (LogicOp Bitwise)* + //! Bitwise := Shift (BitOp Shift )* + //! Shift := Term (ShiftOp Term )* + //! Term := Factor (TermOp Factor )* + //! Factor := Unary (FactorOp Unary )* + //! Unary := (UnaryOp)* Final + //! ``` + use super::*; + + /// Ignores the result of the left sub-expression. + /// Great if you only want the side-effects. + /// # Syntax + /// [`Ignore`] := [`Assign`] ([`operator::Ignore`] [`Assign`])* + #[derive(Clone, Debug)] + pub struct Ignore(pub Assign, pub Vec<(operator::Ignore, Assign)>); + + /// Assigns the result of the right sub-expression to the left sub-expression. + /// Resolves to the Empty type. + /// # Syntax + /// [`Assign`] := [`Compare`] ([`operator::Assign`] [`Compare`])? + #[derive(Clone, Debug)] + pub struct Assign(pub Compare, pub Vec<(operator::Assign, Compare)>); + + /// Compares the values of the right and left sub-expressions, + /// and resolves to a boolean. + /// # Syntax + /// [`Compare`] := [`Logic`] ([`operator::Compare`] [`Logic`])* + #[derive(Clone, Debug)] + pub struct Compare(pub Logic, pub Vec<(operator::Compare, Logic)>); + + /// Performs a boolean logic operation on the left and right sub-expressions. + /// # Syntax + /// [`Logic`] := [`Bitwise`] ([`operator::Logic`] [`Bitwise`])* + #[derive(Clone, Debug)] + pub struct Logic(pub Bitwise, pub Vec<(operator::Logic, Bitwise)>); + + /// Performs a bitwise opration on the left and right sub-expressions. + /// # Syntax + /// [`Bitwise`] := [`Shift`] ([`operator::Bitwise`] [`Shift`])* + #[derive(Clone, Debug)] + pub struct Bitwise(pub Shift, pub Vec<(operator::Bitwise, Shift)>); + + /// Shifts the left sub-expression by the right sub-expression + /// # Syntax + /// [`Shift`] := [`Term`] ([`operator::Shift`] [`Term`])* + #[derive(Clone, Debug)] + pub struct Shift(pub Term, pub Vec<(operator::Shift, Term)>); + + /// Adds or subtracts the right sub-expression from the left sub-expression + /// # Syntax + /// [`Term`] := [`Factor`] ([`operator::Term`] [`Factor`])* + #[derive(Clone, Debug)] + pub struct Term(pub Factor, pub Vec<(operator::Term, Factor)>); + + /// Multiplies, Divides, or finds the remainder of the right sub-expression + /// from the left sub-expression + /// # Syntax + /// [`Factor`] := [`Unary`] ([`operator::Factor`] [`Unary`])* + #[derive(Clone, Debug)] + pub struct Factor(pub Unary, pub Vec<(operator::Factor, Unary)>); + + /// Performs a unary operation on the right sub-expression. + /// # Syntax + /// [`Unary`] := ([`operator::Unary`])* [`Final`] + #[derive(Clone, Debug)] + pub struct Unary(pub Vec, pub Final); + + pub mod operator { + //! | # | Operators | Associativity + //! |---|---------------------------------------|-------------- + //! | 0 | ([Unary]) `*`, `&`, `-`, `!` | Left to Right + //! | 1 | `*`, `/`, `%` | Left to Right + //! | 2 | `+`, `-` | Left to Right + //! | 3 | `<<`, `>>` | Left to Right + //! | 4 | `&`, |, `^` | Left to Right + //! | 5 | `&&`, ||, `^^` | Left to Right + //! | 6 | `>`. `>=`. `==`. `!=`. `<=`. `<` | Left to Right + #![doc = concat!( + r"| 7 |", r"`*=`, `/=`, `%=`, `+=`, `-=`, ",//| + /* | |*/ r"`&=`, |=, ", //| + /* | |*/ r"`^=`, `<<=`, `>>=`, `=`", r"| Left to Right")] + //! | 8 | `;` | + use crate::token::Type; + /// Defines an operator enum and a conversion + macro operator ($($(#[$doc:meta])* $T:ident { + $( $v:ident := $tty:pat ),*$(,)? + })*) {$( + #[doc = concat!("[`",stringify!($T),"`](super::",stringify!($T),") operators")] + $(#[$doc])* #[derive(Clone, Copy, Debug, PartialEq, Eq)] + pub enum $T { $($v,)* } + impl From for Option<$T> { + fn from(value: Type) -> Option<$T> { + match value { $($tty => Some(<$T>::$v),)* _ => None } + } + } + )*} + + operator! { + /// (`*`, `&`, `-`, `!`) + Unary { + Deref := Type::Star, + Ref := Type::Amp, + Neg := Type::Minus, + Not := Type::Bang, + At := Type::At, + Hash := Type::Hash, + Tilde := Type::Tilde, + } + /// (`*`, `/`, `%`) + Factor { + Mul := Type::Star, + Div := Type::Div, + Rem := Type::Rem, + } + /// (`+`, `-`) + Term { + Add := Type::Plus, + Sub := Type::Minus, + } + /// (`<<`, `>>`) + Shift { + Lsh := Type::Lsh, + Rsh := Type::Rsh, + } + /// (`&`, `|`, `^`) + Bitwise { + BitAnd := Type::Amp, + BitOr := Type::Bar, + BitXor := Type::Xor, + } + /// (`&&`, `||`, `^^`) + Logic { + LogAnd := Type::AmpAmp, + LogOr := Type::BarBar, + LogXor := Type::CatEar, + } + /// (`<`, `<=`, `==`, `!=`, `>=`, `>`) + Compare { + Less := Type::Lt, + LessEq := Type::LtEq, + Equal := Type::EqEq, + NotEq := Type::NotEq, + GreaterEq := Type::GtEq, + Greater := Type::Gt, + } + /// (`=`, `+=`, `-=`, `*=`, `/=`, + /// `&=`, `|=`, `^=`, `<<=`, `>>=`) + Assign { + Assign := Type::Eq, + AddAssign := Type::AddEq, + SubAssign := Type::SubEq, + MulAssign := Type::StarEq, + DivAssign := Type::DivEq, + BitAndAssign := Type::AndEq, + BitOrAssign := Type::OrEq, + BitXorAssign := Type::XorEq, + ShlAssign := Type::LshEq, + ShrAssign := Type::RshEq, + } + /// (`;`) + Ignore { + Ignore := Type::Semi, + } + } + } + } + + pub mod control { + //! # Control Flow Expressions + //! ## Conditional Branch Expressions + //! [`if` expressions][1] split a program's control flow based on a boolean + //! condition. \ + //! It is equivalent to a [`while` expression][2] that runs at most once. + //! + //! [`while` expressions][2] repeat a block of code (the loop body) until either + //! - a boolean condition fails + //! - a value is returned from the loop with a [`break` expression][5] + //! + //! [`for` expressions][3] repeat a block of code (the loop body) until either + //! - an iterable expression fails to return a value + //! - a value is returned from the loop with a [`break` expression][5] + //! + //! [`else` expressions][4] are evaluated when the body of a + //! conditional branch expression does not return a value: + //! - If the body was never run (`if false`, `while false`) + //! - If the loop exited without encountering a [`break` expression][5] + //! ## Unconditional Branch Expressions + //! [`break` expressions][5] return a value from within a loop + //! + //! [`return` expressions][6] return a value from within a function + //! + //! [`continue` expressions][7] skip to the next iteration of a loop + //! # Syntax + //! ```rust,ignore + //! Branch := While | If | For + //! If := "if" Expr Block Else? + //! While := "while" Expr Block Else? + //! For := "for" Identifier "in" Expr Block Else? + //! Else := "else" Block + //! + //! Break := "break" Expr + //! ``` + //! + //! [1]: If + //! [2]: While + //! [3]: For + //! [4]: Else + //! [5]: Break + //! [6]: Return + //! [7]: Flow::Continue + use super::*; + + /// Contains a [ConditionalBranch Expression](control). + /// + /// [While], [If], [For] + #[derive(Clone, Debug)] + pub enum Branch { + While(While), + If(If), + For(For), + } + + /// Contains an [Unconditional Branch Expression](control). + /// + /// [Continue](Flow::Continue), [Return], [Break] + #[derive(Clone, Debug)] + pub enum Flow { + /// Represents a [`continue` expression](Flow::Continue) + /// + /// # Syntax + /// [`Flow::Continue`] := `"continue"` + Continue(Continue), + /// Represents a [`return` expression](Return) + Return(Return), + /// Represents a [`break` expression](Break) + Break(Break), + } + + /// Represents a [`while` loop](control). + /// + /// A [`while` expression](While) contains a [loop condition expression](Expr), + /// a [block expression, (the loop body,)](Block) and + /// an optional¹ [else expression](Else). + /// + /// ¹ A value can be returned from within the body using a + /// [`break` expression](Break) \ + /// If a `break` expression is used in this way, the `else` block is mandatory. + /// + /// # Examples + /// ```rust,ignore + /// let var = while boolean_variable { + /// break true + /// } else { + /// false + /// } + /// ``` + /// # Syntax + /// [`While`] := `"while"` [`Expr`] [`Block`] [`Else`]`?` + #[derive(Clone, Debug)] + pub struct While { + pub cond: Box, + pub body: Block, + pub else_: Option, + } + + /// Represents an [`if`-`else` control flow structure](control). + /// + /// An [`if` expression](If) contains a [condition expression](Expr), + /// a [block expression](Block) to be executed, + /// and an optional¹ [`else` block](Else). + /// + /// ¹ If the body evaluates to anything other than the Empty type, + /// the `else` block is mandatory. + /// # Syntax + /// [`If`] := `"if"` [`Expr`] [`Block`] [`Else`]`?` + #[derive(Clone, Debug)] + pub struct If { + pub cond: Box, + pub body: Block, + pub else_: Option, + } + + /// Represents a [`for` loop](control). + /// + /// A [`for` expression](For) contains a [loop variable](Identifier), + /// an [iterable expression, (TBD,)](Expr), + /// a [block expression(the loop body)](Block), + /// and an optional¹ [`else` block](Else) + /// + /// + /// ¹ A value can be returned from within the body using a + /// [`break` expression](Break) \ + /// If a `break` expression is used in this way, the `else` block is mandatory. + /// # Syntax + /// [`For`] := `"for"` [`Identifier`] `"in"` [`Expr`]² [`Block`] [`Else`]`?` + /// + /// ² [`Expr`] returns something Iterable + #[derive(Clone, Debug)] + pub struct For { + pub var: Identifier, + pub iter: Box, + pub body: Block, + pub else_: Option, + } + + /// Represents an [`else` block](control). + /// + /// An [`else` block](Else) contains instructions to be executed if + /// the corresponding body refused to produce a value. In the case of + /// [`if` expressions](If), this happens if the condition fails. + /// In the case of loop ([`while`](While), [`for`](For))expressions, + /// this executes when the loop does *not* [`break`](Break). + /// + /// If one of the aforementioned control flow expressions evaluates + /// to something other than the Empty type, this block is mandatory. + /// + /// # Syntax + /// [`Else`] := `"else"` [`Block`] + #[derive(Clone, Debug)] + pub struct Else { + pub block: Block, + } + + /// Represents a [`continue` expression][control] + /// + /// # Syntax + /// [`Continue`] := `"continue"` + #[derive(Clone, Debug)] + pub struct Continue; + + /// Represents a [`break` expression][control]. + /// + /// # Syntax + /// [`Break`] := `"break"` [`Expr`] + #[derive(Clone, Debug)] + pub struct Break { + pub expr: Box, + } + /// Represents a [`return` expression][control]. + /// + /// # Syntax + /// [`Return`] := `"return"` [`Expr`] + #[derive(Clone, Debug)] + pub struct Return { + pub expr: Box, + } + } + } } pub mod lexer {