Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use operator precedence parser for infix expressions and add prefix/postfix expressions #16

Merged
merged 13 commits into from Feb 21, 2023
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Expand Up @@ -15,6 +15,7 @@ pest = "2.5"
pest_derive = "2.5"
simple_logger = { version = "4.0.0", default-features = false, features = ["colored", "colors"]}
unescape = "0.1.0"
once_cell = "1.17"

[dev-dependencies]
test-utils = { path = "test-utils" }
15 changes: 15 additions & 0 deletions examples/expressions.why
@@ -0,0 +1,15 @@
declare print : (str) -> void
declare printi : (int) -> void

let a := 2 + 4 * 5
let b := 4 * 9 + 3

printi(a)
print(" ")

printi(b)
print(" ")

let c := ((54) - 9 * (23 + 5) - 3)

printi(c)
27 changes: 8 additions & 19 deletions src/ast/binary_expr.rs
Expand Up @@ -12,27 +12,16 @@ pub struct BinaryExpr<T> {
}

impl BinaryExpr<()> {
pub fn from_pair(pair: Pair<Rule>, file: &str) -> BinaryExpr<()> {
assert_eq!(pair.as_rule(), Rule::binaryExpr);
pub fn from_lhs_op_rhs(
lhs: Expression<()>,
op_pair: Pair<Rule>,
rhs: Expression<()>,
file: &str,
) -> BinaryExpr<()> {
let (line, col) = op_pair.line_col();

let (line, col) = pair.line_col();
let op = BinaryOp::from(op_pair.as_rule());

let mut inner = pair.clone().into_inner();

let lhs = Expression::from_pair(inner.next().unwrap(), file);

let op = inner.next().unwrap_or_else(|| {
panic!(
"Expected op in binary expression '{}' at {}:{}",
pair.as_str(),
pair.line_col().0,
pair.line_col().1
)
});

let op = op.as_str().parse::<BinaryOp>().expect("Invalid binary op");

let rhs = Expression::from_pair(inner.next().unwrap(), file);
BinaryExpr {
lhs: Box::new(lhs),
rhs: Box::new(rhs),
Expand Down
17 changes: 17 additions & 0 deletions src/ast/binary_op.rs
@@ -1,5 +1,7 @@
use std::{fmt::Display, str::FromStr};

use super::Rule;

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum BinaryOp {
GreaterThan,
Expand Down Expand Up @@ -44,3 +46,18 @@ impl Display for BinaryOp {
})
}
}

impl From<Rule> for BinaryOp {
fn from(rule: Rule) -> Self {
match rule {
Rule::greaterThan => BinaryOp::GreaterThan,
Rule::lessThan => BinaryOp::LessThan,
Rule::equal => BinaryOp::Equal,
Rule::plus => BinaryOp::Plus,
Rule::minus => BinaryOp::Minus,
Rule::times => BinaryOp::Times,
Rule::dividedBy => BinaryOp::DividedBy,
_ => unreachable!("Unexpected rule {:?}", rule),
}
}
}
16 changes: 6 additions & 10 deletions src/ast/fn_call.rs → src/ast/call.rs
@@ -1,33 +1,29 @@
use pest::iterators::Pair;

use super::{Expression, Ident, Position, Rule};
use super::{Expression, Position, Rule};

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct FnCall<T> {
pub ident: Ident<T>,
pub struct Call<T> {
pub params: Vec<Expression<T>>,
pub position: Position,
pub info: T,
}

impl FnCall<()> {
pub fn from_pair(pair: Pair<Rule>, file: &str) -> FnCall<()> {
assert_eq!(pair.as_rule(), Rule::fnCall);
impl Call<()> {
pub fn from_pair(pair: Pair<Rule>, file: &str) -> Call<()> {
assert_eq!(pair.as_rule(), Rule::call);

let (line, col) = pair.line_col();

let mut inner = pair.into_inner();

let ident = inner.next().unwrap();

let mut params = vec![];

for param in inner {
params.push(Expression::from_pair(param, file));
}

FnCall {
ident: Ident::from_pair(ident, file),
Call {
params,
position: (file.to_owned(), line, col),
info: (),
Expand Down
70 changes: 44 additions & 26 deletions src/ast/expression.rs
@@ -1,13 +1,20 @@
use log::error;
use pest::iterators::Pair;
use once_cell::sync::Lazy;
use pest::{
iterators::Pair,
pratt_parser::{Assoc, Op, PrattParser},
};

use super::{BinaryExpr, Block, Boolean, FnCall, FnDef, Ident, If, Integer, Position, Rule, Str};
use super::{
BinaryExpr, Block, Boolean, FnDef, Ident, If, Integer, Position, PostfixExpr, PrefixExpr, Rule,
Str,
};

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum Expression<T> {
If(If<T>),
Binary(BinaryExpr<T>),
FnCall(FnCall<T>),
Prefix(PrefixExpr<T>),
Postfix(PostfixExpr<T>),
Integer(Integer<T>),
Ident(Ident<T>),
Str(Str<T>),
Expand All @@ -16,28 +23,37 @@ pub enum Expression<T> {
Boolean(Boolean<T>),
}

static PRATT_PARSER: Lazy<PrattParser<Rule>> = Lazy::new(|| {
PrattParser::new()
.op(Op::infix(Rule::lessThan, Assoc::Left)
| Op::infix(Rule::greaterThan, Assoc::Left)
| Op::infix(Rule::equal, Assoc::Left))
.op(Op::infix(Rule::plus, Assoc::Left) | Op::infix(Rule::minus, Assoc::Left))
.op(Op::infix(Rule::times, Assoc::Left) | Op::infix(Rule::dividedBy, Assoc::Left))
.op(Op::prefix(Rule::unaryMinus) | Op::prefix(Rule::not))
.op(Op::postfix(Rule::call))
});

impl Expression<()> {
pub fn from_pair(pair: Pair<Rule>, file: &str) -> Expression<()> {
match pair.as_rule() {
Rule::integer => Expression::Integer(Integer::from_pair(pair, file)),
Rule::ident => Expression::Ident(Ident::from_pair(pair, file)),
Rule::fnCall => Expression::FnCall(FnCall::from_pair(pair, file)),
Rule::string => Expression::Str(Str::from_pair(pair, file)),
Rule::binaryExpr => Expression::Binary(BinaryExpr::from_pair(pair, file)),
Rule::fnDef => Expression::FnDef(FnDef::from_pair(pair, file)),
Rule::ifStmt => Expression::If(If::from_pair(pair, file)),
Rule::block => Expression::Block(Block::from_pair(pair, file)),
Rule::boolean => Expression::Boolean(Boolean::from_pair(pair, file)),
_ => {
error!(
"Unexpected expression '{}' at {}:{}",
pair.as_str(),
pair.line_col().0,
pair.line_col().1
);
std::process::exit(-1)
}
}
PRATT_PARSER
.map_primary(|primary| match primary.as_rule() {
Rule::expr => Expression::from_pair(primary, file),
Rule::integer => Expression::Integer(Integer::from_pair(primary, file)),
Rule::ident => Expression::Ident(Ident::from_pair(primary, file)),
Rule::string => Expression::Str(Str::from_pair(primary, file)),
Rule::fnDef => Expression::FnDef(FnDef::from_pair(primary, file)),
Rule::ifStmt => Expression::If(If::from_pair(primary, file)),
Rule::block => Expression::Block(Block::from_pair(primary, file)),
Rule::boolean => Expression::Boolean(Boolean::from_pair(primary, file)),
rule => unreachable!("Unexpected rule {:?} while parsing primary", rule),
})
.map_prefix(|op, rhs| Expression::Prefix(PrefixExpr::from_op_rhs(op, rhs, file)))
.map_postfix(|lhs, op| Expression::Postfix(PostfixExpr::from_lhs_op(lhs, op, file)))
.map_infix(|lhs, op, rhs| {
Expression::Binary(BinaryExpr::from_lhs_op_rhs(lhs, op, rhs, file))
})
.parse(pair.into_inner())
}
}

Expand All @@ -49,7 +65,8 @@ where
match self {
Expression::If(If { position, .. })
| Expression::Binary(BinaryExpr { position, .. })
| Expression::FnCall(FnCall { position, .. })
| Expression::Prefix(PrefixExpr { position, .. })
| Expression::Postfix(PostfixExpr { position, .. })
| Expression::Integer(Integer { position, .. })
| Expression::Ident(Ident { position, .. })
| Expression::Str(Str { position, .. })
Expand All @@ -63,7 +80,8 @@ where
match self {
Expression::If(If { info, .. })
| Expression::Binary(BinaryExpr { info, .. })
| Expression::FnCall(FnCall { info, .. })
| Expression::Prefix(PrefixExpr { info, .. })
| Expression::Postfix(PostfixExpr { info, .. })
| Expression::Integer(Integer { info, .. })
| Expression::Ident(Ident { info, .. })
| Expression::Str(Str { info, .. })
Expand Down
12 changes: 10 additions & 2 deletions src/ast/mod.rs
Expand Up @@ -3,10 +3,10 @@ mod binary_expr;
mod binary_op;
mod block;
mod boolean;
mod call;
mod declaration;
mod definition;
mod expression;
mod fn_call;
mod fn_def;
mod ident;
mod if_statement;
Expand All @@ -15,6 +15,10 @@ mod integer;
mod intrinsic;
mod param;
mod parser;
mod postfix_expr;
mod postfix_op;
mod prefix_expr;
mod prefix_op;
mod statement;
mod str;
mod type_annotation;
Expand All @@ -27,10 +31,10 @@ pub use self::binary_expr::*;
pub use self::binary_op::*;
pub use self::block::*;
pub use self::boolean::*;
pub use self::call::*;
pub use self::declaration::*;
pub use self::definition::*;
pub use self::expression::*;
pub use self::fn_call::*;
pub use self::fn_def::*;
pub use self::ident::*;
pub use self::if_statement::*;
Expand All @@ -39,6 +43,10 @@ pub use self::integer::*;
pub use self::intrinsic::*;
pub use self::param::*;
pub use self::parser::*;
pub use self::postfix_expr::*;
pub use self::postfix_op::*;
pub use self::prefix_expr::*;
pub use self::prefix_op::*;
pub use self::statement::*;
pub use self::str::*;
pub use self::type_annotation::*;
Expand Down
26 changes: 26 additions & 0 deletions src/ast/postfix_expr.rs
@@ -0,0 +1,26 @@
use pest::iterators::Pair;

use super::{Expression, Position, PostfixOp, Rule};

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct PostfixExpr<T> {
pub op: PostfixOp<T>,
pub lhs: Box<Expression<T>>,
pub position: Position,
pub info: T,
}

impl PostfixExpr<()> {
pub fn from_lhs_op(lhs: Expression<()>, op_pair: Pair<Rule>, file: &str) -> PostfixExpr<()> {
let (line, col) = op_pair.line_col();

let op = PostfixOp::from_pair(op_pair, file);

PostfixExpr {
op,
lhs: Box::new(lhs),
position: (file.to_owned(), line, col),
info: (),
}
}
}
17 changes: 17 additions & 0 deletions src/ast/postfix_op.rs
@@ -0,0 +1,17 @@
use pest::iterators::Pair;

use super::{Call, Rule};

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum PostfixOp<T> {
Call(Call<T>),
}

impl PostfixOp<()> {
pub fn from_pair(pair: Pair<Rule>, file: &str) -> PostfixOp<()> {
match pair.as_rule() {
Rule::call => PostfixOp::Call(Call::from_pair(pair, file)),
rule => unreachable!("Unexpected rule {:?} while parsing postfix op", rule),
}
}
}
26 changes: 26 additions & 0 deletions src/ast/prefix_expr.rs
@@ -0,0 +1,26 @@
use pest::iterators::Pair;

use super::{Expression, Position, PrefixOp, Rule};

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct PrefixExpr<T> {
pub op: PrefixOp,
pub rhs: Box<Expression<T>>,
pub position: Position,
pub info: T,
}

impl PrefixExpr<()> {
pub fn from_op_rhs(op_pair: Pair<Rule>, rhs: Expression<()>, file: &str) -> PrefixExpr<()> {
let (line, col) = op_pair.line_col();

let op = PrefixOp::from(op_pair.as_rule());

PrefixExpr {
op,
rhs: Box::new(rhs),
position: (file.to_owned(), line, col),
info: (),
}
}
}