// // Copyright 2016 Pixar // // Licensed under the Apache License, Version 2.0 (the "Apache License") // with the following modification; you may not use this file except in // compliance with the Apache License and the following modification to it: // Section 6. Trademarks. is deleted and replaced with: // // 6. Trademarks. This License does not grant permission to use the trade // names, trademarks, service marks, or product names of the Licensor // and its affiliates, except as required to comply with Section 4(c) of // the License and to reproduce the content of the NOTICE file. // // You may obtain a copy of the Apache License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the Apache License with the above modification is // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the Apache License for the specific // language governing permissions and limitations under the Apache License. // #ifndef PXR_USD_SDF_PARSER_HELPERS_H #define PXR_USD_SDF_PARSER_HELPERS_H #include "pxr/pxr.h" #include "pxr/usd/sdf/assetPath.h" #include "pxr/usd/sdf/valueTypeName.h" #include "pxr/base/arch/inttypes.h" #include "pxr/base/vt/value.h" #include #include #include #include #include #include #include PXR_NAMESPACE_OPEN_SCOPE bool Sdf_BoolFromString(const std::string &, bool *parseOk); namespace Sdf_ParserHelpers { // Internal variant type. typedef boost::variant _Variant; //////////////////////////////////////////////////////////////////////// // Utilities that implement the Sdf_ParserHelpers::Value::Get() method. The // _GetImpl class template provides the right implementation for T. There // are several partial specializations that provide the right behavior for // various types. // General Get case, requires exact match. template struct _GetImpl { typedef const T &ResultType; static const T &Visit(_Variant const &variant) { return boost::get(variant); } }; //////////////////////////////////////////////////////////////////////// // _GetImpl for integral type T. Convert finite doubles by static_cast, // throw bad_get for non-finite doubles. Throw bad_get for out-of-range // integral values. template struct _GetImpl< T, std::enable_if_t::value>> : public boost::static_visitor { typedef T ResultType; T Visit(_Variant const &variant) { return boost::apply_visitor(*this, variant); } // Fallback case: throw bad_get. template T operator()(Held held) { throw boost::bad_get(); } // Attempt to cast unsigned and signed int64_t. T operator()(uint64_t in) { return _Cast(in); } T operator()(int64_t in) { return _Cast(in); } // Attempt to cast finite doubles, throw otherwise. T operator()(double in) { if (std::isfinite(in)) return _Cast(in); throw boost::bad_get(); } private: template T _Cast(In in) { try { return boost::numeric_cast(in); } catch (const boost::bad_numeric_cast &) { throw boost::bad_get(); } } }; //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// // _GetImpl for floating point type T. Attempts to cast numeric values. // Also handles strings like "inf", "-inf", and "nan" to produce +/- infinity // and a quiet NaN. template struct _GetImpl< T, std::enable_if_t::value>> : public boost::static_visitor { typedef T ResultType; T Visit(_Variant const &variant) { return boost::apply_visitor(*this, variant); } // Fallback case: throw bad_get. template T operator()(Held held) { throw boost::bad_get(); } // For numeric types, attempt to cast. T operator()(uint64_t in) { return _Cast(in); } T operator()(int64_t in) { return _Cast(in); } T operator()(double in) { return static_cast(in); } // Convert special strings if possible. T operator()(const std::string &str) { return _FromString(str); } T operator()(const TfToken &tok) { return _FromString(tok.GetString()); } private: T _FromString(const std::string &str) const { // Special case the strings 'inf', '-inf' and 'nan'. if (str == "inf") return std::numeric_limits::infinity(); if (str == "-inf") return -std::numeric_limits::infinity(); if (str == "nan") return std::numeric_limits::quiet_NaN(); throw boost::bad_get(); } template T _Cast(In in) { try { return boost::numeric_cast(in); } catch (const boost::bad_numeric_cast &) { throw boost::bad_get(); } } }; //////////////////////////////////////////////////////////////////////// // Get an asset path: converts string to asset path, otherwise throw bad_get. template <> struct _GetImpl { typedef SdfAssetPath ResultType; SdfAssetPath Visit(_Variant const &variant) { if (std::string const *str = boost::get(&variant)) return SdfAssetPath(*str); return boost::get(variant); } }; // Get a bool. Numbers are considered true if nonzero, false otherwise. // Strings and tokens get parsed via Sdf_BoolFromString. Otherwise throw // bad_get. template <> struct _GetImpl : public boost::static_visitor { typedef bool ResultType; bool Visit(_Variant const &variant) { return boost::apply_visitor(*this, variant); } // Parse string via Sdf_BoolFromString. bool operator()(const std::string &str) { bool parseOK = false; bool result = Sdf_BoolFromString(str, &parseOK); if (!parseOK) throw boost::bad_get(); return result; } // Treat tokens as strings. bool operator()(const TfToken &tok) { return (*this)(tok.GetString()); } // For numbers, return true if not zero. template std::enable_if_t::value, bool> operator()(Number val) { return val != static_cast(0); } // For anything else, throw bad_get(). template std::enable_if_t::value, bool> operator()(T) { throw boost::bad_get(); } }; // A parser value. This is used as the fundamental value object in the text // parser. It can hold one of a few different types: (u)int64_t, double, // string, TfToken, and SdfAssetPath. The lexer only ever produces Value // objects holding (u)int64_t, double, and string. The presence of TfToken and // SdfAssetPath here are for a relatively obscure case where we're parsing a // value whose type is unknown to the parser. See // StartRecordingString/StopRecordingString/IsRecordingString in // parserValueContext.{cpp.h}. We'd like to change this. // // Value's primary function is to provide a Get() convenience API that // handles appropriate conversions from the held types. For example, it is okay // to call Get() on a Value that's holding an integral type, a double, or // a string if that string's value is one of 'inf', '-inf', or 'nan'. Similarly // Get() works on numbers and strings like 'yes', 'no', 'on', 'off', // 'true', 'false'. If a Get() call fails, it throws boost::bad_get, which // the parser responds to and raises a parse error. // // The lexer constructs Value objects from input tokens. It creates them to // retain all the input information possible. For example, negative integers // are stored as int64_t Values, positive numbers are stored as uint64_t values, // and so on. As a special case of this, '-0' is stored as a double, since it // is the only way to preserve a signed zero (integral types have no signed // zero). struct Value { // Default constructor leaves the value in an undefined state. Value() {} // Construct and implicitly convert from an integral type \p Int. If \p Int // is signed, the resulting value holds an 'int64_t' internally. If \p Int // is unsigned, the result value holds an 'uint64_t'. template Value(Int in, std::enable_if_t::value> * = 0) { if (std::is_signed::value) { _variant = static_cast(in); } else { _variant = static_cast(in); } } // Construct and implicitly convert from a floating point type \p Flt. The // resulting value holds a double internally. template Value(Flt in, std::enable_if_t::value> * = 0) : _variant(static_cast(in)) {} // Construct and implicitly convert from std::string. Value(const std::string &in) : _variant(in) {} // Construct and implicitly convert from TfToken. Value(const TfToken &in) : _variant(in) {} // Construct and implicitly convert from SdfAssetPath. Value(const SdfAssetPath &in) : _variant(in) {} // Attempt to get a value of type T from this Value, applying appropriate // conversions. If this value cannot be converted to T, throw // boost::bad_get. template typename _GetImpl::ResultType Get() const { return _GetImpl().Visit(_variant); } // Hopefully short-lived API that applies an external visitor to the held // variant type. template typename Visitor::result_type ApplyVisitor(const Visitor &visitor) { return boost::apply_visitor(visitor, _variant); } template typename Visitor::result_type ApplyVisitor(Visitor &visitor) { return boost::apply_visitor(visitor, _variant); } template typename Visitor::result_type ApplyVisitor(const Visitor &visitor) const { return _variant.apply_visitor(visitor); } template typename Visitor::result_type ApplyVisitor(Visitor &visitor) const { return _variant.apply_visitor(visitor); } private: _Variant _variant; }; typedef std::function const &, std::vector const &, size_t &, std::string *)> ValueFactoryFunc; struct ValueFactory { ValueFactory() {} ValueFactory(std::string typeName_, SdfTupleDimensions dimensions_, bool isShaped_, ValueFactoryFunc func_) : typeName(typeName_), dimensions(dimensions_), isShaped(isShaped_), func(func_) {} std::string typeName; SdfTupleDimensions dimensions; bool isShaped; ValueFactoryFunc func; }; ValueFactory const &GetValueFactoryForMenvaName(std::string const &name, bool *found); } /// Converts a string to a bool. /// Accepts case insensitive "yes", "no", "false", true", "0", "1". /// Defaults to "true" if the string is not recognized. /// /// If parseOK is supplied, the pointed-to bool will be set to indicate /// whether the parse was successful. bool Sdf_BoolFromString(const std::string &s, bool *parseOk = NULL); // Read the quoted string at [x..x+n], trimming 'trimBothSides' number // of chars from either side, and evaluating any embedded escaped characters. // If numLines is given, it will be populated with the number of newline // characters present in the original string. std::string Sdf_EvalQuotedString(const char* x, size_t n, size_t trimBothSides, unsigned int* numLines = NULL); // Read the string representing an asset path at [x..x+n]. If tripleDelimited // is true, the string is assumed to have 3 delimiters on both sides of the // asset path, otherwise the string is assumed to have just 1. std::string Sdf_EvalAssetPath(const char* x, size_t n, bool tripleDelimited); PXR_NAMESPACE_CLOSE_SCOPE #endif // PXR_USD_SDF_PARSER_HELPERS_H