From 83e6c0a40cce2e5b0787980fe36e5e63542b6840 Mon Sep 17 00:00:00 2001 From: lionarius Date: Wed, 13 Nov 2024 19:41:56 +0300 Subject: [PATCH] value-moving optimization --- src/ast/mod.rs | 279 +++++----------------------- src/ast/typed.rs | 464 +++++++++++++++++++++++++++++++++++++++++++---- src/cli.rs | 20 +- src/main.rs | 37 ++-- src/util.rs | 32 +++- 5 files changed, 529 insertions(+), 303 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 2620dc2..c873d06 100644 --- a/src/ast/mod.rs +++ b/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(&self, lhs: T, rhs: T) -> T + where + T: Add + Sub + Mul + Div, + { + 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 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 { - 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 { - 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 { - 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) -} diff --git a/src/ast/typed.rs b/src/ast/typed.rs index 97bb604..22027f9 100644 --- a/src/ast/typed.rs +++ b/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, + op: BinOp, + rhs: Box, + }, + IntToFloat { + value: Box, + }, +} + impl From 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, - op: BinOp, - rhs: Box, - }, - IntToFloat { - value: Box, - }, -} - 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 { + 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 { + 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 { + 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 { + 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 { + 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), + }) + } } diff --git a/src/cli.rs b/src/cli.rs index 606fdde..3604ad8 100644 --- a/src/cli.rs +++ b/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 { 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, diff --git a/src/main.rs b/src/main.rs index d5f1e5d..db85c74 100644 --- a/src/main.rs +++ b/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) => { diff --git a/src/util.rs b/src/util.rs index 6048132..b22b055 100644 --- a/src/util.rs +++ b/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, " ", 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, "", name, ty) + let ty = symbols.resolve(name).and_then(|data| data.ty); + write!(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(