From d3ac0bc149a9e62feec8e3a00f10ca88491e2254 Mon Sep 17 00:00:00 2001 From: Wouter van Oortmerssen Date: Wed, 15 Jun 2016 13:54:17 -0700 Subject: [PATCH] Added conversion operations that can be used inline in JSON. e.g.: { myfield: cos(rad(180)) } is equivalent to writing { myfield: -1.0 } Bug: 29338398 Change-Id: I6fc4ef1fd10bda3ba78cba464414dd071a2f50ca Tested: on Linux. --- docs/source/Schemas.md | 4 ++++ src/idl_parser.cpp | 34 ++++++++++++++++++++++++++++++++-- tests/test.cpp | 23 ++++++++++++++++------- 3 files changed, 52 insertions(+), 9 deletions(-) diff --git a/docs/source/Schemas.md b/docs/source/Schemas.md index 4004803de..14bd3bd0e 100755 --- a/docs/source/Schemas.md +++ b/docs/source/Schemas.md @@ -333,6 +333,10 @@ JSON: - A field that has the value `null` (e.g. `field: null`) is intended to have the default value for that field (thus has the same effect as if that field wasn't specified at all). +- It has some built in conversion functions, so you can write for example + `rad(180)` where ever you'd normally write `3.14159`. + Currently supports the following functions: `rad`, `deg`, `cos`, `sin`, + `tan`, `acos`, `asin`, `atan`. When parsing JSON, it recognizes the following escape codes in strings: diff --git a/src/idl_parser.cpp b/src/idl_parser.cpp index f5badab16..66e794b3d 100644 --- a/src/idl_parser.cpp +++ b/src/idl_parser.cpp @@ -17,6 +17,14 @@ #include #include +#ifdef _WIN32 +#if !defined(_USE_MATH_DEFINES) +#define _USE_MATH_DEFINES // For M_PI. +#endif // !defined(_USE_MATH_DEFINES) +#endif // _WIN32 + +#include + #include "flatbuffers/idl.h" #include "flatbuffers/util.h" @@ -1004,8 +1012,30 @@ CheckedError Parser::ParseHash(Value &e, FieldDef* field) { } CheckedError Parser::ParseSingleValue(Value &e) { - // First check if this could be a string/identifier enum value: - if (e.type.base_type != BASE_TYPE_STRING && + // First see if this could be a conversion function: + if (token_ == kTokenIdentifier && *cursor_ == '(') { + auto functionname = attribute_; + NEXT(); + EXPECT('('); + ECHECK(ParseSingleValue(e)); + EXPECT(')'); + #define FLATBUFFERS_FN_DOUBLE(name, op) \ + if (functionname == name) { \ + auto x = strtod(e.constant.c_str(), nullptr); \ + e.constant = NumToString(op); \ + } + FLATBUFFERS_FN_DOUBLE("deg", x / M_PI * 180); + FLATBUFFERS_FN_DOUBLE("rad", x * M_PI / 180); + FLATBUFFERS_FN_DOUBLE("sin", sin(x)); + FLATBUFFERS_FN_DOUBLE("cos", cos(x)); + FLATBUFFERS_FN_DOUBLE("tan", tan(x)); + FLATBUFFERS_FN_DOUBLE("asin", asin(x)); + FLATBUFFERS_FN_DOUBLE("acos", acos(x)); + FLATBUFFERS_FN_DOUBLE("atan", atan(x)); + // TODO(wvo): add more useful conversion functions here. + #undef FLATBUFFERS_FN_DOUBLE + // Then check if this could be a string/identifier enum value: + } else if (e.type.base_type != BASE_TYPE_STRING && e.type.base_type != BASE_TYPE_NONE && (token_ == kTokenIdentifier || token_ == kTokenStringConstant)) { if (IsIdentifierStart(attribute_[0])) { // Enum value. diff --git a/tests/test.cpp b/tests/test.cpp index bd85da231..73e009c20 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -814,20 +814,29 @@ void ErrorTest() { TestError("table X { Y:byte; } root_type X; { Y:1, Y:2 }", "more than once"); } -// Additional parser testing not covered elsewhere. -void ScientificTest() { +float TestValue(const char *json) { flatbuffers::Parser parser; // Simple schema. TEST_EQ(parser.Parse("table X { Y:float; } root_type X;"), true); - // Test scientific notation numbers. - TEST_EQ(parser.Parse("{ Y:0.0314159e+2 }"), true); + TEST_EQ(parser.Parse(json), true); auto root = flatbuffers::GetRoot(parser.builder_.GetBufferPointer()); // root will point to the table, which is a 32bit vtable offset followed // by a float: - TEST_EQ(sizeof(flatbuffers::soffset_t) == 4 && // Test assumes 32bit offsets - fabs(root[1] - 3.14159) < 0.001, true); + TEST_EQ(sizeof(flatbuffers::soffset_t), 4); // Test assumes 32bit offsets + return root[1]; +} + +bool FloatCompare(float a, float b) { return fabs(a - b) < 0.001; } + +// Additional parser testing not covered elsewhere. +void ValueTest() { + // Test scientific notation numbers. + TEST_EQ(FloatCompare(TestValue("{ Y:0.0314159e+2 }"), 3.14159), true); + + // Test conversion functions. + TEST_EQ(FloatCompare(TestValue("{ Y:cos(rad(180)) }"), -1), true); } void EnumStringsTest() { @@ -963,7 +972,7 @@ int main(int /*argc*/, const char * /*argv*/[]) { FuzzTest2(); ErrorTest(); - ScientificTest(); + ValueTest(); EnumStringsTest(); IntegerOutOfRangeTest(); UnicodeTest();