diff --git a/grammar.ebnf b/grammar.ebnf index e521c9b..2cfb3e6 100644 --- a/grammar.ebnf +++ b/grammar.ebnf @@ -1,55 +1,48 @@ -# Conlang Expression Grammar -Start = Expr +(* Conlang Expression Grammar *) +Start = Expr ; -# literal -Literal = String | Char | Float | Int | Bool -String = STRING -Float = FLOAT -Char = CHARACTER -Bool = "true" | "false" -Int = INTEGER +(* literal *) +Literal = STRING | CHARACTER | FLOAT | INTEGER | Bool ; +Bool = "true" | "false" ; +Identifier = IDENTIFIER ; +(* # Expressions *) +(* expression *) +Expr = Ignore +Block = '{' Expr? '}' ; +Group = '(' Expr? ')' ; +Primary = Item | Identifier | Literal + | Block | Group | Branch ; -Identifier = IDENTIFIER +(* expression::math *) +Ignore = Assign (IgnoreOp Assign )* ; +Assign = Compare (AssignOp Compare)* ; +Compare = Logic (CompareOp Logic )* ; +Logic = Bitwise (LogicOp Bitwise)* ; +Bitwise = Shift (BitwiseOp Shift )* ; +Shift = Term (ShiftOp Term )* ; +Term = Factor (TermOp Factor )* ; +Factor = Unary (FactorOp Unary )* ; +Unary = (UnaryOp)* Primary ; -# Expressions -Expr = Flow | Ignore -Block = '{' Expr '}' -Group = '(' Expr ')' -Final = Identifier | Literal | - Block | Group | Branch - -# expression::math -Ignore = Assign (IgnoreOp Assign )* -Assign = Compare (AssignOp Compare)* -Compare = Logic (CompareOp Logic )* -Logic = Bitwise (LogicOp Bitwise)* -Bitwise = Shift (BitwiseOp Shift )* -Shift = Term (ShiftOp Term )* -Term = Factor (TermOp Factor )* -Factor = Unary (FactorOp Unary )* -Unary = (UnaryOp)* Final - -# expression::math::operator -IgnoreOp = ';' -CompareOp = '<' | "<=" | "==" | "!=" | ">=" | '>' +(* expression::math::operator *) +IgnoreOp = ';' ; +CompareOp = '<' | "<=" | "==" | "!=" | ">=" | '>' ; AssignOp = '=' | "+=" | "-=" | "*=" | "/=" | - "&=" | "|=" | "^=" |"<<=" |">>=" -LogicOp = "&&" | "||" | "^^" + "&=" | "|=" | "^=" |"<<=" |">>=" ; +LogicOp = "&&" | "||" | "^^" ; -BitwiseOp = '&' | '|' | '^' -ShiftOp = "<<" | ">>" -TermOp = '+' | '-' -FactorOp = '*' | '/' | '%' -UnaryOp = '*' | '&' | '-' | '!' +BitwiseOp = '&' | '|' | '^' ; +ShiftOp = "<<" | ">>"; +TermOp = '+' | '-' ; +FactorOp = '*' | '/' | '%' ; +UnaryOp = '*' | '&' | '-' | '!' ; -# expression::control -Branch = While | If | For -If = "if" Expr Block (Else)? -While = "while" Expr Block (Else)? -For = "for" Identifier "in" Expr Block (Else)? -Else = "else" Block - -Flow = Break | Return | Continue -Break = "break" Expr -Return = "return" Expr -Continue = "continue" +(* expression::control *) +Branch = While | If | For | Break | Return | Continue ; +If = "if" Expr Block (Else)? ; +While = "while" Expr Block (Else)? ; +For = "for" Identifier "in" Expr Block (Else)? ; +Else = "else" Block ; +Break = "break" Expr ; +Return = "return" Expr ; +Continue = "continue" ; diff --git a/libconlang/Cargo.toml b/libconlang/Cargo.toml index 0ba9739..3dcdc02 100644 --- a/libconlang/Cargo.toml +++ b/libconlang/Cargo.toml @@ -8,5 +8,5 @@ license.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -lerox ={ path = "../lerox" } +lerox = { path = "../lerox" } unicode-xid = "0.2.4" diff --git a/libconlang/src/ast.rs b/libconlang/src/ast.rs index d544c87..916ff9e 100644 --- a/libconlang/src/ast.rs +++ b/libconlang/src/ast.rs @@ -31,10 +31,6 @@ mod visitor { *, }; /// [Walk] is the lexical inverse of [Visitor] - /// - /// # Examples - /// ```rust,ignore - /// ``` pub trait Walk + ?Sized, R> { /// fn walk(&self, visitor: &mut T) -> R; @@ -43,7 +39,7 @@ mod visitor { use super::*; macro_rules! impl_walk { ($($T:ty => $f:ident),*$(,)?) => { - $(impl, R> Walk for $T { + $(impl + ?Sized, R> Walk for $T { fn walk(&self, visitor: &mut T) -> R { visitor.$f(self) } @@ -59,10 +55,10 @@ mod visitor { // Identifier Identifier => visit_identifier, // ast::literal - &str => visit_string_literal, + str => visit_string_literal, char => visit_char_literal, bool => visit_bool_literal, - u128 => visit_int_literal, + u128 => visit_int_literal, Float => visit_float_literal, // ast::math Ignore => visit_ignore, @@ -91,25 +87,29 @@ mod visitor { Else => visit_else, // ast::control::Flow Continue => visit_continue, - Return =>visit_return, + Return => visit_return, Break => visit_break, } + impl + ?Sized, R> Walk for () { + fn walk(&self, visitor: &mut T) -> R { + visitor.visit_empty() + } + } 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 { + impl + ?Sized, R> Walk for Primary { 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), + Primary::Identifier(i) => visitor.visit_identifier(i), + Primary::Literal(l) => visitor.visit_literal(l), + Primary::Block(b) => visitor.visit_block(b), + Primary::Group(g) => visitor.visit_group(g), + Primary::Branch(b) => visitor.visit_branch_expr(b), } } } @@ -124,18 +124,12 @@ mod visitor { } } } - 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::While(w) => visitor.visit_while(w), + Flow::If(i) => visitor.visit_if(i), + Flow::For(f) => visitor.visit_for(f), Flow::Continue(c) => visitor.visit_continue(c), Flow::Return(r) => visitor.visit_return(r), Flow::Break(b) => visitor.visit_break(b), @@ -157,36 +151,60 @@ mod visitor { // Block expression /// Visit a [Block] expression fn visit_block(&mut self, expr: &Block) -> R { - self.visit_expr(&expr.expr) + match &expr.expr { + Some(expr) => self.visit_expr(expr), + None => self.visit_empty(), + } } /// Visit a [Group] expression fn visit_group(&mut self, expr: &Group) -> R { - self.visit_expr(&expr.expr) + match &expr.expr { + Some(expr) => self.visit_expr(expr), + None => self.visit_empty(), + } } // 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; + fn visit_ignore(&mut self, expr: &Ignore) -> R { + self.visit_binary(expr) + } /// Visit an [Assign] expression - fn visit_assign(&mut self, expr: &Assign) -> R; + fn visit_assign(&mut self, expr: &Assign) -> R { + self.visit_binary(expr) + } /// Visit a [Compare] expression - fn visit_compare(&mut self, expr: &Compare) -> R; + fn visit_compare(&mut self, expr: &Compare) -> R { + self.visit_binary(expr) + } /// Visit a [Logic] expression - fn visit_logic(&mut self, expr: &Logic) -> R; + fn visit_logic(&mut self, expr: &Logic) -> R { + self.visit_binary(expr) + } /// Visit a [Bitwise] expression - fn visit_bitwise(&mut self, expr: &Bitwise) -> R; + fn visit_bitwise(&mut self, expr: &Bitwise) -> R { + self.visit_binary(expr) + } /// Visit a [Shift] expression - fn visit_shift(&mut self, expr: &Shift) -> R; + fn visit_shift(&mut self, expr: &Shift) -> R { + self.visit_binary(expr) + } /// Visit a [Term] expression - fn visit_term(&mut self, expr: &Term) -> R; + fn visit_term(&mut self, expr: &Term) -> R { + self.visit_binary(expr) + } /// Visit a [Factor] expression - fn visit_factor(&mut self, expr: &Factor) -> R; + 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 a [Final] expression + /// Visit a [Primary] expression /// - /// [Final] := [Identifier] | [Literal] | [Block] | [Branch] - fn visit_final(&mut self, expr: &Final) -> R { + /// [Primary] := [Identifier] | [Literal] | [Block] | [Flow] + fn visit_primary(&mut self, expr: &Primary) -> R { expr.walk(self) } // Math operators @@ -209,10 +227,10 @@ mod visitor { /// Visit a [Unary] [operator](operator::Unary) fn visit_unary_op(&mut self, op: &operator::Unary) -> R; - /// Visit a [Branch] expression. + /// Visit a [Flow] expression. /// - /// [Branch] := [While] | [If] | [For] - fn visit_branch_expr(&mut self, expr: &Branch) -> R { + /// [Flow] := [While] | [If] | [For] + fn visit_branch_expr(&mut self, expr: &Flow) -> R { expr.walk(self) } /// Visit an [If] expression @@ -223,12 +241,6 @@ mod visitor { 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 @@ -236,12 +248,12 @@ mod visitor { /// Visit a [Return] expression fn visit_return(&mut self, expr: &Return) -> R; - // final symbols + // primary symbols /// Visit an [Identifier] fn visit_identifier(&mut self, ident: &Identifier) -> R; /// Visit a [Literal] /// - /// [Literal] := [String] | [char] | [bool] | [Float] | [Int] + /// [Literal] := [String] | [char] | [bool] | [Float] | [u128] fn visit_literal(&mut self, literal: &Literal) -> R { literal.walk(self) } @@ -253,8 +265,10 @@ mod visitor { 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 + /// Visit an [integer](u128) literal fn visit_int_literal(&mut self, int: &u128) -> R; + /// Visit an Empty literal + fn visit_empty(&mut self) -> R; } } /// Marks the root of a tree @@ -283,8 +297,7 @@ pub mod todo { //! - [ ] Store token spans in AST pub mod path { //! Path support - //! - [ ] Add namespace syntax (i.e. `::crate::foo::bar` | `foo::bar::Baz` | - //! `foo::bar::*`) + //! - [ ] 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. @@ -330,7 +343,7 @@ pub mod literal { /// Represents a literal value /// # Syntax - /// [`Literal`] := [`String`] | [`char`] | [`bool`] | [`Float`] | [`Int`] + /// [`Literal`] := [`String`] | [`char`] | [`bool`] | [`Float`] | [`u128`] #[derive(Clone, Debug)] pub enum Literal { /// Represents a literal string value @@ -353,7 +366,7 @@ pub mod literal { Float(Float), /// Represents a literal integer value /// # Syntax - /// [`Int`] := [`INTEGER`](crate::token::Type::Integer) + /// [`u128`] := [`INTEGER`](crate::token::Type::Integer) Int(u128), } @@ -378,28 +391,27 @@ pub mod expression { //! //! | # | 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) + //! | 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 + //! | 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) //! //! ## Syntax //! ```ignore //! Expr := control::Flow | math::Ignore //! Block := '{' Expr '}' - //! Group := '(' Expr ')' - //! Final := Identifier | Literal | Block | control::Branch + //! Group := '(' Expr? ')' + //! Primary := Identifier | Literal | Block | control::Branch //! ``` //! See [control] and [math] for their respective production rules. use super::*; @@ -407,28 +419,27 @@ pub mod expression { /// Contains an expression /// /// # Syntax - /// [`Expr`] := [`control::Flow`] | [`math::Ignore`] + /// [`Expr`] := [`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 + /// A [Primary] Expression is the expression with the highest precedence (i.e. the deepest /// derivation) /// # Syntax - /// [`Final`] := + /// [`Primary`] := /// [`IDENTIFIER`](Identifier) /// | [`Literal`](literal::Literal) /// | [`Block`] - /// | [`Branch`](control::Branch) + /// | [`Branch`](control::Flow) #[derive(Clone, Debug)] - pub enum Final { + pub enum Primary { Identifier(Identifier), Literal(literal::Literal), Block(Block), Group(Group), - Branch(control::Branch), + Branch(control::Flow), } /// Contains a Block Expression @@ -436,15 +447,15 @@ pub mod expression { /// [`Block`] := `'{'` [`Expr`] `'}'` #[derive(Clone, Debug)] pub struct Block { - pub expr: Box, + pub expr: Option>, } /// Contains a Parenthesized Expression /// # Syntax - /// [`Group`] := `'('` [`Expr`] `')'` + /// [`Group`] := `'('` [`Expr`]? `')'` #[derive(Clone, Debug)] pub struct Group { - pub expr: Box, + pub expr: Option>, } pub mod math { @@ -481,70 +492,82 @@ pub mod expression { //! Shift := Term (ShiftOp Term )* //! Term := Factor (TermOp Factor )* //! Factor := Unary (FactorOp Unary )* - //! Unary := (UnaryOp)* Final + //! Unary := (UnaryOp)* Primary //! ``` use super::*; - /// Ignores the result of the left sub-expression. + /// The template for [Binary] operations. + /// # Syntax + /// [`Binary`] := `First` (`Other`)* + #[derive(Clone, Debug)] + pub struct Binary { + pub first: Box, + pub other: Vec, + } + 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 + } + } + + /// Ignores the result of the leading 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)>); + pub type Ignore = Binary; - /// Assigns the result of the right sub-expression to the left sub-expression. + /// Assigns the result of the trailing sub-expression to the leading 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)>); + pub type Assign = Binary; - /// Compares the values of the right and left sub-expressions, + /// Compares the values of the trailing and leading 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)>); + pub type Compare = Binary; - /// Performs a boolean logic operation on the left and right sub-expressions. + /// Performs a boolean logic operation on the leading and trailing sub-expressions. /// # Syntax /// [`Logic`] := [`Bitwise`] ([`operator::Logic`] [`Bitwise`])* - #[derive(Clone, Debug)] - pub struct Logic(pub Bitwise, pub Vec<(operator::Logic, Bitwise)>); + pub type Logic = Binary; - /// Performs a bitwise opration on the left and right sub-expressions. + /// Performs a bitwise opration on the leading and trailing sub-expressions. /// # Syntax /// [`Bitwise`] := [`Shift`] ([`operator::Bitwise`] [`Shift`])* - #[derive(Clone, Debug)] - pub struct Bitwise(pub Shift, pub Vec<(operator::Bitwise, Shift)>); + pub type Bitwise = Binary; - /// Shifts the left sub-expression by the right sub-expression + /// Shifts the leading sub-expression by the trailing sub-expression /// # Syntax /// [`Shift`] := [`Term`] ([`operator::Shift`] [`Term`])* - #[derive(Clone, Debug)] - pub struct Shift(pub Term, pub Vec<(operator::Shift, Term)>); + pub type Shift = Binary; - /// Adds or subtracts the right sub-expression from the left sub-expression + /// Adds or subtracts the trailing sub-expression from the leading sub-expression /// # Syntax /// [`Term`] := [`Factor`] ([`operator::Term`] [`Factor`])* - #[derive(Clone, Debug)] - pub struct Term(pub Factor, pub Vec<(operator::Term, Factor)>); + pub type Term = Binary; - /// Multiplies, Divides, or finds the remainder of the right sub-expression - /// from the left sub-expression + /// Multiplies, Divides, or finds the remainder of the trailing sub-expression + /// from the leading sub-expression /// # Syntax /// [`Factor`] := [`Unary`] ([`operator::Factor`] [`Unary`])* - #[derive(Clone, Debug)] - pub struct Factor(pub Unary, pub Vec<(operator::Factor, Unary)>); + pub type Factor = Binary; - /// Performs a unary operation on the right sub-expression. + /// Performs a unary operation on the trailing sub-expression. /// # Syntax - /// [`Unary`] := ([`operator::Unary`])* [`Final`] + /// [`Unary`] := ([`operator::Unary`])* [`Primary`] #[derive(Clone, Debug)] - pub struct Unary(pub Vec, pub Final); + pub struct Unary(pub Vec, pub Primary); pub mod operator { - //! | # | Operators | Associativity + //! | # | [Operators](Operator) | Associativity //! |---|---------------------------------------|-------------- //! | 0 | ([Unary]) `*`, `&`, `-`, `!` | Left to Right //! | 1 | `*`, `/`, `%` | Left to Right @@ -560,29 +583,29 @@ pub mod expression { //! | 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")] + 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, + 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 { @@ -685,28 +708,23 @@ pub mod expression { //! [4]: Else //! [5]: Break //! [6]: Return - //! [7]: Flow::Continue + //! [7]: Continue use super::*; - /// Contains a [ConditionalBranch Expression](control). + /// Contains a [Control Flow Expression](control). /// - /// [While], [If], [For] - #[derive(Clone, Debug)] - pub enum Branch { - While(While), - If(If), - For(For), - } - - /// Contains an [Unconditional Branch Expression](control). + /// See the module-level documentation for more information. /// - /// [Continue](Flow::Continue), [Return], [Break] + /// [While], [If], [For], [Continue], [Return], or [Break] #[derive(Clone, Debug)] pub enum Flow { - /// Represents a [`continue` expression](Flow::Continue) - /// - /// # Syntax - /// [`Flow::Continue`] := `"continue"` + /// Represents a [`while` expression](While) + While(While), + /// Represents a [`if` expression](If) + If(If), + /// Represents a [`for` expression](For) + For(For), + /// Represents a [`continue` expression](Continue) Continue(Continue), /// Represents a [`return` expression](Return) Return(Return), diff --git a/libconlang/src/lib.rs b/libconlang/src/lib.rs index d21d2f8..8f5124c 100644 --- a/libconlang/src/lib.rs +++ b/libconlang/src/lib.rs @@ -1,6 +1,7 @@ //! Conlang is an expression-based programming language with similarities to Rust #![warn(clippy::all)] #![feature(decl_macro)] + pub mod token; pub mod ast; diff --git a/libconlang/src/parser.rs b/libconlang/src/parser.rs index 512a233..d162230 100644 --- a/libconlang/src/parser.rs +++ b/libconlang/src/parser.rs @@ -1,10 +1,12 @@ //! Parses [tokens](super::token) into an [AST](super::ast) +use std::vec; + use super::{ ast::preamble::*, lexer::Lexer, token::{Keyword, Token, Type}, }; -use error::{Error, *}; +use error::{Error, Reason::*, *}; mod error { use super::{Token, Type}; @@ -15,6 +17,7 @@ mod error { NotIdentifier, NotLiteral, NotString, + NotChar, NotBool, NotFloat, FloatExponentOverflow, @@ -38,14 +41,17 @@ mod error { macro error_impl($($fn:ident$(($($p:ident: $t:ty),*))?: $reason:expr),*$(,)?) {$( /// Creates an [Error] with this [Reason]: #[doc = concat!("[`", stringify!($reason), "`]")] - pub fn $fn($($($p : $t),*)?) -> Self { - Self { reason: $reason$(($($p)*))?, start: None } - } -)*} + pub fn $fn($($($p : $t),*)?) -> Self { + Self { reason: $reason$(($($p)*))?, start: None } + } + )*} impl Error { pub fn token(self, start: Token) -> Self { Self { start: Some(start), ..self } } + pub fn maybe_token(self, start: Option) -> Self { + Self { start, ..self } + } pub fn start(&self) -> Option { self.start } @@ -57,6 +63,7 @@ mod error { not_identifier: NotIdentifier, not_literal: NotLiteral, not_string: NotString, + not_char: NotChar, not_bool: NotBool, not_float: NotFloat, float_exponent_overflow: FloatExponentOverflow, @@ -113,14 +120,6 @@ impl<'t> Parser<'t> { pub fn peek(&self) -> Option<&Token> { self.tokens.get(self.curr) } - /// Look ahead `n` tokens - pub fn ahead(&self, n: usize) -> Option<&Token> { - self.tokens.get(self.curr.wrapping_add(n)) - } - /// Look behind `n` tokens - pub fn behind(&self, n: usize) -> Option<&Token> { - self.tokens.get(self.curr.wrapping_sub(n)) - } /// Records the current position on the panic stack pub fn mark(&mut self) -> &mut Self { self.panic_stack.push(self.curr); @@ -175,9 +174,9 @@ 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); + let out = mid(self)?; self.consume_type(rhs)?; - out + Ok(out) } } macro ptodo_err($self:expr $(, $t:expr)*) { @@ -195,7 +194,10 @@ fn check_eof(t: Option<&Token>) -> PResult<&Token> { /// # Terminals and Pseudo-Terminals impl<'t> Parser<'t> { pub fn identifier(&mut self) -> PResult { - let range = self.matches(Type::Identifier)?.range(); + let range = self + .matches(Type::Identifier) + .map_err(|e| Error::not_identifier().maybe_token(e.start()))? + .range(); Ok(Identifier(self.consume().text[range].into())) } pub fn literal(&mut self) -> PResult { @@ -204,7 +206,7 @@ impl<'t> Parser<'t> { let tok = check_eof(self.peek())?; match tok.ty() { Type::Float => self.float().map(Float), - Type::Integer => self.int().map(Int), + Type::Integer => self.int::<10>().map(Int), Type::String => self.string().map(String), Type::Character => self.char().map(Char), Type::Keyword(True | False) => self.bool().map(Bool), @@ -227,7 +229,11 @@ impl<'t> Parser<'t> { Ok(self.consume().text[range].into()) } pub fn char(&mut self) -> PResult { - ptodo!(self) + let token = *self.matches(Type::Character)?; + self.consume().text[&token] + .chars() + .next() + .ok_or(Error::not_char().token(token)) } pub fn bool(&mut self) -> PResult { use Keyword::{False, True}; @@ -245,26 +251,41 @@ impl<'t> Parser<'t> { impl<'t> Parser<'t> { pub fn expr(&mut self) -> PResult { use expression::Expr; - self.flow() - .map(Expr::Flow) - .or_else(|_| self.ignore().map(Expr::Ignore)) + self.ignore().map(Expr::Ignore) + } + pub fn if_not_expr(&mut self, matches: Type) -> PResult> { + if check_eof(self.peek())?.ty() == matches { + Ok(None) + } else { + Some(self.expr()).transpose() + } } pub fn block(&mut self) -> PResult { - self.delimited(Type::LCurly, Parser::expr, Type::RCurly) - .map(|e| expression::Block { expr: Box::new(e) }) + 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 { - self.delimited(Type::LParen, Parser::expr, Type::RParen) - .map(|e| expression::Group { expr: Box::new(e) }) + let t = check_eof(self.consume_type(Type::LParen)?.peek())?; + match t.ty() { + Type::RParen => { + self.consume(); + Ok(expression::Group { expr: None }) + } + _ => { + let out = self.expr().map(|expr| expression::Group {expr: Some(expr.into())}); + self.consume_type(Type::RParen)?; + out + } + } } - pub fn r#final(&mut self) -> PResult { - use expression::Final; + pub fn primary(&mut self) -> PResult { + use expression::Primary; self.identifier() - .map(Final::Identifier) - .or_else(|_| self.literal().map(Final::Literal)) - .or_else(|_| self.block().map(Final::Block)) - .or_else(|_| self.group().map(Final::Group)) - .or_else(|_| self.branch().map(Final::Branch)) + .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)) } } @@ -274,7 +295,7 @@ impl<'t> Parser<'t> { /// ``` /// # Examples /// ```rust,ignore -/// math_impl!{ +/// binary!{ /// function_name: ret::Value = parse_operands, parse_operators; /// } /// ``` @@ -282,18 +303,18 @@ impl<'t> Parser<'t> { /// ```rust,ignore /// pub fn function_name(&mut self) -> PResult { ... } /// ``` -macro math_impl ($($f: ident: $Ret:path = $a:ident, $b:ident);*$(;)?) {$( +macro binary ($($f:ident: $Ret:ty = $a:ident, $b:ident);*$(;)?) {$( pub fn $f (&mut self) -> PResult<$Ret> { let (first, mut others) = (self.$a()?, vec![]); while let Some(op) = self.$b() { others.push((op, self.$a()?)); } - Ok($Ret(first, others)) + Ok(<$Ret>::new(first, others)) } )*} /// # [Arithmetic and Logical Subexpressions](math) impl<'t> Parser<'t> { - math_impl! { + binary! { //name returns operands operators ignore: math::Ignore = assign, ignore_op; assign: math::Assign = compare, assign_op; @@ -309,7 +330,7 @@ impl<'t> Parser<'t> { while let Some(op) = self.unary_op() { ops.push(op) } - Ok(math::Unary(ops, self.r#final()?)) + Ok(math::Unary(ops, self.primary()?)) } } macro operator_impl($($(#[$m:meta])*$f:ident: $Ret:ty),*$(,)*) {$( @@ -335,19 +356,22 @@ impl<'t> Parser<'t> { } /// # [Control Flow](control) impl<'t> Parser<'t> { - pub fn branch(&mut self) -> PResult { - use control::Branch; - use Keyword::{For, If, While}; + pub fn flow(&mut self) -> PResult { + use control::Flow; + use Keyword::{Break, Continue, For, If, Return, While}; let token = check_eof(self.peek())?; match token.ty() { - Type::Keyword(While) => self.parse_while().map(Branch::While), - Type::Keyword(For) => self.parse_for().map(Branch::For), - Type::Keyword(If) => self.parse_if().map(Branch::If), + Type::Keyword(While) => self.parse_while().map(Flow::While), + Type::Keyword(For) => self.parse_for().map(Flow::For), + Type::Keyword(If) => self.parse_if().map(Flow::If), + 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)), } } pub fn parse_if(&mut self) -> PResult { - self.consume_type(Type::Keyword(Keyword::If))?; + self.keyword(Keyword::If)?; Ok(control::If { cond: self.expr()?.into(), body: self.block()?, @@ -355,7 +379,7 @@ impl<'t> Parser<'t> { }) } pub fn parse_while(&mut self) -> PResult { - self.consume_type(Type::Keyword(Keyword::While))?; + self.keyword(Keyword::While)?; Ok(control::While { cond: self.expr()?.into(), body: self.block()?, @@ -373,21 +397,10 @@ impl<'t> Parser<'t> { } pub fn parse_else(&mut self) -> PResult> { // it's fine for `else` to be missing entirely - match self.keyword(Keyword::Else) { - Ok(_) => Ok(Some(control::Else { block: self.block()? })), - Err(_) => Ok(None), - } - } - pub fn flow(&mut self) -> PResult { - use control::Flow; - use Keyword::{Break, Continue, Return}; - let token = check_eof(self.peek())?; - match token.ty() { - 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_control_flow().token(*token)), - } + self.keyword(Keyword::Else) + .ok() + .map(|p| Ok(control::Else { block: p.block()? })) + .transpose() } pub fn parse_break(&mut self) -> PResult { Ok(control::Break { expr: self.keyword(Keyword::Break)?.expr()?.into() }) diff --git a/libconlang/src/pretty_printer.rs b/libconlang/src/pretty_printer.rs index b390ce5..c603a95 100644 --- a/libconlang/src/pretty_printer.rs +++ b/libconlang/src/pretty_printer.rs @@ -43,9 +43,12 @@ impl Printer { self.pad() } fn put(&mut self, d: impl Display) -> IOResult<&mut Self> { - write!(self.writer, "{d} ")?; + write!(self.writer, "{d}")?; Ok(self) } + fn space(&mut self) -> IOResult<&mut Self> { + write!(self.writer, " ").map(|_| self) + } /// Increase the indentation level by 1 fn indent(&mut self) -> &mut Self { self.level += 1; @@ -56,58 +59,34 @@ impl Printer { self } } -macro visit_math($self:expr, $expr:expr) {{ - $expr.0.walk($self)?; - for (op, target) in &$expr.1 { - op.walk($self)?; - target.walk($self)?; - } - Ok(()) -}} +macro visit_operator($self:ident.$op:expr) { + $self.space()?.put($op)?.space().map(drop) +} impl Visitor> for Printer { - fn visit_ignore(&mut self, expr: &math::Ignore) -> IOResult<()> { - expr.0.walk(self)?; - for (op, target) in &expr.1 { + 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.newline()?)?; + target.walk(self)?; } Ok(()) } - fn visit_assign(&mut self, expr: &math::Assign) -> IOResult<()> { - visit_math!(self, expr) - } - fn visit_compare(&mut self, expr: &math::Compare) -> IOResult<()> { - visit_math!(self, expr) - } - fn visit_logic(&mut self, expr: &math::Logic) -> IOResult<()> { - visit_math!(self, expr) - } - fn visit_bitwise(&mut self, expr: &math::Bitwise) -> IOResult<()> { - visit_math!(self, expr) - } - fn visit_shift(&mut self, expr: &math::Shift) -> IOResult<()> { - visit_math!(self, expr) - } - fn visit_term(&mut self, expr: &math::Term) -> IOResult<()> { - visit_math!(self, expr) - } - fn visit_factor(&mut self, expr: &math::Factor) -> IOResult<()> { - visit_math!(self, expr) - } + 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(match op { - operator::Ignore::Ignore => "\x08;", - }) - .map(drop) + 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<()> { - self.put(match op { + visit_operator!(self.match op { operator::Compare::Less => "<", operator::Compare::LessEq => "<=", operator::Compare::Equal => "==", @@ -115,10 +94,9 @@ impl Visitor> for Printer { operator::Compare::GreaterEq => ">=", operator::Compare::Greater => ">", }) - .map(drop) } fn visit_assign_op(&mut self, op: &operator::Assign) -> IOResult<()> { - self.put(match op { + visit_operator!( self.match op { operator::Assign::Assign => "=", operator::Assign::AddAssign => "+=", operator::Assign::SubAssign => "-=", @@ -130,48 +108,43 @@ impl Visitor> for Printer { operator::Assign::ShlAssign => "<<=", operator::Assign::ShrAssign => ">>=", }) - .map(drop) } fn visit_logic_op(&mut self, op: &operator::Logic) -> IOResult<()> { - self.put(match op { + visit_operator!(self.match op { operator::Logic::LogAnd => "&&", operator::Logic::LogOr => "||", operator::Logic::LogXor => "^^", }) - .map(drop) } fn visit_bitwise_op(&mut self, op: &operator::Bitwise) -> IOResult<()> { - self.put(match op { + visit_operator!(self.match op { operator::Bitwise::BitAnd => "&", operator::Bitwise::BitOr => "|", operator::Bitwise::BitXor => "^", }) - .map(drop) } fn visit_shift_op(&mut self, op: &operator::Shift) -> IOResult<()> { - self.put(match op { + visit_operator!(self.match op { operator::Shift::Lsh => "<<", operator::Shift::Rsh => ">>", }) - .map(drop) } fn visit_term_op(&mut self, op: &operator::Term) -> IOResult<()> { - self.put(match op { + visit_operator!(self.match op { operator::Term::Add => "+", operator::Term::Sub => "-", }) - .map(drop) } fn visit_factor_op(&mut self, op: &operator::Factor) -> IOResult<()> { - self.put(match op { + visit_operator!(self.match op { operator::Factor::Mul => "*", operator::Factor::Div => "/", operator::Factor::Rem => "%", }) - .map(drop) } fn visit_unary_op(&mut self, op: &operator::Unary) -> IOResult<()> { self.put(match op { + operator::Unary::RefRef => "&&", operator::Unary::Deref => "*", operator::Unary::Ref => "&", operator::Unary::Neg => "-", @@ -184,42 +157,41 @@ impl Visitor> for Printer { } fn visit_if(&mut self, expr: &control::If) -> IOResult<()> { - expr.cond.walk(self.put("if")?)?; - expr.body.walk(self)?; + expr.cond.walk(self.put("if")?.space()?)?; + expr.body.walk(self.space()?)?; if let Some(e) = &expr.else_ { e.walk(self)? } Ok(()) } fn visit_while(&mut self, expr: &control::While) -> IOResult<()> { - expr.cond.walk(self.put("while")?)?; - expr.body.walk(self)?; + expr.cond.walk(self.put("while")?.space()?)?; + expr.body.walk(self.space()?)?; if let Some(e) = &expr.else_ { e.walk(self)? } Ok(()) } fn visit_for(&mut self, expr: &control::For) -> IOResult<()> { - expr.var.walk(self.put("for")?)?; - expr.iter.walk(self.put("in")?)?; - expr.body.walk(self)?; - self.visit_block(&expr.body)?; + 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)? } Ok(()) } fn visit_else(&mut self, expr: &control::Else) -> IOResult<()> { - expr.block.walk(self.put("else")?) + expr.block.walk(self.space()?.put("else")?.space()?) } fn visit_continue(&mut self, _expr: &control::Continue) -> IOResult<()> { self.put("continue").map(drop) } fn visit_break(&mut self, expr: &control::Break) -> IOResult<()> { - expr.expr.walk(self.put("break")?) + expr.expr.walk(self.put("break")?.space()?) } fn visit_return(&mut self, expr: &control::Return) -> IOResult<()> { - expr.expr.walk(self.put("return")?) + expr.expr.walk(self.put("return")?.space()?) } fn visit_identifier(&mut self, ident: &Identifier) -> IOResult<()> { @@ -229,7 +201,7 @@ impl Visitor> for Printer { self.put("\"")?.put(string)?.put("\"").map(drop) } fn visit_char_literal(&mut self, char: &char) -> IOResult<()> { - self.put(char).map(drop) + self.put("'")?.put(char)?.put("'").map(drop) } fn visit_bool_literal(&mut self, bool: &bool) -> IOResult<()> { self.put(bool).map(drop) @@ -243,15 +215,28 @@ impl Visitor> for Printer { fn visit_int_literal(&mut self, int: &u128) -> IOResult<()> { self.put(int).map(drop) } + fn visit_empty(&mut self) -> IOResult<()> { + self.put("").map(drop) + } fn visit_block(&mut self, expr: &expression::Block) -> IOResult<()> { - self.put('{')?.indent().newline()?.visit_expr(&expr.expr)?; - self.dedent().newline()?.put('}').map(drop) + self.put('{')?; + match &expr.expr { + Some(expr) => { + expr.walk(self.indent().newline()?)?; + self.dedent().newline()?; + } + None => ().walk(self.space()?)?, + } + self.put('}').map(drop) } fn visit_group(&mut self, expr: &expression::Group) -> IOResult<()> { - self.put('(')?; - self.visit_expr(&expr.expr)?; - self.put(')').map(drop) + self.put('(')?.space()?; + match &expr.expr { + Some(expr) => expr.walk(self), + None => ().walk(self), + }?; + self.space()?.put(')').map(drop) } } diff --git a/libconlang/src/token.rs b/libconlang/src/token.rs index c8d1ffb..e92808a 100644 --- a/libconlang/src/token.rs +++ b/libconlang/src/token.rs @@ -132,3 +132,10 @@ impl Token { self.head..self.tail } } + +impl std::ops::Index<&Token> for str { + type Output = str; + fn index(&self, index: &Token) -> &Self::Output { + &self[index.range()] + } +}