conlang: Variable binding and cleanup
ast: Separate concerns, and remove Walk interpreter: implement variable binding
This commit is contained in:
parent
35d214c9f6
commit
8fe89e6297
@ -16,13 +16,13 @@ Fn = "fn" Identifier Block ; (* TODO: params, return value*)
|
|||||||
(* # Expressions *)
|
(* # Expressions *)
|
||||||
(* expression *)
|
(* expression *)
|
||||||
Expr = Assign ;
|
Expr = Assign ;
|
||||||
Block = '{' Expr '}' ;
|
Block = '{' Stmt* Expr? '}' ;
|
||||||
Group = '(' Expr? ')' ;
|
Group = '(' Expr? ')' ;
|
||||||
Primary = Item | Identifier | Literal
|
Primary = Identifier | Literal
|
||||||
| Block | Group | Branch ;
|
| Block | Group | Branch ;
|
||||||
|
|
||||||
(* expression::math *)
|
(* expression::math *)
|
||||||
Assign = Compare (AssignOp Compare)* ;
|
Assign = Identifier (AssignOp Assign) | Compare ;
|
||||||
Compare = Range (CompareOp Range )* ;
|
Compare = Range (CompareOp Range )* ;
|
||||||
Range = Logic (RangeOp Logic )* ;
|
Range = Logic (RangeOp Logic )* ;
|
||||||
Logic = Bitwise (LogicOp Bitwise)* ;
|
Logic = Bitwise (LogicOp Bitwise)* ;
|
||||||
|
@ -18,8 +18,8 @@ pub mod preamble {
|
|||||||
math::{self, operator},
|
math::{self, operator},
|
||||||
},
|
},
|
||||||
literal,
|
literal,
|
||||||
statement::Stmt,
|
statement::*,
|
||||||
visitor::{Visitor, Walk},
|
visitor::Visitor,
|
||||||
Identifier, Program, Start,
|
Identifier, Program, Start,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -39,7 +39,7 @@ pub struct Program(pub Vec<statement::Stmt>);
|
|||||||
/// An Identifier stores the name of an item
|
/// An Identifier stores the name of an item
|
||||||
/// # Syntax
|
/// # Syntax
|
||||||
/// [`Identifier`]` := `[`IDENTIFIER`](crate::token::token_type::Type::Identifier)
|
/// [`Identifier`]` := `[`IDENTIFIER`](crate::token::token_type::Type::Identifier)
|
||||||
#[derive(Clone, Debug, Hash)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct Identifier(pub String);
|
pub struct Identifier(pub String);
|
||||||
|
|
||||||
pub mod todo {
|
pub mod todo {
|
||||||
@ -148,7 +148,12 @@ pub mod statement {
|
|||||||
//! [`Stmt`]` := `[`Let`](Stmt::Let)` | `[`Expr`](Stmt::Expr)
|
//! [`Stmt`]` := `[`Let`](Stmt::Let)` | `[`Expr`](Stmt::Expr)
|
||||||
//! [`Let`](Stmt::Let)` := "let"` [`Identifier`] (`:` `Type`)? (`=` [`Expr`])? `;`
|
//! [`Let`](Stmt::Let)` := "let"` [`Identifier`] (`:` `Type`)? (`=` [`Expr`])? `;`
|
||||||
//! [`Expr`](Stmt::Expr)` := `[`Expr`] `;`
|
//! [`Expr`](Stmt::Expr)` := `[`Expr`] `;`
|
||||||
use super::{expression::Expr, Identifier};
|
use crate::token::Token;
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
expression::{Block, Expr},
|
||||||
|
Identifier,
|
||||||
|
};
|
||||||
|
|
||||||
/// Contains a statement
|
/// Contains a statement
|
||||||
/// # Syntax
|
/// # Syntax
|
||||||
@ -158,17 +163,31 @@ pub mod statement {
|
|||||||
/// Contains a variable declaration
|
/// Contains a variable declaration
|
||||||
/// # Syntax
|
/// # Syntax
|
||||||
/// [`Let`](Stmt::Let) := `"let"` [`Identifier`] (`:` `Type`)? (`=` [`Expr`])? `;`
|
/// [`Let`](Stmt::Let) := `"let"` [`Identifier`] (`:` `Type`)? (`=` [`Expr`])? `;`
|
||||||
Let {
|
Let(Let),
|
||||||
name: Identifier,
|
|
||||||
mutable: bool,
|
|
||||||
ty: Option<Identifier>,
|
|
||||||
init: Option<Expr>,
|
|
||||||
},
|
|
||||||
/// Contains an expression statement
|
/// Contains an expression statement
|
||||||
/// # Syntax
|
/// # Syntax
|
||||||
/// [`Expr`](Stmt::Expr) := [`Expr`] `;`
|
/// [`Expr`](Stmt::Expr) := [`Expr`] `;`
|
||||||
Expr(Expr),
|
Expr(Expr),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Contains a variable declaration
|
||||||
|
/// # Syntax
|
||||||
|
/// [`Let`] := `let` [`Identifier`] (`:`) `Type`)? (`=` [`Expr`])? `;`
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Let {
|
||||||
|
pub name: Identifier,
|
||||||
|
pub mutable: bool,
|
||||||
|
pub ty: Option<Identifier>,
|
||||||
|
pub init: Option<Expr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Fn {
|
||||||
|
pub name: Identifier,
|
||||||
|
pub args: (), // TODO: capture arguments
|
||||||
|
pub rety: Token,
|
||||||
|
pub body: Block,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod expression {
|
pub mod expression {
|
||||||
@ -203,14 +222,14 @@ pub mod expression {
|
|||||||
//! `[`Group`]` | `[`control::Flow`]
|
//! `[`Group`]` | `[`control::Flow`]
|
||||||
//!
|
//!
|
||||||
//! See [control] and [math] for their respective production rules.
|
//! See [control] and [math] for their respective production rules.
|
||||||
use super::*;
|
use super::{statement::Stmt, *};
|
||||||
|
|
||||||
/// Contains an expression
|
/// Contains an expression
|
||||||
///
|
///
|
||||||
/// # Syntax
|
/// # Syntax
|
||||||
/// [`Expr`]` := `[`math::Operation`]
|
/// [`Expr`]` := `[`math::Operation`]
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Expr (pub math::Operation);
|
pub struct Expr(pub math::Operation);
|
||||||
|
|
||||||
/// A [Primary] 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)
|
/// derivation)
|
||||||
@ -234,7 +253,8 @@ pub mod expression {
|
|||||||
/// [`Block`] := `'{'` [`Expr`] `'}'`
|
/// [`Block`] := `'{'` [`Expr`] `'}'`
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Block {
|
pub struct Block {
|
||||||
pub expr: Box<Expr>,
|
pub statements: Vec<Stmt>,
|
||||||
|
pub expr: Option<Box<Expr>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Contains a Parenthesized Expression
|
/// Contains a Parenthesized Expression
|
||||||
@ -252,64 +272,83 @@ pub mod expression {
|
|||||||
//! ## Precedence Order
|
//! ## Precedence Order
|
||||||
//! Operator associativity is always left-to-right among members of the same group
|
//! Operator associativity is always left-to-right among members of the same group
|
||||||
//!
|
//!
|
||||||
//! | # | Name | Operators | Associativity
|
//! | # | Name | Operators | Associativity
|
||||||
//! |---|----------:|:----------------------------------------|---------------
|
//! |---|-----------:|:----------------------------------------|---------------
|
||||||
// | | TODO: Try | `?` |
|
// | | TODO: Try | `?` |
|
||||||
//! | 1 | Unary | [`*` `&` `-` `!`][3] | Right
|
//! | 1 | [Unary][1]| [`*` `&` `-` `!`][4] | Right
|
||||||
//! | 2 | Factor | [`*` `/` `%`][4] | Left to Right
|
//! | 2 | [Factor][2]| [`*` `/` `%`][5] | Left to Right
|
||||||
//! | 3 | Term | [`+` `-`][4] | Left to Right
|
//! | 3 | [Term][2]| [`+` `-`][5] | Left to Right
|
||||||
//! | 4 | Shift | [`<<` `>>`][4] | Left to Right
|
//! | 4 | [Shift][2]| [`<<` `>>`][5] | Left to Right
|
||||||
//! | 5 | Bitwise | [`&` <code>|</code>][4] | Left to Right
|
//! | 5 |[Bitwise][2]| [`&` <code>|</code>][4] | Left to Right
|
||||||
//! | 6 | Logic | [`&&` <code>||</code> `^^`][4]| Left to Right
|
//! | 6 | [Logic][2]| [`&&` <code>||</code> `^^`][5]| Left to Right
|
||||||
//! | 7 | Compare | [`<` `<=` `==` `!=` `>=` `>`][4] | Left to Right
|
//! | 7 |[Compare][2]| [`<` `<=` `==` `!=` `>=` `>`][5] | Left to Right
|
||||||
#![doc = concat!( //| |
|
#![doc = concat!( //| |
|
||||||
r" | 8 | Assign | [`*=`, `/=`, `%=`, `+=`, `-=`, ", //|
|
r" | 8 | [Assign][3]| [`*=`, `/=`, `%=`, `+=`, `-=`, ", //|
|
||||||
/* | | |*/ r"`&=`, <code>|=</code>, ", //|
|
/* | | |*/ r"`&=`, <code>|=</code>, ", //|
|
||||||
/* | | |*/ r"`^=`, `<<=`, `>>=`][4]", r"| Left to Right")]
|
/* | | |*/ r"`^=`, `<<=`, `>>=`][6]", r"| Left to Right")]
|
||||||
//!
|
//!
|
||||||
//! <!-- Note: '|' == '|' /-->
|
//! <!-- Note: '|' == '|' /-->
|
||||||
//!
|
//!
|
||||||
//! ## Syntax
|
//! ## Syntax
|
||||||
//! All precedence levels other than [Unary][1] fold into [Binary][2]
|
//! All precedence levels other than [Unary][1] fold into [Binary][2]
|
||||||
//!
|
//!
|
||||||
//! [`Assign`][2]` := `[`Compare`][2]` (`[`AssignOp`][4]` `[`Compare`][2]`)*` \
|
//! [`Assign`][3]` := `[`Compare`][2]` (`[`AssignOp`][6]` `[`Compare`][2]`)*` \
|
||||||
//! [`Compare`][2]` := `[`Logic`][2]` (`[`CompareOp`][4]` `[`Logic`][2]` )*` \
|
//! [`Compare`][2]` := `[`Logic`][2]` (`[`CompareOp`][5]` `[`Logic`][2]` )*` \
|
||||||
//! [`Logic`][2]` := `[`Bitwise`][2]` (`[`LogicOp`][4]` `[`Bitwise`][2]`)*` \
|
//! [`Logic`][2]` := `[`Bitwise`][2]` (`[`LogicOp`][5]` `[`Bitwise`][2]`)*` \
|
||||||
//! [`Bitwise`][2]` := `[`Shift`][2]` (`[`BitwiseOp`][4]` `[`Shift`][2]` )*` \
|
//! [`Bitwise`][2]` := `[`Shift`][2]` (`[`BitwiseOp`][5]` `[`Shift`][2]` )*` \
|
||||||
//! [`Shift`][2]` := `[`Term`][2]` (`[`ShiftOp`][4]` `[`Term`][2]` )*` \
|
//! [`Shift`][2]` := `[`Term`][2]` (`[`ShiftOp`][5]` `[`Term`][2]` )*` \
|
||||||
//! [`Term`][2]` := `[`Factor`][2]` (`[`TermOp`][4]` `[`Factor`][2]` )*` \
|
//! [`Term`][2]` := `[`Factor`][2]` (`[`TermOp`][5]` `[`Factor`][2]` )*` \
|
||||||
//! [`Factor`][2]` := `[`Unary`][1]` (`[`FactorOp`][4]` `[`Unary`][1]` )*` \
|
//! [`Factor`][2]` := `[`Unary`][1]` (`[`FactorOp`][5]` `[`Unary`][1]` )*` \
|
||||||
//! [`Unary`][1]` := (`[`UnaryOp`][3]`)* `[`Primary`]
|
//! [`Unary`][1]` := (`[`UnaryOp`][4]`)* `[`Primary`]
|
||||||
//!
|
//!
|
||||||
//! [1]: Operation::Unary
|
//! [1]: Operation::Unary
|
||||||
//! [2]: Operation::Binary
|
//! [2]: Operation::Binary
|
||||||
//! [3]: operator::Unary
|
//! [3]: Operation::Assign
|
||||||
//! [4]: operator::Binary
|
//! [4]: operator::Unary
|
||||||
|
//! [5]: operator::Binary
|
||||||
|
//! [6]: operator::Assign
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
/// An Operation is a tree of [operands](Primary) and [operators](operator).
|
/// An Operation is a tree of [operands](Primary) and [operators](operator).
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum Operation {
|
pub enum Operation {
|
||||||
|
/// [`Assign`](Operation::Assign) :=
|
||||||
|
/// [`Identifier`] [`operator::Assign`] [`Operation`] | [`Operation`]
|
||||||
|
Assign(Assign),
|
||||||
/// [`Binary`](Operation::Binary) :=
|
/// [`Binary`](Operation::Binary) :=
|
||||||
/// [`Operation`] ([`operator::Binary`] [`Operation`])*
|
/// [`Operation`] ([`operator::Binary`] [`Operation`])*
|
||||||
Binary {
|
Binary(Binary),
|
||||||
first: Box<Self>,
|
/// [`Unary`](Operation::Unary) := ([`operator::Unary`])*
|
||||||
other: Vec<(operator::Binary, Self)>,
|
/// [`Primary`](Operation::Primary)
|
||||||
},
|
Unary(Unary),
|
||||||
/// [`Unary`](Operation::Unary) := ([`operator::Unary`])* [`Primary`]
|
/// [`Primary`](Operation::Primary) := [`expression::Primary`]
|
||||||
Unary {
|
Primary(Primary),
|
||||||
operators: Vec<operator::Unary>,
|
|
||||||
operand: Primary,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
impl Operation {
|
|
||||||
pub fn binary(first: Self, other: Vec<(operator::Binary, Self)>) -> Self {
|
/// [`Assign`] := [`Identifier`] [`operator::Assign`] [`Operation`] | [`Operation`]
|
||||||
Self::Binary { first: Box::new(first), other }
|
#[derive(Clone, Debug)]
|
||||||
}
|
pub struct Assign {
|
||||||
|
pub target: Identifier,
|
||||||
|
pub operator: operator::Assign,
|
||||||
|
pub init: Box<Operation>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [`Binary`] := [`Operation`] ([`operator::Binary`] [`Operation`])*
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Binary {
|
||||||
|
pub first: Box<Operation>,
|
||||||
|
pub other: Vec<(operator::Binary, Operation)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [`Unary`] := ([`operator::Unary`])* [`Primary`](Operation::Primary)
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Unary {
|
||||||
|
pub operators: Vec<operator::Unary>,
|
||||||
|
pub operand: Box<Operation>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod operator {
|
pub mod operator {
|
||||||
//! # [Unary] and [Binary] operators
|
//! # [Unary], [Binary], and [Assign] operators
|
||||||
//!
|
//!
|
||||||
//! An Operator represents the action taken during an [operation](super::Operation)
|
//! An Operator represents the action taken during an [operation](super::Operation)
|
||||||
|
|
||||||
@ -351,13 +390,6 @@ pub mod expression {
|
|||||||
/// ## Comparison operators
|
/// ## Comparison operators
|
||||||
/// [`<`](Binary::Less), [`<=`](Binary::LessEq), [`==`](Binary::Equal),
|
/// [`<`](Binary::Less), [`<=`](Binary::LessEq), [`==`](Binary::Equal),
|
||||||
/// [`!=`](Binary::NotEq), [`>=`](Binary::GreaterEq), [`>`](Binary::Greater),
|
/// [`!=`](Binary::NotEq), [`>=`](Binary::GreaterEq), [`>`](Binary::Greater),
|
||||||
/// ## Assignment operators
|
|
||||||
/// [`=`](Binary::Assign), [`+=`](Binary::AddAssign), [`-=`](Binary::SubAssign),
|
|
||||||
/// [`*=`](Binary::MulAssign), [`/=`](Binary::DivAssign), [`%=`](Binary::RemAssign),
|
|
||||||
/// [`&=`](Binary::BitAndAssign), [`|=`](Binary::BitOrAssign),
|
|
||||||
/// [`^=`](Binary::BitXorAssign) [`<<=`](Binary::ShlAssign),
|
|
||||||
/// [`>>=`](Binary::ShrAssign)
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub enum Binary {
|
pub enum Binary {
|
||||||
/// `*`: Multiplication
|
/// `*`: Multiplication
|
||||||
@ -402,6 +434,16 @@ pub mod expression {
|
|||||||
GreaterEq,
|
GreaterEq,
|
||||||
/// `>`: Greater-than Comparison
|
/// `>`: Greater-than Comparison
|
||||||
Greater,
|
Greater,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Assignment operators
|
||||||
|
/// [`=`](Assign::Assign), [`+=`](Assign::AddAssign), [`-=`](Assign::SubAssign),
|
||||||
|
/// [`*=`](Assign::MulAssign), [`/=`](Assign::DivAssign), [`%=`](Assign::RemAssign),
|
||||||
|
/// [`&=`](Assign::BitAndAssign), [`|=`](Assign::BitOrAssign),
|
||||||
|
/// [`^=`](Assign::BitXorAssign) [`<<=`](Assign::ShlAssign),
|
||||||
|
/// [`>>=`](Assign::ShrAssign)
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub enum Assign {
|
||||||
/// `=`: Assignment
|
/// `=`: Assignment
|
||||||
Assign,
|
Assign,
|
||||||
/// `+=`: Additive In-place Assignment
|
/// `+=`: Additive In-place Assignment
|
||||||
@ -611,182 +653,11 @@ pub mod visitor {
|
|||||||
//! A [`Visitor`] visits every kind of node in the [Abstract Syntax Tree](super). Nodes,
|
//! A [`Visitor`] visits every kind of node in the [Abstract Syntax Tree](super). Nodes,
|
||||||
//! conversely are [`Walkers`](Walk) for Visitors which return a [`Result<(), E>`](Result)
|
//! conversely are [`Walkers`](Walk) for Visitors which return a [`Result<(), E>`](Result)
|
||||||
use super::{
|
use super::{
|
||||||
expression::{
|
expression::{control::*, math::*, Block, *},
|
||||||
control::*,
|
|
||||||
math::{operator::*, *},
|
|
||||||
Block, *,
|
|
||||||
},
|
|
||||||
literal::*,
|
literal::*,
|
||||||
statement::Stmt,
|
statement::*,
|
||||||
*,
|
*,
|
||||||
};
|
};
|
||||||
/// A [Walker](Walk) is a node in the AST, and calls [`Visitor::visit_*()`](Visitor) on all its
|
|
||||||
/// children
|
|
||||||
pub trait Walk<T: Visitor<R> + ?Sized, R> {
|
|
||||||
/// Traverses the children of this node in order, calling the appropriate [Visitor] function
|
|
||||||
fn walk(&self, visitor: &mut T) -> R;
|
|
||||||
}
|
|
||||||
mod walker {
|
|
||||||
use crate::ast::statement::Stmt;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
macro leaf($($T:ty),*$(,)?) {$(
|
|
||||||
impl<T: Visitor<Result<(), E>> + ?Sized, E> Walk<T, Result<(), E>> 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<T: Visitor<Result<(), E>> + ?Sized, E> Walk<T, Result<(), E>> 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<T: Visitor<Result<(), E>> + ?Sized, E> Walk<T, Result<(), E>> 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<T: Visitor<Result<(), E>> + ?Sized, E> Walk<T, Result<(), E>> 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<T: Visitor<Result<(), E>> + ?Sized, E> Walk<T, Result<(), E>> for Else {
|
|
||||||
fn walk(&self, visitor: &mut T) -> Result<(), E> {
|
|
||||||
visitor.visit_block(&self.block)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T: Visitor<Result<(), E>> + ?Sized, E> Walk<T, Result<(), E>> for Return {
|
|
||||||
fn walk(&self, visitor: &mut T) -> Result<(), E> {
|
|
||||||
visitor.visit_expr(&self.expr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T: Visitor<Result<(), E>> + ?Sized, E> Walk<T, Result<(), E>> for Break {
|
|
||||||
fn walk(&self, visitor: &mut T) -> Result<(), E> {
|
|
||||||
visitor.visit_expr(&self.expr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T: Visitor<Result<(), E>> + ?Sized, E> Walk<T, Result<(), E>> for Start {
|
|
||||||
fn walk(&self, visitor: &mut T) -> Result<(), E> {
|
|
||||||
visitor.visit_program(&self.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T: Visitor<Result<(), E>> + ?Sized, E> Walk<T, Result<(), E>> for Expr {
|
|
||||||
fn walk(&self, visitor: &mut T) -> Result<(), E> {
|
|
||||||
visitor.visit_operation(&self.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T: Visitor<Result<(), E>> + ?Sized, E> Walk<T, Result<(), E>> 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<T: Visitor<Result<(), E>> + ?Sized, E> Walk<T, Result<(), E>> for Block {
|
|
||||||
fn walk(&self, visitor: &mut T) -> Result<(), E> {
|
|
||||||
visitor.visit_expr(&self.expr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T: Visitor<Result<(), E>> + ?Sized, E> Walk<T, Result<(), E>> 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(())
|
|
||||||
}
|
|
||||||
Operation::Unary { operators, operand } => {
|
|
||||||
for op in operators {
|
|
||||||
visitor.visit_unary_op(op)?;
|
|
||||||
}
|
|
||||||
visitor.visit_primary(operand)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T: Visitor<Result<(), E>> + ?Sized, E> Walk<T, Result<(), E>> 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),
|
|
||||||
Primary::Block(b) => visitor.visit_block(b),
|
|
||||||
Primary::Group(g) => visitor.visit_group(g),
|
|
||||||
Primary::Branch(b) => visitor.visit_branch_expr(b),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T: Visitor<Result<(), E>> + ?Sized, E> Walk<T, Result<(), E>> 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),
|
|
||||||
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<T: Visitor<Result<(), E>> + ?Sized, E> Walk<T, Result<(), E>> 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),
|
|
||||||
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),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T: Visitor<Result<(), E>> + ?Sized, E> Walk<T, Result<(), E>> for Stmt {
|
|
||||||
fn walk(&self, visitor: &mut T) -> Result<(), E> {
|
|
||||||
match self {
|
|
||||||
Stmt::Let { name, mutable: _, ty, init } => {
|
|
||||||
visitor.visit_identifier(name)?;
|
|
||||||
if let Some(ty) = ty {
|
|
||||||
visitor.visit_identifier(ty)?;
|
|
||||||
}
|
|
||||||
if let Some(init) = init {
|
|
||||||
visitor.visit_expr(init)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
Stmt::Expr(e) => visitor.visit_expr(e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T: Visitor<Result<(), E>> + ?Sized, E> Walk<T, Result<(), E>> for Program {
|
|
||||||
fn walk(&self, visitor: &mut T) -> Result<(), E> {
|
|
||||||
for stmt in &self.0 {
|
|
||||||
visitor.visit_statement(stmt)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A Visitor traverses every kind of node in the [Abstract Syntax Tree](super)
|
/// A Visitor traverses every kind of node in the [Abstract Syntax Tree](super)
|
||||||
pub trait Visitor<R> {
|
pub trait Visitor<R> {
|
||||||
@ -798,7 +669,14 @@ pub mod visitor {
|
|||||||
fn visit_program(&mut self, prog: &Program) -> R;
|
fn visit_program(&mut self, prog: &Program) -> R;
|
||||||
|
|
||||||
/// Visit a [Statement](Stmt)
|
/// Visit a [Statement](Stmt)
|
||||||
fn visit_statement(&mut self, stmt: &Stmt) -> R;
|
fn visit_statement(&mut self, stmt: &Stmt) -> R {
|
||||||
|
match stmt {
|
||||||
|
Stmt::Let(stmt) => self.visit_let(stmt),
|
||||||
|
Stmt::Expr(expr) => self.visit_expr(expr),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Visit a [Let statement](Let)
|
||||||
|
fn visit_let(&mut self, stmt: &Let) -> R;
|
||||||
|
|
||||||
/// Visit an [Expression](Expr)
|
/// Visit an [Expression](Expr)
|
||||||
fn visit_expr(&mut self, expr: &Expr) -> R {
|
fn visit_expr(&mut self, expr: &Expr) -> R {
|
||||||
@ -806,9 +684,7 @@ pub mod visitor {
|
|||||||
}
|
}
|
||||||
// Block expression
|
// Block expression
|
||||||
/// Visit a [Block] expression
|
/// Visit a [Block] expression
|
||||||
fn visit_block(&mut self, expr: &Block) -> R {
|
fn visit_block(&mut self, block: &Block) -> R;
|
||||||
self.visit_expr(&expr.expr)
|
|
||||||
}
|
|
||||||
/// Visit a [Group] expression
|
/// Visit a [Group] expression
|
||||||
fn visit_group(&mut self, group: &Group) -> R {
|
fn visit_group(&mut self, group: &Group) -> R {
|
||||||
match group {
|
match group {
|
||||||
@ -819,9 +695,23 @@ pub mod visitor {
|
|||||||
|
|
||||||
// Math expression
|
// Math expression
|
||||||
/// Visit an [Operation]
|
/// Visit an [Operation]
|
||||||
fn visit_operation(&mut self, expr: &Operation) -> R;
|
fn visit_operation(&mut self, operation: &Operation) -> R {
|
||||||
/// Visit a [Binary](Operation::Binary) [operator](operator::Binary)
|
match operation {
|
||||||
|
Operation::Assign(assign) => self.visit_assign(assign),
|
||||||
|
Operation::Binary(binary) => self.visit_binary(binary),
|
||||||
|
Operation::Unary(unary) => self.visit_unary(unary),
|
||||||
|
Operation::Primary(primary) => self.visit_primary(primary),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Visit an [Assignment](Assign) operation
|
||||||
|
fn visit_assign(&mut self, assign: &Assign) -> R;
|
||||||
|
/// Visit a [Binary] Operation
|
||||||
|
fn visit_binary(&mut self, binary: &Binary) -> R;
|
||||||
|
/// Visit a [Unary] Operation
|
||||||
|
fn visit_unary(&mut self, unary: &Unary) -> R;
|
||||||
// Math operators
|
// Math operators
|
||||||
|
fn visit_assign_op(&mut self, op: &operator::Assign) -> R;
|
||||||
|
/// Visit a [Binary](Operation::Binary) [operator](operator::Binary)
|
||||||
fn visit_binary_op(&mut self, op: &operator::Binary) -> R;
|
fn visit_binary_op(&mut self, op: &operator::Binary) -> R;
|
||||||
/// Visit a [Unary](Operation::Unary) [operator](operator::Unary)
|
/// Visit a [Unary](Operation::Unary) [operator](operator::Unary)
|
||||||
fn visit_unary_op(&mut self, op: &operator::Unary) -> R;
|
fn visit_unary_op(&mut self, op: &operator::Unary) -> R;
|
||||||
@ -829,8 +719,8 @@ pub mod visitor {
|
|||||||
/// Visit a [Primary] expression
|
/// Visit a [Primary] expression
|
||||||
///
|
///
|
||||||
/// [`Primary`]` := `[`Identifier`]` | `[`Literal`]` | `[`Block`]` | `[`Flow`]
|
/// [`Primary`]` := `[`Identifier`]` | `[`Literal`]` | `[`Block`]` | `[`Flow`]
|
||||||
fn visit_primary(&mut self, expr: &Primary) -> R {
|
fn visit_primary(&mut self, primary: &Primary) -> R {
|
||||||
match expr {
|
match primary {
|
||||||
Primary::Identifier(v) => self.visit_identifier(v),
|
Primary::Identifier(v) => self.visit_identifier(v),
|
||||||
Primary::Literal(v) => self.visit_literal(v),
|
Primary::Literal(v) => self.visit_literal(v),
|
||||||
Primary::Block(v) => self.visit_block(v),
|
Primary::Block(v) => self.visit_block(v),
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
//! Interprets an AST as a program
|
//! Interprets an AST as a program
|
||||||
|
|
||||||
|
use self::scope::Environment;
|
||||||
use crate::ast::preamble::*;
|
use crate::ast::preamble::*;
|
||||||
use error::{Error, IResult, Reason};
|
use error::{Error, IResult, Reason};
|
||||||
use temp_type_impl::ConValue;
|
use temp_type_impl::ConValue;
|
||||||
@ -12,9 +13,10 @@ pub mod temp_type_impl {
|
|||||||
///
|
///
|
||||||
/// This is a hack to work around the fact that Conlang doesn't have a functioning type system
|
/// This is a hack to work around the fact that Conlang doesn't have a functioning type system
|
||||||
/// yet :(
|
/// yet :(
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub enum ConValue {
|
pub enum ConValue {
|
||||||
/// The empty/unit `()` type
|
/// The empty/unit `()` type
|
||||||
|
#[default]
|
||||||
Empty,
|
Empty,
|
||||||
/// An integer
|
/// An integer
|
||||||
Int(i128),
|
Int(i128),
|
||||||
@ -57,6 +59,18 @@ pub mod temp_type_impl {
|
|||||||
gt_eq: true, >=;
|
gt_eq: true, >=;
|
||||||
gt: false, >;
|
gt: false, >;
|
||||||
}
|
}
|
||||||
|
assign! {
|
||||||
|
add_assign: +;
|
||||||
|
bitand_assign: &;
|
||||||
|
bitor_assign: |;
|
||||||
|
bitxor_assign: ^;
|
||||||
|
div_assign: /;
|
||||||
|
mul_assign: *;
|
||||||
|
rem_assign: %;
|
||||||
|
shl_assign: <<;
|
||||||
|
shr_assign: >>;
|
||||||
|
sub_assign: -;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/// Templates comparison functions for [ConValue]
|
/// Templates comparison functions for [ConValue]
|
||||||
macro cmp ($($fn:ident: $empty:literal, $op:tt);*$(;)?) {$(
|
macro cmp ($($fn:ident: $empty:literal, $op:tt);*$(;)?) {$(
|
||||||
@ -73,6 +87,12 @@ pub mod temp_type_impl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
)*}
|
)*}
|
||||||
|
macro assign($( $fn: ident: $op: tt );*$(;)?) {$(
|
||||||
|
pub fn $fn(&mut self, other: Self) -> IResult<()> {
|
||||||
|
*self = (std::mem::take(self) $op other)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
)*}
|
||||||
/// Implements [From] for an enum with 1-tuple variants
|
/// Implements [From] for an enum with 1-tuple variants
|
||||||
macro from ($($T:ty => $v:expr),*$(,)?) {
|
macro from ($($T:ty => $v:expr),*$(,)?) {
|
||||||
$(impl From<$T> for ConValue {
|
$(impl From<$T> for ConValue {
|
||||||
@ -197,6 +217,7 @@ pub mod temp_type_impl {
|
|||||||
/// A work-in-progress tree walk interpreter for Conlang
|
/// A work-in-progress tree walk interpreter for Conlang
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct Interpreter {
|
pub struct Interpreter {
|
||||||
|
scope: Box<Environment>,
|
||||||
stack: Vec<ConValue>,
|
stack: Vec<ConValue>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,6 +251,13 @@ impl Interpreter {
|
|||||||
fn pop_two(&mut self) -> IResult<(ConValue, ConValue)> {
|
fn pop_two(&mut self) -> IResult<(ConValue, ConValue)> {
|
||||||
Ok((self.pop()?, self.pop()?))
|
Ok((self.pop()?, self.pop()?))
|
||||||
}
|
}
|
||||||
|
fn resolve(&mut self, value: &Identifier) -> IResult<ConValue> {
|
||||||
|
self.scope
|
||||||
|
.get(value)
|
||||||
|
.cloned()
|
||||||
|
.ok_or_else(|| Error::with_reason(Reason::NotDefined(value.to_owned())))?
|
||||||
|
.ok_or_else(|| Error::with_reason(Reason::NotInitialized(value.to_owned())))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Visitor<IResult<()>> for Interpreter {
|
impl Visitor<IResult<()>> for Interpreter {
|
||||||
@ -242,10 +270,7 @@ impl Visitor<IResult<()>> for Interpreter {
|
|||||||
|
|
||||||
fn visit_statement(&mut self, stmt: &Stmt) -> IResult<()> {
|
fn visit_statement(&mut self, stmt: &Stmt) -> IResult<()> {
|
||||||
match stmt {
|
match stmt {
|
||||||
Stmt::Let { name, mutable, ty, init } => todo!(
|
Stmt::Let(l) => self.visit_let(l),
|
||||||
"let{} {name:?}: {ty:?} = {init:?}",
|
|
||||||
if *mutable { " mut" } else { "" }
|
|
||||||
),
|
|
||||||
Stmt::Expr(e) => {
|
Stmt::Expr(e) => {
|
||||||
self.visit_expr(e)?;
|
self.visit_expr(e)?;
|
||||||
self.pop().map(drop)
|
self.pop().map(drop)
|
||||||
@ -253,57 +278,124 @@ impl Visitor<IResult<()>> for Interpreter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_operation(&mut self, expr: &math::Operation) -> IResult<()> {
|
fn visit_let(&mut self, stmt: &Let) -> IResult<()> {
|
||||||
use math::Operation;
|
let Let { name, init, .. } = stmt;
|
||||||
// TODO: the indentation depth here is driving me insane.
|
if let Some(init) = init {
|
||||||
// maybe refactor the ast to break binary and unary
|
self.visit_expr(init)?;
|
||||||
// operations into their own nodes, and use
|
let init = self.pop()?;
|
||||||
// Operation to unify them?
|
self.scope.insert(name, Some(init));
|
||||||
match expr {
|
} else {
|
||||||
Operation::Binary { first, other } => {
|
self.scope.insert(name, None);
|
||||||
self.visit_operation(first)?;
|
}
|
||||||
for (op, other) in other {
|
Ok(())
|
||||||
match op {
|
}
|
||||||
operator::Binary::LogAnd => {
|
|
||||||
if self.peek()?.truthy()? {
|
fn visit_block(&mut self, block: &expression::Block) -> IResult<()> {
|
||||||
self.pop()?;
|
for stmt in &block.statements {
|
||||||
self.visit_operation(other)?;
|
self.visit_statement(stmt)?;
|
||||||
}
|
}
|
||||||
}
|
if let Some(expr) = block.expr.as_ref() {
|
||||||
operator::Binary::LogOr => {
|
self.visit_expr(expr)
|
||||||
if !self.peek()?.truthy()? {
|
} else {
|
||||||
self.pop()?;
|
self.push(ConValue::Empty);
|
||||||
self.visit_operation(other)?;
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
operator::Binary::LogXor => {
|
|
||||||
let first = self.pop()?.truthy()?;
|
fn visit_assign(&mut self, assign: &math::Assign) -> IResult<()> {
|
||||||
self.visit_operation(other)?;
|
use operator::Assign;
|
||||||
let second = self.pop()?.truthy()?;
|
let math::Assign { target, operator, init } = assign;
|
||||||
self.push(first ^ second);
|
self.visit_operation(init)?;
|
||||||
}
|
let init = self.pop()?;
|
||||||
_ => {
|
let Some(resolved) = self.scope.get_mut(target) else {
|
||||||
self.visit_operation(other)?;
|
Err(Error::with_reason(Reason::NotDefined(target.to_owned())))?
|
||||||
self.visit_binary_op(op)?;
|
};
|
||||||
}
|
if let Assign::Assign = operator {
|
||||||
|
use std::mem::discriminant as variant;
|
||||||
|
// runtime typecheck
|
||||||
|
match resolved.as_mut() {
|
||||||
|
Some(value) if variant(value) == variant(&init) => {
|
||||||
|
*value = init;
|
||||||
|
}
|
||||||
|
None => *resolved = Some(init),
|
||||||
|
_ => Err(Error::with_reason(Reason::TypeError))?,
|
||||||
|
}
|
||||||
|
self.push(ConValue::Empty);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let Some(target) = resolved.as_mut() else {
|
||||||
|
Err(Error::with_reason(Reason::NotInitialized(
|
||||||
|
target.to_owned(),
|
||||||
|
)))?
|
||||||
|
};
|
||||||
|
match operator {
|
||||||
|
Assign::AddAssign => target.add_assign(init)?,
|
||||||
|
Assign::SubAssign => target.sub_assign(init)?,
|
||||||
|
Assign::MulAssign => target.mul_assign(init)?,
|
||||||
|
Assign::DivAssign => target.div_assign(init)?,
|
||||||
|
Assign::RemAssign => target.rem_assign(init)?,
|
||||||
|
Assign::BitAndAssign => target.bitand_assign(init)?,
|
||||||
|
Assign::BitOrAssign => target.bitor_assign(init)?,
|
||||||
|
Assign::BitXorAssign => target.bitxor_assign(init)?,
|
||||||
|
Assign::ShlAssign => target.shl_assign(init)?,
|
||||||
|
Assign::ShrAssign => target.shr_assign(init)?,
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
self.push(ConValue::Empty);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_binary(&mut self, bin: &math::Binary) -> IResult<()> {
|
||||||
|
use math::Binary;
|
||||||
|
let Binary { first, other } = bin;
|
||||||
|
|
||||||
|
self.visit_operation(first)?;
|
||||||
|
for (op, other) in other {
|
||||||
|
match op {
|
||||||
|
operator::Binary::LogAnd => {
|
||||||
|
if self.peek()?.truthy()? {
|
||||||
|
self.pop()?;
|
||||||
|
self.visit_operation(other)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
operator::Binary::LogOr => {
|
||||||
}
|
if !self.peek()?.truthy()? {
|
||||||
Operation::Unary { operators, operand } => {
|
self.pop()?;
|
||||||
self.visit_primary(operand)?;
|
self.visit_operation(other)?;
|
||||||
for op in operators.iter().rev() {
|
}
|
||||||
self.visit_unary_op(op)?;
|
}
|
||||||
|
operator::Binary::LogXor => {
|
||||||
|
let first = self.pop()?.truthy()?;
|
||||||
|
self.visit_operation(other)?;
|
||||||
|
let second = self.pop()?.truthy()?;
|
||||||
|
self.push(first ^ second);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
self.visit_operation(other)?;
|
||||||
|
self.visit_binary_op(op)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_unary(&mut self, unary: &math::Unary) -> IResult<()> {
|
||||||
|
let math::Unary { operand, operators } = unary;
|
||||||
|
self.visit_operation(operand)?;
|
||||||
|
for op in operators.iter().rev() {
|
||||||
|
self.visit_unary_op(op)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_assign_op(&mut self, _: &operator::Assign) -> IResult<()> {
|
||||||
|
unimplemented!("visit_assign_op is implemented in visit_operation")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_binary_op(&mut self, op: &operator::Binary) -> IResult<()> {
|
fn visit_binary_op(&mut self, op: &operator::Binary) -> IResult<()> {
|
||||||
use operator::Binary;
|
use operator::Binary;
|
||||||
let (second, first) = self.pop_two()?;
|
let (second, first) = self.pop_two()?;
|
||||||
self.push(match op {
|
let out = match op {
|
||||||
Binary::Mul => first * second,
|
Binary::Mul => first * second,
|
||||||
Binary::Div => first / second,
|
Binary::Div => first / second,
|
||||||
Binary::Rem => first % second,
|
Binary::Rem => first % second,
|
||||||
@ -325,18 +417,8 @@ impl Visitor<IResult<()>> for Interpreter {
|
|||||||
Binary::NotEq => first.neq(&second),
|
Binary::NotEq => first.neq(&second),
|
||||||
Binary::GreaterEq => first.gt_eq(&second),
|
Binary::GreaterEq => first.gt_eq(&second),
|
||||||
Binary::Greater => first.gt(&second),
|
Binary::Greater => first.gt(&second),
|
||||||
Binary::Assign => todo!("Assignment"),
|
}?;
|
||||||
Binary::AddAssign => todo!("Assignment"),
|
self.push(out);
|
||||||
Binary::SubAssign => todo!("Assignment"),
|
|
||||||
Binary::MulAssign => todo!("Assignment"),
|
|
||||||
Binary::DivAssign => todo!("Assignment"),
|
|
||||||
Binary::RemAssign => todo!("Assignment"),
|
|
||||||
Binary::BitAndAssign => todo!("Assignment"),
|
|
||||||
Binary::BitOrAssign => todo!("Assignment"),
|
|
||||||
Binary::BitXorAssign => todo!("Assignment"),
|
|
||||||
Binary::ShlAssign => todo!("Assignment"),
|
|
||||||
Binary::ShrAssign => todo!("Assignment"),
|
|
||||||
}?);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -364,12 +446,13 @@ impl Visitor<IResult<()>> for Interpreter {
|
|||||||
self.visit_block(&expr.body)?;
|
self.visit_block(&expr.body)?;
|
||||||
} else if let Some(block) = &expr.else_ {
|
} else if let Some(block) = &expr.else_ {
|
||||||
self.visit_else(block)?;
|
self.visit_else(block)?;
|
||||||
|
} else {
|
||||||
|
self.push(ConValue::Empty)
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_while(&mut self, expr: &control::While) -> IResult<()> {
|
fn visit_while(&mut self, expr: &control::While) -> IResult<()> {
|
||||||
let mut broke = false;
|
|
||||||
while {
|
while {
|
||||||
self.visit_expr(&expr.cond)?;
|
self.visit_expr(&expr.cond)?;
|
||||||
self.pop()?.truthy()?
|
self.pop()?.truthy()?
|
||||||
@ -384,26 +467,28 @@ impl Visitor<IResult<()>> for Interpreter {
|
|||||||
Reason::Continue => continue,
|
Reason::Continue => continue,
|
||||||
Reason::Break(value) => {
|
Reason::Break(value) => {
|
||||||
self.push(value);
|
self.push(value);
|
||||||
broke = true;
|
return Ok(());
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
r => Err(Error::with_reason(r))?,
|
r => Err(Error::with_reason(r))?,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let (Some(r#else), false) = (&expr.else_, broke) {
|
if let Some(r#else) = &expr.else_ {
|
||||||
self.visit_else(r#else)?;
|
self.visit_else(r#else)?;
|
||||||
|
} else {
|
||||||
|
self.push(ConValue::Empty);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_for(&mut self, expr: &control::For) -> IResult<()> {
|
fn visit_for(&mut self, expr: &control::For) -> IResult<()> {
|
||||||
|
self.scope.enter();
|
||||||
self.visit_expr(&expr.iter)?;
|
self.visit_expr(&expr.iter)?;
|
||||||
let mut broke = false;
|
|
||||||
let bounds = match self.pop()? {
|
let bounds = match self.pop()? {
|
||||||
ConValue::RangeExc(a, b) | ConValue::RangeInc(a, b) => (a, b),
|
ConValue::RangeExc(a, b) | ConValue::RangeInc(a, b) => (a, b),
|
||||||
_ => Err(Error::with_reason(Reason::NotIterable))?,
|
_ => Err(Error::with_reason(Reason::NotIterable))?,
|
||||||
};
|
};
|
||||||
for _ in bounds.0..=bounds.1 {
|
for loop_var in bounds.0..=bounds.1 {
|
||||||
|
self.scope.insert(&expr.var, Some(loop_var.into()));
|
||||||
let Err(out) = self.visit_block(&expr.body) else {
|
let Err(out) = self.visit_block(&expr.body) else {
|
||||||
self.pop()?;
|
self.pop()?;
|
||||||
continue;
|
continue;
|
||||||
@ -412,15 +497,17 @@ impl Visitor<IResult<()>> for Interpreter {
|
|||||||
Reason::Continue => continue,
|
Reason::Continue => continue,
|
||||||
Reason::Break(value) => {
|
Reason::Break(value) => {
|
||||||
self.push(value);
|
self.push(value);
|
||||||
broke = true;
|
return Ok(());
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
r => Err(Error::with_reason(r))?,
|
r => Err(Error::with_reason(r))?,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let (Some(r#else), false) = (&expr.else_, broke) {
|
if let Some(r#else) = &expr.else_ {
|
||||||
self.visit_else(r#else)?;
|
self.visit_else(r#else)?;
|
||||||
|
} else {
|
||||||
|
self.push(ConValue::Empty)
|
||||||
}
|
}
|
||||||
|
self.scope.exit()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -447,7 +534,9 @@ impl Visitor<IResult<()>> for Interpreter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn visit_identifier(&mut self, ident: &Identifier) -> IResult<()> {
|
fn visit_identifier(&mut self, ident: &Identifier) -> IResult<()> {
|
||||||
todo!("Identifier lookup and scoping rules: {ident:?}")
|
let value = self.resolve(ident)?;
|
||||||
|
self.push(value);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_string_literal(&mut self, string: &str) -> IResult<()> {
|
fn visit_string_literal(&mut self, string: &str) -> IResult<()> {
|
||||||
@ -480,8 +569,69 @@ impl Visitor<IResult<()>> for Interpreter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod scope {
|
||||||
|
//! Lexical and non-lexical scoping for variables
|
||||||
|
use super::{
|
||||||
|
error::{Error, IResult, Reason},
|
||||||
|
temp_type_impl::ConValue,
|
||||||
|
Identifier,
|
||||||
|
};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub enum Variable {
|
||||||
|
#[default]
|
||||||
|
Uninit,
|
||||||
|
Init(ConValue),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implements a nested lexical scope
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct Environment {
|
||||||
|
outer: Option<Box<Self>>,
|
||||||
|
vars: HashMap<Identifier, Option<ConValue>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Environment {
|
||||||
|
/// Enter a nested scope
|
||||||
|
pub fn enter(self: &mut Box<Self>) {
|
||||||
|
let outer = std::mem::take(self);
|
||||||
|
self.outer = Some(outer);
|
||||||
|
}
|
||||||
|
/// Exits the scope, destroying all local variables and
|
||||||
|
/// returning the outer scope, if there is one
|
||||||
|
pub fn exit(&mut self) -> IResult<()> {
|
||||||
|
if let Some(outer) = std::mem::take(&mut self.outer) {
|
||||||
|
*self = *outer;
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(Error::with_reason(Reason::ScopeExit))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Resolves a variable mutably
|
||||||
|
pub fn get_mut(&mut self, id: &Identifier) -> Option<&mut Option<ConValue>> {
|
||||||
|
match self.vars.get_mut(id) {
|
||||||
|
Some(var) => Some(var),
|
||||||
|
None => self.outer.as_mut().and_then(|o| o.get_mut(id)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Resolves a variable immutably
|
||||||
|
pub fn get(&self, id: &Identifier) -> Option<&Option<ConValue>> {
|
||||||
|
match self.vars.get(id) {
|
||||||
|
Some(var) => Some(var),
|
||||||
|
None => self.outer.as_ref().and_then(|o| o.get(id)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn insert(&mut self, id: &Identifier, value: Option<ConValue>) {
|
||||||
|
self.vars.insert(id.clone(), value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub mod error {
|
pub mod error {
|
||||||
//! The [Error] type represents any error thrown by the [Interpreter](super::Interpreter)
|
//! The [Error] type represents any error thrown by the [Interpreter](super::Interpreter)
|
||||||
|
use crate::ast::Identifier;
|
||||||
|
|
||||||
use super::temp_type_impl::ConValue;
|
use super::temp_type_impl::ConValue;
|
||||||
|
|
||||||
pub type IResult<T> = Result<T, Error>;
|
pub type IResult<T> = Result<T, Error>;
|
||||||
@ -524,11 +674,17 @@ pub mod error {
|
|||||||
Continue,
|
Continue,
|
||||||
/// Underflowed the stack
|
/// Underflowed the stack
|
||||||
StackUnderflow,
|
StackUnderflow,
|
||||||
|
/// Exited the last scope
|
||||||
|
ScopeExit,
|
||||||
/// Type incompatibility
|
/// Type incompatibility
|
||||||
// TODO: store the type information in this error
|
// TODO: store the type information in this error
|
||||||
TypeError,
|
TypeError,
|
||||||
/// In clause of For loop didn't yield a Range
|
/// In clause of For loop didn't yield a Range
|
||||||
NotIterable,
|
NotIterable,
|
||||||
|
/// A name was not defined in scope before being used
|
||||||
|
NotDefined(Identifier),
|
||||||
|
/// A name was defined but not initialized
|
||||||
|
NotInitialized(Identifier),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::error::Error for Error {}
|
impl std::error::Error for Error {}
|
||||||
@ -540,12 +696,19 @@ pub mod error {
|
|||||||
impl std::fmt::Display for Reason {
|
impl std::fmt::Display for Reason {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Reason::Return(value) => write!(f, "return {value:?}"),
|
Reason::Return(value) => write!(f, "return {value}"),
|
||||||
Reason::Break(value) => write!(f, "break {value:?}"),
|
Reason::Break(value) => write!(f, "break {value}"),
|
||||||
Reason::Continue => "continue".fmt(f),
|
Reason::Continue => "continue".fmt(f),
|
||||||
Reason::StackUnderflow => "Stack underflow".fmt(f),
|
Reason::StackUnderflow => "Stack underflow".fmt(f),
|
||||||
Reason::TypeError => "Type error".fmt(f),
|
Reason::ScopeExit => "Exited the last scope. This is a logic bug.".fmt(f),
|
||||||
|
Reason::TypeError => "Incompatible types".fmt(f),
|
||||||
Reason::NotIterable => "`in` clause of `for` loop did not yield an iterable".fmt(f),
|
Reason::NotIterable => "`in` clause of `for` loop did not yield an iterable".fmt(f),
|
||||||
|
Reason::NotDefined(value) => {
|
||||||
|
write!(f, "{} not bound. Did you mean `let {};`?", value.0, value.0)
|
||||||
|
}
|
||||||
|
Reason::NotInitialized(value) => {
|
||||||
|
write!(f, "{} bound, but not initialized", value.0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ pub mod error {
|
|||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
/// The reason for the [Error]
|
/// The reason for the [Error]
|
||||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||||
pub enum Reason {
|
pub enum Reason {
|
||||||
Expected(Type),
|
Expected(Type),
|
||||||
Unexpected(Type),
|
Unexpected(Type),
|
||||||
@ -101,7 +101,11 @@ pub mod error {
|
|||||||
self.start.as_ref()
|
self.start.as_ref()
|
||||||
}
|
}
|
||||||
/// Gets the [Reason] for this error
|
/// Gets the [Reason] for this error
|
||||||
pub fn reason(self, reason: Reason) -> Self {
|
pub fn reason(&self) -> Reason {
|
||||||
|
self.reason
|
||||||
|
}
|
||||||
|
/// Modifies the [Reason] of this error
|
||||||
|
pub fn with_reason(self, reason: Reason) -> Self {
|
||||||
Self { reason, ..self }
|
Self { reason, ..self }
|
||||||
}
|
}
|
||||||
error_impl! {
|
error_impl! {
|
||||||
@ -131,6 +135,7 @@ pub mod error {
|
|||||||
pub struct Parser {
|
pub struct Parser {
|
||||||
tokens: Vec<Token>,
|
tokens: Vec<Token>,
|
||||||
panic_stack: Vec<usize>,
|
panic_stack: Vec<usize>,
|
||||||
|
pub errors: Vec<Error>,
|
||||||
cursor: usize,
|
cursor: usize,
|
||||||
}
|
}
|
||||||
impl<'t> From<Lexer<'t>> for Parser {
|
impl<'t> From<Lexer<'t>> for Parser {
|
||||||
@ -153,7 +158,7 @@ impl Parser {
|
|||||||
///
|
///
|
||||||
/// [1]: Token
|
/// [1]: Token
|
||||||
pub fn new(tokens: Vec<Token>) -> Self {
|
pub fn new(tokens: Vec<Token>) -> Self {
|
||||||
Self { tokens, panic_stack: vec![], cursor: 0 }
|
Self { tokens, panic_stack: vec![], errors: vec![], cursor: 0 }
|
||||||
}
|
}
|
||||||
/// Parses the [start of an AST](Start)
|
/// Parses the [start of an AST](Start)
|
||||||
pub fn parse(&mut self) -> PResult<Start> {
|
pub fn parse(&mut self) -> PResult<Start> {
|
||||||
@ -184,6 +189,10 @@ impl Parser {
|
|||||||
self.consume_comments();
|
self.consume_comments();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
/// Panicking
|
||||||
|
#[allow(dead_code)]
|
||||||
|
impl Parser {
|
||||||
/// Records the current position on the panic stack
|
/// Records the current position on the panic stack
|
||||||
fn mark(&mut self) -> &mut Self {
|
fn mark(&mut self) -> &mut Self {
|
||||||
self.panic_stack.push(self.cursor);
|
self.panic_stack.push(self.cursor);
|
||||||
@ -204,11 +213,22 @@ impl Parser {
|
|||||||
fn advance_until(&mut self, t: Type) -> PResult<&mut Self> {
|
fn advance_until(&mut self, t: Type) -> PResult<&mut Self> {
|
||||||
while self.matches(t).is_err() {
|
while self.matches(t).is_err() {
|
||||||
self.check_eof()
|
self.check_eof()
|
||||||
.map_err(|e| e.reason(Expected(t)))?
|
.map_err(|e| e.with_reason(Expected(t)))?
|
||||||
.consume();
|
.consume();
|
||||||
}
|
}
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
/// Marks the current position, and unwinds the panic stack if `f` fails.
|
||||||
|
fn attempt<F, R>(&mut self, f: F) -> PResult<R>
|
||||||
|
where F: FnOnce(&mut Self) -> PResult<R> {
|
||||||
|
self.mark();
|
||||||
|
let out = f(self);
|
||||||
|
match out {
|
||||||
|
Ok(_) => self.unmark(),
|
||||||
|
Err(_) => self.unwind()?,
|
||||||
|
};
|
||||||
|
out
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/// Helpers
|
/// Helpers
|
||||||
impl Parser {
|
impl Parser {
|
||||||
@ -241,22 +261,6 @@ impl Parser {
|
|||||||
self.matches(t)?;
|
self.matches(t)?;
|
||||||
Ok(self.consume())
|
Ok(self.consume())
|
||||||
}
|
}
|
||||||
/// Parses anything wrapped in `lhs` and `rhs` delimiters.
|
|
||||||
fn delimited<F, R>(&mut self, lhs: Type, mid: F, rhs: Type) -> PResult<R>
|
|
||||||
where F: Fn(&mut Self) -> PResult<R> {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
fn todo_error(&mut self, l: u32, c: u32, s: &str) -> Error {
|
fn todo_error(&mut self, l: u32, c: u32, s: &str) -> Error {
|
||||||
eprintln!("TODO: {s}:{l}:{c}");
|
eprintln!("TODO: {s}:{l}:{c}");
|
||||||
@ -359,15 +363,7 @@ impl Parser {
|
|||||||
fn stmt(&mut self) -> PResult<Stmt> {
|
fn stmt(&mut self) -> PResult<Stmt> {
|
||||||
let token = self.peek()?;
|
let token = self.peek()?;
|
||||||
match token.ty() {
|
match token.ty() {
|
||||||
Type::Keyword(Keyword::Let) => Ok(Stmt::Let {
|
Type::Keyword(Keyword::Let) => self.let_stmt().map(Stmt::Let),
|
||||||
mutable: self.consume().keyword(Keyword::Mut).is_ok(),
|
|
||||||
name: self.identifier()?,
|
|
||||||
ty: self
|
|
||||||
.consume_type(Type::Colon)
|
|
||||||
.and_then(Self::identifier)
|
|
||||||
.ok(),
|
|
||||||
init: self.consume_type(Type::Eq).and_then(Self::expr).ok(),
|
|
||||||
}),
|
|
||||||
_ => {
|
_ => {
|
||||||
let out = Stmt::Expr(self.expr()?);
|
let out = Stmt::Expr(self.expr()?);
|
||||||
self.consume_type(Type::Semi)?;
|
self.consume_type(Type::Semi)?;
|
||||||
@ -375,6 +371,23 @@ impl Parser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Parses a [Let] statement
|
||||||
|
fn let_stmt(&mut self) -> PResult<Let> {
|
||||||
|
let out = Let {
|
||||||
|
mutable: self.consume().keyword(Keyword::Mut).is_ok(),
|
||||||
|
name: self.identifier()?,
|
||||||
|
ty: self
|
||||||
|
.consume_type(Type::Colon)
|
||||||
|
.and_then(Self::identifier)
|
||||||
|
.ok(),
|
||||||
|
init: self.consume_type(Type::Eq).and_then(Self::expr).ok(),
|
||||||
|
};
|
||||||
|
self.consume_type(Type::Semi)?;
|
||||||
|
Ok(out)
|
||||||
|
}
|
||||||
|
// /// Parses a [Function] statement
|
||||||
|
// fn function_stmt(&mut self) -> PResult<Function> {
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
/// Expressions
|
/// Expressions
|
||||||
impl Parser {
|
impl Parser {
|
||||||
@ -385,8 +398,23 @@ impl Parser {
|
|||||||
}
|
}
|
||||||
/// Parses a [block expression](expression::Block)
|
/// Parses a [block expression](expression::Block)
|
||||||
fn block(&mut self) -> PResult<expression::Block> {
|
fn block(&mut self) -> PResult<expression::Block> {
|
||||||
self.delimited(Type::LCurly, |p| p.expr(), Type::RCurly)
|
use expression::{Block, Expr};
|
||||||
.map(|e| expression::Block { expr: Box::new(e) })
|
let mut statements = vec![];
|
||||||
|
let mut expr: Option<Box<Expr>> = None;
|
||||||
|
self.consume_type(Type::LCurly)?;
|
||||||
|
// tHeRe Is No PlAcE iN yOuR gRaMmAr WhErE bOtH aN eXpReSsIoN aNd A sTaTeMeNt ArE eXpEcTeD
|
||||||
|
while self.consume_type(Type::RCurly).is_err() {
|
||||||
|
match self.expr() {
|
||||||
|
Ok(e) if self.consume_type(Type::Semi).is_ok() => statements.push(Stmt::Expr(e)),
|
||||||
|
Ok(e) => {
|
||||||
|
expr = Some(Box::new(e));
|
||||||
|
self.consume_type(Type::RCurly)?;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Err(_) => statements.push(self.stmt()?),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Block { statements, expr })
|
||||||
}
|
}
|
||||||
/// Parses a [group expression](expression::Group)
|
/// Parses a [group expression](expression::Group)
|
||||||
fn group(&mut self) -> PResult<expression::Group> {
|
fn group(&mut self) -> PResult<expression::Group> {
|
||||||
@ -440,20 +468,35 @@ impl Parser {
|
|||||||
macro binary ($($f:ident = $a:ident, $b:ident);*$(;)?) {$(
|
macro binary ($($f:ident = $a:ident, $b:ident);*$(;)?) {$(
|
||||||
#[doc = concat!("Parses a(n) [", stringify!($f), " operation](math::Operation::Binary) expression")]
|
#[doc = concat!("Parses a(n) [", stringify!($f), " operation](math::Operation::Binary) expression")]
|
||||||
fn $f (&mut self) -> PResult<math::Operation> {
|
fn $f (&mut self) -> PResult<math::Operation> {
|
||||||
let (first, mut others) = (self.$a()?, vec![]);
|
use math::{Operation, Binary};
|
||||||
|
let (first, mut other) = (self.$a()?, vec![]);
|
||||||
while let Ok(op) = self.$b() {
|
while let Ok(op) = self.$b() {
|
||||||
others.push((op, self.$a()?));
|
other.push((op, self.$a()?));
|
||||||
}
|
}
|
||||||
Ok(if others.is_empty() { first } else {
|
Ok(if other.is_empty() { first } else {
|
||||||
math::Operation::binary(first, others)
|
Operation::Binary(Binary { first: first.into(), other })
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
)*}
|
)*}
|
||||||
/// # [Arithmetic and Logical Subexpressions](math)
|
/// # [Arithmetic and Logical Subexpressions](math)
|
||||||
impl Parser {
|
impl Parser {
|
||||||
|
fn assign(&mut self) -> PResult<math::Operation> {
|
||||||
|
use math::{Assign, Operation};
|
||||||
|
let next = self.compare()?;
|
||||||
|
let Ok(operator) = self.assign_op() else {
|
||||||
|
return Ok(next);
|
||||||
|
};
|
||||||
|
let Operation::Primary(expression::Primary::Identifier(target)) = next else {
|
||||||
|
return Ok(next);
|
||||||
|
};
|
||||||
|
Ok(Operation::Assign(Assign {
|
||||||
|
target,
|
||||||
|
operator,
|
||||||
|
init: self.assign()?.into(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
binary! {
|
binary! {
|
||||||
// name operands operators
|
// name operands operators
|
||||||
assign = compare, assign_op;
|
|
||||||
compare = range, compare_op;
|
compare = range, compare_op;
|
||||||
range = logic, range_op;
|
range = logic, range_op;
|
||||||
logic = bitwise, logic_op;
|
logic = bitwise, logic_op;
|
||||||
@ -464,11 +507,22 @@ impl Parser {
|
|||||||
}
|
}
|
||||||
/// Parses a [unary operation](math::Operation::Unary) expression
|
/// Parses a [unary operation](math::Operation::Unary) expression
|
||||||
fn unary(&mut self) -> PResult<math::Operation> {
|
fn unary(&mut self) -> PResult<math::Operation> {
|
||||||
|
use math::{Operation, Unary};
|
||||||
let mut operators = vec![];
|
let mut operators = vec![];
|
||||||
while let Ok(op) = self.unary_op() {
|
while let Ok(op) = self.unary_op() {
|
||||||
operators.push(op)
|
operators.push(op)
|
||||||
}
|
}
|
||||||
Ok(math::Operation::Unary { operators, operand: self.primary()? })
|
if operators.is_empty() {
|
||||||
|
return self.primary_operation();
|
||||||
|
}
|
||||||
|
Ok(Operation::Unary(Unary {
|
||||||
|
operators,
|
||||||
|
operand: self.primary_operation()?.into(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
/// Parses a [primary operation](math::Operation::Primary) expression
|
||||||
|
fn primary_operation(&mut self) -> PResult<math::Operation> {
|
||||||
|
Ok(math::Operation::Primary(self.primary()?))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
macro operator_impl ($($(#[$m:meta])* $f:ident : {$($type:pat => $op:ident),*$(,)?})*) {
|
macro operator_impl ($($(#[$m:meta])* $f:ident : {$($type:pat => $op:ident),*$(,)?})*) {
|
||||||
@ -528,20 +582,27 @@ impl Parser {
|
|||||||
Type::GtEq => GreaterEq,
|
Type::GtEq => GreaterEq,
|
||||||
Type::Gt => Greater,
|
Type::Gt => Greater,
|
||||||
}
|
}
|
||||||
/// Parses an [assign operator](operator)
|
}
|
||||||
assign_op: {
|
/// Parses an [assign operator](operator::Assign)
|
||||||
Type::Eq => Assign,
|
fn assign_op(&mut self) -> PResult<operator::Assign> {
|
||||||
Type::PlusEq => AddAssign,
|
use operator::Assign;
|
||||||
Type::MinusEq => SubAssign,
|
let token = self.peek()?;
|
||||||
Type::StarEq => MulAssign,
|
let out = Ok(match token.ty() {
|
||||||
Type::SlashEq => DivAssign,
|
Type::Eq => Assign::Assign,
|
||||||
Type::RemEq => RemAssign,
|
Type::PlusEq => Assign::AddAssign,
|
||||||
Type::AmpEq => BitAndAssign,
|
Type::MinusEq => Assign::SubAssign,
|
||||||
Type::BarEq => BitOrAssign,
|
Type::StarEq => Assign::MulAssign,
|
||||||
Type::XorEq => BitXorAssign,
|
Type::SlashEq => Assign::DivAssign,
|
||||||
Type::LtLtEq => ShlAssign,
|
Type::RemEq => Assign::RemAssign,
|
||||||
Type::GtGtEq => ShrAssign,
|
Type::AmpEq => Assign::BitAndAssign,
|
||||||
}
|
Type::BarEq => Assign::BitOrAssign,
|
||||||
|
Type::XorEq => Assign::BitXorAssign,
|
||||||
|
Type::LtLtEq => Assign::ShlAssign,
|
||||||
|
Type::GtGtEq => Assign::ShrAssign,
|
||||||
|
_ => Err(Error::not_operator().token(token.clone()))?,
|
||||||
|
});
|
||||||
|
self.consume();
|
||||||
|
out
|
||||||
}
|
}
|
||||||
/// Parses a [unary operator](operator::Unary)
|
/// Parses a [unary operator](operator::Unary)
|
||||||
fn unary_op(&mut self) -> PResult<operator::Unary> {
|
fn unary_op(&mut self) -> PResult<operator::Unary> {
|
||||||
@ -578,7 +639,7 @@ impl Parser {
|
|||||||
Type::Keyword(Continue) => self.parse_continue().map(Flow::Continue),
|
Type::Keyword(Continue) => self.parse_continue().map(Flow::Continue),
|
||||||
e => Err(Error::unexpected(e).token(token.clone()))?,
|
e => Err(Error::unexpected(e).token(token.clone()))?,
|
||||||
}
|
}
|
||||||
.map_err(|e| e.reason(IncompleteBranch))
|
.map_err(|e| e.with_reason(IncompleteBranch))
|
||||||
}
|
}
|
||||||
/// Parses an [if](control::If) expression
|
/// Parses an [if](control::If) expression
|
||||||
fn parse_if(&mut self) -> PResult<control::If> {
|
fn parse_if(&mut self) -> PResult<control::If> {
|
||||||
|
@ -13,10 +13,10 @@ pub trait PrettyPrintable {
|
|||||||
}
|
}
|
||||||
impl PrettyPrintable for Start {
|
impl PrettyPrintable for Start {
|
||||||
fn print(&self) {
|
fn print(&self) {
|
||||||
let _ = self.walk(&mut Printer::default());
|
let _ = Printer::default().visit(self);
|
||||||
}
|
}
|
||||||
fn write(&self, into: impl Write) -> IOResult<()> {
|
fn write(&self, into: impl Write) -> IOResult<()> {
|
||||||
self.walk(&mut Printer::from(into))
|
Printer::from(into).visit(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,47 +70,75 @@ macro visit_operator($self:ident.$op:expr) {
|
|||||||
impl<W: Write> Visitor<IOResult<()>> for Printer<W> {
|
impl<W: Write> Visitor<IOResult<()>> for Printer<W> {
|
||||||
fn visit_program(&mut self, prog: &Program) -> IOResult<()> {
|
fn visit_program(&mut self, prog: &Program) -> IOResult<()> {
|
||||||
// delegate to the walker
|
// delegate to the walker
|
||||||
prog.walk(self)
|
for stmt in &prog.0 {
|
||||||
|
self.visit_statement(stmt)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
fn visit_statement(&mut self, stmt: &Stmt) -> IOResult<()> {
|
fn visit_statement(&mut self, stmt: &Stmt) -> IOResult<()> {
|
||||||
match stmt {
|
match stmt {
|
||||||
Stmt::Let { name, mutable, ty, init } => {
|
Stmt::Let(stmt) => self.visit_let(stmt)?,
|
||||||
self.put("let")?.space()?;
|
|
||||||
if *mutable {
|
|
||||||
self.put("mut")?.space()?;
|
|
||||||
}
|
|
||||||
self.visit_identifier(name)?;
|
|
||||||
if let Some(ty) = ty {
|
|
||||||
self.put(':')?.space()?.visit_identifier(ty)?;
|
|
||||||
}
|
|
||||||
if let Some(init) = init {
|
|
||||||
self.space()?.put('=')?.space()?.visit_expr(init)?;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Stmt::Expr(e) => {
|
Stmt::Expr(e) => {
|
||||||
self.visit_expr(e)?;
|
self.visit_expr(e)?;
|
||||||
},
|
self.put(';').map(drop)?
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self.put(';')?.newline().map(drop)
|
self.newline().map(drop)
|
||||||
}
|
}
|
||||||
fn visit_operation(&mut self, expr: &math::Operation) -> IOResult<()> {
|
fn visit_let(&mut self, stmt: &Let) -> IOResult<()> {
|
||||||
use math::Operation;
|
let Let { name, mutable, ty, init } = stmt;
|
||||||
match expr {
|
self.put("let")?.space()?;
|
||||||
Operation::Binary { first, other } => {
|
if *mutable {
|
||||||
self.put('(')?.visit_operation(first)?;
|
self.put("mut")?.space()?;
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
self.visit_identifier(name)?;
|
||||||
|
if let Some(ty) = ty {
|
||||||
|
self.put(':')?.space()?.visit_identifier(ty)?;
|
||||||
|
}
|
||||||
|
if let Some(init) = init {
|
||||||
|
self.space()?.put('=')?.space()?.visit_expr(init)?;
|
||||||
|
}
|
||||||
|
self.put(';').map(drop)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_assign(&mut self, assign: &math::Assign) -> IOResult<()> {
|
||||||
|
let math::Assign { target, operator, init } = assign;
|
||||||
|
self.visit_identifier(target)?;
|
||||||
|
self.visit_assign_op(operator)?;
|
||||||
|
self.visit_operation(init)
|
||||||
|
}
|
||||||
|
fn visit_binary(&mut self, binary: &math::Binary) -> IOResult<()> {
|
||||||
|
let math::Binary { first, other } = binary;
|
||||||
|
self.put('(')?.visit_operation(first)?;
|
||||||
|
for (op, other) in other {
|
||||||
|
self.visit_binary_op(op)?;
|
||||||
|
self.visit_operation(other)?;
|
||||||
|
}
|
||||||
|
self.put(')').map(drop)
|
||||||
|
}
|
||||||
|
fn visit_unary(&mut self, unary: &math::Unary) -> IOResult<()> {
|
||||||
|
let math::Unary { operators, operand } = unary;
|
||||||
|
for op in operators {
|
||||||
|
self.visit_unary_op(op)?;
|
||||||
|
}
|
||||||
|
self.visit_operation(operand)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_assign_op(&mut self, op: &operator::Assign) -> IOResult<()> {
|
||||||
|
use operator::Assign;
|
||||||
|
visit_operator!(self.match op {
|
||||||
|
Assign::Assign => "=",
|
||||||
|
Assign::AddAssign => "+=",
|
||||||
|
Assign::SubAssign => "-=",
|
||||||
|
Assign::MulAssign => "*=",
|
||||||
|
Assign::DivAssign => "/=",
|
||||||
|
Assign::RemAssign => "%=",
|
||||||
|
Assign::BitAndAssign => "&=",
|
||||||
|
Assign::BitOrAssign => "|=",
|
||||||
|
Assign::BitXorAssign => "^=",
|
||||||
|
Assign::ShlAssign => "<<=",
|
||||||
|
Assign::ShrAssign => ">>=",
|
||||||
|
})
|
||||||
}
|
}
|
||||||
fn visit_binary_op(&mut self, op: &operator::Binary) -> IOResult<()> {
|
fn visit_binary_op(&mut self, op: &operator::Binary) -> IOResult<()> {
|
||||||
use operator::Binary;
|
use operator::Binary;
|
||||||
@ -136,17 +164,6 @@ impl<W: Write> Visitor<IOResult<()>> for Printer<W> {
|
|||||||
Binary::NotEq => "!=",
|
Binary::NotEq => "!=",
|
||||||
Binary::GreaterEq => ">=",
|
Binary::GreaterEq => ">=",
|
||||||
Binary::Greater => ">",
|
Binary::Greater => ">",
|
||||||
Binary::Assign => "=",
|
|
||||||
Binary::AddAssign => "+=",
|
|
||||||
Binary::SubAssign => "-=",
|
|
||||||
Binary::MulAssign => "*=",
|
|
||||||
Binary::DivAssign => "/=",
|
|
||||||
Binary::RemAssign => "%=",
|
|
||||||
Binary::BitAndAssign => "&=",
|
|
||||||
Binary::BitOrAssign => "|=",
|
|
||||||
Binary::BitXorAssign => "^=",
|
|
||||||
Binary::ShlAssign => "<<=",
|
|
||||||
Binary::ShrAssign => ">>=",
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
fn visit_unary_op(&mut self, op: &operator::Unary) -> IOResult<()> {
|
fn visit_unary_op(&mut self, op: &operator::Unary) -> IOResult<()> {
|
||||||
@ -223,18 +240,28 @@ impl<W: Write> Visitor<IOResult<()>> for Printer<W> {
|
|||||||
self.put(int).map(drop)
|
self.put(int).map(drop)
|
||||||
}
|
}
|
||||||
fn visit_empty(&mut self) -> IOResult<()> {
|
fn visit_empty(&mut self) -> IOResult<()> {
|
||||||
self.put("").map(drop)
|
self.put("()").map(drop)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_block(&mut self, expr: &expression::Block) -> IOResult<()> {
|
fn visit_block(&mut self, block: &expression::Block) -> IOResult<()> {
|
||||||
self.put('{')?.indent().newline()?;
|
self.put('{')?.indent().newline()?;
|
||||||
expr.walk(self)?;
|
for stmt in &block.statements {
|
||||||
|
self.visit_statement(stmt)?;
|
||||||
|
}
|
||||||
|
for expr in &block.expr {
|
||||||
|
self.visit_expr(expr)?;
|
||||||
|
}
|
||||||
self.dedent().newline()?.put('}').map(drop)
|
self.dedent().newline()?.put('}').map(drop)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_group(&mut self, expr: &expression::Group) -> IOResult<()> {
|
fn visit_group(&mut self, expr: &expression::Group) -> IOResult<()> {
|
||||||
self.put('(')?.space()?;
|
match expr {
|
||||||
expr.walk(self)?;
|
expression::Group::Expr(expr) => {
|
||||||
self.space()?.put(')').map(drop)
|
self.put('(')?.space()?;
|
||||||
|
self.visit_expr(expr)?;
|
||||||
|
self.space()?.put(')').map(drop)
|
||||||
|
}
|
||||||
|
expression::Group::Empty => self.visit_empty(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user