0

I want to write code in dart that can calculate like this expression: 100 * ( 2 + 12 ) / 14 (infix notation) taking into consideration the priority of operators. for ex: "*" is stronger than "+". The code below fixes my issue but it's written in JavaScript, I tried to convert it to dart but I am stuck.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Infix Notation</title>
</head>
<body>
    <script>
        function evaluate(expression){
            let tokens = expression.split(' ');
            // Stack for numbers: 'values'
            let values = [];
            // Stack for Operators: 'ops'
            let ops = [];
    
            for (let i = 0; i < tokens.length; i++){
                if (tokens[i] >= '0' && tokens[i] <= '9'){
                    let number = "";
                    // There may be more than one digits in number
                    while (i < tokens.length && tokens[i] >= '0' && tokens[i] <= '9'){
                        number = number + tokens[i++];
                    }

                    document.write(number + "</br>");
                    values.push(parseInt(number, 10));
                    
                    // Right now the i points to
                    // the character next to the digit,
                    // since the for loop also increases
                    // the i, we would skip one
                    // token position; we need to
                    // decrease the value of i by 1 to
                    // correct the offset.
                    i--;
                }
    
                // Current token is an opening
                // brace, push it to 'ops'
                else if (tokens[i] == '(')
                {
                    ops.push(tokens[i]);
                }
    
                // Closing brace encountered,
                // solve entire brace
                else if (tokens[i] == ')')
                {
                    while (ops[ops.length - 1] != '(')
                    {
                    values.push(applyOp(ops.pop(),
                                    values.pop(),
                                    values.pop()));
                    }
                    ops.pop();
                }
    
                // Current token is an operator.
                else if (tokens[i] == '+' ||
                        tokens[i] == '-' ||
                        tokens[i] == '*' ||
                        tokens[i] == '/')
                {
                    
                    // While top of 'ops' has same
                    // or greater precedence to current
                    // token, which is an operator.
                    // Apply operator on top of 'ops'
                    // to top two elements in values stack
                    while (ops.length > 0 &&
                            hasPrecedence(tokens[i],
                                        ops[ops.length - 1]))
                    {
                    values.push(applyOp(ops.pop(),
                                    values.pop(),
                                    values.pop()));
                    }
    
                    // Push current token to 'ops'.
                    ops.push(tokens[i]);
                }
            }
    
            // Entire expression has been
            // parsed at this point, apply remaining
            // ops to remaining values
            while (ops.length > 0){
                values.push(applyOp(ops.pop(),
                                values.pop(),
                                values.pop()));
            }
    
            // Top of 'values' contains
            // result, return it
            return values.pop();
        }
    
        // Returns true if 'op2' has
        // higher or same precedence as 'op1',
        // otherwise returns false.
        function hasPrecedence(op1, op2){
            if (op2 == '(' || op2 == ')')
            {
                return false;
            }
            if ((op1 == '*' || op1 == '/') &&
                (op2 == '+' || op2 == '-'))
            {
                return false;
            }
            else
            {
                return true;
            }
        }
    
        // A utility method to apply an
        // operator 'op' on operands 'a'
        // and 'b'. Return the result.
        function applyOp(op, b, a){
            switch (op)
            {
            case '+':
                return a + b;
            case '-':
                return a - b;
            case '*':
                return a * b;
            case '/':
                if (b == 0)
                {
                    document.write("Cannot divide by zero");
                }
                return parseInt(a / b, 10);
            }
            return 0;
        }
        document.write(evaluate("10,5 + 2 * 6") + "</br>");
        document.write(evaluate("100 * 2 + 12") + "</br>");
        document.write(evaluate("100 * ( 2 + 12 )") + "</br>");
        document.write(evaluate( "100 * ( 2 + 12 ) / 14") + "</br>");
    
    // This code is contributed by decode2207.
    </script>
    
</body>
</html>

In dart there is no built in function for stack so we have to write its class in order to use push(), pop(), length.. as below:

class Values<T> {
  final _list = <T>[];
  void push(T value) => _list.add(value);
  T pop() => _list.removeLast();
  T get top => _list.last;
  bool get isEmpty => _list.isEmpty;
  bool get isNotEmpty => _list.isNotEmpty;
  int get length => _list.length;
  T get peek => _list.last;
  @override
  String toString() => _list.toString();
}

1
  • Where are you stuck? JavaScript doesn't really have a build-in stack. It just has a growable Array with convenience push/pop methods. You can just use a Dart List directly as a stack in the same way using add instead of push, removeLast instead of pop and last instead of top/peek, without needing to wrap it. The list is the stack. Commented Oct 25, 2022 at 21:50

1 Answer 1

1

1. Closest translation to Dart with some refactoring (personal advice: don't use this past simple use cases, like the ones in the examples above)

num evaluate(String expression) {
  var tokens = expression.split(' ');
  // Stack for numbers: 'values'
  List<num> values = [];
  // Stack for Operators: 'ops'
  List<String> ops = [];

  for (var i = 0; i < tokens.length; i++) {
    if (num.tryParse(tokens[i]) != null) {
      // dart reafctor: removed the while loop here as it is not needed and bad
      values.add(num.parse(tokens[i]));
    }

    // Current token is an opening
    // brace, push it to 'ops'
    else if (tokens[i] == '(') {
      ops.add(tokens[i]);
    }

    // Closing brace encountered,
    // solve entire brace
    else if (tokens[i] == ')') {
      while (ops.last != '(') {
        values.add(applyOp(
            ops.removeLast(), values.removeLast(), values.removeLast()));
      }
      ops.removeLast();
    }

    // Current token is an operator.
    else if (tokens[i] == '+' ||
        tokens[i] == '-' ||
        tokens[i] == '*' ||
        tokens[i] == '/') {
      // While top of 'ops' has same
      // or greater precedence to current
      // token, which is an operator.
      // Apply operator on top of 'ops'
      // to top two elements in values stack
      
      while (ops.isNotEmpty && hasPrecedence(tokens[i], ops.last)) {
        values.add(applyOp(
            ops.removeLast(), values.removeLast(), values.removeLast()));
      }

      // Push current token to 'ops'.
      ops.add(tokens[i]);
    }
  }

  // Entire expression has been
  // parsed at this point, apply remaining
  // ops to remaining values
  while (ops.isNotEmpty) {
    values.add(
        applyOp(ops.removeLast(), values.removeLast(), values.removeLast()));
  }

  // Top of 'values' contains
  // result, return it
  return values.removeLast();
}

bool hasPrecedence(String op1, String op2) {
  if (op2 == '(' || op2 == ')') {
    return false;
  }
  if ((op1 == '*' || op1 == '/') && (op2 == '+' || op2 == '-')) {
    return false;
  } else {
    return true;
  }
}

num applyOp(String op, num b, num a) {
  switch (op) {
    case '+':
      return a + b;
    case '-':
      return a - b;
    case '*':
      return a * b;
    case '/':
      if (b == 0) {
        throw "Cannot divide by zero";
      }
      return a / b;
  }
  return 0;
}

Dart pad

Test cases

void main() {
  print(evaluate("100 * 2 + 12")); // 212
  print(evaluate("100 * ( 2 + 12 )")); // 1400
  print(evaluate( "100 * ( 2 + 12 ) / 14")); // 100
}

2. This evaluates an arithmetic expression by using Regular expression matches. So not a direct translation of your code, but will work nonetheless.

num evaluate(String expr) {
  var divAndMult =
      RegExp(r"\(?\s*(\d+(?:\.\d*)?)\s*([/*])\s*(\d+(?:\.\d*)?)\s*\)?");
  var addAndSub =
      RegExp(r"\(?\s*(\d+(?:\.\d*)?)\s*([+-])\s*(\d+(?:\.\d*)?)\s*\)?");
  
  var nextInput = expr;

  while (divAndMult.hasMatch(nextInput) || addAndSub.hasMatch(nextInput)) {
    nextInput = nextInput.replaceAllMapped(divAndMult, (match) {
      if (match[2] == "*") {
        return (num.parse(match[1]!) * num.parse(match[3]!)).toString();
      }
      return (num.parse(match[1]!) / num.parse(match[3]!)).toString();
    }).replaceAllMapped(addAndSub, (match) {
      if (match[2] == "+") {
        return (num.parse(match[1]!) + num.parse(match[3]!)).toString();
      }
      return (num.parse(match[1]!) - num.parse(match[3]!)).toString();
    });
  }
  return num.parse(nextInput);
}

This can be refactored to ease adding new operators, like so:

extension ApplyOp on Match {
 String applyOp(num Function(num a, num b) op) {
   return op(num.parse(this[1]!), num.parse(this[3]!)).toString();
 }
}

num evaluate(String expr) {
 var divMultAndModulo =
     RegExp(r"\(?\s*(\d+(?:\.\d*)?)\s*([/*%])\s*(\d+(?:\.\d*)?)\s*\)?");
 var addAndSub =
     RegExp(r"\(?\s*(\d+(?:\.\d*)?)\s*([+-])\s*(\d+(?:\.\d*)?)\s*\)?");

 var nextInput = expr;

 while (divMultAndModulo.hasMatch(nextInput) || addAndSub.hasMatch(nextInput)) {
   nextInput = nextInput.replaceAllMapped(divMultAndModulo, (match) {
     if (match[2] == "*") {
       return match.applyOp((a, b) => a * b);
     }
     if(match[2] == '%') {
       return match.applyOp((a, b) => a % b);
     }
     return match.applyOp((a, b) => a / b);
   }).replaceAllMapped(addAndSub, (match) {
     if (match[2] == "+") {
       return match.applyOp((a, b) => a + b);
     }
     return match.applyOp((a, b) => a - b);
   });
 }

 return num.parse(nextInput);
}

Test cases

void main() {
  print(evaluate("(8 * 8) - (20 / 5) + 8")); // 68
  print(evaluate("( 2 + 12 )")); // 14
  print(evaluate("100 * ( 2 + 12 ) / 14")); // 100
  print(evaluate("10.5 + 2 * 6")); // 22.5
  print(evaluate("20 % 3")); // 2
}

Dart pad

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.