1
0

refactor 2

This commit is contained in:
2024-11-13 20:24:40 +03:00
parent 83e6c0a40c
commit 8f7d085ae7
5 changed files with 252 additions and 240 deletions

View File

@@ -1,3 +1,4 @@
pub mod optimization;
pub mod typed; pub mod typed;
pub mod untyped; pub mod untyped;

227
src/ast/optimization.rs Normal file
View File

@@ -0,0 +1,227 @@
use std::fmt;
use crate::ast::typed::{Type, TypedExpr};
use crate::ast::{BinOp, OpCommutative};
use crate::symbols::SymbolsTable;
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, clap::ValueEnum)]
pub enum OLevel {
None = 0,
O1 = 1,
O2 = 2,
O3 = 3,
}
pub fn optimize_expr(expr: TypedExpr) -> TypedExpr {
match expr {
TypedExpr::BinOp { lhs, op, rhs } => optimize_binop(*lhs, op, *rhs),
TypedExpr::IntToFloat { value } => optimize_int_to_float(*value),
expr => expr,
}
}
pub fn bubble_binop_vars(expr: TypedExpr) -> TypedExpr {
let expr = reorder_commutative_expr(expr);
let expr = match expr {
TypedExpr::BinOp { lhs, op, rhs } => {
let lhs = *lhs;
let rhs = *rhs;
let lhs = bubble_binop_vars(lhs);
let rhs = bubble_binop_vars(rhs);
let (lhs, rhs, op) = match (lhs, rhs) {
(
TypedExpr::BinOp {
lhs: lhs1,
op: op1,
rhs: rhs1,
},
rhs,
) if rhs.is_const() && op.commutative(&op1) => (
TypedExpr::BinOp {
lhs: lhs1,
op,
rhs: Box::new(rhs),
},
*rhs1,
op1,
),
(lhs, rhs) if !lhs.is_const() && rhs.is_const() && op.swappable(&op) => (rhs, lhs, op),
(lhs, rhs) if op.precedence() < lhs.precedence() && op.swappable(&op) => {
(rhs, lhs, op)
},
(lhs, rhs) => (lhs, rhs, op),
};
let lhs = bubble_binop_vars(lhs);
let rhs = bubble_binop_vars(rhs);
TypedExpr::BinOp {
lhs: Box::new(lhs),
op,
rhs: Box::new(rhs),
}
},
TypedExpr::IntToFloat { value } => TypedExpr::IntToFloat {
value: Box::new(bubble_binop_vars(*value)),
},
expr => expr,
};
expr
}
pub fn propagate_type_conversions(
expr: TypedExpr,
root_ty: Type,
symbols: &SymbolsTable,
) -> TypedExpr {
match expr {
TypedExpr::Int { value } if root_ty == Type::Float => TypedExpr::IntToFloat {
value: Box::new(TypedExpr::Int { value }),
},
TypedExpr::Var { name }
if root_ty == Type::Float && symbols.resolve(&name).unwrap().ty == Some(Type::Int) =>
{
TypedExpr::IntToFloat {
value: Box::new(TypedExpr::Var { name }),
}
},
TypedExpr::IntToFloat { value } if root_ty == Type::Float => {
propagate_type_conversions(*value, Type::Float, symbols)
},
TypedExpr::BinOp { lhs, rhs, op } => TypedExpr::BinOp {
lhs: Box::new(propagate_type_conversions(*lhs, root_ty, symbols)),
rhs: Box::new(propagate_type_conversions(*rhs, root_ty, symbols)),
op,
},
expr => expr,
}
}
fn optimize_binop(lhs: TypedExpr, op: BinOp, rhs: TypedExpr) -> TypedExpr {
let lhs = optimize_expr(lhs);
let rhs = optimize_expr(rhs);
match (lhs, rhs) {
(TypedExpr::Int { value: lhs, .. }, TypedExpr::Int { value: rhs, .. }) => TypedExpr::Int {
value: op.evaluate(lhs, rhs),
},
(TypedExpr::Float { value: lhs, .. }, TypedExpr::Float { value: rhs, .. }) => {
TypedExpr::Float {
value: op.evaluate(lhs, rhs),
}
},
(lhs, rhs) => optimize_special_cases(lhs, op, rhs),
}
}
fn optimize_special_cases(lhs: TypedExpr, op: BinOp, rhs: TypedExpr) -> TypedExpr {
match (lhs, rhs) {
// Addition of zero
(lhs, TypedExpr::Int { value: 0, .. }) | (TypedExpr::Int { value: 0, .. }, lhs)
if matches!(op, BinOp::Add) =>
{
lhs
},
(lhs, TypedExpr::Float { value: 0.0, .. }) | (TypedExpr::Float { value: 0.0, .. }, lhs)
if matches!(op, BinOp::Add) =>
{
lhs
},
// Multiplication/Division by one
(lhs, TypedExpr::Int { value: 1, .. }) if matches!(op, BinOp::Mul | BinOp::Div) => lhs,
(lhs, TypedExpr::Float { value: 1.0, .. }) if matches!(op, BinOp::Mul | BinOp::Div) => lhs,
(TypedExpr::Int { value: 1, .. }, rhs) if matches!(op, BinOp::Mul) => rhs,
(TypedExpr::Float { value: 1.0, .. }, rhs) if matches!(op, BinOp::Mul) => rhs,
// Multiplication by zero
(_, TypedExpr::Int { value: 0, .. }) | (TypedExpr::Int { value: 0, .. }, _)
if matches!(op, BinOp::Mul) =>
{
TypedExpr::Int { value: 0 }
},
(_, TypedExpr::Float { value: 0.0, .. }) | (TypedExpr::Float { value: 0.0, .. }, _)
if matches!(op, BinOp::Mul) =>
{
TypedExpr::Float { value: 0.0 }
},
// Zero division
(TypedExpr::Int { value: 0, .. }, _) if matches!(op, BinOp::Div) => {
TypedExpr::Int { value: 0 }
},
(TypedExpr::Float { value: 0.0, .. }, _) if matches!(op, BinOp::Div) => {
TypedExpr::Float { value: 0.0 }
},
// Default
(lhs, rhs) => TypedExpr::BinOp {
lhs: Box::new(lhs),
op,
rhs: Box::new(rhs),
},
}
}
fn optimize_int_to_float(value: TypedExpr) -> TypedExpr {
let value = optimize_expr(value);
if let TypedExpr::Int { value } = value {
TypedExpr::Float {
value: value as f64,
}
} else {
TypedExpr::IntToFloat {
value: Box::new(value),
}
}
}
fn reorder_commutative_expr(expr: TypedExpr) -> TypedExpr {
match expr {
TypedExpr::BinOp { lhs, op, rhs, .. } => {
let commutative = rhs
.bin_op()
.map_or(OpCommutative::No, |op1| op.commutative_expr(&op1));
if let OpCommutative::Yes(op1) = commutative {
let (lhs, rhs) = match *rhs {
TypedExpr::BinOp {
lhs: lhs1,
rhs: rhs1,
..
} => (
TypedExpr::BinOp { lhs, op, rhs: lhs1 },
reorder_commutative_expr(*rhs1),
),
_ => unreachable!(),
};
TypedExpr::BinOp {
lhs: Box::new(lhs),
op: op1,
rhs: Box::new(rhs),
}
} else {
TypedExpr::BinOp { lhs, op, rhs }
}
},
expr => expr,
}
}
impl fmt::Display for OLevel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let str = match self {
OLevel::None => "none",
OLevel::O1 => "o1",
OLevel::O2 => "o2",
OLevel::O3 => "o3",
};
write!(f, "{}", str)
}
}

View File

@@ -1,33 +1,12 @@
use std::fmt; use std::fmt;
use std::fmt::Display;
use std::str::FromStr; use std::str::FromStr;
use super::{BinOp, OpCommutative, UntypedExpr}; use super::{optimization, BinOp, UntypedExpr};
use crate::ast::optimization::OLevel;
use crate::error; use crate::error;
use crate::error::{SemanticError, SemanticErrorKind}; use crate::error::{SemanticError, SemanticErrorKind};
use crate::symbols::{Symbol, SymbolsTable}; use crate::symbols::{Symbol, SymbolsTable};
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, clap::ValueEnum)]
pub enum OLevel {
None = 0,
O1 = 1,
O2 = 2,
O3 = 3,
}
impl Display for OLevel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let str = match self {
OLevel::None => "none",
OLevel::O1 => "o1",
OLevel::O2 => "o2",
OLevel::O3 => "o3",
};
write!(f, "{}", str)
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Type { pub enum Type {
Int, Int,
@@ -99,18 +78,18 @@ impl fmt::Display for Type {
impl TypedExpr { impl TypedExpr {
pub fn cast_to_float(self) -> TypedExpr { pub fn cast_to_float(self) -> TypedExpr {
TypedExpr::IntToFloat { Self::IntToFloat {
value: Box::new(self), value: Box::new(self),
} }
} }
pub fn ty(&self, symbols: &SymbolsTable) -> Type { pub fn ty(&self, symbols: &SymbolsTable) -> Type {
match self { match self {
TypedExpr::Int { .. } => Type::Int, Self::Int { .. } => Type::Int,
TypedExpr::Float { .. } => Type::Float, Self::Float { .. } => Type::Float,
TypedExpr::Var { name, .. } => symbols.type_of(name).expect("type not found"), Self::Var { name, .. } => symbols.type_of(name).expect("type not found"),
TypedExpr::BinOp { lhs, .. } => lhs.ty(symbols), Self::BinOp { lhs, .. } => lhs.ty(symbols),
TypedExpr::IntToFloat { .. } => Type::Float, Self::IntToFloat { .. } => Type::Float,
} }
} }
@@ -123,27 +102,27 @@ impl TypedExpr {
pub fn is_var(&self) -> bool { pub fn is_var(&self) -> bool {
match self { match self {
TypedExpr::Var { .. } => true, Self::Var { .. } => true,
TypedExpr::IntToFloat { value } => value.is_var(), Self::IntToFloat { value } => value.is_var(),
_ => false, _ => false,
} }
} }
pub fn is_const(&self) -> bool { pub fn is_const(&self) -> bool {
match self { match self {
TypedExpr::Int { .. } | TypedExpr::Float { .. } => true, Self::Int { .. } | Self::Float { .. } => true,
TypedExpr::IntToFloat { value } => value.is_const(), Self::IntToFloat { value } => value.is_const(),
TypedExpr::BinOp { lhs, rhs, .. } => lhs.is_const() && rhs.is_const(), Self::BinOp { lhs, rhs, .. } => lhs.is_const() && rhs.is_const(),
_ => false, _ => false,
} }
} }
pub fn precedence(&self) -> u8 { pub fn precedence(&self) -> u8 {
match self { match self {
TypedExpr::Int { .. } | TypedExpr::Float { .. } => 0, Self::Int { .. } | Self::Float { .. } => 0,
TypedExpr::Var { .. } => 0, Self::Var { .. } => 0,
TypedExpr::BinOp { op, .. } => op.precedence(), Self::BinOp { op, .. } => op.precedence(),
TypedExpr::IntToFloat { value } => value.precedence() + 1, Self::IntToFloat { value } => value.precedence(),
} }
} }
@@ -151,24 +130,24 @@ impl TypedExpr {
expr: UntypedExpr, expr: UntypedExpr,
optimization_level: OLevel, optimization_level: OLevel,
symbols: &mut SymbolsTable, symbols: &mut SymbolsTable,
) -> error::Result<TypedExpr> { ) -> error::Result<Self> {
let expr = Self::convert_to_typed_expr(expr, symbols)?; let expr = Self::convert_to_typed_expr(expr, symbols)?;
let expr = Self::coerce_types(expr, symbols)?; let expr = Self::coerce_types(expr, symbols)?;
let expr = if optimization_level > OLevel::None { let expr = if optimization_level > OLevel::None {
let expr = if optimization_level >= OLevel::O3 { let expr = if optimization_level >= OLevel::O3 {
let ty = expr.ty(symbols); let ty = expr.ty(symbols);
Self::propagate_type_conversions(expr, ty, symbols) optimization::propagate_type_conversions(expr, ty, symbols)
} else { } else {
expr expr
}; };
let expr = if optimization_level >= OLevel::O2 { let expr = if optimization_level >= OLevel::O2 {
Self::bubble_binop_vars(expr) optimization::bubble_binop_vars(expr)
} else { } else {
expr expr
}; };
let expr = Self::optimize_expr(expr); let expr = optimization::optimize_expr(expr);
expr expr
} else { } else {
@@ -252,202 +231,6 @@ impl TypedExpr {
Ok(()) Ok(())
} }
fn bubble_binop_vars(expr: Self) -> Self {
let expr = Self::reorder_commutative_expr(expr);
let expr = match expr {
Self::BinOp { lhs, op, rhs } => {
let lhs = *lhs;
let rhs = *rhs;
let lhs = Self::bubble_binop_vars(lhs);
let rhs = Self::bubble_binop_vars(rhs);
let (lhs, rhs, op) = match (lhs, rhs) {
(
Self::BinOp {
lhs: lhs1,
op: op1,
rhs: rhs1,
},
rhs,
) if rhs.is_const() && op.commutative(&op1) => (
Self::BinOp {
lhs: lhs1,
op,
rhs: Box::new(rhs),
},
*rhs1,
op1,
),
(lhs, rhs) if lhs.is_var() && rhs.is_const() && op.swappable(&op) => {
(rhs, lhs, op)
},
(lhs, rhs) if op.precedence() < lhs.precedence() && op.swappable(&op) => {
(rhs, lhs, op)
},
(lhs, rhs) => (lhs, rhs, op),
};
let lhs = Self::bubble_binop_vars(lhs);
let rhs = Self::bubble_binop_vars(rhs);
Self::BinOp {
lhs: Box::new(lhs),
op,
rhs: Box::new(rhs),
}
},
Self::IntToFloat { value } => Self::IntToFloat {
value: Box::new(Self::bubble_binop_vars(*value)),
},
expr => expr,
};
expr
}
fn reorder_commutative_expr(expr: Self) -> Self {
match expr {
Self::BinOp { lhs, op, rhs, .. } => {
let commutative = rhs
.bin_op()
.map_or(OpCommutative::No, |op1| op.commutative_expr(&op1));
if let OpCommutative::Yes(op1) = commutative {
let (lhs, rhs) = match *rhs {
Self::BinOp {
lhs: lhs1,
rhs: rhs1,
..
} => (
Self::BinOp { lhs, op, rhs: lhs1 },
Self::reorder_commutative_expr(*rhs1),
),
_ => unreachable!(),
};
Self::BinOp {
lhs: Box::new(lhs),
op: op1,
rhs: Box::new(rhs),
}
} else {
Self::BinOp { lhs, op, rhs }
}
},
expr => expr,
}
}
fn optimize_expr(expr: Self) -> Self {
match expr {
Self::BinOp { lhs, op, rhs } => Self::optimize_binop(*lhs, op, *rhs),
Self::IntToFloat { value } => Self::optimize_int_to_float(*value),
expr => expr,
}
}
fn optimize_binop(lhs: Self, op: BinOp, rhs: Self) -> Self {
let lhs = Self::optimize_expr(lhs);
let rhs = Self::optimize_expr(rhs);
match (lhs, rhs) {
(Self::Int { value: lhs, .. }, Self::Int { value: rhs, .. }) => Self::Int {
value: op.evaluate(lhs, rhs),
},
(Self::Float { value: lhs, .. }, Self::Float { value: rhs, .. }) => Self::Float {
value: op.evaluate(lhs, rhs),
},
(lhs, rhs) => Self::optimize_special_cases(lhs, op, rhs),
}
}
fn optimize_special_cases(lhs: Self, op: BinOp, rhs: Self) -> Self {
match (lhs, rhs) {
// Addition of zero
(lhs, Self::Int { value: 0, .. }) | (Self::Int { value: 0, .. }, lhs)
if matches!(op, BinOp::Add) =>
{
lhs
},
(lhs, Self::Float { value: 0.0, .. }) | (Self::Float { value: 0.0, .. }, lhs)
if matches!(op, BinOp::Add) =>
{
lhs
},
// Multiplication/Division by one
(lhs, Self::Int { value: 1, .. }) if matches!(op, BinOp::Mul | BinOp::Div) => lhs,
(lhs, Self::Float { value: 1.0, .. }) if matches!(op, BinOp::Mul | BinOp::Div) => lhs,
(Self::Int { value: 1, .. }, rhs) if matches!(op, BinOp::Mul) => rhs,
(Self::Float { value: 1.0, .. }, rhs) if matches!(op, BinOp::Mul) => rhs,
// Multiplication by zero
(_, Self::Int { value: 0, .. }) | (Self::Int { value: 0, .. }, _)
if matches!(op, BinOp::Mul) =>
{
Self::Int { value: 0 }
},
(_, Self::Float { value: 0.0, .. }) | (Self::Float { value: 0.0, .. }, _)
if matches!(op, BinOp::Mul) =>
{
Self::Float { value: 0.0 }
},
// Zero division
(Self::Int { value: 0, .. }, _) if matches!(op, BinOp::Div) => Self::Int { value: 0 },
(Self::Float { value: 0.0, .. }, _) if matches!(op, BinOp::Div) => {
Self::Float { value: 0.0 }
},
// Default
(lhs, rhs) => Self::BinOp {
lhs: Box::new(lhs),
op,
rhs: Box::new(rhs),
},
}
}
fn optimize_int_to_float(value: Self) -> Self {
let value = Self::optimize_expr(value);
if let Self::Int { value } = value {
Self::Float {
value: value as f64,
}
} else {
Self::IntToFloat {
value: Box::new(value),
}
}
}
fn propagate_type_conversions(expr: Self, root_ty: Type, symbols: &SymbolsTable) -> Self {
match expr {
Self::Int { value } if root_ty == Type::Float => Self::IntToFloat {
value: Box::new(Self::Int { value }),
},
Self::Var { name }
if root_ty == Type::Float
&& symbols.resolve(&name).unwrap().ty == Some(Type::Int) =>
{
Self::IntToFloat {
value: Box::new(Self::Var { name }),
}
},
Self::IntToFloat { value } if root_ty == Type::Float => {
Self::propagate_type_conversions(*value, Type::Float, symbols)
},
Self::BinOp { lhs, rhs, op } => Self::BinOp {
lhs: Box::new(Self::propagate_type_conversions(*lhs, root_ty, symbols)),
rhs: Box::new(Self::propagate_type_conversions(*rhs, root_ty, symbols)),
op,
},
expr => expr,
}
}
fn coerce_types(expr: Self, symbols: &mut SymbolsTable) -> error::Result<Self> { fn coerce_types(expr: Self, symbols: &mut SymbolsTable) -> error::Result<Self> {
let expr = match expr { let expr = match expr {
TypedExpr::Int { .. } | TypedExpr::Float { .. } | TypedExpr::IntToFloat { .. } => expr, TypedExpr::Int { .. } | TypedExpr::Float { .. } | TypedExpr::IntToFloat { .. } => expr,

View File

@@ -2,7 +2,7 @@ use std::ops::Deref;
use std::path::PathBuf; use std::path::PathBuf;
use clap::{CommandFactory, Parser, Subcommand}; use clap::{CommandFactory, Parser, Subcommand};
use developing_compilers::ast::typed::OLevel; use developing_compilers::ast::optimization::OLevel;
pub struct Args { pub struct Args {
inner: ArgsInner, inner: ArgsInner,

View File

@@ -5,7 +5,8 @@ use std::io::{self, Write};
use std::path::Path; use std::path::Path;
use cli::GenMode; use cli::GenMode;
use developing_compilers::ast::typed::{OLevel, Type, TypedExpr}; use developing_compilers::ast::optimization::OLevel;
use developing_compilers::ast::typed::{Type, TypedExpr};
use developing_compilers::interpreter::{Interpreter, Value}; use developing_compilers::interpreter::{Interpreter, Value};
use developing_compilers::representation::intermediate::IntermediateExpr; use developing_compilers::representation::intermediate::IntermediateExpr;
use developing_compilers::*; use developing_compilers::*;