Allow the digit group separator, "_", in expressions.

pull/106/head
whitequark 2017-01-02 23:34:36 +00:00
parent daf3c7b070
commit 02ab358bd9
3 changed files with 32 additions and 12 deletions

View File

@ -43,6 +43,7 @@ Other new features:
* When zooming to fit, constraints are also considered. * When zooming to fit, constraints are also considered.
* When selecting a point and a line, projected distance to to current * When selecting a point and a line, projected distance to to current
workplane is displayed. workplane is displayed.
* In expressions, numbers can contain the digit group separator, "_".
* The "=" key is bound to "Zoom In", like "+" key. * The "=" key is bound to "Zoom In", like "+" key.
* The numpad decimal separator key is bound to "." regardless of locale. * The numpad decimal separator key is bound to "." regardless of locale.

View File

@ -612,7 +612,6 @@ public:
char ReadChar(); char ReadChar();
char PeekChar(); char PeekChar();
double ReadNumber();
std::string ReadWord(); std::string ReadWord();
void SkipSpace(); void SkipSpace();
@ -620,6 +619,7 @@ public:
Token PopOperand(std::string *error); Token PopOperand(std::string *error);
int Precedence(Token token); int Precedence(Token token);
Token LexNumber(std::string *error);
Token Lex(std::string *error); Token Lex(std::string *error);
bool Reduce(std::string *error); bool Reduce(std::string *error);
bool Parse(std::string *error, size_t reduceUntil = 0); bool Parse(std::string *error, size_t reduceUntil = 0);
@ -650,14 +650,6 @@ char ExprParser::PeekChar() {
return input[inputPos]; return input[inputPos];
} }
double ExprParser::ReadNumber() {
char *endptr;
double d = strtod(input + inputPos, &endptr);
unsigned len = endptr - (input + inputPos);
inputPos += len;
return d;
}
std::string ExprParser::ReadWord() { std::string ExprParser::ReadWord() {
std::string s; std::string s;
@ -676,6 +668,31 @@ void ExprParser::SkipSpace() {
} }
} }
ExprParser::Token ExprParser::LexNumber(std::string *error) {
std::string s;
while(char c = PeekChar()) {
if(!((c >= '0' && c <= '9') || c == 'e' || c == 'E' || c == '.' || c == '_')) break;
if(c == '_') {
ReadChar();
continue;
}
s.push_back(ReadChar());
}
char *endptr;
double d = strtod(s.c_str(), &endptr);
Token t = Token::From();
if(endptr == &*s.end()) {
t = Token::From(TokenType::OPERAND, Expr::Op::CONSTANT);
t.expr->v = d;
} else {
*error = "'" + s + "' is not a valid number";
}
return t;
}
ExprParser::Token ExprParser::Lex(std::string *error) { ExprParser::Token ExprParser::Lex(std::string *error) {
SkipSpace(); SkipSpace();
@ -705,9 +722,7 @@ ExprParser::Token ExprParser::Lex(std::string *error) {
*error = "'" + s + "' is not a valid variable, function or constant"; *error = "'" + s + "' is not a valid variable, function or constant";
} }
} else if(isdigit(c) || c == '.') { } else if(isdigit(c) || c == '.') {
double d = ReadNumber(); return LexNumber(error);
t = Token::From(TokenType::OPERAND, Expr::Op::CONSTANT);
t.expr->v = d;
} else if(ispunct(c)) { } else if(ispunct(c)) {
ReadChar(); ReadChar();
if(c == '+') { if(c == '+') {

View File

@ -26,6 +26,8 @@ TEST_CASE(literal) {
CHECK_TRUE(e->Eval() == 42); CHECK_TRUE(e->Eval() == 42);
CHECK_PARSE(e, "42.5"); CHECK_PARSE(e, "42.5");
CHECK_TRUE(e->Eval() == 42.5); CHECK_TRUE(e->Eval() == 42.5);
CHECK_PARSE(e, "1_000_000");
CHECK_TRUE(e->Eval() == 1000000);
} }
TEST_CASE(unary_ops) { TEST_CASE(unary_ops) {
@ -95,6 +97,8 @@ TEST_CASE(errors) {
"Unexpected character"); "Unexpected character");
CHECK_PARSE_ERR("notavar", CHECK_PARSE_ERR("notavar",
"'notavar' is not a valid variable, function or constant"); "'notavar' is not a valid variable, function or constant");
CHECK_PARSE_ERR("1e2e3",
"'1e2e3' is not a valid number");
CHECK_PARSE_ERR("_", CHECK_PARSE_ERR("_",
"'_' is not a valid operator"); "'_' is not a valid operator");
CHECK_PARSE_ERR("2 2", CHECK_PARSE_ERR("2 2",