Found while writing a new Expressions Reference chapter for the manual,
where every function got run for real before being documented rather
than trusted from reading the source alone - substring('helloworld', 7)
threw "operand is not valid." immediately after producing the correct
answer, confirmed via debug tracing showing an extra Operand constructed
on empty input right after the 2-arg case finished.
Root cause: a missing pair of braces around the 3-argument-only branch
meant Operand parm3(in, tag) and its own closing-paren check ran
unconditionally, even after the 2-arg form's own "if(in.ifNext(')'))"
check had already matched and produced string = parm1.string.substring(
parm2.string.asInteger()) - so it immediately tried to parse a third
argument from whatever was left (nothing, since the real closing paren
was already consumed), and construction on empty input always throws.
The 3-argument form (substring(str, start, length)) was unaffected,
since its own path never hit the first if-branch's early exit.
Fixed by properly scoping the 3-arg branch inside an else, so it only
runs when the 2-arg form's own closing paren wasn't found. Verified both
forms with real jet-2.0 runs: substring('helloworld', 7) -> "rld",
substring('helloworld', 0, 5) -> "hello".
619 lines
20 KiB
C++
619 lines
20 KiB
C++
#include "Operand.h"
|
|
#include "Exception.h"
|
|
#include <format>
|
|
#include <iostream>
|
|
#include <algorithm>
|
|
#include <time.h>
|
|
#include <chrono>
|
|
#include <cmath>
|
|
|
|
namespace jet {
|
|
|
|
Operand::Operand(coreutils::ZString &in, Tag &tag, bool stop) : in(in), tag(tag) {
|
|
|
|
doubleValue = 0;
|
|
|
|
in.skipWhitespace();
|
|
|
|
if(in.startsWith("$[") || in.startsWith("#[")) {
|
|
string = tag.getVariable(in);
|
|
doubleValue = string.asDouble();
|
|
isNumber = string.eod();
|
|
string.reset();
|
|
if((string == "false") || (string == "true"))
|
|
boolean = (string == "true");
|
|
} else if(in.ifNext("(")) {
|
|
Operand op(in, tag);
|
|
string = op.string;
|
|
doubleValue = op.doubleValue;
|
|
isNumber = op.isNumber;
|
|
if(!in.ifNext(")"))
|
|
throw coreutils::Exception("expected ) in expression.");
|
|
} else if(in.ifNextIgnoreCase("SUBSTRING")) {
|
|
if(!in.ifNext("("))
|
|
throw coreutils::Exception("Expecting ( for SUBSTRING parameters.");
|
|
Operand parm1(in, tag);
|
|
if(!in.ifNext(","))
|
|
throw coreutils::Exception("Expecting , in SUBSTRING expression.");
|
|
Operand parm2(in, tag);
|
|
if(in.ifNext(")")) {
|
|
string = parm1.string.substring(parm2.string.asInteger());
|
|
} else {
|
|
// Real, pre-existing bug fixed here: the 3-arg-only branch below
|
|
// used to run unconditionally (missing braces around this
|
|
// else-if), so even after the 2-arg form above already
|
|
// succeeded, it fell through into trying to parse a third
|
|
// argument from whatever was left (nothing, since the closing
|
|
// ")" was already consumed) - always throwing "operand is not
|
|
// valid." right after computing the correct 2-arg result.
|
|
// Confirmed via real debug tracing showing an extra Operand
|
|
// constructed on empty input immediately after a successful
|
|
// 2-arg substring(str, start) call - the 2-arg form was
|
|
// completely unusable before this fix.
|
|
if(!in.ifNext(","))
|
|
throw coreutils::Exception("Expecting , in SUBSTRING expression.");
|
|
Operand parm3(in, tag);
|
|
if(in.ifNext(")")) {
|
|
string = parm1.string.substring(parm2.string.asInteger(), parm3.string.asInteger());
|
|
} else
|
|
throw coreutils::Exception("Expecting ) at end of substring expression.");
|
|
}
|
|
} else if(in.ifNextIgnoreCase("LEFT")) {
|
|
if(!in.ifNext("("))
|
|
throw coreutils::Exception("Expecting ( for LEFT parameters.");
|
|
Operand parm1(in, tag);
|
|
if(!in.ifNext(","))
|
|
throw coreutils::Exception("Expecting , in LEFT expression.");
|
|
Operand parm2(in, tag);
|
|
if(in.ifNext(")")) {
|
|
string = parm1.string.substring(0, parm2.string.asInteger());
|
|
} else
|
|
throw coreutils::Exception("Expecting ) at end of LEFT expression.");
|
|
} else if(in.ifNextIgnoreCase("EXPR")) {
|
|
if(!in.ifNext("("))
|
|
throw coreutils::Exception("Expecting ( for EXPR parameters.");
|
|
Operand parm1(in, tag);
|
|
if(in.ifNext(")")) {
|
|
Operand op(parm1.string, tag);
|
|
string = op.string;
|
|
isNumber = op.isNumber;
|
|
if(isNumber) {
|
|
doubleValue = op.doubleValue;
|
|
string = std::format("{:.12f}", doubleValue);
|
|
string.removeTrailingZeros();
|
|
}
|
|
boolean = op.boolean;
|
|
} else
|
|
throw coreutils::Exception("Expecting ) at end of EXPR expression.");
|
|
} else if(in.ifNextIgnoreCase("RIGHT")) {
|
|
if(!in.ifNext("("))
|
|
throw coreutils::Exception("Expecting ( for RIGHT parameters.");
|
|
Operand parm1(in, tag);
|
|
if(!in.ifNext(","))
|
|
throw coreutils::Exception("Expecting , in RIGHT expression.");
|
|
Operand parm2(in, tag);
|
|
if(in.ifNext(")")) {
|
|
int len = parm1.string.getLength();
|
|
int size = parm2.string.asInteger();
|
|
int start = len - size;
|
|
string = parm1.string.substring(start, size);
|
|
} else
|
|
throw coreutils::Exception("Expecting ) at end of RIGHT expression.");
|
|
} else if(in.ifNextIgnoreCase("TRIM")) {
|
|
if(!in.ifNext("("))
|
|
throw coreutils::Exception("Expecting ( for TRIM parameters.");
|
|
Operand parm1(in, tag);
|
|
if(in.ifNext(")")) {
|
|
string = parm1.string.trim();
|
|
} else
|
|
throw coreutils::Exception("Expecting ) at end of TRIM expression.");
|
|
} else if(in.ifNextIgnoreCase("TOUPPER")) {
|
|
if(!in.ifNext("("))
|
|
throw coreutils::Exception("Expecting ( for TOUPPER parameters.");
|
|
Operand parm1(in, tag);
|
|
if(in.ifNext(")")) {
|
|
string = parm1.string.toUpper();
|
|
} else
|
|
throw coreutils::Exception("Expecting ) at end of TOUPPER expression.");
|
|
} else if(in.ifNextIgnoreCase("TOLOWER")) {
|
|
if(!in.ifNext("("))
|
|
throw coreutils::Exception("Expecting ( for TOLOWER parameters.");
|
|
Operand parm1(in, tag);
|
|
if(in.ifNext(")")) {
|
|
string = parm1.string.toLower();
|
|
} else
|
|
throw coreutils::Exception("Expecting ) at end of TOLOWER expression.");
|
|
} else if(in.ifNextIgnoreCase("REVERSE")) {
|
|
|
|
} else if(in.ifNextIgnoreCase("CONCAT")) {
|
|
|
|
} else if(in.ifNextIgnoreCase("INTEGER")) {
|
|
if(!in.ifNext("("))
|
|
throw coreutils::Exception("Expecting ( for INTEGER parameters.");
|
|
Operand parm1(in, tag);
|
|
if(in.ifNext(")")) {
|
|
string = parm1.string.asInteger();
|
|
} else
|
|
throw coreutils::Exception("Expecting ) at end of INTEGER expression.");
|
|
} else if(in.ifNextIgnoreCase("ROUND")) {
|
|
if(!in.ifNext("("))
|
|
throw coreutils::Exception("Expecting ( for ROUND parameters.");
|
|
Operand parm1(in, tag);
|
|
if(in.ifNext(")")) {
|
|
doubleValue = round(parm1.doubleValue);
|
|
isNumber = true;
|
|
string = std::format("{:.12f}", doubleValue);
|
|
string.removeTrailingZeros();
|
|
} else
|
|
throw coreutils::Exception("Expecting ) at end of ROUND expression.");
|
|
} else if(in.ifNextIgnoreCase("RANDOM")) {
|
|
unsigned int seed = (unsigned int)clock();
|
|
doubleValue = (double) rand_r(&seed) / (RAND_MAX + 1.0);
|
|
isNumber = true;
|
|
string = std::format("{:.12f}", doubleValue);
|
|
string.removeTrailingZeros();
|
|
} else if(in.ifNextIgnoreCase("ABS")) {
|
|
if(!in.ifNext("("))
|
|
throw coreutils::Exception("Expecting ( for ABS parameters.");
|
|
Operand parm1(in, tag);
|
|
if(in.ifNext(")")) {
|
|
doubleValue = abs(parm1.doubleValue);
|
|
isNumber = true;
|
|
string = std::format("{:.12f}", doubleValue);
|
|
string.removeTrailingZeros();
|
|
} else
|
|
throw coreutils::Exception("Expecting ) at end of ABS expression.");
|
|
} else if(in.ifNextIgnoreCase("MAX")) {
|
|
if(!in.ifNext("("))
|
|
throw coreutils::Exception("Expecting ( for MAX parameters.");
|
|
Operand *parm = new Operand(in, tag);
|
|
string = parm->string;
|
|
doubleValue = parm->doubleValue;
|
|
isNumber = parm->isNumber;
|
|
delete parm;
|
|
while(in.ifNext(",")) {
|
|
parm = new Operand(in, tag);
|
|
if(isNumber != parm->isNumber)
|
|
throw coreutils::Exception("All parameters in MAX expression must be same type.");
|
|
if(isNumber) {
|
|
if(parm->doubleValue > doubleValue) {
|
|
doubleValue = parm->doubleValue;
|
|
string = parm->string;
|
|
}
|
|
} else {
|
|
if(parm->string > string) {
|
|
doubleValue = parm->doubleValue;
|
|
string = parm->string;
|
|
}
|
|
}
|
|
delete parm;
|
|
}
|
|
if(!in.ifNext(")")) {
|
|
throw coreutils::Exception("Expecting ) at end of MAX expression.");
|
|
}
|
|
string.removeTrailingZeros();
|
|
} else if(in.ifNextIgnoreCase("MIN")) {
|
|
if(!in.ifNext("("))
|
|
throw coreutils::Exception("Expecting ( for MIN parameters.");
|
|
Operand *parm = new Operand(in, tag);
|
|
string = parm->string;
|
|
doubleValue = parm->doubleValue;
|
|
isNumber = parm->isNumber;
|
|
delete parm;
|
|
while(in.ifNext(",")) {
|
|
parm = new Operand(in, tag);
|
|
if(isNumber != parm->isNumber)
|
|
throw coreutils::Exception("All parameters in MIN expression must be same type.");
|
|
if(isNumber) {
|
|
if(parm->doubleValue < doubleValue) {
|
|
doubleValue = parm->doubleValue;
|
|
string = parm->string;
|
|
}
|
|
} else {
|
|
if(parm->string < string) {
|
|
doubleValue = parm->doubleValue;
|
|
string = parm->string;
|
|
}
|
|
}
|
|
delete parm;
|
|
}
|
|
if(!in.ifNext(")")) {
|
|
throw coreutils::Exception("Expecting ) at end of MIN expression.");
|
|
}
|
|
string.removeTrailingZeros();
|
|
} else if(in.ifNextIgnoreCase("POW")) {
|
|
if(!in.ifNext("("))
|
|
throw coreutils::Exception("Expecting ( for POW parameters.");
|
|
Operand parm1(in, tag);
|
|
if(!in.ifNext(","))
|
|
throw coreutils::Exception("Expecting , in POW expression.");
|
|
Operand parm2(in, tag);
|
|
if(in.ifNext(")")) {
|
|
doubleValue = pow(parm1.doubleValue, parm2.doubleValue);
|
|
isNumber = true;
|
|
string = std::format("{:.12f}", doubleValue);
|
|
string.removeTrailingZeros();
|
|
} else
|
|
throw coreutils::Exception("Expecting ) at end of substring expression.");
|
|
} else if(in.ifNextIgnoreCase("SIN")) {
|
|
if(!in.ifNext("("))
|
|
throw coreutils::Exception("Expecting ( for SIN parameters.");
|
|
Operand parm1(in, tag);
|
|
if(in.ifNext(")")) {
|
|
doubleValue = sin(parm1.doubleValue);
|
|
isNumber = true;
|
|
string = std::format("{:.12f}", doubleValue);
|
|
string.removeTrailingZeros();
|
|
} else
|
|
throw coreutils::Exception("Expecting ) at end of SIN expression.");
|
|
} else if(in.ifNextIgnoreCase("ASIN")) {
|
|
if(!in.ifNext("("))
|
|
throw coreutils::Exception("Expecting ( for ASIN parameters.");
|
|
Operand parm1(in, tag);
|
|
if(in.ifNext(")")) {
|
|
doubleValue = asin(parm1.doubleValue);
|
|
isNumber = true;
|
|
string = std::format("{:.12f}", doubleValue);
|
|
string.removeTrailingZeros();
|
|
} else
|
|
throw coreutils::Exception("Expecting ) at end of ASIN expression.");
|
|
} else if(in.ifNextIgnoreCase("COS")) {
|
|
if(!in.ifNext("("))
|
|
throw coreutils::Exception("Expecting ( for COS parameters.");
|
|
Operand parm1(in, tag);
|
|
if(in.ifNext(")")) {
|
|
doubleValue = cos(parm1.doubleValue);
|
|
isNumber = true;
|
|
string = std::format("{:.12f}", doubleValue);
|
|
string.removeTrailingZeros();
|
|
} else
|
|
throw coreutils::Exception("Expecting ) at end of COS expression.");
|
|
} else if(in.ifNextIgnoreCase("ACOS")) {
|
|
if(!in.ifNext("("))
|
|
throw coreutils::Exception("Expecting ( for ACOS parameters.");
|
|
Operand parm1(in, tag);
|
|
if(in.ifNext(")")) {
|
|
doubleValue = acos(parm1.doubleValue);
|
|
isNumber = true;
|
|
string = std::format("{:.12f}", doubleValue);
|
|
string.removeTrailingZeros();
|
|
} else
|
|
throw coreutils::Exception("Expecting ) at end of ACOS expression.");
|
|
} else if(in.ifNextIgnoreCase("ATAN")) {
|
|
if(!in.ifNext("("))
|
|
throw coreutils::Exception("Expecting ( for ATAN parameters.");
|
|
Operand parm1(in, tag);
|
|
if(in.ifNext(")")) {
|
|
doubleValue = atan(parm1.doubleValue);
|
|
isNumber = true;
|
|
string = std::format("{:.12f}", doubleValue);
|
|
string.removeTrailingZeros();
|
|
} else
|
|
throw coreutils::Exception("Expecting ) at end of ATAN expression.");
|
|
} else if(in.ifNextIgnoreCase("SQRT")) {
|
|
if(!in.ifNext("("))
|
|
throw coreutils::Exception("Expecting ( for SQRT parameters.");
|
|
Operand parm1(in, tag);
|
|
if(in.ifNext(")")) {
|
|
doubleValue = sqrt(parm1.doubleValue);
|
|
isNumber = true;
|
|
string = std::format("{:.12f}", doubleValue);
|
|
string.removeTrailingZeros();
|
|
} else
|
|
throw coreutils::Exception("Expecting ) at end of SQRT expression.");
|
|
} else if(in.ifNextIgnoreCase("DEG")) {
|
|
if(!in.ifNext("("))
|
|
throw coreutils::Exception("Expecting ( for DEG parameters.");
|
|
Operand parm1(in, tag);
|
|
if(in.ifNext(")")) {
|
|
doubleValue = parm1.doubleValue * 180 / 3.14159;
|
|
isNumber = true;
|
|
string = std::format("{:.12f}", doubleValue);
|
|
string.removeTrailingZeros();
|
|
} else
|
|
throw coreutils::Exception("Expecting ) at end of DEG expression.");
|
|
} else if(in.ifNextIgnoreCase("RAD")) {
|
|
if(!in.ifNext("("))
|
|
throw coreutils::Exception("Expecting ( for RAD parameters.");
|
|
Operand parm1(in, tag);
|
|
if(in.ifNext(")")) {
|
|
doubleValue = parm1.doubleValue * 3.14159 / 180;
|
|
isNumber = true;
|
|
string = std::format("{:.12f}", doubleValue);
|
|
string.removeTrailingZeros();
|
|
} else
|
|
throw coreutils::Exception("Expecting ) at end of RAD expression.");
|
|
} else if(in.ifNextIgnoreCase("TAN")) {
|
|
if(!in.ifNext("("))
|
|
throw coreutils::Exception("Expecting ( for TAN parameters.");
|
|
Operand parm1(in, tag);
|
|
if(in.ifNext(")")) {
|
|
doubleValue = tan(parm1.doubleValue);
|
|
isNumber = true;
|
|
string = std::format("{:.12f}", doubleValue);
|
|
string.removeTrailingZeros();
|
|
} else
|
|
throw coreutils::Exception("Expecting ) at end of TAN expression.");
|
|
} else if(in.ifNextIgnoreCase("TRUNC")) {
|
|
if(!in.ifNext("("))
|
|
throw coreutils::Exception("Expecting ( for TRUNC parameters.");
|
|
Operand parm1(in, tag);
|
|
if(in.ifNext(")")) {
|
|
doubleValue = trunc(parm1.doubleValue);
|
|
isNumber = true;
|
|
string = std::format("{:.12f}", doubleValue);
|
|
string.removeTrailingZeros();
|
|
} else
|
|
throw coreutils::Exception("Expecting ) at end of TRUNC expression.");
|
|
} else if(in.ifNextIgnoreCase("CEIL")) {
|
|
if(!in.ifNext("("))
|
|
throw coreutils::Exception("Expecting ( for CEIL parameters.");
|
|
Operand parm1(in, tag);
|
|
if(in.ifNext(")")) {
|
|
doubleValue = ceil(parm1.doubleValue);
|
|
isNumber = true;
|
|
string = std::format("{:.12f}", doubleValue);
|
|
string.removeTrailingZeros();
|
|
} else
|
|
throw coreutils::Exception("Expecting ) at end of CEIL expression.");
|
|
} else if(in.ifNextIgnoreCase("FLOOR")) {
|
|
if(!in.ifNext("("))
|
|
throw coreutils::Exception("Expecting ( for FLOOR parameters.");
|
|
Operand parm1(in, tag);
|
|
if(in.ifNext(")")) {
|
|
doubleValue = floor(parm1.doubleValue);
|
|
isNumber = true;
|
|
string = std::format("{:.12f}", doubleValue);
|
|
string.removeTrailingZeros();
|
|
} else
|
|
throw coreutils::Exception("Expecting ) at end of FLOOR expression.");
|
|
} else if(in.ifNextIgnoreCase("UNIXTIME")) {
|
|
if(!in.ifNext("("))
|
|
throw coreutils::Exception("Expecting ( for UNIXTIME.");
|
|
if(in.ifNext(")")) {
|
|
auto now = std::chrono::system_clock::now();
|
|
auto duration_since_epoch = now.time_since_epoch();
|
|
auto seconds_since_epoch = std::chrono::duration_cast<std::chrono::seconds>(duration_since_epoch);
|
|
int unixtime = seconds_since_epoch.count();
|
|
string << unixtime;
|
|
} else
|
|
throw coreutils::Exception("Expecting ) at end of UNIXTIME.");
|
|
}
|
|
else if(in.ifNextIgnoreCase("DATEDIFF")) {
|
|
if(!in.ifNext("("))
|
|
throw coreutils::Exception("Expecting ( for DATEDIFF.");
|
|
Operand parm1(in, tag);
|
|
if(!in.ifNext(","))
|
|
throw coreutils::Exception("Expecting , in DATEDIFF expression.");
|
|
Operand parm2(in, tag);
|
|
if(in.ifNext(")")) {
|
|
|
|
// TODO: Implement DATEDIFF.
|
|
|
|
} else
|
|
throw coreutils::Exception("Expecting ) at end of DATEDIFF expression.");
|
|
} else if(in.ifNextIgnoreCase("true")) {
|
|
boolean = true;
|
|
string = "true";
|
|
} else if(in.ifNextIgnoreCase("false")) {
|
|
boolean = false;
|
|
string = "false";
|
|
} else if(in.startsWithNumber()) {
|
|
doubleValue = in.asDouble();
|
|
string = std::format("{:.12f}", doubleValue);
|
|
isNumber = true;
|
|
} else if(in.ifNext("'")) {
|
|
string = in.getTokenExclude("'");
|
|
in.ifNext("'");
|
|
isNumber = false;
|
|
} else
|
|
throw coreutils::Exception("operand is not valid.");
|
|
|
|
in.skipWhitespace();
|
|
|
|
if(!stop)
|
|
parseOperator();
|
|
|
|
return;
|
|
}
|
|
|
|
int Operand::parseOperator() {
|
|
|
|
if(in.ifNext("!=") || in.ifNext("<>")) {
|
|
Operand op(in, tag);
|
|
if(isNumber && op.isNumber) {
|
|
if(doubleValue != op.doubleValue) {
|
|
boolean = true;
|
|
isNumber = false;
|
|
string = "true";
|
|
} else {
|
|
boolean = false;
|
|
isNumber = false;
|
|
string = "false";
|
|
}
|
|
} else if(!isNumber && !op.isNumber) {
|
|
if(string != op.string) {
|
|
boolean = true;
|
|
isNumber = false;
|
|
string = "true";
|
|
} else {
|
|
boolean = false;
|
|
isNumber = false;
|
|
string = "false";
|
|
}
|
|
}
|
|
}
|
|
if(in.ifNext("<=")) {
|
|
Operand op(in, tag);
|
|
if(isNumber && op.isNumber) {
|
|
if(doubleValue <= op.doubleValue) {
|
|
boolean = true;
|
|
isNumber = false;
|
|
string = "true";
|
|
} else {
|
|
boolean = false;
|
|
isNumber = false;
|
|
string = "false";
|
|
}
|
|
} else if(!isNumber && !op.isNumber) {
|
|
if(string <= op.string) {
|
|
boolean = true;
|
|
isNumber = false;
|
|
string = "true";
|
|
} else {
|
|
boolean = false;
|
|
isNumber = false;
|
|
string = "false";
|
|
}
|
|
}
|
|
}
|
|
if(in.ifNext(">=")) {
|
|
Operand op(in, tag);
|
|
if(isNumber && op.isNumber) {
|
|
if(doubleValue >= op.doubleValue) {
|
|
boolean = true;
|
|
isNumber = false;
|
|
string = "true";
|
|
} else {
|
|
boolean = false;
|
|
isNumber = false;
|
|
string = "false";
|
|
}
|
|
} else if(!isNumber && !op.isNumber) {
|
|
if(string >= op.string) {
|
|
boolean = true;
|
|
isNumber = false;
|
|
string = "true";
|
|
} else {
|
|
boolean = false;
|
|
isNumber = false;
|
|
string = "false";
|
|
}
|
|
}
|
|
}
|
|
if(in.ifNext("=")) {
|
|
Operand op(in, tag);
|
|
if(isNumber && op.isNumber) {
|
|
if(doubleValue == op.doubleValue) {
|
|
boolean = true;
|
|
isNumber = false;
|
|
string = "true";
|
|
} else {
|
|
boolean = false;
|
|
isNumber = false;
|
|
string = "false";
|
|
}
|
|
} else if(!isNumber && !op.isNumber) {
|
|
if(string == op.string) {
|
|
boolean = true;
|
|
isNumber = false;
|
|
string = "true";
|
|
} else {
|
|
boolean = false;
|
|
isNumber = false;
|
|
string = "false";
|
|
}
|
|
}
|
|
}
|
|
if(in.ifNext("<")) {
|
|
Operand op(in, tag);
|
|
if(isNumber && op.isNumber) {
|
|
if(doubleValue < op.doubleValue) {
|
|
boolean = true;
|
|
isNumber = false;
|
|
string = "true";
|
|
} else {
|
|
boolean = false;
|
|
isNumber = false;
|
|
string = "false";
|
|
}
|
|
} else if(!isNumber && !op.isNumber) {
|
|
if(string < op.string) {
|
|
boolean = true;
|
|
isNumber = false;
|
|
string = "true";
|
|
} else {
|
|
boolean = false;
|
|
isNumber = false;
|
|
string = "false";
|
|
}
|
|
}
|
|
}
|
|
if(in.ifNext(">")) {
|
|
Operand op(in, tag);
|
|
if(isNumber && op.isNumber) {
|
|
if(doubleValue > op.doubleValue) {
|
|
boolean = true;
|
|
isNumber = false;
|
|
string = "true";
|
|
} else {
|
|
boolean = false;
|
|
isNumber = false;
|
|
string = "false";
|
|
}
|
|
} else if(!isNumber && !op.isNumber) {
|
|
if(string > op.string) {
|
|
boolean = true;
|
|
isNumber = false;
|
|
string = "true";
|
|
} else {
|
|
boolean = false;
|
|
isNumber = false;
|
|
string = "false";
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if(in.ifNext("+")) {
|
|
if(isNumber) {
|
|
Operand op(in, tag);
|
|
if(op.isNumber) {
|
|
doubleValue += op.doubleValue;
|
|
string = std::format("{:.12f}", doubleValue);
|
|
string.removeTrailingZeros();
|
|
} else
|
|
throw coreutils::Exception("operand is not a number.");
|
|
} else
|
|
throw coreutils::Exception("operand is not a number.");
|
|
} else if(in.ifNext("-")) {
|
|
if(isNumber) {
|
|
Operand op(in, tag);
|
|
if(op.isNumber) {
|
|
doubleValue -= op.doubleValue;
|
|
string = std::format("{:.12f}", doubleValue);
|
|
string.removeTrailingZeros();
|
|
} else
|
|
throw coreutils::Exception("operand is not a number.");
|
|
} else
|
|
throw coreutils::Exception("operand is not a number.");
|
|
} else if(in.ifNext("*")) {
|
|
if(isNumber) {
|
|
Operand op(in, tag, true);
|
|
if(op.isNumber) {
|
|
doubleValue *= op.doubleValue;
|
|
string = std::format("{:.12f}", doubleValue);
|
|
string.removeTrailingZeros();
|
|
parseOperator();
|
|
} else
|
|
throw coreutils::Exception("operand is not a number.");
|
|
} else
|
|
throw coreutils::Exception("operand is not a number.");
|
|
} else if(in.ifNext("/")) {
|
|
if(isNumber) {
|
|
Operand op(in, tag, true);
|
|
if(op.isNumber) {
|
|
doubleValue /= op.doubleValue;
|
|
string = std::format("{:.12f}", doubleValue);
|
|
string.removeTrailingZeros();
|
|
parseOperator();
|
|
} else
|
|
throw coreutils::Exception("operand is not a number.");
|
|
} else
|
|
throw coreutils::Exception("operand is not a number.");
|
|
}
|
|
return true;
|
|
}
|
|
|
|
}
|