LCOV - code coverage report
Current view: top level - lib - rpn.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 73 77 94.8 %
Date: 2024-05-20 13:51:31 Functions: 0 0 -

          Line data    Source code
       1             : // RPN(Reverse Polish Notation) expression
       2             : import 'dart:math';
       3             : import 'package:logger/logger.dart';
       4             : 
       5           9 : var log = Logger(
       6           3 :   printer: SimplePrinter(printTime: true),
       7             : );
       8             : 
       9             : abstract class RPN<N> {
      10             :   List<String> expression = [];
      11             :   List<N> stack = [];
      12             : 
      13           2 :   void fromString(String input, String delim) {
      14           4 :     expression = input.split(delim);
      15             :   }
      16             : 
      17             :   // for Infix Notation
      18             :   Map<String, String> braces = {
      19             :     ")": "(",
      20             :     "}": "{",
      21             :     "]": "[",
      22             :   };
      23             : 
      24             :   Map<String, int> opcodes = {
      25             :     "S-": 1,
      26             :     "S+": 1,
      27             :     "S!": 1,
      28             :     "*": 2,
      29             :     "/": 2,
      30             :     "%": 2,
      31             :     "×": 2,
      32             :     "÷": 2,
      33             :     "+": 3,
      34             :     "-": 3,
      35             :     "<<": 4,
      36             :     ">>": 4,
      37             :     "&": 5,
      38             :     "^": 6,
      39             :     "|": 7,
      40             :   };
      41             : 
      42           3 :   void fromInfixString(String input, RegExp number, [RegExp? func]) {
      43             :     // load infix notation string to rev polish notation
      44          18 :     var opcodeLength = opcodes.keys.map((x) => x.length).reduce(max);
      45           3 :     List<String> p = [];
      46           3 :     List<MapEntry<String, int>> s = [];
      47             :     int idx = 0;
      48             :     bool single = true;
      49           6 :     while (idx < input.length) {
      50             :       // skip space
      51           3 :       var rest0 = input.substring(idx);
      52           3 :       var rest = rest0.trimLeft();
      53          12 :       idx += rest0.length - rest.length;
      54             : 
      55           9 :       log.d("p=$p, s=$s");
      56           9 :       log.d("rest: idx=$idx, $rest");
      57             :       var oplen = 0;
      58             :       var prio = 100;
      59             :       String? opnum;
      60           3 :       var isnum = number.matchAsPrefix(rest);
      61           3 :       var isfunc = func?.matchAsPrefix(rest);
      62             :       if (isnum != null) {
      63             :         // number
      64           3 :         opnum = isnum[0]!;
      65           3 :         oplen = opnum.length;
      66             :         prio = 0;
      67             :         single = false;
      68           9 :         log.d("number: $opnum len=$oplen prio=$prio");
      69          18 :       } else if (braces.containsValue(rest[0]) || braces.containsKey(rest[0])) {
      70             :         // braces
      71             :         oplen = 1;
      72           2 :         opnum = rest[0];
      73             :         prio = 100;
      74           6 :         single = braces.containsValue(rest[0]);
      75           6 :         log.d("brace: $opnum len=$oplen prio=$prio");
      76             :       } else if (isfunc != null) {
      77             :         // func
      78           1 :         opnum = isfunc[0]!;
      79           1 :         oplen = opnum.length;
      80             :         prio = 0;
      81             :         single = false;
      82           3 :         log.d("func: $opnum len=$oplen prio=$prio");
      83             :       } else {
      84             :         // opcode
      85          12 :         for (int x = min(opcodeLength, rest.length); x != 0; x--) {
      86           3 :           var op = rest.substring(0, x);
      87             :           if (single) {
      88           2 :             op = "S$op";
      89             :           }
      90           6 :           if (opcodes.containsKey(op)) {
      91             :             opnum = op;
      92             :             oplen = x;
      93           6 :             prio = opcodes[op]!;
      94             :             single = true;
      95           9 :             log.d("opcode: $opnum len=$oplen prio=$prio");
      96             :             break;
      97             :           }
      98             :         }
      99             :       }
     100             :       if (opnum == null) {
     101           0 :         log.e("error: cannot find op/num: idx=$idx, rest=$rest");
     102             :         break;
     103             :       }
     104           6 :       if (braces.containsValue(opnum)) {
     105           6 :         log.d("brace-start push: $opnum");
     106           4 :         s.add(MapEntry(opnum, prio));
     107           6 :       } else if (braces.containsKey(opnum)) {
     108           4 :         log.d("brace-end move");
     109           4 :         var sbrace = braces[opnum]!;
     110           6 :         while (s.last.key != sbrace) {
     111           2 :           var tmp = s.removeLast();
     112          12 :           if (braces.containsKey(tmp.key) || braces.containsValue(tmp.key)) {
     113           0 :             log.d("brace skip move: $tmp");
     114             :           } else {
     115           6 :             log.d("brace move: $tmp");
     116           4 :             p.add(tmp.key);
     117             :           }
     118             :         }
     119           6 :         if (s.last.key == sbrace) {
     120             :           // pop sbrace
     121           2 :           s.removeLast();
     122             :         }
     123             :       } else {
     124          12 :         while (s.isNotEmpty && s.last.value <= prio) {
     125          15 :           log.d("notempty=${s.isNotEmpty}, lastval=${s.last}");
     126           3 :           var tmp = s.removeLast();
     127          18 :           if (braces.containsKey(tmp.key) || braces.containsValue(tmp.key)) {
     128           0 :             log.d("move: brace skip: $tmp");
     129             :           } else {
     130           9 :             log.d("move: $tmp");
     131           6 :             p.add(tmp.key);
     132             :           }
     133             :         }
     134          12 :         log.d("finished: notempty=${s.isNotEmpty}");
     135           3 :         if (s.isNotEmpty) {
     136          12 :           log.d("finished: lastval=${s.last}");
     137             :         }
     138           6 :         s.add(MapEntry(opnum, prio));
     139             :       }
     140           3 :       idx += oplen;
     141             :     }
     142           3 :     while (s.isNotEmpty) {
     143           3 :       var tmp = s.removeLast();
     144          18 :       if (braces.containsKey(tmp.key) || braces.containsValue(tmp.key)) {
     145           0 :         log.d("last move: brace skip: $tmp");
     146             :       } else {
     147           9 :         log.d("last move: $tmp");
     148           6 :         p.add(tmp.key);
     149             :       }
     150             :     }
     151           9 :     log.d("result: $input -> $p");
     152           3 :     expression = p;
     153             :   }
     154             : 
     155           3 :   N? pop() {
     156           6 :     if (stack.isEmpty) {
     157             :       return null;
     158             :     }
     159           6 :     return stack.removeLast();
     160             :   }
     161             : 
     162           3 :   void push(N v) {
     163           6 :     stack.add(v);
     164             :   }
     165             : 
     166             :   void exec1(String v);
     167             : 
     168           3 :   N? evaluate() {
     169           6 :     for (var x in expression) {
     170           3 :       exec1(x);
     171             :     }
     172           6 :     if (stack.isEmpty) {
     173             :       return null;
     174             :     }
     175           6 :     return stack.last;
     176             :   }
     177             : }

Generated by: LCOV version 1.14