| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198 |
- // SPDX-License-Identifier: Apache-2.0
- use num_bigint::BigInt;
- use num_bigint::Sign;
- use num_rational::BigRational;
- use num_traits::One;
- use num_traits::ToPrimitive;
- use num_traits::Zero;
- use super::ast::{Diagnostic, Expression, Namespace};
- use solang_parser::pt;
- use solang_parser::pt::CodeLocation;
- /// Resolve an expression where a compile-time constant is expected
- pub fn eval_const_number(
- expr: &Expression,
- ns: &Namespace,
- ) -> Result<(pt::Loc, BigInt), Diagnostic> {
- match expr {
- Expression::Add(loc, _, _, l, r) => Ok((
- *loc,
- eval_const_number(l, ns)?.1 + eval_const_number(r, ns)?.1,
- )),
- Expression::Subtract(loc, _, _, l, r) => Ok((
- *loc,
- eval_const_number(l, ns)?.1 - eval_const_number(r, ns)?.1,
- )),
- Expression::Multiply(loc, _, _, l, r) => Ok((
- *loc,
- eval_const_number(l, ns)?.1 * eval_const_number(r, ns)?.1,
- )),
- Expression::Divide(loc, _, l, r) => {
- let divisor = eval_const_number(r, ns)?.1;
- if divisor.is_zero() {
- Err(Diagnostic::error(*loc, "divide by zero".to_string()))
- } else {
- Ok((*loc, eval_const_number(l, ns)?.1 / divisor))
- }
- }
- Expression::Modulo(loc, _, l, r) => {
- let divisor = eval_const_number(r, ns)?.1;
- if divisor.is_zero() {
- Err(Diagnostic::error(*loc, "divide by zero".to_string()))
- } else {
- Ok((*loc, eval_const_number(l, ns)?.1 % divisor))
- }
- }
- Expression::BitwiseAnd(loc, _, l, r) => Ok((
- *loc,
- eval_const_number(l, ns)?.1 & eval_const_number(r, ns)?.1,
- )),
- Expression::BitwiseOr(loc, _, l, r) => Ok((
- *loc,
- eval_const_number(l, ns)?.1 | eval_const_number(r, ns)?.1,
- )),
- Expression::BitwiseXor(loc, _, l, r) => Ok((
- *loc,
- eval_const_number(l, ns)?.1 ^ eval_const_number(r, ns)?.1,
- )),
- Expression::Power(loc, _, _, base, exp) => {
- let b = eval_const_number(base, ns)?.1;
- let mut e = eval_const_number(exp, ns)?.1;
- if e.sign() == Sign::Minus {
- Err(Diagnostic::error(
- expr.loc(),
- "power cannot take negative number as exponent".to_string(),
- ))
- } else if e.sign() == Sign::NoSign {
- Ok((*loc, BigInt::one()))
- } else {
- let mut res = b.clone();
- e -= BigInt::one();
- while e.sign() == Sign::Plus {
- res *= b.clone();
- e -= BigInt::one();
- }
- Ok((*loc, res))
- }
- }
- Expression::ShiftLeft(loc, _, left, right) => {
- let l = eval_const_number(left, ns)?.1;
- let r = eval_const_number(right, ns)?.1;
- let r = match r.to_usize() {
- Some(r) => r,
- None => {
- return Err(Diagnostic::error(
- expr.loc(),
- format!("cannot left shift by {}", r),
- ));
- }
- };
- Ok((*loc, l << r))
- }
- Expression::ShiftRight(loc, _, left, right, _) => {
- let l = eval_const_number(left, ns)?.1;
- let r = eval_const_number(right, ns)?.1;
- let r = match r.to_usize() {
- Some(r) => r,
- None => {
- return Err(Diagnostic::error(
- expr.loc(),
- format!("cannot right shift by {}", r),
- ));
- }
- };
- Ok((*loc, l >> r))
- }
- Expression::NumberLiteral(loc, _, n) => Ok((*loc, n.clone())),
- Expression::ZeroExt(loc, _, n) => Ok((*loc, eval_const_number(n, ns)?.1)),
- Expression::SignExt(loc, _, n) => Ok((*loc, eval_const_number(n, ns)?.1)),
- Expression::Cast(loc, _, n) => Ok((*loc, eval_const_number(n, ns)?.1)),
- Expression::Not(loc, n) => Ok((*loc, !eval_const_number(n, ns)?.1)),
- Expression::Complement(loc, _, n) => Ok((*loc, !eval_const_number(n, ns)?.1)),
- Expression::UnaryMinus(loc, _, n) => Ok((*loc, -eval_const_number(n, ns)?.1)),
- Expression::ConstantVariable(_, _, Some(contract_no), var_no) => {
- let expr = ns.contracts[*contract_no].variables[*var_no]
- .initializer
- .as_ref()
- .unwrap()
- .clone();
- eval_const_number(&expr, ns)
- }
- Expression::ConstantVariable(_, _, None, var_no) => {
- let expr = ns.constants[*var_no].initializer.as_ref().unwrap().clone();
- eval_const_number(&expr, ns)
- }
- _ => Err(Diagnostic::error(
- expr.loc(),
- "expression not allowed in constant number expression".to_string(),
- )),
- }
- }
- /// Resolve an expression where a compile-time constant(rational) is expected
- pub fn eval_const_rational(
- expr: &Expression,
- ns: &Namespace,
- ) -> Result<(pt::Loc, BigRational), Diagnostic> {
- match expr {
- Expression::Add(loc, _, _, l, r) => Ok((
- *loc,
- eval_const_rational(l, ns)?.1 + eval_const_rational(r, ns)?.1,
- )),
- Expression::Subtract(loc, _, _, l, r) => Ok((
- *loc,
- eval_const_rational(l, ns)?.1 - eval_const_rational(r, ns)?.1,
- )),
- Expression::Multiply(loc, _, _, l, r) => Ok((
- *loc,
- eval_const_rational(l, ns)?.1 * eval_const_rational(r, ns)?.1,
- )),
- Expression::Divide(loc, _, l, r) => {
- let divisor = eval_const_rational(r, ns)?.1;
- if divisor.is_zero() {
- Err(Diagnostic::error(*loc, "divide by zero".to_string()))
- } else {
- Ok((*loc, eval_const_rational(l, ns)?.1 / divisor))
- }
- }
- Expression::Modulo(loc, _, l, r) => {
- let divisor = eval_const_rational(r, ns)?.1;
- if divisor.is_zero() {
- Err(Diagnostic::error(*loc, "divide by zero".to_string()))
- } else {
- Ok((*loc, eval_const_rational(l, ns)?.1 % divisor))
- }
- }
- Expression::NumberLiteral(loc, _, n) => Ok((*loc, BigRational::from_integer(n.clone()))),
- Expression::RationalNumberLiteral(loc, _, n) => Ok((*loc, n.clone())),
- Expression::Cast(loc, _, n) => Ok((*loc, eval_const_rational(n, ns)?.1)),
- Expression::UnaryMinus(loc, _, n) => Ok((*loc, -eval_const_rational(n, ns)?.1)),
- Expression::ConstantVariable(_, _, Some(contract_no), var_no) => {
- let expr = ns.contracts[*contract_no].variables[*var_no]
- .initializer
- .as_ref()
- .unwrap()
- .clone();
- eval_const_rational(&expr, ns)
- }
- Expression::ConstantVariable(_, _, None, var_no) => {
- let expr = ns.constants[*var_no].initializer.as_ref().unwrap().clone();
- eval_const_rational(&expr, ns)
- }
- _ => Err(Diagnostic::error(
- expr.loc(),
- "expression not allowed in constant rational number expression".to_string(),
- )),
- }
- }
|