| 492 | } |
| 493 | |
| 494 | private evaluateBinary(operator: BinaryOp, lhsExpr: Expression, rhsExpr: Expression): Value { |
| 495 | let [lhs, rhs] = [this.evaluate(lhsExpr), this.evaluate(rhsExpr)]; |
| 496 | |
| 497 | const arithmetic = (f: (x: number, y: number) => number) => { |
| 498 | const numType = lhs.type === 'float' || rhs.type === 'float' ? float : int; |
| 499 | if (lhs.type === 'string') { |
| 500 | lhs = int(toInt(lhs)); |
| 501 | } |
| 502 | if (rhs.type === 'string') { |
| 503 | rhs = int(toInt(rhs)); |
| 504 | } |
| 505 | return numType(f(toFloat(lhs), toFloat(rhs))); |
| 506 | }; |
| 507 | |
| 508 | switch (operator) { |
| 509 | case '+': |
| 510 | if (lhs.type === 'list' && rhs.type === 'list') { |
| 511 | return list(lhs.items.concat(rhs.items)); |
| 512 | } else { |
| 513 | return arithmetic((x, y) => x + y); |
| 514 | } |
| 515 | case '-': |
| 516 | return arithmetic((x, y) => x - y); |
| 517 | case '*': |
| 518 | return arithmetic((x, y) => x * y); |
| 519 | case '/': |
| 520 | return arithmetic((x, y) => x / y); |
| 521 | case '.': |
| 522 | case '..': |
| 523 | return str(toString(lhs) + toString(rhs)); |
| 524 | case '%': { |
| 525 | if (lhs.type === 'float' || rhs.type === 'float') { |
| 526 | throw VimError.CannotUseModuloWithFloat(); |
| 527 | } |
| 528 | const [_lhs, _rhs] = [toInt(lhs), toInt(rhs)]; |
| 529 | if (_rhs === 0) { |
| 530 | return int(0); |
| 531 | } |
| 532 | |
| 533 | return int(_lhs % _rhs); |
| 534 | } |
| 535 | case '&&': |
| 536 | return bool(toInt(lhs) !== 0 && toInt(rhs) !== 0); |
| 537 | case '||': |
| 538 | return bool(toInt(lhs) !== 0 || toInt(rhs) !== 0); |
| 539 | } |
| 540 | } |
| 541 | |
| 542 | private evaluateComparison( |
| 543 | operator: ComparisonOp, |