diff --git a/libconlang/examples/parse_input.rs b/libconlang/examples/parse_input.rs index 73c7d9b..6d51d81 100644 --- a/libconlang/examples/parse_input.rs +++ b/libconlang/examples/parse_input.rs @@ -3,7 +3,7 @@ use conlang::{lexer::Lexer, parser::Parser, pretty_printer::PrettyPrintable, token::Token}; use std::{ error::Error, - io::{stdin, IsTerminal, Read}, + io::{stdin, stdout, IsTerminal, Read, Write}, path::{Path, PathBuf}, }; @@ -30,9 +30,18 @@ impl Config { } fn take_stdin() -> Result<(), Box> { + const PROMPT: &str = "> "; if stdin().is_terminal() { + print!("{PROMPT}"); + stdout().flush()?; for line in stdin().lines() { - parse(&line?, None) + let line = line?; + if !line.is_empty() { + parse(&line, None); + println!(); + } + print!("{PROMPT}"); + stdout().flush()?; } } else { parse(&std::io::read_to_string(stdin())?, None) @@ -44,8 +53,8 @@ fn parse(file: &str, path: Option<&Path>) { use conlang::parser::error::Error; match Parser::from(Lexer::new(file)).parse() { Ok(ast) => ast.print(), - Err(e) if e.start().is_some() => println!("{:?}:{}", path.unwrap_or(Path::new("-")), e), - Err(e) => println!("{e}"), + Err(e) if e.start().is_some() => print!("{:?}:{}", path.unwrap_or(Path::new("-")), e), + Err(e) => print!("{e}"), } println!(); } diff --git a/libconlang/src/ast.rs b/libconlang/src/ast.rs index 1b0fb71..fb0e161 100644 --- a/libconlang/src/ast.rs +++ b/libconlang/src/ast.rs @@ -26,82 +26,120 @@ pub mod preamble { mod visitor { use super::{ - expression::{control::*, math::*, Block, *}, + expression::{ + control::*, + math::{operator::*, *}, + Block, *, + }, literal::*, *, }; - /// [Walk] is the lexical inverse of [Visitor] + /// [Walk] traverses the AST, calling [`Visitor::visit_*()`](Visitor) on all the nodes pub trait Walk + ?Sized, R> { - /// + /// Traverses the children of this node in order, calling the appropriate [Visitor] function fn walk(&self, visitor: &mut T) -> R; } pub mod walker { use super::*; - macro_rules! impl_walk { - ($($T:ty => $f:ident),*$(,)?) => { - $(impl + ?Sized, R> Walk for $T { - fn walk(&self, visitor: &mut T) -> R { - visitor.$f(self) + macro leaf($($T:ty),*$(,)?) {$( + impl> + ?Sized, E> Walk> for $T { + #[doc = concat!("A(n) [`", stringify!($T), "`] is a leaf node.")] + /// Calling this will do nothing. + fn walk(&self, _visitor: &mut T) -> Result<(), E> { Ok(()) } + } + )*} + leaf!(Binary, bool, char, Continue, Float, Identifier, str, u128, Unary); + impl> + ?Sized, E> Walk> for While { + fn walk(&self, visitor: &mut T) -> Result<(), E> { + visitor.visit_expr(&self.cond)?; + visitor.visit_block(&self.body)?; + match &self.else_ { + Some(expr) => visitor.visit_else(expr), + None => Ok(()), + } + } + } + impl> + ?Sized, E> Walk> for If { + fn walk(&self, visitor: &mut T) -> Result<(), E> { + visitor.visit_expr(&self.cond)?; + visitor.visit_block(&self.body)?; + match &self.else_ { + Some(expr) => visitor.visit_else(expr), + None => Ok(()), + } + } + } + impl> + ?Sized, E> Walk> for For { + fn walk(&self, visitor: &mut T) -> Result<(), E> { + visitor.visit_identifier(&self.var)?; + visitor.visit_expr(&self.iter)?; + visitor.visit_block(&self.body)?; + match &self.else_ { + Some(expr) => visitor.visit_else(expr), + None => Ok(()), + } + } + } + impl> + ?Sized, E> Walk> for Else { + fn walk(&self, visitor: &mut T) -> Result<(), E> { + visitor.visit_block(&self.block) + } + } + impl> + ?Sized, E> Walk> for Return { + fn walk(&self, visitor: &mut T) -> Result<(), E> { + visitor.visit_expr(&self.expr) + } + } + impl> + ?Sized, E> Walk> for Break { + fn walk(&self, visitor: &mut T) -> Result<(), E> { + visitor.visit_expr(&self.expr) + } + } + impl> + ?Sized, E> Walk> for Start { + fn walk(&self, visitor: &mut T) -> Result<(), E> { + visitor.visit_expr(&self.0) + } + } + impl> + ?Sized, E> Walk> for Expr { + fn walk(&self, visitor: &mut T) -> Result<(), E> { + visitor.visit_operation(&self.ignore) + } + } + impl> + ?Sized, E> Walk> for Group { + fn walk(&self, visitor: &mut T) -> Result<(), E> { + match self { + Group::Expr(expr) => visitor.visit_expr(expr), + Group::Empty => visitor.visit_empty(), + } + } + } + impl> + ?Sized, E> Walk> for Block { + fn walk(&self, visitor: &mut T) -> Result<(), E> { + visitor.visit_expr(&self.expr) + } + } + impl> + ?Sized, E> Walk> for Operation { + fn walk(&self, visitor: &mut T) -> Result<(), E> { + match self { + Operation::Binary { first, other } => { + visitor.visit_operation(first)?; + for (op, other) in other { + visitor.visit_binary_op(op)?; + visitor.visit_operation(other)?; + } + Ok(()) } - })* - }; - } - 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 () { - fn walk(&self, visitor: &mut T) -> R { - visitor.visit_empty() + Operation::Unary { operators, operand } => { + for op in operators { + visitor.visit_unary_op(op)?; + } + visitor.visit_primary(operand) + } + } } } - impl + ?Sized, R> Walk for Expr { - fn walk(&self, visitor: &mut T) -> R { - visitor.visit_ignore(&self.ignore) - } - } - impl + ?Sized, R> Walk for Primary { - fn walk(&self, visitor: &mut T) -> R { + impl> + ?Sized, E> Walk> for Primary { + fn walk(&self, visitor: &mut T) -> Result<(), E> { match self { Primary::Identifier(i) => visitor.visit_identifier(i), Primary::Literal(l) => visitor.visit_literal(l), @@ -111,8 +149,8 @@ mod visitor { } } } - impl + ?Sized, R> Walk for Literal { - fn walk(&self, visitor: &mut T) -> R { + impl> + ?Sized, E> Walk> for Literal { + fn walk(&self, visitor: &mut T) -> Result<(), E> { match self { Literal::String(s) => visitor.visit_string_literal(s), Literal::Char(c) => visitor.visit_char_literal(c), @@ -122,8 +160,8 @@ mod visitor { } } } - impl + ?Sized, R> Walk for Flow { - fn walk(&self, visitor: &mut T) -> R { + impl> + ?Sized, E> Walk> for Flow { + fn walk(&self, visitor: &mut T) -> Result<(), E> { match self { Flow::While(w) => visitor.visit_while(w), Flow::If(i) => visitor.visit_if(i), @@ -144,15 +182,12 @@ mod visitor { /// Visit an [Expression](Expr) fn visit_expr(&mut self, expr: &Expr) -> R { - expr.walk(self) + self.visit_operation(&expr.ignore) } // Block expression /// Visit a [Block] expression fn visit_block(&mut self, expr: &Block) -> R { - match &expr.expr { - Some(expr) => self.visit_expr(expr), - None => self.visit_empty(), - } + self.visit_expr(&expr.expr) } /// Visit a [Group] expression fn visit_group(&mut self, group: &Group) -> R { @@ -163,73 +198,39 @@ mod visitor { } // Math expression - fn visit_binary(&mut self, expr: &Binary) -> R - where F: Walk, Op: Walk; - /// Visit an [Ignore] expression - fn visit_ignore(&mut self, expr: &Ignore) -> R { - self.visit_binary(expr) - } - /// Visit an [Assign] expression - fn visit_assign(&mut self, expr: &Assign) -> R { - self.visit_binary(expr) - } - /// Visit a [Compare] expression - fn visit_compare(&mut self, expr: &Compare) -> R { - self.visit_binary(expr) - } - /// Visit a [Logic] expression - fn visit_logic(&mut self, expr: &Logic) -> R { - self.visit_binary(expr) - } - /// Visit a [Bitwise] expression - fn visit_bitwise(&mut self, expr: &Bitwise) -> R { - self.visit_binary(expr) - } - /// Visit a [Shift] expression - fn visit_shift(&mut self, expr: &Shift) -> R { - self.visit_binary(expr) - } - /// Visit a [Term] expression - fn visit_term(&mut self, expr: &Term) -> R { - self.visit_binary(expr) - } - /// Visit a [Factor] expression - fn visit_factor(&mut self, expr: &Factor) -> R { - self.visit_binary(expr) - } - /// Visit a [Unary] expression - fn visit_unary(&mut self, expr: &Unary) -> R; + /// Visit an [Operation] + fn visit_operation(&mut self, expr: &Operation) -> R; + /// Visit a [Binary](Operation::Binary) [operator](operator::Binary) + // Math operators + fn visit_binary_op(&mut self, op: &operator::Binary) -> R; + /// Visit a [Unary](Operation::Unary) [operator](operator::Unary) + fn visit_unary_op(&mut self, op: &operator::Unary) -> R; + /// Visit a [Primary] expression /// /// [Primary] := [Identifier] | [Literal] | [Block] | [Flow] fn visit_primary(&mut self, expr: &Primary) -> R { - expr.walk(self) + match expr { + Primary::Identifier(v) => self.visit_identifier(v), + Primary::Literal(v) => self.visit_literal(v), + Primary::Block(v) => self.visit_block(v), + Primary::Group(v) => self.visit_group(v), + Primary::Branch(v) => self.visit_branch_expr(v), + } } - // 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 [Flow] expression. /// /// [Flow] := [While] | [If] | [For] fn visit_branch_expr(&mut self, expr: &Flow) -> R { - expr.walk(self) + match expr { + Flow::While(e) => self.visit_while(e), + Flow::If(e) => self.visit_if(e), + Flow::For(e) => self.visit_for(e), + Flow::Continue(e) => self.visit_continue(e), + Flow::Return(e) => self.visit_return(e), + Flow::Break(e) => self.visit_break(e), + } } /// Visit an [If] expression fn visit_if(&mut self, expr: &If) -> R; @@ -253,7 +254,13 @@ mod visitor { /// /// [Literal] := [String] | [char] | [bool] | [Float] | [u128] fn visit_literal(&mut self, literal: &Literal) -> R { - literal.walk(self) + match literal { + Literal::String(l) => self.visit_string_literal(l), + Literal::Char(l) => self.visit_char_literal(l), + Literal::Bool(l) => self.visit_bool_literal(l), + Literal::Float(l) => self.visit_float_literal(l), + Literal::Int(l) => self.visit_int_literal(l), + } } /// Visit a [string](str) literal fn visit_string_literal(&mut self, string: &str) -> R; @@ -265,7 +272,8 @@ mod visitor { fn visit_float_literal(&mut self, float: &Float) -> R; /// Visit an [integer](u128) literal fn visit_int_literal(&mut self, int: &u128) -> R; - /// Visit an Empty literal + + /// Visit an Empty fn visit_empty(&mut self) -> R; } } @@ -390,19 +398,19 @@ pub mod expression { //! | # | Node | Function //! |----|------------------:|:---------------------------------------------- //! | 0 | [`Expr`] | Contains an expression - //! | 1 | [`math::Ignore`] | Ignores the preceding sub-expression's result - //! | 2 | [`math::Assign`] | Assignment - //! | 3 | [`math::Compare`] | Value Comparison - //! | 4 | [`math::Logic`] | Boolean And, Or, Xor - //! | 5 | [`math::Bitwise`] | Bitwise And, Or, Xor - //! | 6 | [`math::Shift`] | Shift Left/Right - //! | 7 | [`math::Term`] | Add, Subtract - //! | 8 | [`math::Factor`] | Multiply, Divide, Remainder - //! | 9 | [`math::Unary`] | Unary Dereference, Reference, Negate, Not + //! | 1 | [`Ignore`](math) | Ignores the preceding sub-expression's result + //! | 2 | [`Assign`](math) | Assignment + //! | 3 | [`Compare`](math) | Value Comparison + //! | 4 | [`Logic`](math) | Boolean And, Or, Xor + //! | 5 | [`Bitwise`](math) | Bitwise And, Or, Xor + //! | 6 | [`Shift`](math) | Shift Left/Right + //! | 7 | [`Term`](math) | Add, Subtract + //! | 8 | [`Factor`](math) | Multiply, Divide, Remainder + //! | 9 | [`Unary`](math) | Unary Dereference, Reference, Negate, Not //! | 10 | [`control::Flow`] | Branch expressions (`if`, `while`, `for`, `return`, `break`, `continue`), `else` //! | 10 | [`Group`] | Group expressions `(` [Expr]? `)` /* Can evaluate to Empty! */ //! | 10 | [`Block`] | Block expressions `{` [Expr] `}` - //! | 10 | [`Primary`] | Contains an [Identifier], [Literal](literal::Literal), [Block], or [Flow](control::Flow) + //! | 10 | [`Primary`] | Contains an [Identifier], [Literal](literal::Literal), [Block], [Group], or [Flow](control::Flow) //! //! ## Syntax //! ```ignore @@ -417,10 +425,10 @@ pub mod expression { /// Contains an expression /// /// # Syntax - /// [`Expr`] := [`math::Ignore`] + /// [`Expr`] := [`math::Operation`] #[derive(Clone, Debug)] pub struct Expr { - pub ignore: math::Ignore, + pub ignore: math::Operation, } /// A [Primary] Expression is the expression with the highest precedence (i.e. the deepest @@ -430,6 +438,7 @@ pub mod expression { /// [`IDENTIFIER`](Identifier) /// | [`Literal`](literal::Literal) /// | [`Block`] + /// | [`Group`] /// | [`Branch`](control::Flow) #[derive(Clone, Debug)] pub enum Primary { @@ -445,7 +454,7 @@ pub mod expression { /// [`Block`] := `'{'` [`Expr`] `'}'` #[derive(Clone, Debug)] pub struct Block { - pub expr: Option>, + pub expr: Box, } /// Contains a Parenthesized Expression @@ -466,23 +475,24 @@ pub mod expression { //! | # | 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 + //! | 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" | 8 | Assign |", r"`*=`, `/=`, `%=`, `+=`, `-=`, ",//| /* | | |*/ r"`&=`, |=, ", //| /* | | |*/ r"`^=`, `<<=`, `>>=`", r"| Right to Left")] - //! | 9 | [Ignore] | `;` | + //! | 9 | Ignore | `;` | //! - //! + //! //! //! ## Syntax //! ```ignore + //! /* All precedence levels other than Unary fold into Binary */ //! Ignore := Assign (CompareOp Assign )* //! Assign := Compare (IgnoreOp Compare)* //! Compare := Logic (AssignOp Logic )* @@ -495,80 +505,31 @@ pub mod expression { //! ``` use super::*; - /// The template for [Binary] operations. - /// # Syntax - /// [`Binary`] := `First` (`Other`)* + /// An Operation is a tree of [operands](Primary) and [operators](operator). #[derive(Clone, Debug)] - pub struct Binary { - pub first: Box, - pub other: Vec, + pub enum Operation { + /// [`Binary`](Operation::Binary) := + /// [`Operation`] ([`operator::Binary`] [`Operation`])* + Binary { + first: Box, + other: Vec<(operator::Binary, Self)>, + }, + /// [`Unary`](Operation::Unary) := ([`operator::Unary`])* [`Primary`] + Unary { + operators: Vec, + operand: Primary, + }, } - impl Binary { - pub fn new(first: First, other: Vec) -> Self { - Self { first: Box::new(first), other } - } - pub fn first(&self) -> &First { - &self.first - } - pub fn other(&self) -> &[Other] { - &self.other + impl Operation { + pub fn binary(first: Self, other: Vec<(operator::Binary, Self)>) -> Self { + Self::Binary { first: Box::new(first), other } } } - /// Ignores the result of the leading sub-expression. - /// Great if you only want the side-effects. - /// # Syntax - /// [`Ignore`] := [`Assign`] ([`operator::Ignore`] [`Assign`])* - pub type Ignore = Binary; - - /// Assigns the result of the trailing sub-expression to the leading sub-expression. - /// Resolves to the Empty type. - /// # Syntax - /// [`Assign`] := [`Compare`] ([`operator::Assign`] [`Compare`])? - pub type Assign = Binary; - - /// Compares the values of the trailing and leading sub-expressions, - /// and resolves to a boolean. - /// # Syntax - /// [`Compare`] := [`Logic`] ([`operator::Compare`] [`Logic`])* - pub type Compare = Binary; - - /// Performs a boolean logic operation on the leading and trailing sub-expressions. - /// # Syntax - /// [`Logic`] := [`Bitwise`] ([`operator::Logic`] [`Bitwise`])* - pub type Logic = Binary; - - /// Performs a bitwise opration on the leading and trailing sub-expressions. - /// # Syntax - /// [`Bitwise`] := [`Shift`] ([`operator::Bitwise`] [`Shift`])* - pub type Bitwise = Binary; - - /// Shifts the leading sub-expression by the trailing sub-expression - /// # Syntax - /// [`Shift`] := [`Term`] ([`operator::Shift`] [`Term`])* - pub type Shift = Binary; - - /// Adds or subtracts the trailing sub-expression from the leading sub-expression - /// # Syntax - /// [`Term`] := [`Factor`] ([`operator::Term`] [`Factor`])* - pub type Term = Binary; - - /// Multiplies, Divides, or finds the remainder of the trailing sub-expression - /// from the leading sub-expression - /// # Syntax - /// [`Factor`] := [`Unary`] ([`operator::Factor`] [`Unary`])* - pub type Factor = Binary; - - /// Performs a unary operation on the trailing sub-expression. - /// # Syntax - /// [`Unary`] := ([`operator::Unary`])* [`Primary`] - #[derive(Clone, Debug)] - pub struct Unary(pub Vec, pub Primary); - pub mod operator { - //! | # | [Operators](Operator) | Associativity + //! | # | [Operators](self) | Associativity //! |---|---------------------------------------|-------------- - //! | 0 | ([Unary]) `*`, `&`, `-`, `!` | Left to Right + //! | 0 |[`*`, `&`, `-`, `!`](Unary) | Left to Right //! | 1 | `*`, `/`, `%` | Left to Right //! | 2 | `+`, `-` | Left to Right //! | 3 | `<<`, `>>` | Left to Right @@ -580,87 +541,108 @@ pub mod expression { /* | |*/ 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 } - } - })* + + /// Operators which take a single argument + /// + /// (`*`, `&`, `-`, `!`, `@`, `#`, `~`) + #[derive(Clone, Copy, Debug, PartialEq, Eq)] + pub enum Unary { + /// `&&`: Take a reference, twice + RefRef, + /// `&`: Take a reference + Ref, + /// `*`: Dereference + Deref, + /// `-`: Arithmetic negation + Neg, + /// `!`: Binary/Boolean negation + Not, + /// `@`: Undefined + At, + /// `#`: Undefined + Hash, + /// `~`: Undefined + Tilde, } - operator! { - /// (`*`, `&`, `-`, `!`, `@`, `#`, `~`) - Unary { - RefRef := Type::AmpAmp, - 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::Slash, - Rem := Type::Rem, - } - /// (`+`, `-`) - Term { - Add := Type::Plus, - Sub := Type::Minus, - } - /// (`<<`, `>>`) - Shift { - Lsh := Type::LtLt, - Rsh := Type::GtGt, - } - /// (`&`, `|`, `^`) - Bitwise { - BitAnd := Type::Amp, - BitOr := Type::Bar, - BitXor := Type::Xor, - } - /// (`&&`, `||`, `^^`) - Logic { - LogAnd := Type::AmpAmp, - LogOr := Type::BarBar, - LogXor := Type::XorXor, - } - /// (`<`, `<=`, `==`, `!=`, `>=`, `>`) - Compare { - Less := Type::Lt, - LessEq := Type::LtEq, - Equal := Type::EqEq, - NotEq := Type::BangEq, - GreaterEq := Type::GtEq, - Greater := Type::Gt, - } - /// (`=`, `+=`, `-=`, `*=`, `/=`, - /// `&=`, `|=`, `^=`, `<<=`, `>>=`) - Assign { - Assign := Type::Eq, - AddAssign := Type::PlusEq, - SubAssign := Type::MinusEq, - MulAssign := Type::StarEq, - DivAssign := Type::SlashEq, - BitAndAssign := Type::AmpEq, - BitOrAssign := Type::BarEq, - BitXorAssign := Type::XorEq, - ShlAssign := Type::LtLtEq, - ShrAssign := Type::GtGtEq, - } - /// (`;`) - Ignore { - Ignore := Type::Semi, - } + /// Operators which take two arguments + #[derive(Clone, Copy, Debug, PartialEq, Eq)] + pub enum Binary { + // Term operators + /// `*`: Multiplication + Mul, + /// `/`: Division + Div, + /// `%`: Remainder + Rem, + + // Factor operators + /// `+`: Addition + Add, + /// `-`: Subtraction + Sub, + + // Shift operators + /// `<<`: Left Shift + Lsh, + /// `>>`: Right Shift + Rsh, + + // Bitwise operators + /// `&`: Bitwise AND + BitAnd, + /// `|`: Bitwise OR + BitOr, + /// `^`: Bitwise XOR + BitXor, + + // Logic operators + /// `&&`: Short-circuiting logical AND + LogAnd, + /// `||`: Short-circuiting logical OR + LogOr, + /// `^^`: **Non-short-circuiting** logical XOR + LogXor, + + // Comparison operators + /// `<`: Less-than Comparison + Less, + /// `<=`: Less-than or Equal Comparison + LessEq, + /// `==`: Equal Comparison + Equal, + /// `!=`: Not Equal Comparison + NotEq, + /// `>=`: Greater-than or Equal Comparison + GreaterEq, + /// `>`: Greater-than Comparison + Greater, + + // Assignment operators + /// `=`: Assignment + Assign, + /// `+=`: Additive In-place Assignment + AddAssign, + /// `-=`: Subtractive In-place Assignment + SubAssign, + /// `*=`: Multiplicative In-place Assignment + MulAssign, + /// `/=`: Divisive In-place Assignment + DivAssign, + /// `%=`: Remainder In-place Assignment + RemAssign, + /// `&=`: Bitwise-AND In-place Assignment + BitAndAssign, + /// `|=`: Bitwise-OR In-place Assignment + BitOrAssign, + /// `^=`: Bitwise-XOR In-place Assignment + BitXorAssign, + /// `<<=`: Left Shift In-place Assignment + ShlAssign, + /// `>>=`: Right Shift In-place Assignment + ShrAssign, + // Ignorance operators + /// `;`: Ignore + Ignore, } } } diff --git a/libconlang/src/parser.rs b/libconlang/src/parser.rs index de75857..53d8f3e 100644 --- a/libconlang/src/parser.rs +++ b/libconlang/src/parser.rs @@ -17,18 +17,21 @@ pub mod error { pub enum Reason { Expected(Type), NotIdentifier, + NotOperator, NotLiteral, NotString, NotChar, NotBool, NotFloat, + NotInt, FloatExponentOverflow, FloatMantissaOverflow, - NotInt, IntOverflow, - NotControlFlow, NotBranch, + IncompleteBranch, + AllElseFailed, EndOfFile, + PanicStackUnderflow, #[default] Unspecified, } @@ -38,28 +41,30 @@ pub mod error { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Expected(t) => write!(f, "Expected {t}"), - Self::NotIdentifier => Display::fmt("Not an identifier", f), - Self::NotLiteral => Display::fmt("Not a literal", f), - Self::NotString => Display::fmt("Not a string", f), - Self::NotChar => Display::fmt("Not a char", f), - Self::NotBool => Display::fmt("Not a bool", f), - Self::NotFloat => Display::fmt("Not a float", f), - Self::FloatExponentOverflow => Display::fmt("Float exponent too large", f), - Self::FloatMantissaOverflow => Display::fmt("Float mantissa too large", f), - Self::NotInt => Display::fmt("Not an integer", f), - Self::IntOverflow => Display::fmt("Integer too large", f), - Self::NotControlFlow => Display::fmt("Control flow expression was incomplete", f), - Self::NotBranch => Display::fmt("Branch expression was incomplete", f), - Self::EndOfFile => Display::fmt("Got end of file", f), - Self::Unspecified => Display::fmt( - "Unspecified error. You are permitted to slap the code author.", - f, - ), + Self::NotIdentifier => "Not an identifier".fmt(f), + Self::NotOperator => "Not an operator".fmt(f), + Self::NotLiteral => "Not a literal".fmt(f), + Self::NotString => "Not a string".fmt(f), + Self::NotChar => "Not a char".fmt(f), + Self::NotBool => "Not a bool".fmt(f), + Self::NotFloat => "Not a float".fmt(f), + Self::FloatExponentOverflow => "Float exponent too large".fmt(f), + Self::FloatMantissaOverflow => "Float mantissa too large".fmt(f), + Self::NotInt => "Not an integer".fmt(f), + Self::IntOverflow => "Integer too large".fmt(f), + Self::IncompleteBranch => "Branch expression was incomplete".fmt(f), + Self::NotBranch => "Expected branch expression".fmt(f), + Self::AllElseFailed => "Did not match any rule".fmt(f), + Self::EndOfFile => "Got end of file".fmt(f), + Self::PanicStackUnderflow => "Could not recover from panic".fmt(f), + Self::Unspecified => { + "Unspecified error. You are permitted to slap the code author.".fmt(f) + } } } } - /// [Parser] [Result] + /// [Parser](super::Parser) [Result] pub type PResult = Result; #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct Error { @@ -99,6 +104,7 @@ pub mod error { error_impl! { expected(e: Type): Expected, not_identifier: NotIdentifier, + not_operator: NotOperator, not_literal: NotLiteral, not_string: NotString, not_char: NotChar, @@ -108,9 +114,10 @@ pub mod error { float_mantissa_overflow: FloatMantissaOverflow, not_int: NotInt, int_overflow: IntOverflow, - not_control_flow: NotControlFlow, not_branch: NotBranch, + all_else_failed: AllElseFailed, end_of_file: EndOfFile, + panic_underflow: PanicStackUnderflow, unspecified: Unspecified, } } @@ -161,7 +168,9 @@ impl<'t> Parser<'t> { } /// Peek at the current token pub fn peek(&self) -> PResult<&Token> { - self.tokens.get(self.curr).ok_or(Error::end_of_file()) + self.tokens + .get(self.curr) + .ok_or(Error::end_of_file().maybe_token(self.tokens.last().copied())) } /// Records the current position on the panic stack pub fn mark(&mut self) -> &mut Self { @@ -174,12 +183,18 @@ impl<'t> Parser<'t> { self } /// Unwinds the panic stack one step - pub fn unwind(&mut self) -> Option { - let out = self.panic_stack.pop(); - if let Some(v) = out { - self.curr = v; + pub fn unwind(&mut self) -> PResult<&mut Self> { + let v = self.panic_stack.pop().ok_or(Error::panic_underflow())?; + self.curr = v; + Ok(self) + } + pub fn advance_until(&mut self, t: Type) -> PResult<&mut Self> { + while self.matches(t).is_err() { + self.check_eof() + .map_err(|e| e.reason(Expected(t)))? + .consume(); } - out + Ok(self) } } /// Helpers @@ -192,7 +207,7 @@ impl<'t> Parser<'t> { if self.curr < self.tokens.len() { Ok(self) } else { - Err(Error::end_of_file()) + Err(Error::end_of_file().maybe_token(self.tokens.last().copied())) } } fn todo_error(&mut self, l: u32, c: u32, s: &str) -> Error { @@ -211,9 +226,17 @@ impl<'t> Parser<'t> { } fn delimited(&mut self, lhs: Type, mid: F, rhs: Type) -> PResult where F: Fn(&mut Self) -> PResult { - self.consume_type(lhs)?; - let out = mid(self)?; - self.consume_type(rhs)?; + self.consume_type(lhs)?.mark(); + let out = match mid(self) { + Ok(out) => out, + Err(e) => { + eprintln!("{e}"); + // Jump back in time and try to re-parse from the next brace + self.unwind()?.advance_until(lhs)?.mark(); + return self.delimited(lhs, mid, rhs); + } + }; + self.consume_type(rhs)?.unmark(); Ok(out) } } @@ -228,41 +251,44 @@ macro ptodo($self:expr $(, $t:expr)*) { /// # Terminals and Pseudo-Terminals impl<'t> Parser<'t> { - pub fn identifier(&mut self) -> PResult { + fn identifier(&mut self) -> PResult { let token = *self .matches(Type::Identifier) .map_err(|e| Error::not_identifier().maybe_token(e.start()))?; Ok(Identifier(self.consume().text[&token].into())) } - pub fn literal(&mut self) -> PResult { + fn literal(&mut self) -> PResult { use literal::Literal::*; use Keyword::{False, True}; let tok = self.peek()?; match tok.ty() { Type::Float => self.float().map(Float), - Type::Integer => self.int::<10>().map(Int), + Type::Integer => self.int().map(Int), Type::String => self.string().map(String), Type::Character => self.char().map(Char), Type::Keyword(True | False) => self.bool().map(Bool), _ => Err(Error::not_literal().token(*tok)), } } - pub fn float(&mut self) -> PResult { + fn float(&mut self) -> PResult { ptodo!(self) } - pub fn int(&mut self) -> PResult { + fn int(&mut self) -> PResult { let token = *self.matches(Type::Integer)?; - u128::from_str_radix(&self.consume().text[&token], BASE) - .map_err(|_| Error::not_int().token(token)) + self.consume().text[&token] + .chars() + .parse_int::() + .next() + .ok_or(Error::not_int().token(token)) } - pub fn string(&mut self) -> PResult { + fn string(&mut self) -> PResult { let range = self .matches(Type::String) .map_err(|e| e.reason(NotString))? .range(); Ok(self.consume().text[range].chars().unescape().collect()) } - pub fn char(&mut self) -> PResult { + fn char(&mut self) -> PResult { let token = *self.matches(Type::Character)?; self.consume().text[&token] .chars() @@ -270,7 +296,7 @@ impl<'t> Parser<'t> { .next() .ok_or(Error::not_char().token(token)) } - pub fn bool(&mut self) -> PResult { + fn bool(&mut self) -> PResult { use Keyword::{False, True}; let token = self.peek()?; let out = match token.ty() { @@ -284,22 +310,15 @@ impl<'t> Parser<'t> { } /// Expressions impl<'t> Parser<'t> { - pub fn expr(&mut self) -> PResult { + fn expr(&mut self) -> PResult { use expression::Expr; Ok(Expr { ignore: self.ignore()? }) } - pub fn if_not_expr(&mut self, matches: Type) -> PResult> { - if self.peek()?.ty() == matches { - Ok(None) - } else { - Some(self.expr()).transpose() - } + fn block(&mut self) -> PResult { + self.delimited(Type::LCurly, |p| p.expr(), Type::RCurly) + .map(|e| expression::Block { expr: Box::new(e) }) } - pub fn block(&mut self) -> PResult { - self.delimited(Type::LCurly, |p| p.if_not_expr(Type::RCurly), Type::RCurly) - .map(|e| expression::Block { expr: e.map(Box::new) }) - } - pub fn group(&mut self) -> PResult { + fn group(&mut self) -> PResult { use expression::Group; let t = self.consume_type(Type::LParen)?.peek()?; match t.ty() { @@ -314,14 +333,21 @@ impl<'t> Parser<'t> { } } } - pub fn primary(&mut self) -> PResult { + fn primary(&mut self) -> PResult { use expression::Primary; - self.identifier() - .map(Primary::Identifier) - .or_else(|_| self.literal().map(Primary::Literal)) - .or_else(|_| self.block().map(Primary::Block)) - .or_else(|_| self.group().map(Primary::Group)) - .or_else(|_| self.flow().map(Primary::Branch)) + let token = *self.peek()?; + match token.ty() { + Type::Identifier => self.identifier().map(Primary::Identifier), + Type::String + | Type::Character + | Type::Integer + | Type::Float + | Type::Keyword(Keyword::True | Keyword::False) => self.literal().map(Primary::Literal), + Type::LCurly => self.block().map(Primary::Block), + Type::LParen => self.group().map(Primary::Group), + Type::Keyword(_) => self.flow().map(Primary::Branch), + _ => Err(Error::all_else_failed().token(token))?, + } } } @@ -337,65 +363,130 @@ impl<'t> Parser<'t> { /// ``` /// becomes /// ```rust,ignore -/// pub fn function_name(&mut self) -> PResult { ... } +/// fn function_name(&mut self) -> PResult { ... } /// ``` -macro binary ($($f:ident: $Ret:ty = $a:ident, $b:ident);*$(;)?) {$( - pub fn $f (&mut self) -> PResult<$Ret> { +macro binary ($($f:ident = $a:ident, $b:ident);*$(;)?) {$( + fn $f (&mut self) -> PResult { let (first, mut others) = (self.$a()?, vec![]); - while let Some(op) = self.$b() { + while let Ok(op) = self.$b() { others.push((op, self.$a()?)); } - Ok(<$Ret>::new(first, others)) + Ok(if others.is_empty() { first } else { + math::Operation::binary(first, others) + }) } )*} /// # [Arithmetic and Logical Subexpressions](math) impl<'t> Parser<'t> { binary! { - //name returns operands operators - ignore: math::Ignore = assign, ignore_op; - assign: math::Assign = compare, assign_op; - compare: math::Compare = logic, compare_op; - logic: math::Logic = bitwise, logic_op; - bitwise: math::Bitwise = shift, bitwise_op; - shift: math::Shift = term, shift_op; - term: math::Term = factor, term_op; - factor: math::Factor = unary, factor_op; + //name operands operators + ignore = assign, ignore_op; + assign = compare, assign_op; + compare = logic, compare_op; + logic = bitwise, logic_op; + bitwise = shift, bitwise_op; + shift = term, shift_op; + term = factor, term_op; + factor = unary, factor_op; } - pub fn unary(&mut self) -> PResult { - let mut ops = vec![]; - while let Some(op) = self.unary_op() { - ops.push(op) + + fn unary(&mut self) -> PResult { + let mut operators = vec![]; + while let Ok(op) = self.unary_op() { + operators.push(op) } - Ok(math::Unary(ops, self.primary()?)) + Ok(math::Operation::Unary { operators, operand: self.primary()? }) } } -macro operator_impl($($(#[$m:meta])*$f:ident: $Ret:ty),*$(,)*) {$( - $(#[$m])* pub fn $f(&mut self) -> Option<$Ret> { - let out: Option<$Ret> = self.peek().ok()?.ty().into(); - if out.is_some() { self.consume(); } +macro operator_impl ($($(#[$m:meta])* $f:ident : {$($type:pat => $op:ident),*$(,)?})*) { + $($(#[$m])* fn $f(&mut self) -> PResult { + use operator::Binary; + let token = *self.peek()?; + let out = Ok(match token.ty() { + $($type => Binary::$op,)* + _ => Err(Error::not_operator().token(token))?, + }); + self.consume(); out - } -)*} + })* +} /// # [Operators](operator) impl<'t> Parser<'t> { operator_impl! { - ignore_op: operator::Ignore, - compare_op: operator::Compare, - assign_op: operator::Assign, - logic_op: operator::Logic, - bitwise_op: operator::Bitwise, - shift_op: operator::Shift, - term_op: operator::Term, - factor_op: operator::Factor, - unary_op: operator::Unary, + factor_op: { + Type::Star => Mul, + Type::Slash => Div, + Type::Rem => Rem, + } + term_op: { + Type::Plus => Add, + Type::Minus => Sub, + } + shift_op: { + Type::LtLt => Lsh, + Type::GtGt => Rsh, + } + bitwise_op: { + Type::Amp => BitAnd, + Type::Bar => BitOr, + Type::Xor => BitXor, + } + logic_op: { + Type::AmpAmp => LogAnd, + Type::BarBar => LogOr, + Type::XorXor => LogXor, + } + compare_op: { + Type::Lt => Less, + Type::LtEq => LessEq, + Type::EqEq => Equal, + Type::BangEq => NotEq, + Type::GtEq => GreaterEq, + Type::Gt => Greater, + } + assign_op: { + Type::Eq => Assign, + Type::PlusEq => AddAssign, + Type::MinusEq => SubAssign, + Type::StarEq => MulAssign, + Type::SlashEq => DivAssign, + Type::RemEq => RemAssign, + Type::AmpEq => BitAndAssign, + Type::BarEq => BitOrAssign, + Type::XorEq => BitXorAssign, + Type::LtLtEq => ShlAssign, + Type::GtGtEq => ShrAssign, + } + ignore_op: { + Type::Semi => Ignore, + } + + } + /// Parse a [unary operator](operator::Unary) + fn unary_op(&mut self) -> PResult { + use operator::Unary; + let token = *self.peek()?; + let out = Ok(match token.ty() { + Type::AmpAmp => Unary::RefRef, + Type::Amp => Unary::Ref, + Type::Star => Unary::Deref, + Type::Minus => Unary::Neg, + Type::Bang => Unary::Not, + Type::At => Unary::At, + Type::Hash => Unary::Hash, + Type::Tilde => Unary::Tilde, + _ => Err(Error::not_operator().token(token))?, + }); + self.consume(); + out } } /// # [Control Flow](control) impl<'t> Parser<'t> { - pub fn flow(&mut self) -> PResult { + fn flow(&mut self) -> PResult { use control::Flow; use Keyword::{Break, Continue, For, If, Return, While}; - let token = self.peek()?; + let token = *self.peek()?; match token.ty() { Type::Keyword(While) => self.parse_while().map(Flow::While), Type::Keyword(For) => self.parse_for().map(Flow::For), @@ -403,10 +494,11 @@ impl<'t> Parser<'t> { Type::Keyword(Break) => self.parse_break().map(Flow::Break), Type::Keyword(Return) => self.parse_return().map(Flow::Return), Type::Keyword(Continue) => self.parse_continue().map(Flow::Continue), - _ => Err(Error::not_branch().token(*token)), + _ => Err(Error::all_else_failed().token(token)), } + .map_err(|e| e.reason(IncompleteBranch).token(token)) } - pub fn parse_if(&mut self) -> PResult { + fn parse_if(&mut self) -> PResult { self.keyword(Keyword::If)?; Ok(control::If { cond: self.expr()?.into(), @@ -414,7 +506,7 @@ impl<'t> Parser<'t> { else_: self.parse_else()?, }) } - pub fn parse_while(&mut self) -> PResult { + fn parse_while(&mut self) -> PResult { self.keyword(Keyword::While)?; Ok(control::While { cond: self.expr()?.into(), @@ -422,7 +514,7 @@ impl<'t> Parser<'t> { else_: self.parse_else()?, }) } - pub fn parse_for(&mut self) -> PResult { + fn parse_for(&mut self) -> PResult { self.keyword(Keyword::For)?; Ok(control::For { var: self.identifier()?, @@ -431,20 +523,21 @@ impl<'t> Parser<'t> { else_: self.parse_else()?, }) } - pub fn parse_else(&mut self) -> PResult> { + fn parse_else(&mut self) -> PResult> { // it's fine for `else` to be missing entirely self.keyword(Keyword::Else) .ok() .map(|p| Ok(control::Else { block: p.block()? })) .transpose() } - pub fn parse_break(&mut self) -> PResult { + fn parse_break(&mut self) -> PResult { Ok(control::Break { expr: self.keyword(Keyword::Break)?.expr()?.into() }) } - pub fn parse_return(&mut self) -> PResult { + fn parse_return(&mut self) -> PResult { Ok(control::Return { expr: self.keyword(Keyword::Return)?.expr()?.into() }) } - pub fn parse_continue(&mut self) -> PResult { - ptodo!(self) + fn parse_continue(&mut self) -> PResult { + self.keyword(Keyword::Continue)?; + Ok(control::Continue) } } diff --git a/libconlang/src/pretty_printer.rs b/libconlang/src/pretty_printer.rs index be4510d..4740804 100644 --- a/libconlang/src/pretty_printer.rs +++ b/libconlang/src/pretty_printer.rs @@ -63,135 +63,111 @@ macro visit_operator($self:ident.$op:expr) { $self.space()?.put($op)?.space().map(drop) } impl Visitor> for Printer { - fn visit_binary(&mut self, expr: &math::Binary) -> IOResult<()> - where - F: Walk>, - Op: Walk>, - { - expr.first().walk(self)?; - for (op, target) in expr.other() { - op.walk(self)?; - target.walk(self)?; + fn visit_operation(&mut self, expr: &math::Operation) -> IOResult<()> { + use math::Operation; + match expr { + Operation::Binary { first, other } => { + self.put('(')?.visit_operation(first)?; + for (op, other) in other { + self.visit_binary_op(op)?; + self.visit_operation(other)?; + } + self.put(')').map(drop) + } + Operation::Unary { operators, operand } => { + for op in operators { + self.visit_unary_op(op)?; + } + self.visit_primary(operand) + } } - Ok(()) } - - fn visit_unary(&mut self, expr: &math::Unary) -> IOResult<()> { - for op in &expr.0 { - op.walk(self)?; - } - expr.1.walk(self) - } - fn visit_ignore_op(&mut self, _op: &operator::Ignore) -> IOResult<()> { - self.put(";")?.newline().map(drop) - } - fn visit_compare_op(&mut self, op: &operator::Compare) -> IOResult<()> { + fn visit_binary_op(&mut self, op: &operator::Binary) -> IOResult<()> { + use operator::Binary; visit_operator!(self.match op { - operator::Compare::Less => "<", - operator::Compare::LessEq => "<=", - operator::Compare::Equal => "==", - operator::Compare::NotEq => "!=", - operator::Compare::GreaterEq => ">=", - operator::Compare::Greater => ">", - }) - } - fn visit_assign_op(&mut self, op: &operator::Assign) -> IOResult<()> { - visit_operator!( self.match op { - operator::Assign::Assign => "=", - operator::Assign::AddAssign => "+=", - operator::Assign::SubAssign => "-=", - operator::Assign::MulAssign => "*=", - operator::Assign::DivAssign => "/=", - operator::Assign::BitAndAssign => "&=", - operator::Assign::BitOrAssign => "|=", - operator::Assign::BitXorAssign => "^=", - operator::Assign::ShlAssign => "<<=", - operator::Assign::ShrAssign => ">>=", - }) - } - fn visit_logic_op(&mut self, op: &operator::Logic) -> IOResult<()> { - visit_operator!(self.match op { - operator::Logic::LogAnd => "&&", - operator::Logic::LogOr => "||", - operator::Logic::LogXor => "^^", - }) - } - fn visit_bitwise_op(&mut self, op: &operator::Bitwise) -> IOResult<()> { - visit_operator!(self.match op { - operator::Bitwise::BitAnd => "&", - operator::Bitwise::BitOr => "|", - operator::Bitwise::BitXor => "^", - }) - } - fn visit_shift_op(&mut self, op: &operator::Shift) -> IOResult<()> { - visit_operator!(self.match op { - operator::Shift::Lsh => "<<", - operator::Shift::Rsh => ">>", - }) - } - fn visit_term_op(&mut self, op: &operator::Term) -> IOResult<()> { - visit_operator!(self.match op { - operator::Term::Add => "+", - operator::Term::Sub => "-", - }) - } - fn visit_factor_op(&mut self, op: &operator::Factor) -> IOResult<()> { - visit_operator!(self.match op { - operator::Factor::Mul => "*", - operator::Factor::Div => "/", - operator::Factor::Rem => "%", + Binary::Mul => "*", + Binary::Div => "/", + Binary::Rem => "%", + Binary::Add => "+", + Binary::Sub => "-", + Binary::Lsh => "<<", + Binary::Rsh => ">>", + Binary::BitAnd => "&", + Binary::BitOr => "|", + Binary::BitXor => "^", + Binary::LogAnd => "&&", + Binary::LogOr => "||", + Binary::LogXor => "^^", + Binary::Less => "<", + Binary::LessEq => "<=", + Binary::Equal => "==", + Binary::NotEq => "!=", + Binary::GreaterEq => ">=", + Binary::Greater => ">", + Binary::Assign => "=", + Binary::AddAssign => "+=", + Binary::SubAssign => "-=", + Binary::MulAssign => "*=", + Binary::DivAssign => "/=", + Binary::RemAssign => "%=", + Binary::BitAndAssign => "&=", + Binary::BitOrAssign => "|=", + Binary::BitXorAssign => "^=", + Binary::ShlAssign => "<<=", + Binary::ShrAssign => ">>=", + Binary::Ignore => ";", }) } fn visit_unary_op(&mut self, op: &operator::Unary) -> IOResult<()> { + use operator::Unary; self.put(match op { - operator::Unary::RefRef => "&&", - operator::Unary::Deref => "*", - operator::Unary::Ref => "&", - operator::Unary::Neg => "-", - operator::Unary::Not => "!", - operator::Unary::At => "@", - operator::Unary::Hash => "#", - operator::Unary::Tilde => "~", + Unary::RefRef => "&&", + Unary::Deref => "*", + Unary::Ref => "&", + Unary::Neg => "-", + Unary::Not => "!", + Unary::At => "@", + Unary::Hash => "#", + Unary::Tilde => "~", }) .map(drop) } - fn visit_if(&mut self, expr: &control::If) -> IOResult<()> { - expr.cond.walk(self.put("if")?.space()?)?; - expr.body.walk(self.space()?)?; - if let Some(e) = &expr.else_ { - e.walk(self)? + self.put("while")?.space()?.visit_expr(&expr.cond)?; + self.space()?.visit_block(&expr.body)?; + match &expr.else_ { + Some(e) => self.visit_else(e), + None => Ok(()), } - Ok(()) } fn visit_while(&mut self, expr: &control::While) -> IOResult<()> { - expr.cond.walk(self.put("while")?.space()?)?; - expr.body.walk(self.space()?)?; - if let Some(e) = &expr.else_ { - e.walk(self)? + self.put("while")?.space()?.visit_expr(&expr.cond)?; + self.space()?.visit_block(&expr.body)?; + match &expr.else_ { + Some(e) => self.visit_else(e), + None => Ok(()), } - Ok(()) } fn visit_for(&mut self, expr: &control::For) -> IOResult<()> { - expr.var.walk(self.put("for")?.space()?)?; - expr.iter.walk(self.space()?.put("in")?.space()?)?; - expr.body.walk(self.space()?)?; - if let Some(e) = &expr.else_ { - e.walk(self)? + self.put("for")?.space()?.visit_identifier(&expr.var)?; + self.space()?.put("in")?.space()?.visit_expr(&expr.iter)?; + self.space()?.visit_block(&expr.body)?; + match &expr.else_ { + Some(e) => self.visit_else(e), + None => Ok(()), } - Ok(()) } fn visit_else(&mut self, expr: &control::Else) -> IOResult<()> { - expr.block.walk(self.space()?.put("else")?.space()?) + self.space()?.put("else")?.space()?.visit_block(&expr.block) } - fn visit_continue(&mut self, _expr: &control::Continue) -> IOResult<()> { + fn visit_continue(&mut self, _: &control::Continue) -> IOResult<()> { self.put("continue").map(drop) } - fn visit_break(&mut self, expr: &control::Break) -> IOResult<()> { - expr.expr.walk(self.put("break")?.space()?) + fn visit_break(&mut self, brk: &control::Break) -> IOResult<()> { + self.put("break")?.space()?.visit_expr(&brk.expr) } - fn visit_return(&mut self, expr: &control::Return) -> IOResult<()> { - expr.expr.walk(self.put("return")?.space()?) + fn visit_return(&mut self, ret: &control::Return) -> IOResult<()> { + self.put("return")?.space()?.visit_expr(&ret.expr) } fn visit_identifier(&mut self, ident: &Identifier) -> IOResult<()> { @@ -220,15 +196,9 @@ impl Visitor> for Printer { } fn visit_block(&mut self, expr: &expression::Block) -> IOResult<()> { - self.put('{')?; - match &expr.expr { - Some(expr) => { - expr.walk(self.indent().newline()?)?; - self.dedent().newline()?; - } - None => ().walk(self.space()?)?, - } - self.put('}').map(drop) + self.put('{')?.indent().newline()?; + expr.walk(self)?; + self.dedent().newline()?.put('}').map(drop) } fn visit_group(&mut self, expr: &expression::Group) -> IOResult<()> {