value-moving optimization
This commit is contained in:
279
src/ast/mod.rs
279
src/ast/mod.rs
@@ -2,14 +2,10 @@ pub mod typed;
|
||||
pub mod untyped;
|
||||
|
||||
use core::fmt;
|
||||
use std::str::FromStr;
|
||||
use std::ops::{Add, Div, Mul, Sub};
|
||||
|
||||
use typed::{Type, TypedExpr};
|
||||
pub use untyped::UntypedExpr;
|
||||
|
||||
use crate::error::{self, SemanticError, SemanticErrorKind};
|
||||
use crate::symbols::SymbolsTable;
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct Span {
|
||||
pub start: usize,
|
||||
@@ -30,6 +26,12 @@ pub enum BinOp {
|
||||
Div,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum OpCommutative {
|
||||
Yes(BinOp),
|
||||
No,
|
||||
}
|
||||
|
||||
impl BinOp {
|
||||
pub fn precedence(&self) -> u8 {
|
||||
match self {
|
||||
@@ -39,6 +41,44 @@ impl BinOp {
|
||||
BinOp::Div => 2,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn commutative(&self, other: &Self) -> bool {
|
||||
matches!(self.commutative_expr(other), OpCommutative::Yes(_))
|
||||
}
|
||||
|
||||
pub fn swappable(&self, other: &Self) -> bool {
|
||||
if let OpCommutative::Yes(new_op) = self.commutative_expr(other) {
|
||||
other == self && new_op == *other
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn commutative_expr(&self, other: &Self) -> OpCommutative {
|
||||
match (self, other) {
|
||||
(BinOp::Add, BinOp::Add) => OpCommutative::Yes(BinOp::Add),
|
||||
(BinOp::Add, BinOp::Sub) => OpCommutative::Yes(BinOp::Sub),
|
||||
(BinOp::Sub, BinOp::Add) => OpCommutative::Yes(BinOp::Sub),
|
||||
(BinOp::Sub, BinOp::Sub) => OpCommutative::Yes(BinOp::Add),
|
||||
(BinOp::Mul, BinOp::Mul) => OpCommutative::Yes(BinOp::Mul),
|
||||
(BinOp::Mul, BinOp::Div) => OpCommutative::Yes(BinOp::Div),
|
||||
(BinOp::Div, BinOp::Mul) => OpCommutative::Yes(BinOp::Div),
|
||||
(BinOp::Div, BinOp::Div) => OpCommutative::Yes(BinOp::Mul),
|
||||
_ => OpCommutative::No,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn evaluate<T>(&self, lhs: T, rhs: T) -> T
|
||||
where
|
||||
T: Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>,
|
||||
{
|
||||
match self {
|
||||
BinOp::Add => lhs + rhs,
|
||||
BinOp::Sub => lhs - rhs,
|
||||
BinOp::Mul => lhs * rhs,
|
||||
BinOp::Div => lhs / rhs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&BinOp> for u8 {
|
||||
@@ -67,7 +107,7 @@ impl TryFrom<u8> for BinOp {
|
||||
}
|
||||
|
||||
impl fmt::Display for BinOp {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if f.alternate() {
|
||||
match self {
|
||||
BinOp::Add => f.write_str("add"),
|
||||
@@ -85,230 +125,3 @@ impl fmt::Display for BinOp {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_typed_expr(
|
||||
expr: UntypedExpr,
|
||||
optimize: bool,
|
||||
symbols: &mut SymbolsTable,
|
||||
) -> error::Result<TypedExpr> {
|
||||
let expr = convert_to_typed_expr(expr, symbols)?;
|
||||
let expr = coerce_types(expr, symbols)?;
|
||||
let expr = if optimize { optimize_expr(expr) } else { expr };
|
||||
|
||||
Ok(expr)
|
||||
}
|
||||
|
||||
fn optimize_expr(expr: TypedExpr) -> TypedExpr {
|
||||
let expr = match expr {
|
||||
TypedExpr::BinOp { lhs, rhs, op, span } => {
|
||||
let lhs = optimize_expr(*lhs);
|
||||
let rhs = optimize_expr(*rhs);
|
||||
|
||||
match (lhs, rhs) {
|
||||
(TypedExpr::Int { value: lhs, .. }, TypedExpr::Int { value: rhs, .. }) => {
|
||||
TypedExpr::Int {
|
||||
span,
|
||||
value: match op {
|
||||
BinOp::Add => lhs + rhs,
|
||||
BinOp::Sub => lhs - rhs,
|
||||
BinOp::Mul => lhs * rhs,
|
||||
BinOp::Div => lhs / rhs,
|
||||
},
|
||||
}
|
||||
},
|
||||
(TypedExpr::Float { value: lhs, .. }, TypedExpr::Float { value: rhs, .. }) => {
|
||||
TypedExpr::Float {
|
||||
span,
|
||||
value: match op {
|
||||
BinOp::Add => lhs + rhs,
|
||||
BinOp::Sub => lhs - rhs,
|
||||
BinOp::Mul => lhs * rhs,
|
||||
BinOp::Div => lhs / rhs,
|
||||
},
|
||||
}
|
||||
},
|
||||
(lhs, TypedExpr::Int { value: 0, .. })
|
||||
| (TypedExpr::Float { value: 0.0, .. }, lhs)
|
||||
if matches!(op, BinOp::Add | BinOp::Sub) =>
|
||||
{
|
||||
lhs
|
||||
},
|
||||
(lhs, TypedExpr::Int { value: 1, .. })
|
||||
| (lhs, TypedExpr::Float { value: 1.0, .. })
|
||||
if matches!(op, BinOp::Mul | BinOp::Div) =>
|
||||
{
|
||||
lhs
|
||||
},
|
||||
(TypedExpr::Int { value: 0, .. }, rhs)
|
||||
| (TypedExpr::Float { value: 0.0, .. }, rhs)
|
||||
if matches!(op, BinOp::Add) =>
|
||||
{
|
||||
rhs
|
||||
},
|
||||
(TypedExpr::Int { value: 1, .. }, rhs)
|
||||
| (TypedExpr::Float { value: 1.0, .. }, rhs)
|
||||
if matches!(op, BinOp::Mul) =>
|
||||
{
|
||||
rhs
|
||||
},
|
||||
(TypedExpr::Int { value: 0, .. }, _) | (_, TypedExpr::Int { value: 0, .. })
|
||||
if matches!(op, BinOp::Mul) =>
|
||||
{
|
||||
TypedExpr::Int { span, value: 0 }
|
||||
},
|
||||
(TypedExpr::Float { value: 0.0, .. }, _)
|
||||
| (_, TypedExpr::Float { value: 0.0, .. })
|
||||
if matches!(op, BinOp::Mul) =>
|
||||
{
|
||||
TypedExpr::Float { span, value: 0.0 }
|
||||
},
|
||||
(lhs, rhs) => TypedExpr::BinOp {
|
||||
span,
|
||||
lhs: Box::new(lhs),
|
||||
op,
|
||||
rhs: Box::new(rhs),
|
||||
},
|
||||
}
|
||||
},
|
||||
TypedExpr::IntToFloat { value } => {
|
||||
let value = optimize_expr(*value);
|
||||
if let TypedExpr::Int { value, span } = value {
|
||||
TypedExpr::Float {
|
||||
span,
|
||||
value: value as f64,
|
||||
}
|
||||
} else {
|
||||
TypedExpr::IntToFloat {
|
||||
value: Box::new(value),
|
||||
}
|
||||
}
|
||||
},
|
||||
expr => expr,
|
||||
};
|
||||
|
||||
expr
|
||||
}
|
||||
|
||||
fn convert_to_typed_expr(
|
||||
expr: UntypedExpr,
|
||||
symbols: &mut SymbolsTable,
|
||||
) -> error::Result<TypedExpr> {
|
||||
let expr = match expr {
|
||||
UntypedExpr::Int { span, value } => TypedExpr::Int { span, value },
|
||||
UntypedExpr::Float { span, value } => TypedExpr::Float { span, value },
|
||||
UntypedExpr::Var {
|
||||
span,
|
||||
name,
|
||||
typename,
|
||||
} => {
|
||||
let ty = typename
|
||||
.and_then(|t| symbols.resolve(&t))
|
||||
.map(|data| data.name.as_str())
|
||||
.map(Type::from_str)
|
||||
.transpose()
|
||||
.map_err(|e| SemanticError::new(span, e))?;
|
||||
{
|
||||
let symbol = symbols.resolve_mut(&name).unwrap();
|
||||
match (symbol.ty, ty) {
|
||||
(Some(ty), Some(ty2)) if ty != ty2 => {
|
||||
return Err(SemanticError::new(
|
||||
span,
|
||||
SemanticErrorKind::DuplicateSymbol(symbol.name.clone()),
|
||||
)
|
||||
.into());
|
||||
},
|
||||
(None, Some(ty)) => {
|
||||
symbol.ty = Some(ty);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
TypedExpr::Var { span, name }
|
||||
},
|
||||
UntypedExpr::BinOp { span, lhs, op, rhs } => {
|
||||
let rhs = *rhs;
|
||||
let lhs = *lhs;
|
||||
|
||||
match op {
|
||||
BinOp::Div
|
||||
if matches!(rhs, UntypedExpr::Int { .. } | UntypedExpr::Float { .. }) =>
|
||||
{
|
||||
match &rhs {
|
||||
UntypedExpr::Int { span, value } if *value == 0 => {
|
||||
return Err(SemanticError::new(
|
||||
*span,
|
||||
SemanticErrorKind::DivisionByZero,
|
||||
)
|
||||
.into());
|
||||
},
|
||||
UntypedExpr::Float { span, value } if *value == 0.0 => {
|
||||
return Err(SemanticError::new(
|
||||
*span,
|
||||
SemanticErrorKind::DivisionByZero,
|
||||
)
|
||||
.into());
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
||||
let lhs = convert_to_typed_expr(lhs, symbols)?;
|
||||
let rhs = convert_to_typed_expr(rhs, symbols)?;
|
||||
|
||||
TypedExpr::BinOp {
|
||||
span,
|
||||
lhs: Box::new(lhs),
|
||||
op,
|
||||
rhs: Box::new(rhs),
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
Ok(expr)
|
||||
}
|
||||
|
||||
fn coerce_types(expr: TypedExpr, symbols: &mut SymbolsTable) -> error::Result<TypedExpr> {
|
||||
let expr = match expr {
|
||||
TypedExpr::Int { .. } => expr,
|
||||
TypedExpr::Float { .. } => expr,
|
||||
TypedExpr::Var { name, .. } => {
|
||||
let symbol = symbols.resolve_mut(&name).unwrap();
|
||||
|
||||
if symbol.ty.is_none() {
|
||||
symbol.ty = Some(Type::Int);
|
||||
}
|
||||
|
||||
expr
|
||||
},
|
||||
TypedExpr::BinOp { lhs, rhs, op, span } => {
|
||||
let lhs = coerce_types(*lhs, symbols)?;
|
||||
let rhs = coerce_types(*rhs, symbols)?;
|
||||
|
||||
let (lhs, rhs) = match (lhs.ty(symbols), rhs.ty(symbols)) {
|
||||
(Type::Int, Type::Int) => (lhs, rhs),
|
||||
(Type::Float, Type::Float) => (lhs, rhs),
|
||||
(Type::Int, Type::Float) => {
|
||||
let lhs = TypedExpr::cast_to_float(lhs);
|
||||
(lhs, rhs)
|
||||
},
|
||||
(Type::Float, Type::Int) => {
|
||||
let rhs = TypedExpr::cast_to_float(rhs);
|
||||
(lhs, rhs)
|
||||
},
|
||||
};
|
||||
|
||||
TypedExpr::BinOp {
|
||||
span,
|
||||
lhs: Box::new(lhs),
|
||||
op,
|
||||
rhs: Box::new(rhs),
|
||||
}
|
||||
},
|
||||
TypedExpr::IntToFloat { .. } => expr,
|
||||
};
|
||||
|
||||
Ok(expr)
|
||||
}
|
||||
|
||||
464
src/ast/typed.rs
464
src/ast/typed.rs
@@ -1,16 +1,60 @@
|
||||
use std::fmt;
|
||||
use std::fmt::Display;
|
||||
use std::str::FromStr;
|
||||
|
||||
use super::{BinOp, Span};
|
||||
use crate::error::SemanticErrorKind;
|
||||
use super::{BinOp, OpCommutative, UntypedExpr};
|
||||
use crate::error;
|
||||
use crate::error::{SemanticError, SemanticErrorKind};
|
||||
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)]
|
||||
pub enum Type {
|
||||
Int,
|
||||
Float,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum TypedExpr {
|
||||
Int {
|
||||
value: i64,
|
||||
},
|
||||
Float {
|
||||
value: f64,
|
||||
},
|
||||
Var {
|
||||
name: Symbol,
|
||||
},
|
||||
BinOp {
|
||||
lhs: Box<TypedExpr>,
|
||||
op: BinOp,
|
||||
rhs: Box<TypedExpr>,
|
||||
},
|
||||
IntToFloat {
|
||||
value: Box<TypedExpr>,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<Type> for u8 {
|
||||
fn from(value: Type) -> Self {
|
||||
match value {
|
||||
@@ -45,7 +89,7 @@ impl FromStr for Type {
|
||||
}
|
||||
|
||||
impl fmt::Display for Type {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Type::Int => write!(f, "int"),
|
||||
Type::Float => write!(f, "float"),
|
||||
@@ -53,31 +97,6 @@ impl fmt::Display for Type {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum TypedExpr {
|
||||
Int {
|
||||
span: Span,
|
||||
value: i64,
|
||||
},
|
||||
Float {
|
||||
span: Span,
|
||||
value: f64,
|
||||
},
|
||||
Var {
|
||||
span: Span,
|
||||
name: Symbol,
|
||||
},
|
||||
BinOp {
|
||||
span: Span,
|
||||
lhs: Box<TypedExpr>,
|
||||
op: BinOp,
|
||||
rhs: Box<TypedExpr>,
|
||||
},
|
||||
IntToFloat {
|
||||
value: Box<TypedExpr>,
|
||||
},
|
||||
}
|
||||
|
||||
impl TypedExpr {
|
||||
pub fn cast_to_float(self) -> TypedExpr {
|
||||
TypedExpr::IntToFloat {
|
||||
@@ -85,23 +104,390 @@ impl TypedExpr {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn span(&self) -> Span {
|
||||
match self {
|
||||
TypedExpr::Float { span, .. } => *span,
|
||||
TypedExpr::Int { span, .. } => *span,
|
||||
TypedExpr::Var { span, .. } => *span,
|
||||
TypedExpr::BinOp { span, .. } => *span,
|
||||
TypedExpr::IntToFloat { value } => value.span(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ty(&self, symbols: &SymbolsTable) -> Type {
|
||||
match self {
|
||||
TypedExpr::Int { .. } => Type::Int,
|
||||
TypedExpr::Float { .. } => Type::Float,
|
||||
TypedExpr::Var { name, .. } => symbols.type_of(name).expect("type not found"),
|
||||
TypedExpr::BinOp { rhs, .. } => rhs.ty(symbols),
|
||||
TypedExpr::BinOp { lhs, .. } => lhs.ty(symbols),
|
||||
TypedExpr::IntToFloat { .. } => Type::Float,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bin_op(&self) -> Option<BinOp> {
|
||||
match self {
|
||||
Self::BinOp { op, .. } => Some(*op),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_var(&self) -> bool {
|
||||
match self {
|
||||
TypedExpr::Var { .. } => true,
|
||||
TypedExpr::IntToFloat { value } => value.is_var(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_const(&self) -> bool {
|
||||
match self {
|
||||
TypedExpr::Int { .. } | TypedExpr::Float { .. } => true,
|
||||
TypedExpr::IntToFloat { value } => value.is_const(),
|
||||
TypedExpr::BinOp { lhs, rhs, .. } => lhs.is_const() && rhs.is_const(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn precedence(&self) -> u8 {
|
||||
match self {
|
||||
TypedExpr::Int { .. } | TypedExpr::Float { .. } => 0,
|
||||
TypedExpr::Var { .. } => 0,
|
||||
TypedExpr::BinOp { op, .. } => op.precedence(),
|
||||
TypedExpr::IntToFloat { value } => value.precedence() + 1,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_untyped(
|
||||
expr: UntypedExpr,
|
||||
optimization_level: OLevel,
|
||||
symbols: &mut SymbolsTable,
|
||||
) -> error::Result<TypedExpr> {
|
||||
let expr = Self::convert_to_typed_expr(expr, symbols)?;
|
||||
let expr = Self::coerce_types(expr, symbols)?;
|
||||
let expr = if optimization_level > OLevel::None {
|
||||
let expr = if optimization_level >= OLevel::O3 {
|
||||
let ty = expr.ty(symbols);
|
||||
Self::propagate_type_conversions(expr, ty, symbols)
|
||||
} else {
|
||||
expr
|
||||
};
|
||||
|
||||
let expr = if optimization_level >= OLevel::O2 {
|
||||
Self::bubble_binop_vars(expr)
|
||||
} else {
|
||||
expr
|
||||
};
|
||||
|
||||
let expr = Self::optimize_expr(expr);
|
||||
|
||||
expr
|
||||
} else {
|
||||
expr
|
||||
};
|
||||
|
||||
Ok(expr)
|
||||
}
|
||||
|
||||
fn convert_to_typed_expr(expr: UntypedExpr, symbols: &mut SymbolsTable) -> error::Result<Self> {
|
||||
let expr = match expr {
|
||||
UntypedExpr::Int { value, .. } => Self::Int { value },
|
||||
UntypedExpr::Float { value, .. } => Self::Float { value },
|
||||
UntypedExpr::Var {
|
||||
name,
|
||||
typename,
|
||||
span,
|
||||
} => {
|
||||
let ty = typename
|
||||
.and_then(|t| symbols.resolve(&t))
|
||||
.map(|data| data.name.as_str())
|
||||
.map(Type::from_str)
|
||||
.transpose()
|
||||
.map_err(|e| SemanticError::new(span, e))?;
|
||||
{
|
||||
let symbol = symbols.resolve_mut(&name).unwrap();
|
||||
match (symbol.ty, ty) {
|
||||
(Some(ty), Some(ty2)) if ty != ty2 => {
|
||||
return Err(SemanticError::new(
|
||||
span,
|
||||
SemanticErrorKind::DuplicateSymbol(symbol.name.clone()),
|
||||
)
|
||||
.into());
|
||||
},
|
||||
(None, Some(ty)) => {
|
||||
symbol.ty = Some(ty);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
Self::Var { name }
|
||||
},
|
||||
UntypedExpr::BinOp { lhs, op, rhs, .. } => {
|
||||
Self::check_division_by_zero(op, &rhs)?;
|
||||
|
||||
let lhs = Self::convert_to_typed_expr(*lhs, symbols)?;
|
||||
let rhs = Self::convert_to_typed_expr(*rhs, symbols)?;
|
||||
|
||||
Self::BinOp {
|
||||
lhs: Box::new(lhs),
|
||||
op,
|
||||
rhs: Box::new(rhs),
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
Ok(expr)
|
||||
}
|
||||
|
||||
fn check_division_by_zero(op: BinOp, rhs: &UntypedExpr) -> error::Result<()> {
|
||||
match op {
|
||||
BinOp::Div if matches!(rhs, UntypedExpr::Int { .. } | UntypedExpr::Float { .. }) => {
|
||||
match &rhs {
|
||||
UntypedExpr::Int { span, value } if *value == 0 => {
|
||||
return Err(
|
||||
SemanticError::new(*span, SemanticErrorKind::DivisionByZero).into()
|
||||
);
|
||||
},
|
||||
UntypedExpr::Float { span, value } if *value == 0.0 => {
|
||||
return Err(
|
||||
SemanticError::new(*span, SemanticErrorKind::DivisionByZero).into()
|
||||
);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
||||
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> {
|
||||
let expr = match expr {
|
||||
TypedExpr::Int { .. } | TypedExpr::Float { .. } | TypedExpr::IntToFloat { .. } => expr,
|
||||
TypedExpr::Var { name, .. } => {
|
||||
let symbol = symbols.resolve_mut(&name).unwrap();
|
||||
|
||||
if symbol.ty.is_none() {
|
||||
symbol.ty = Some(Type::Int);
|
||||
}
|
||||
|
||||
expr
|
||||
},
|
||||
TypedExpr::BinOp { lhs, rhs, op } => {
|
||||
let lhs = Self::coerce_types(*lhs, symbols)?;
|
||||
let rhs = Self::coerce_types(*rhs, symbols)?;
|
||||
|
||||
Self::coerce_binop_types(lhs, op, rhs, symbols)?
|
||||
},
|
||||
};
|
||||
|
||||
Ok(expr)
|
||||
}
|
||||
|
||||
fn coerce_binop_types(
|
||||
lhs: Self,
|
||||
op: BinOp,
|
||||
rhs: Self,
|
||||
symbols: &SymbolsTable,
|
||||
) -> error::Result<Self> {
|
||||
let (lhs, rhs) = match (lhs.ty(symbols), rhs.ty(symbols)) {
|
||||
(Type::Int, Type::Int) => (lhs, rhs),
|
||||
(Type::Float, Type::Float) => (lhs, rhs),
|
||||
(Type::Int, Type::Float) => (lhs.cast_to_float(), rhs),
|
||||
(Type::Float, Type::Int) => (lhs, rhs.cast_to_float()),
|
||||
};
|
||||
|
||||
Ok(Self::BinOp {
|
||||
lhs: Box::new(lhs),
|
||||
op,
|
||||
rhs: Box::new(rhs),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
20
src/cli.rs
20
src/cli.rs
@@ -2,6 +2,7 @@ use std::ops::Deref;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use clap::{CommandFactory, Parser, Subcommand};
|
||||
use developing_compilers::ast::typed::OLevel;
|
||||
|
||||
pub struct Args {
|
||||
inner: ArgsInner,
|
||||
@@ -27,18 +28,20 @@ pub enum Command {
|
||||
Sem {
|
||||
input: PathBuf,
|
||||
output_tree: PathBuf,
|
||||
#[clap(short, long, default_value_t = OLevel::None)]
|
||||
optimization_level: OLevel,
|
||||
},
|
||||
Gen {
|
||||
mode: GenMode,
|
||||
#[clap(short, long, default_value_t = false)]
|
||||
optimize: bool,
|
||||
#[clap(short, long, default_value_t = OLevel::None)]
|
||||
optimization_level: OLevel,
|
||||
input: PathBuf,
|
||||
output: PathBuf,
|
||||
output_symbols: PathBuf,
|
||||
},
|
||||
Com {
|
||||
#[clap(short, long, default_value_t = false)]
|
||||
optimize: bool,
|
||||
#[clap(short, long, default_value_t = OLevel::None)]
|
||||
optimization_level: OLevel,
|
||||
input: PathBuf,
|
||||
output: PathBuf,
|
||||
},
|
||||
@@ -83,9 +86,14 @@ fn validate_inner(args: ArgsInner) -> Result<ArgsInner, clap::Error> {
|
||||
input,
|
||||
output_tokens,
|
||||
output_symbols,
|
||||
..
|
||||
} => validate_lex(input, output_tokens, output_symbols)?,
|
||||
Command::Syn { input, output_tree } => validate_syn(input, output_tree)?,
|
||||
Command::Sem { input, output_tree } => validate_sem(input, output_tree)?,
|
||||
Command::Syn {
|
||||
input, output_tree, ..
|
||||
} => validate_syn(input, output_tree)?,
|
||||
Command::Sem {
|
||||
input, output_tree, ..
|
||||
} => validate_sem(input, output_tree)?,
|
||||
Command::Gen {
|
||||
input,
|
||||
output,
|
||||
|
||||
37
src/main.rs
37
src/main.rs
@@ -5,7 +5,7 @@ use std::io::{self, Write};
|
||||
use std::path::Path;
|
||||
|
||||
use cli::GenMode;
|
||||
use developing_compilers::ast::typed::Type;
|
||||
use developing_compilers::ast::typed::{OLevel, Type, TypedExpr};
|
||||
use developing_compilers::interpreter::{Interpreter, Value};
|
||||
use developing_compilers::representation::intermediate::IntermediateExpr;
|
||||
use developing_compilers::*;
|
||||
@@ -24,19 +24,23 @@ fn main() -> anyhow::Result<()> {
|
||||
output_symbols,
|
||||
} => lex_command(input, output_tokens, output_symbols),
|
||||
cli::Command::Syn { input, output_tree } => syn_command(input, output_tree),
|
||||
cli::Command::Sem { input, output_tree } => sem_command(input, output_tree),
|
||||
cli::Command::Sem {
|
||||
optimization_level,
|
||||
input,
|
||||
output_tree,
|
||||
} => sem_command(*optimization_level, input, output_tree),
|
||||
cli::Command::Gen {
|
||||
mode,
|
||||
optimize,
|
||||
optimization_level,
|
||||
input,
|
||||
output,
|
||||
output_symbols,
|
||||
} => gen_command(mode, *optimize, input, output, output_symbols),
|
||||
} => gen_command(mode, *optimization_level, input, output, output_symbols),
|
||||
cli::Command::Com {
|
||||
optimize,
|
||||
optimization_level,
|
||||
input,
|
||||
output,
|
||||
} => com_command(*optimize, input, output),
|
||||
} => com_command(*optimization_level, input, output),
|
||||
cli::Command::Int { input } => int_command(input),
|
||||
};
|
||||
|
||||
@@ -85,7 +89,7 @@ fn int_command(input: &Path) -> Result<(), anyhow::Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn com_command(optimize: bool, input: &Path, output: &Path) -> Result<(), anyhow::Error> {
|
||||
fn com_command(o_level: OLevel, input: &Path, output: &Path) -> Result<(), anyhow::Error> {
|
||||
let input = fs::read_to_string(input)?;
|
||||
let mut symbols = SymbolsTable::default();
|
||||
let typed_expr = match {
|
||||
@@ -93,7 +97,7 @@ fn com_command(optimize: bool, input: &Path, output: &Path) -> Result<(), anyhow
|
||||
let mut parser = Parser::new(tokens);
|
||||
parser.parse()
|
||||
}
|
||||
.and_then(|expr| ast::to_typed_expr(expr, optimize, &mut symbols))
|
||||
.and_then(|expr| TypedExpr::from_untyped(expr, o_level, &mut symbols))
|
||||
{
|
||||
Ok(expr) => expr,
|
||||
Err(e) => {
|
||||
@@ -101,7 +105,7 @@ fn com_command(optimize: bool, input: &Path, output: &Path) -> Result<(), anyhow
|
||||
},
|
||||
};
|
||||
|
||||
let ir = IntermediateExpr::from_typed_expr(typed_expr, optimize, &mut symbols);
|
||||
let ir = IntermediateExpr::from_typed_expr(typed_expr, o_level >= OLevel::None, &mut symbols);
|
||||
{
|
||||
let used_symbols = util::collect_used_symbols(&ir);
|
||||
symbols.retain(&used_symbols);
|
||||
@@ -119,7 +123,7 @@ fn com_command(optimize: bool, input: &Path, output: &Path) -> Result<(), anyhow
|
||||
|
||||
fn gen_command(
|
||||
mode: &GenMode,
|
||||
optimize: bool,
|
||||
o_level: OLevel,
|
||||
input: &Path,
|
||||
output: &Path,
|
||||
output_symbols: &Path,
|
||||
@@ -131,7 +135,7 @@ fn gen_command(
|
||||
let mut parser = Parser::new(tokens);
|
||||
parser.parse()
|
||||
}
|
||||
.and_then(|expr| ast::to_typed_expr(expr, optimize, &mut symbols))
|
||||
.and_then(|expr| TypedExpr::from_untyped(expr, o_level, &mut symbols))
|
||||
{
|
||||
Ok(expr) => expr,
|
||||
Err(e) => {
|
||||
@@ -143,8 +147,11 @@ fn gen_command(
|
||||
|
||||
match mode {
|
||||
GenMode::Intermediate => {
|
||||
let intermediate_exprs =
|
||||
IntermediateExpr::from_typed_expr(typed_expr, optimize, &mut symbols);
|
||||
let intermediate_exprs = IntermediateExpr::from_typed_expr(
|
||||
typed_expr,
|
||||
o_level >= OLevel::None,
|
||||
&mut symbols,
|
||||
);
|
||||
|
||||
let used_symbols = util::collect_used_symbols(&intermediate_exprs);
|
||||
symbols.retain(&used_symbols);
|
||||
@@ -164,7 +171,7 @@ fn gen_command(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn sem_command(input: &Path, output_tree: &Path) -> Result<(), anyhow::Error> {
|
||||
fn sem_command(o_level: OLevel, input: &Path, output_tree: &Path) -> Result<(), anyhow::Error> {
|
||||
let input = fs::read_to_string(input)?;
|
||||
let mut symbols = SymbolsTable::default();
|
||||
|
||||
@@ -173,7 +180,7 @@ fn sem_command(input: &Path, output_tree: &Path) -> Result<(), anyhow::Error> {
|
||||
let mut parser = Parser::new(tokens);
|
||||
parser.parse()
|
||||
}
|
||||
.and_then(|expr| ast::to_typed_expr(expr, false, &mut symbols));
|
||||
.and_then(|expr| TypedExpr::from_untyped(expr, o_level, &mut symbols));
|
||||
|
||||
match res {
|
||||
Ok(expr) => {
|
||||
|
||||
32
src/util.rs
32
src/util.rs
@@ -58,7 +58,13 @@ pub fn print_intermediate_exprs(
|
||||
pub fn print_postfix_expr(expr: &TypedExpr, writer: &mut impl io::Write) -> io::Result<()> {
|
||||
match expr {
|
||||
TypedExpr::Int { value, .. } => write!(writer, "{} ", value)?,
|
||||
TypedExpr::Float { value, .. } => write!(writer, "{} ", value)?,
|
||||
TypedExpr::Float { value, .. } => {
|
||||
if value == &value.trunc() {
|
||||
write!(writer, "{}.0 ", value)?
|
||||
} else {
|
||||
write!(writer, "{} ", value)?
|
||||
}
|
||||
},
|
||||
TypedExpr::Var { name, .. } => write!(writer, "<id,{}> ", name)?,
|
||||
TypedExpr::BinOp { lhs, op, rhs, .. } => {
|
||||
print_postfix_expr(lhs, writer)?;
|
||||
@@ -86,17 +92,21 @@ fn write_typed_expr(
|
||||
write!(writer, "{}{}", prefix, branch)?;
|
||||
|
||||
match expr {
|
||||
TypedExpr::Int { value, .. } => writeln!(writer, "<{}>", value),
|
||||
TypedExpr::Int { value, .. } => writeln!(writer, "<{}>", value)?,
|
||||
TypedExpr::Float { value, .. } => {
|
||||
if value == &value.trunc() {
|
||||
writeln!(writer, "<{}.0>", value)
|
||||
writeln!(writer, "<{}.0>", value)?
|
||||
} else {
|
||||
writeln!(writer, "<{}>", value)
|
||||
}
|
||||
writeln!(writer, "<{}>", value)?
|
||||
};
|
||||
},
|
||||
TypedExpr::Var { name, .. } => {
|
||||
let ty = symbols.resolve(name).unwrap().ty.unwrap();
|
||||
writeln!(writer, "<id,{},{}>", name, ty)
|
||||
let ty = symbols.resolve(name).and_then(|data| data.ty);
|
||||
write!(writer, "<id,{}", name)?;
|
||||
if let Some(ty) = ty {
|
||||
write!(writer, ",{}", ty)?
|
||||
};
|
||||
writeln!(writer, ">")?;
|
||||
},
|
||||
TypedExpr::BinOp { lhs, op, rhs, .. } => {
|
||||
writeln!(writer, "<{}>", op)?;
|
||||
@@ -108,7 +118,7 @@ fn write_typed_expr(
|
||||
};
|
||||
|
||||
write_typed_expr(lhs, symbols, writer, &new_prefix, false)?;
|
||||
write_typed_expr(rhs, symbols, writer, &new_prefix, true)
|
||||
write_typed_expr(rhs, symbols, writer, &new_prefix, true)?;
|
||||
},
|
||||
TypedExpr::IntToFloat { value, .. } => {
|
||||
writeln!(writer, "i2f")?;
|
||||
@@ -119,9 +129,11 @@ fn write_typed_expr(
|
||||
format!("{}│ ", prefix)
|
||||
};
|
||||
|
||||
write_typed_expr(value, symbols, writer, &new_prefix, true)
|
||||
write_typed_expr(value, symbols, writer, &new_prefix, true)?;
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn print_typed_expr(
|
||||
|
||||
Reference in New Issue
Block a user