mirror of
https://github.com/lighttransport/tinyusdz.git
synced 2026-01-18 01:11:17 +01:00
4451 lines
110 KiB
C++
4451 lines
110 KiB
C++
// SPDX-License-Identifier: MIT
|
|
// Copyright 2021 - Present, Syoyo Fujita.
|
|
//
|
|
// To reduce compilation time and sections generated in .obj(object file),
|
|
// We split implementaion to multiple of .cc for ascii-parser.hh
|
|
|
|
#include <cstdio>
|
|
#ifdef _MSC_VER
|
|
#ifndef NOMINMAX
|
|
#define NOMINMAX
|
|
#endif
|
|
#endif
|
|
|
|
#include <algorithm>
|
|
#include <atomic>
|
|
//#include <cassert>
|
|
#include <cstdlib>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <iterator>
|
|
#include <map>
|
|
#include <set>
|
|
#include <sstream>
|
|
#include <stack>
|
|
#if defined(__wasi__)
|
|
#else
|
|
#include <mutex>
|
|
#include <thread>
|
|
#endif
|
|
#include <vector>
|
|
|
|
#include "ascii-parser.hh"
|
|
#include "str-util.hh"
|
|
#include "tiny-format.hh"
|
|
|
|
//
|
|
#if !defined(TINYUSDZ_DISABLE_MODULE_USDA_READER)
|
|
|
|
//
|
|
|
|
#ifdef __clang__
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Weverything"
|
|
#endif
|
|
|
|
// external
|
|
|
|
//#include "external/fast_float/include/fast_float/fast_float.h"
|
|
//#include "external/jsteemann/atoi.h"
|
|
//#include "external/simple_match/include/simple_match/simple_match.hpp"
|
|
#include "nonstd/expected.hpp"
|
|
|
|
//
|
|
|
|
#ifdef __clang__
|
|
#pragma clang diagnostic pop
|
|
#endif
|
|
|
|
//
|
|
|
|
// Tentative
|
|
#ifdef __clang__
|
|
#pragma clang diagnostic ignored "-Wunused-parameter"
|
|
#endif
|
|
|
|
#include "io-util.hh"
|
|
#include "pprinter.hh"
|
|
#include "prim-types.hh"
|
|
#include "str-util.hh"
|
|
#include "stream-reader.hh"
|
|
#include "tinyusdz.hh"
|
|
#include "value-pprint.hh"
|
|
#include "value-types.hh"
|
|
|
|
#if 0
|
|
// s = std::string
|
|
#define PUSH_ERROR_AND_RETURN(s) \
|
|
do { \
|
|
std::ostringstream ss_e; \
|
|
ss_e << __FILE__ << ":" << __func__ << "():" << __LINE__ << " "; \
|
|
ss_e << s; \
|
|
PushError(ss_e.str()); \
|
|
return false; \
|
|
} while (0)
|
|
|
|
#define PUSH_WARN(s) \
|
|
do { \
|
|
std::ostringstream ss_w; \
|
|
ss_w << __FILE__ << ":" << __func__ << "():" << __LINE__ << " "; \
|
|
ss_w << s; \
|
|
PushWarn(ss_w.str()); \
|
|
} while (0)
|
|
#endif
|
|
|
|
#include "common-macros.inc"
|
|
|
|
namespace tinyusdz {
|
|
|
|
namespace ascii {
|
|
|
|
constexpr auto kRel = "rel";
|
|
constexpr auto kTimeSamplesSuffix = ".timeSamples";
|
|
constexpr auto kConnectSuffix = ".connect";
|
|
|
|
constexpr auto kAscii = "[ASCII]";
|
|
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<bool>> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<int32_t>> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<uint32_t>> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<int64_t>> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<uint64_t>> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<value::half>> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<value::half2>> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<value::half3>> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<value::half4>> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<float>> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<value::float2>> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<value::float3>> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<value::float4>> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<double>> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<value::double2>> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<value::double3>> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<value::double4>> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<value::texcoord2h>> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<value::texcoord2f>> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<value::texcoord2d>> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<value::texcoord3h>> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<value::texcoord3f>> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<value::texcoord3d>> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<value::point3h>> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<value::point3f>> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<value::point3d>> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<value::normal3h>> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<value::normal3f>> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<value::normal3d>> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<value::vector3h>> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<value::vector3f>> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<value::vector3d>> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<value::color3h>> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<value::color3f>> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<value::color3d>> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<value::color4h>> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<value::color4f>> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<value::color4d>> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<value::matrix2d>> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<value::matrix3d>> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<value::matrix4d>> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<value::token>> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<value::StringData>> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<std::string>> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<Reference>> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<Path>> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<nonstd::optional<value::AssetPath>> *result);
|
|
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<bool> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<int32_t> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<uint32_t> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<int64_t> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<uint64_t> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::half> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::half2> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::half3> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::half4> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<float> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::float2> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::float3> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::float4> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<double> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::double2> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::double3> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::double4> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::texcoord2h> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::texcoord2f> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::texcoord2d> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::texcoord3h> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::texcoord3f> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::texcoord3d> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::point3h> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::point3f> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::point3d> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::normal3h> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::normal3f> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::normal3d> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::vector3h> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::vector3f> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::vector3d> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::color3h> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::color3f> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::color3d> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::color4h> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::color4f> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::color4d> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::matrix2d> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::matrix3d> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::matrix4d> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::token> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::StringData> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<std::string> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<Reference> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<Path> *result);
|
|
extern template bool AsciiParser::ParseBasicTypeArray(std::vector<value::AssetPath> *result);
|
|
|
|
static void RegisterStageMetas(
|
|
std::map<std::string, AsciiParser::VariableDef> &metas) {
|
|
metas.clear();
|
|
metas["doc"] = AsciiParser::VariableDef(value::kString, "doc");
|
|
metas["documentation"] =
|
|
AsciiParser::VariableDef(value::kString, "doc"); // alias to 'doc'
|
|
|
|
metas["comment"] = AsciiParser::VariableDef(value::kString, "comment");
|
|
|
|
// TODO: both support float and double?
|
|
metas["metersPerUnit"] =
|
|
AsciiParser::VariableDef(value::kDouble, "metersPerUnit");
|
|
metas["timeCodesPerSecond"] =
|
|
AsciiParser::VariableDef(value::kDouble, "timeCodesPerSecond");
|
|
metas["framesPerSecond"] =
|
|
AsciiParser::VariableDef(value::kDouble, "framesPerSecond");
|
|
|
|
metas["startTimeCode"] =
|
|
AsciiParser::VariableDef(value::kDouble, "startTimeCode");
|
|
metas["endTimeCode"] =
|
|
AsciiParser::VariableDef(value::kDouble, "endTimeCode");
|
|
|
|
metas["defaultPrim"] = AsciiParser::VariableDef(value::kToken, "defaultPrim");
|
|
metas["upAxis"] = AsciiParser::VariableDef(value::kToken, "upAxis");
|
|
metas["customLayerData"] =
|
|
AsciiParser::VariableDef(value::kDictionary, "customLayerData");
|
|
|
|
// Composition arc.
|
|
// Type can be array. i.e. asset, asset[]
|
|
metas["subLayers"] = AsciiParser::VariableDef(value::kAssetPath, "subLayers",
|
|
/* allow array type */ true);
|
|
|
|
// USDZ extension
|
|
metas["autoPlay"] = AsciiParser::VariableDef(value::kBool, "autoPlay");
|
|
metas["playbackMode"] = AsciiParser::VariableDef(value::kToken, "playbackMode");
|
|
|
|
}
|
|
|
|
static void RegisterPrimMetas(
|
|
std::map<std::string, AsciiParser::VariableDef> &metas) {
|
|
metas.clear();
|
|
|
|
metas["kind"] = AsciiParser::VariableDef(value::kToken, "kind");
|
|
metas["doc"] = AsciiParser::VariableDef(value::kString, "doc");
|
|
|
|
//
|
|
// Composition arcs -----------------------
|
|
//
|
|
|
|
// Type can be array. i.e. path, path[]
|
|
metas["references"] = AsciiParser::VariableDef("Reference", "references",
|
|
/* allow array type */ true);
|
|
metas["inherits"] = AsciiParser::VariableDef(value::kPath, "inherits", true);
|
|
metas["payload"] = AsciiParser::VariableDef("Reference", "payload", true);
|
|
metas["specializes"] =
|
|
AsciiParser::VariableDef(value::kPath, "specializes", true);
|
|
|
|
// Use `string`
|
|
metas["variantSets"] = AsciiParser::VariableDef(value::kString, "variantSets",
|
|
/* allow array type */ true);
|
|
|
|
// Parse as dict. TODO: Use ParseVariants()
|
|
metas["variants"] = AsciiParser::VariableDef(value::kDictionary, "variants");
|
|
|
|
// ------------------------------------------
|
|
|
|
metas["assetInfo"] =
|
|
AsciiParser::VariableDef(value::kDictionary, "assetInfo");
|
|
metas["customData"] =
|
|
AsciiParser::VariableDef(value::kDictionary, "customData");
|
|
|
|
metas["active"] = AsciiParser::VariableDef(value::kBool, "active");
|
|
metas["hidden"] = AsciiParser::VariableDef(value::kBool, "hidden");
|
|
|
|
// ListOp
|
|
metas["apiSchemas"] = AsciiParser::VariableDef(
|
|
value::Add1DArraySuffix(value::kToken), "apiSchemas");
|
|
|
|
// USDZ extension
|
|
metas["sceneName"] = AsciiParser::VariableDef(value::kString, "sceneName");
|
|
|
|
// Omniverse extension
|
|
// Builtin from pxrUSD 23.xx?
|
|
metas["displayName"] = AsciiParser::VariableDef(value::kString, "displayName");
|
|
}
|
|
|
|
static void RegisterPropMetas(
|
|
std::map<std::string, AsciiParser::VariableDef> &metas) {
|
|
metas.clear();
|
|
|
|
metas["doc"] = AsciiParser::VariableDef(value::kString, "doc");
|
|
metas["active"] = AsciiParser::VariableDef(value::kBool, "active");
|
|
metas["hidden"] = AsciiParser::VariableDef(value::kBool, "hidden");
|
|
metas["customData"] =
|
|
AsciiParser::VariableDef(value::kDictionary, "customData");
|
|
|
|
// usdSkel
|
|
metas["elementSize"] = AsciiParser::VariableDef(value::kInt, "elementSize");
|
|
|
|
// usdSkel?
|
|
metas["weight"] = AsciiParser::VariableDef(value::kDouble, "weight");
|
|
|
|
// usdShade?
|
|
metas["colorSpace"] = AsciiParser::VariableDef(value::kInt, "colorSpace");
|
|
|
|
metas["interpolation"] = AsciiParser::VariableDef(value::kToken, "interpolation");
|
|
}
|
|
|
|
|
|
static void RegisterPrimAttrTypes(std::set<std::string> &d) {
|
|
d.clear();
|
|
|
|
d.insert(value::kBool);
|
|
|
|
d.insert(value::kInt);
|
|
d.insert(value::kInt2);
|
|
d.insert(value::kInt3);
|
|
d.insert(value::kInt4);
|
|
|
|
d.insert(value::kFloat);
|
|
d.insert(value::kFloat2);
|
|
d.insert(value::kFloat3);
|
|
d.insert(value::kFloat4);
|
|
|
|
d.insert(value::kDouble);
|
|
d.insert(value::kDouble2);
|
|
d.insert(value::kDouble3);
|
|
d.insert(value::kDouble4);
|
|
|
|
d.insert(value::kHalf);
|
|
d.insert(value::kHalf2);
|
|
d.insert(value::kHalf3);
|
|
d.insert(value::kHalf4);
|
|
|
|
d.insert(value::kQuath);
|
|
d.insert(value::kQuatf);
|
|
d.insert(value::kQuatd);
|
|
|
|
d.insert(value::kNormal3f);
|
|
d.insert(value::kPoint3f);
|
|
d.insert(value::kTexCoord2h);
|
|
d.insert(value::kTexCoord3h);
|
|
d.insert(value::kTexCoord4h);
|
|
d.insert(value::kTexCoord2f);
|
|
d.insert(value::kTexCoord3f);
|
|
d.insert(value::kTexCoord4f);
|
|
d.insert(value::kTexCoord2d);
|
|
d.insert(value::kTexCoord3d);
|
|
d.insert(value::kTexCoord4d);
|
|
d.insert(value::kVector3f);
|
|
d.insert(value::kVector4f);
|
|
d.insert(value::kColor3h);
|
|
d.insert(value::kColor3f);
|
|
d.insert(value::kColor3d);
|
|
d.insert(value::kColor4h);
|
|
d.insert(value::kColor4f);
|
|
d.insert(value::kColor4d);
|
|
|
|
// It looks no `matrixNf` type for USDA
|
|
// d.insert(value::kMatrix2f);
|
|
// d.insert(value::kMatrix3f);
|
|
// d.insert(value::kMatrix4f);
|
|
|
|
d.insert(value::kMatrix2d);
|
|
d.insert(value::kMatrix3d);
|
|
d.insert(value::kMatrix4d);
|
|
|
|
d.insert(value::kToken);
|
|
d.insert(value::kString);
|
|
|
|
d.insert(value::kRelationship);
|
|
d.insert(value::kAssetPath);
|
|
|
|
d.insert(value::kDictionary);
|
|
|
|
// variantSet. Require special treatment.
|
|
d.insert("variantSet");
|
|
|
|
// TODO: Add more types...
|
|
}
|
|
|
|
static void RegisterPrimTypes(std::set<std::string> &d) {
|
|
d.insert("Xform");
|
|
d.insert("Sphere");
|
|
d.insert("Cube");
|
|
d.insert("Cone");
|
|
d.insert("Cylinder");
|
|
d.insert("Capsule");
|
|
d.insert("BasisCurves");
|
|
d.insert("Mesh");
|
|
d.insert("Points");
|
|
d.insert("GeomSubset");
|
|
d.insert("Scope");
|
|
d.insert("Material");
|
|
d.insert("NodeGraph");
|
|
d.insert("Shader");
|
|
d.insert("SphereLight");
|
|
d.insert("DomeLight");
|
|
d.insert("DiskLight");
|
|
d.insert("DistantLight");
|
|
d.insert("CylinderLight");
|
|
// d.insert("PortalLight");
|
|
d.insert("Camera");
|
|
d.insert("SkelRoot");
|
|
d.insert("Skeleton");
|
|
d.insert("SkelAnimation");
|
|
d.insert("BlendShape");
|
|
|
|
d.insert("GPrim");
|
|
}
|
|
|
|
// TinyUSDZ does not allow user-defined API schema at the moment
|
|
// (Primarily for security reason, secondary it requires re-design of Prim
|
|
// classes to support user-defined API schema)
|
|
static void RegisterAPISchemas(std::set<std::string> &d) {
|
|
d.insert("MaterialBindingAPI");
|
|
d.insert("SkelBindingAPI");
|
|
|
|
// TODO:
|
|
// d.insett("PhysicsCollisionAPI");
|
|
// d.insett("PhysicsRigidBodyAPI");
|
|
|
|
// TODO: Support Multi-apply API(`CollectionAPI`)
|
|
// d.insett("PhysicsLimitAPI");
|
|
// d.insett("PhysicsDriveAPI");
|
|
// d.insett("CollectionAPI");
|
|
}
|
|
|
|
namespace {
|
|
|
|
#if 0
|
|
// parseInt
|
|
// 0 = success
|
|
// -1 = bad input
|
|
// -2 = overflow
|
|
// -3 = underflow
|
|
int parseInt(const std::string &s, int *out_result) {
|
|
size_t n = s.size();
|
|
const char *c = s.c_str();
|
|
|
|
if ((c == nullptr) || (*c) == '\0') {
|
|
return -1;
|
|
}
|
|
|
|
size_t idx = 0;
|
|
bool negative = c[0] == '-';
|
|
if ((c[0] == '+') | (c[0] == '-')) {
|
|
idx = 1;
|
|
if (n == 1) {
|
|
// sign char only
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
int64_t result = 0;
|
|
|
|
// allow zero-padded digits(e.g. "003")
|
|
while (idx < n) {
|
|
if ((c[idx] >= '0') && (c[idx] <= '9')) {
|
|
int digit = int(c[idx] - '0');
|
|
result = result * 10 + digit;
|
|
} else {
|
|
// bad input
|
|
return -1;
|
|
}
|
|
|
|
if (negative) {
|
|
if ((-result) < (std::numeric_limits<int>::min)()) {
|
|
return -3;
|
|
}
|
|
} else {
|
|
if (result > (std::numeric_limits<int>::max)()) {
|
|
return -2;
|
|
}
|
|
}
|
|
|
|
idx++;
|
|
}
|
|
|
|
if (negative) {
|
|
(*out_result) = -int(result);
|
|
} else {
|
|
(*out_result) = int(result);
|
|
}
|
|
|
|
return 0; // OK
|
|
}
|
|
#endif
|
|
|
|
} // namespace
|
|
|
|
|
|
namespace {
|
|
|
|
using ReferenceList = std::vector<std::pair<ListEditQual, Reference>>;
|
|
|
|
// https://www.techiedelight.com/trim-string-cpp-remove-leading-trailing-spaces/
|
|
std::string TrimString(const std::string &str) {
|
|
const std::string WHITESPACE = " \n\r\t\f\v";
|
|
|
|
// remove leading and trailing whitespaces
|
|
std::string s = str;
|
|
{
|
|
size_t start = s.find_first_not_of(WHITESPACE);
|
|
s = (start == std::string::npos) ? "" : s.substr(start);
|
|
}
|
|
|
|
{
|
|
size_t end = s.find_last_not_of(WHITESPACE);
|
|
s = (end == std::string::npos) ? "" : s.substr(0, end + 1);
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
inline bool isChar(char c) { return std::isalpha(int(c)); }
|
|
|
|
inline bool hasConnect(const std::string &str) {
|
|
return endsWith(str, ".connect");
|
|
}
|
|
|
|
inline bool hasInputs(const std::string &str) {
|
|
return startsWith(str, "inputs:");
|
|
}
|
|
|
|
inline bool hasOutputs(const std::string &str) {
|
|
return startsWith(str, "outputs:");
|
|
}
|
|
|
|
inline bool is_digit(char x) {
|
|
return (static_cast<unsigned int>((x) - '0') < static_cast<unsigned int>(10));
|
|
}
|
|
|
|
#if 0
|
|
static nonstd::expected<float, std::string> ParseFloat(const std::string &s) {
|
|
// Parse with fast_float
|
|
float result;
|
|
auto ans = fast_float::from_chars(s.data(), s.data() + s.size(), result);
|
|
if (ans.ec != std::errc()) {
|
|
// Current `fast_float` implementation does not report detailed parsing err.
|
|
return nonstd::make_unexpected("Parse failed.");
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static nonstd::expected<double, std::string> ParseDouble(const std::string &s) {
|
|
// Parse with fast_float
|
|
double result;
|
|
auto ans = fast_float::from_chars(s.data(), s.data() + s.size(), result);
|
|
if (ans.ec != std::errc()) {
|
|
// Current `fast_float` implementation does not report detailed parsing err.
|
|
return nonstd::make_unexpected("Parse failed.");
|
|
}
|
|
|
|
return result;
|
|
}
|
|
#endif
|
|
|
|
void AsciiParser::SetBaseDir(const std::string &str) { _base_dir = str; }
|
|
|
|
void AsciiParser::SetStream(StreamReader *sr) { _sr = sr; }
|
|
|
|
std::string AsciiParser::GetError() {
|
|
if (err_stack.empty()) {
|
|
return std::string();
|
|
}
|
|
|
|
std::stringstream ss;
|
|
while (!err_stack.empty()) {
|
|
ErrorDiagnositc diag = err_stack.top();
|
|
|
|
ss << "Near line " << (diag.cursor.row + 1) << ", col "
|
|
<< (diag.cursor.col + 1) << ": ";
|
|
ss << diag.err << "\n";
|
|
|
|
err_stack.pop();
|
|
}
|
|
|
|
return ss.str();
|
|
}
|
|
|
|
std::string AsciiParser::GetWarning() {
|
|
if (warn_stack.empty()) {
|
|
return std::string();
|
|
}
|
|
|
|
std::stringstream ss;
|
|
while (!warn_stack.empty()) {
|
|
ErrorDiagnositc diag = warn_stack.top();
|
|
|
|
ss << "Near line " << (diag.cursor.row + 1) << ", col "
|
|
<< (diag.cursor.col + 1) << ": ";
|
|
ss << diag.err << "\n";
|
|
|
|
warn_stack.pop();
|
|
}
|
|
|
|
return ss.str();
|
|
}
|
|
|
|
// -- end basic
|
|
|
|
bool AsciiParser::ParseDictElement(std::string *out_key,
|
|
MetaVariable *out_var) {
|
|
(void)out_key;
|
|
(void)out_var;
|
|
|
|
// dict_element: type (array_qual?) name '=' value
|
|
// ;
|
|
|
|
std::string type_name;
|
|
|
|
if (!ReadIdentifier(&type_name)) {
|
|
return false;
|
|
}
|
|
|
|
if (!SkipWhitespace()) {
|
|
return false;
|
|
}
|
|
|
|
if (!IsSupportedPrimAttrType(type_name)) {
|
|
PUSH_ERROR_AND_RETURN("Unknown or unsupported type `" + type_name + "`\n");
|
|
}
|
|
|
|
// Has array qualifier? `[]`
|
|
bool array_qual = false;
|
|
{
|
|
char c0, c1;
|
|
if (!Char1(&c0)) {
|
|
return false;
|
|
}
|
|
|
|
if (c0 == '[') {
|
|
if (!Char1(&c1)) {
|
|
return false;
|
|
}
|
|
|
|
if (c1 == ']') {
|
|
array_qual = true;
|
|
} else {
|
|
// Invalid syntax
|
|
PushError("Invalid syntax found.\n");
|
|
return false;
|
|
}
|
|
|
|
} else {
|
|
if (!Rewind(1)) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!SkipWhitespace()) {
|
|
return false;
|
|
}
|
|
|
|
std::string key_name;
|
|
if (!ReadIdentifier(&key_name)) {
|
|
// string literal is also supported. e.g. "0"
|
|
if (ReadStringLiteral(&key_name)) {
|
|
// ok
|
|
} else {
|
|
PushError("Failed to parse dictionary key identifier.\n");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!SkipWhitespace()) {
|
|
return false;
|
|
}
|
|
|
|
if (!Expect('=')) {
|
|
return false;
|
|
}
|
|
|
|
if (!SkipWhitespace()) {
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// Supports limited types for customData/Dictionary.
|
|
// TODO: support more types?
|
|
//
|
|
primvar::PrimVar var;
|
|
if (type_name == value::kBool) {
|
|
bool val;
|
|
if (!ReadBasicType(&val)) {
|
|
PUSH_ERROR_AND_RETURN("Failed to parse `bool`");
|
|
}
|
|
var.set_value(val);
|
|
} else if (type_name == value::kInt) {
|
|
if (array_qual) {
|
|
std::vector<int32_t> vss;
|
|
if (!ParseBasicTypeArray(&vss)) {
|
|
PUSH_ERROR_AND_RETURN("Failed to parse `int[]`");
|
|
}
|
|
var.set_value(vss);
|
|
} else {
|
|
int32_t val;
|
|
if (!ReadBasicType(&val)) {
|
|
PUSH_ERROR_AND_RETURN("Failed to parse `int`");
|
|
}
|
|
var.set_value(val);
|
|
}
|
|
} else if (type_name == value::kUInt) {
|
|
if (array_qual) {
|
|
std::vector<uint32_t> vss;
|
|
if (!ParseBasicTypeArray(&vss)) {
|
|
PUSH_ERROR_AND_RETURN("Failed to parse `uint[]`");
|
|
}
|
|
var.set_value(vss);
|
|
} else {
|
|
uint32_t val;
|
|
if (!ReadBasicType(&val)) {
|
|
PUSH_ERROR_AND_RETURN("Failed to parse `uint`");
|
|
}
|
|
var.set_value(val);
|
|
}
|
|
} else if (type_name == value::kFloat) {
|
|
if (array_qual) {
|
|
std::vector<float> vss;
|
|
if (!ParseBasicTypeArray(&vss)) {
|
|
PUSH_ERROR_AND_RETURN("Failed to parse `float[]`");
|
|
}
|
|
var.set_value(vss);
|
|
} else {
|
|
float val;
|
|
if (!ReadBasicType(&val)) {
|
|
PUSH_ERROR_AND_RETURN("Failed to parse `float`");
|
|
}
|
|
var.set_value(val);
|
|
}
|
|
} else if (type_name == value::kDouble) {
|
|
if (array_qual) {
|
|
std::vector<double> vss;
|
|
if (!ParseBasicTypeArray(&vss)) {
|
|
PUSH_ERROR_AND_RETURN("Failed to parse `double[]`");
|
|
}
|
|
var.set_value(vss);
|
|
} else {
|
|
double str;
|
|
if (!ReadBasicType(&str)) {
|
|
PUSH_ERROR_AND_RETURN("Failed to parse `double`");
|
|
}
|
|
var.set_value(str);
|
|
}
|
|
} else if (type_name == value::kString) {
|
|
if (array_qual) {
|
|
std::vector<value::StringData> strs;
|
|
if (!ParseBasicTypeArray(&strs)) {
|
|
PUSH_ERROR_AND_RETURN("Failed to parse `string[]`");
|
|
}
|
|
var.set_value(strs);
|
|
} else {
|
|
value::StringData str;
|
|
if (!ReadBasicType(&str)) {
|
|
PUSH_ERROR_AND_RETURN("Failed to parse `string`");
|
|
}
|
|
var.set_value(str);
|
|
}
|
|
} else if (type_name == "token") {
|
|
if (array_qual) {
|
|
std::vector<value::token> toks;
|
|
if (!ParseBasicTypeArray(&toks)) {
|
|
PUSH_ERROR_AND_RETURN("Failed to parse `token[]`");
|
|
}
|
|
var.set_value(toks);
|
|
} else {
|
|
value::token tok;
|
|
if (!ReadBasicType(&tok)) {
|
|
PUSH_ERROR_AND_RETURN("Failed to parse `token`");
|
|
}
|
|
var.set_value(tok);
|
|
}
|
|
} else if (type_name == "dictionary") {
|
|
CustomDataType dict;
|
|
|
|
DCOUT("Parse dictionary");
|
|
if (!ParseDict(&dict)) {
|
|
PUSH_ERROR_AND_RETURN("Failed to parse `dictionary`");
|
|
}
|
|
var.set_value(dict);
|
|
} else {
|
|
PUSH_ERROR_AND_RETURN("TODO: type = " + type_name);
|
|
}
|
|
|
|
MetaVariable metavar;
|
|
metavar.set_value(key_name, var.value_raw());
|
|
|
|
#if 0
|
|
var.type = type_name;
|
|
if (array_qual) {
|
|
// TODO: 2D array
|
|
var.type += "[]";
|
|
}
|
|
var.name = key_name;
|
|
#endif
|
|
|
|
DCOUT("key: " << key_name << ", type: " << type_name);
|
|
|
|
(*out_key) = key_name;
|
|
(*out_var) = metavar;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool AsciiParser::MaybeCustom() {
|
|
std::string tok;
|
|
|
|
auto loc = CurrLoc();
|
|
bool ok = ReadIdentifier(&tok);
|
|
|
|
if (!ok) {
|
|
// revert
|
|
SeekTo(loc);
|
|
return false;
|
|
}
|
|
|
|
if (tok == "custom") {
|
|
// cosume `custom` token.
|
|
return true;
|
|
}
|
|
|
|
// revert
|
|
SeekTo(loc);
|
|
return false;
|
|
}
|
|
|
|
bool AsciiParser::ParseDict(std::map<std::string, MetaVariable> *out_dict) {
|
|
// '{' (type name '=' value)+ '}'
|
|
if (!Expect('{')) {
|
|
return false;
|
|
}
|
|
|
|
if (!SkipWhitespaceAndNewline()) {
|
|
return false;
|
|
}
|
|
|
|
while (!Eof()) {
|
|
char c;
|
|
if (!Char1(&c)) {
|
|
return false;
|
|
}
|
|
|
|
if (c == '}') {
|
|
break;
|
|
} else {
|
|
if (!Rewind(1)) {
|
|
return false;
|
|
}
|
|
|
|
std::string key;
|
|
MetaVariable var;
|
|
if (!ParseDictElement(&key, &var)) {
|
|
PUSH_ERROR_AND_RETURN("Failed to parse dict element.");
|
|
}
|
|
|
|
if (!SkipWhitespaceAndNewline()) {
|
|
return false;
|
|
}
|
|
|
|
if (!var.is_valid()) {
|
|
PUSH_ERROR_AND_RETURN("Invalid Dict element(probably internal issue).");
|
|
}
|
|
|
|
DCOUT("Add to dict: " << key);
|
|
(*out_dict)[key] = var;
|
|
}
|
|
}
|
|
|
|
if (!SkipWhitespaceAndNewline()) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool AsciiParser::ParseVariantsElement(std::string *out_key,
|
|
std::string *out_var) {
|
|
// variants_element: string name '=' value
|
|
// ;
|
|
|
|
std::string type_name;
|
|
|
|
if (!ReadIdentifier(&type_name)) {
|
|
return false;
|
|
}
|
|
|
|
// must be `string`
|
|
if (type_name != value::kString) {
|
|
PUSH_ERROR_AND_RETURN(
|
|
"TinyUSDZ only accepts type `string` for `variants` element.");
|
|
}
|
|
|
|
if (!SkipWhitespace()) {
|
|
return false;
|
|
}
|
|
|
|
std::string key_name;
|
|
if (!ReadIdentifier(&key_name)) {
|
|
// string literal is also supported. e.g. "0"
|
|
if (ReadStringLiteral(&key_name)) {
|
|
// ok
|
|
} else {
|
|
PushError("Failed to parse dictionary key identifier.\n");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!SkipWhitespace()) {
|
|
return false;
|
|
}
|
|
|
|
if (!Expect('=')) {
|
|
return false;
|
|
}
|
|
|
|
if (!SkipWhitespace()) {
|
|
return false;
|
|
}
|
|
|
|
std::string var;
|
|
if (!ReadBasicType(&var)) {
|
|
PUSH_ERROR_AND_RETURN("Failed to parse `string`");
|
|
}
|
|
|
|
DCOUT("key: " << key_name << ", value: " << var);
|
|
|
|
(*out_key) = key_name;
|
|
(*out_var) = var;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool AsciiParser::ParseVariants(VariantSelectionMap *out_map) {
|
|
// '{' (string name '=' value)+ '}'
|
|
if (!Expect('{')) {
|
|
return false;
|
|
}
|
|
|
|
if (!SkipWhitespaceAndNewline()) {
|
|
return false;
|
|
}
|
|
|
|
while (!Eof()) {
|
|
char c;
|
|
if (!Char1(&c)) {
|
|
return false;
|
|
}
|
|
|
|
if (c == '}') {
|
|
break;
|
|
} else {
|
|
if (!Rewind(1)) {
|
|
return false;
|
|
}
|
|
|
|
std::string key;
|
|
std::string var;
|
|
if (!ParseVariantsElement(&key, &var)) {
|
|
PUSH_ERROR_AND_RETURN("Failed to parse an element of `variants`.");
|
|
}
|
|
|
|
if (!SkipWhitespaceAndNewline()) {
|
|
return false;
|
|
}
|
|
|
|
DCOUT("Add to variants: " << key);
|
|
(*out_map)[key] = var;
|
|
}
|
|
}
|
|
|
|
if (!SkipWhitespaceAndNewline()) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// 'None'
|
|
bool AsciiParser::MaybeNone() {
|
|
std::vector<char> buf;
|
|
|
|
auto loc = CurrLoc();
|
|
|
|
if (!CharN(4, &buf)) {
|
|
SeekTo(loc);
|
|
return false;
|
|
}
|
|
|
|
if ((buf[0] == 'N') && (buf[1] == 'o') && (buf[2] == 'n') &&
|
|
(buf[3] == 'e')) {
|
|
// got it
|
|
return true;
|
|
}
|
|
|
|
SeekTo(loc);
|
|
|
|
return false;
|
|
}
|
|
|
|
bool AsciiParser::MaybeListEditQual(tinyusdz::ListEditQual *qual) {
|
|
if (!SkipWhitespace()) {
|
|
return false;
|
|
}
|
|
|
|
std::string tok;
|
|
|
|
auto loc = CurrLoc();
|
|
if (!ReadIdentifier(&tok)) {
|
|
SeekTo(loc);
|
|
return false;
|
|
}
|
|
|
|
if (tok == "prepend") {
|
|
DCOUT("`prepend` list edit qualifier.");
|
|
(*qual) = tinyusdz::ListEditQual::Prepend;
|
|
} else if (tok == "append") {
|
|
DCOUT("`append` list edit qualifier.");
|
|
(*qual) = tinyusdz::ListEditQual::Append;
|
|
} else if (tok == "add") {
|
|
DCOUT("`add` list edit qualifier.");
|
|
(*qual) = tinyusdz::ListEditQual::Add;
|
|
} else if (tok == "delete") {
|
|
DCOUT("`delete` list edit qualifier.");
|
|
(*qual) = tinyusdz::ListEditQual::Delete;
|
|
} else {
|
|
DCOUT("No ListEdit qualifier.");
|
|
// unqualified
|
|
// rewind
|
|
SeekTo(loc);
|
|
(*qual) = tinyusdz::ListEditQual::ResetToExplicit;
|
|
}
|
|
|
|
if (!SkipWhitespace()) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool AsciiParser::IsSupportedPrimType(const std::string &ty) {
|
|
return _supported_prim_types.count(ty);
|
|
}
|
|
|
|
|
|
bool AsciiParser::IsSupportedPrimAttrType(const std::string &ty) {
|
|
return _supported_prim_attr_types.count(ty);
|
|
}
|
|
|
|
bool AsciiParser::IsSupportedAPISchema(const std::string &ty) {
|
|
return _supported_api_schemas.count(ty);
|
|
}
|
|
|
|
bool AsciiParser::ReadStringLiteral(std::string *literal) {
|
|
std::stringstream ss;
|
|
|
|
char c0;
|
|
if (!Char1(&c0)) {
|
|
return false;
|
|
}
|
|
|
|
// TODO: Allow triple-quotated string?
|
|
|
|
bool single_quote{false};
|
|
|
|
if (c0 == '"') {
|
|
// ok
|
|
} else if (c0 == '\'') {
|
|
// ok
|
|
single_quote = true;
|
|
} else {
|
|
DCOUT("c0 = " << c0);
|
|
PUSH_ERROR_AND_RETURN(
|
|
"String or Token literal expected but it does not start with \" or '");
|
|
}
|
|
|
|
bool end_with_quotation{false};
|
|
|
|
while (!Eof()) {
|
|
char c;
|
|
if (!Char1(&c)) {
|
|
// this should not happen.
|
|
return false;
|
|
}
|
|
|
|
if ((c == '\n') || (c == '\r')) {
|
|
PUSH_ERROR_AND_RETURN("New line in string literal.");
|
|
}
|
|
|
|
if (single_quote) {
|
|
if (c == '\'') {
|
|
end_with_quotation = true;
|
|
break;
|
|
}
|
|
} else if (c == '"') {
|
|
end_with_quotation = true;
|
|
break;
|
|
}
|
|
|
|
ss << c;
|
|
}
|
|
|
|
if (!end_with_quotation) {
|
|
PUSH_ERROR_AND_RETURN(
|
|
fmt::format("String literal expected but it does not end with {}.",
|
|
single_quote ? "'" : "\""));
|
|
}
|
|
|
|
(*literal) = ss.str();
|
|
|
|
_curr_cursor.col += int(literal->size() + 2); // +2 for quotation chars
|
|
|
|
return true;
|
|
}
|
|
|
|
bool AsciiParser::MaybeString(value::StringData *str) {
|
|
std::stringstream ss;
|
|
|
|
if (!str) {
|
|
return false;
|
|
}
|
|
|
|
auto loc = CurrLoc();
|
|
auto start_cursor = _curr_cursor;
|
|
|
|
char c0;
|
|
if (!Char1(&c0)) {
|
|
SeekTo(loc);
|
|
return false;
|
|
}
|
|
|
|
// ' or " allowed.
|
|
if ((c0 != '"') && (c0 != '\'')) {
|
|
SeekTo(loc);
|
|
return false;
|
|
}
|
|
|
|
bool single_quote = (c0 == '\'');
|
|
|
|
bool end_with_quotation{false};
|
|
|
|
while (!Eof()) {
|
|
char c;
|
|
if (!Char1(&c)) {
|
|
// this should not happen.
|
|
SeekTo(loc);
|
|
return false;
|
|
}
|
|
|
|
if ((c == '\n') || (c == '\r')) {
|
|
SeekTo(loc);
|
|
return false;
|
|
}
|
|
|
|
if (single_quote) {
|
|
if (c == '\'') {
|
|
end_with_quotation = true;
|
|
break;
|
|
}
|
|
} else {
|
|
if (c == '"') {
|
|
end_with_quotation = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
ss << c;
|
|
}
|
|
|
|
if (!end_with_quotation) {
|
|
SeekTo(loc);
|
|
return false;
|
|
}
|
|
|
|
DCOUT("Single quoted string found. col " << start_cursor.col << ", row "
|
|
<< start_cursor.row);
|
|
|
|
// Unescape backslash required.
|
|
size_t displayed_string_len = ss.str().size();
|
|
str->value = unescapeBackslash(ss.str());
|
|
str->line_col = start_cursor.col;
|
|
str->line_row = start_cursor.row;
|
|
str->is_triple_quoted = false;
|
|
|
|
_curr_cursor.col += int(displayed_string_len + 2); // +2 for quotation chars
|
|
|
|
return true;
|
|
}
|
|
|
|
bool AsciiParser::MaybeTripleQuotedString(value::StringData *str) {
|
|
std::stringstream ss;
|
|
|
|
auto loc = CurrLoc();
|
|
auto start_cursor = _curr_cursor;
|
|
|
|
std::vector<char> triple_quote;
|
|
if (!CharN(3, &triple_quote)) {
|
|
SeekTo(loc);
|
|
return false;
|
|
}
|
|
|
|
if (triple_quote.size() != 3) {
|
|
SeekTo(loc);
|
|
return false;
|
|
}
|
|
|
|
bool single_quote = false;
|
|
|
|
if (triple_quote[0] == '"' && triple_quote[1] == '"' &&
|
|
triple_quote[2] == '"') {
|
|
// ok
|
|
} else if (triple_quote[0] == '\'' && triple_quote[1] == '\'' &&
|
|
triple_quote[2] == '\'') {
|
|
// ok
|
|
single_quote = true;
|
|
} else {
|
|
SeekTo(loc);
|
|
return false;
|
|
}
|
|
|
|
// Read until next triple-quote `"""`
|
|
std::stringstream str_buf;
|
|
|
|
auto locinfo = _curr_cursor;
|
|
|
|
int single_quote_count = 0; // '
|
|
int double_quote_count = 0; // "
|
|
|
|
bool got_triple_quote{false};
|
|
|
|
while (!Eof()) {
|
|
char c;
|
|
|
|
if (!Char1(&c)) {
|
|
SeekTo(loc);
|
|
return false;
|
|
}
|
|
|
|
str_buf << c;
|
|
|
|
if (c == '"') {
|
|
double_quote_count++;
|
|
single_quote_count = 0;
|
|
} else if (c == '\'') {
|
|
double_quote_count = 0;
|
|
single_quote_count++;
|
|
} else {
|
|
double_quote_count = 0;
|
|
single_quote_count = 0;
|
|
}
|
|
|
|
// Update loc info
|
|
locinfo.col++;
|
|
if (c == '\n') {
|
|
locinfo.col = 0;
|
|
locinfo.row++;
|
|
} else if (c == '\r') {
|
|
// CRLF?
|
|
if (_sr->tell() < (_sr->size() - 1)) {
|
|
char d;
|
|
if (!Char1(&d)) {
|
|
// this should not happen.
|
|
SeekTo(loc);
|
|
return false;
|
|
}
|
|
|
|
if (d == '\n') {
|
|
// CRLF
|
|
str_buf << d;
|
|
} else {
|
|
// unwind 1 char
|
|
if (!_sr->seek_from_current(-1)) {
|
|
// this should not happen.
|
|
SeekTo(loc);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
locinfo.col = 0;
|
|
locinfo.row++;
|
|
}
|
|
|
|
if (double_quote_count == 3) {
|
|
// got '"""'
|
|
got_triple_quote = true;
|
|
break;
|
|
}
|
|
if (single_quote_count == 3) {
|
|
// got '''
|
|
got_triple_quote = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!got_triple_quote) {
|
|
SeekTo(loc);
|
|
return false;
|
|
}
|
|
|
|
DCOUT("single_quote = " << single_quote);
|
|
DCOUT("Triple quoted string found. col " << start_cursor.col << ", row "
|
|
<< start_cursor.row);
|
|
|
|
// remove last '"""' or '''
|
|
str->single_quote = single_quote;
|
|
std::string s = str_buf.str();
|
|
if (s.size() > 3) { // just in case
|
|
s.erase(s.size() - 3);
|
|
}
|
|
// Unescape backslash required.
|
|
str->value = unescapeBackslash(s);
|
|
str->line_col = start_cursor.col;
|
|
str->line_row = start_cursor.row;
|
|
str->is_triple_quoted = true;
|
|
|
|
_curr_cursor = locinfo;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool AsciiParser::ReadPrimAttrIdentifier(std::string *token) {
|
|
// Example:
|
|
// - xformOp:transform
|
|
// - primvars:uvmap1
|
|
|
|
std::stringstream ss;
|
|
|
|
while (!Eof()) {
|
|
char c;
|
|
if (!Char1(&c)) {
|
|
// this should not happen.
|
|
return false;
|
|
}
|
|
|
|
if (c == '_') {
|
|
// ok
|
|
} else if (c == ':') { // namespace
|
|
// ':' must lie in the middle of string literal
|
|
if (ss.str().size() == 0) {
|
|
PushError("PrimAttr name must not starts with `:`\n");
|
|
return false;
|
|
}
|
|
} else if (c == '.') { // delimiter for `connect`
|
|
// '.' must lie in the middle of string literal
|
|
if (ss.str().size() == 0) {
|
|
PushError("PrimAttr name must not starts with `.`\n");
|
|
return false;
|
|
}
|
|
} else if (std::isalnum(int(c))) {
|
|
// number must not be allowed for the first char.
|
|
if (ss.str().size() == 0) {
|
|
if (!std::isalpha(int(c))) {
|
|
PushError("PrimAttr name must not starts with number.\n");
|
|
return false;
|
|
}
|
|
}
|
|
} else {
|
|
_sr->seek_from_current(-1);
|
|
break;
|
|
}
|
|
|
|
_curr_cursor.col++;
|
|
|
|
ss << c;
|
|
}
|
|
|
|
// ':' must lie in the middle of string literal
|
|
if (ss.str().back() == ':') {
|
|
PushError("PrimAttr name must not ends with `:`\n");
|
|
return false;
|
|
}
|
|
|
|
// '.' must lie in the middle of string literal
|
|
if (ss.str().back() == '.') {
|
|
PushError("PrimAttr name must not ends with `.`\n");
|
|
return false;
|
|
}
|
|
|
|
// Currently we only support '.connect'
|
|
std::string tok = ss.str();
|
|
|
|
if (contains(tok, '.')) {
|
|
if (endsWith(tok, ".connect") || endsWith(tok, ".timeSamples")) {
|
|
// OK
|
|
} else {
|
|
PUSH_ERROR_AND_RETURN_TAG(
|
|
kAscii, fmt::format("Must ends with `.connect` or `.timeSamples` for "
|
|
"attrbute name: `{}`",
|
|
tok));
|
|
}
|
|
|
|
// Multiple `.` is not allowed(e.g. attr.connect.timeSamples)
|
|
if (counts(tok, '.') > 1) {
|
|
PUSH_ERROR_AND_RETURN_TAG(
|
|
kAscii, fmt::format("Attribute identifier `{}` containing multiple "
|
|
"`.` is not allowed.",
|
|
tok));
|
|
}
|
|
}
|
|
|
|
(*token) = ss.str();
|
|
DCOUT("primAttr identifier = " << (*token));
|
|
return true;
|
|
}
|
|
|
|
bool AsciiParser::ReadIdentifier(std::string *token) {
|
|
// identifier = (`_` | [a-zA-Z]) (`_` | [a-zA-Z0-9]+)
|
|
std::stringstream ss;
|
|
|
|
// The first character.
|
|
{
|
|
char c;
|
|
if (!Char1(&c)) {
|
|
// this should not happen.
|
|
DCOUT("read1 failed.");
|
|
return false;
|
|
}
|
|
|
|
if (c == '_') {
|
|
// ok
|
|
} else if (!std::isalpha(int(c))) {
|
|
DCOUT(fmt::format("Invalid identiefier: '{}'", c));
|
|
_sr->seek_from_current(-1);
|
|
return false;
|
|
}
|
|
_curr_cursor.col++;
|
|
|
|
ss << c;
|
|
}
|
|
|
|
while (!Eof()) {
|
|
char c;
|
|
if (!Char1(&c)) {
|
|
// this should not happen.
|
|
return false;
|
|
}
|
|
|
|
if (c == '_') {
|
|
// ok
|
|
} else if (!std::isalnum(int(c))) {
|
|
_sr->seek_from_current(-1);
|
|
break; // end of identifier(e.g. ' ')
|
|
}
|
|
|
|
_curr_cursor.col++;
|
|
|
|
ss << c;
|
|
}
|
|
|
|
(*token) = ss.str();
|
|
return true;
|
|
}
|
|
|
|
bool AsciiParser::ReadPathIdentifier(std::string *path_identifier) {
|
|
// path_identifier = `<` string `>`
|
|
std::stringstream ss;
|
|
|
|
if (!Expect('<')) {
|
|
return false;
|
|
}
|
|
|
|
if (!SkipWhitespace()) {
|
|
return false;
|
|
}
|
|
|
|
// read until '>'
|
|
bool ok = false;
|
|
while (!Eof()) {
|
|
char c;
|
|
if (!Char1(&c)) {
|
|
// this should not happen.
|
|
return false;
|
|
}
|
|
|
|
if (c == '>') {
|
|
// end
|
|
ok = true;
|
|
_curr_cursor.col++;
|
|
break;
|
|
}
|
|
|
|
// TODO: Check if character is valid for path identifier
|
|
ss << c;
|
|
}
|
|
|
|
if (!ok) {
|
|
return false;
|
|
}
|
|
|
|
(*path_identifier) = TrimString(ss.str());
|
|
// std::cout << "PathIdentifier: " << (*path_identifier) << "\n";
|
|
|
|
return true;
|
|
}
|
|
|
|
bool AsciiParser::SkipUntilNewline() {
|
|
while (!Eof()) {
|
|
char c;
|
|
if (!Char1(&c)) {
|
|
// this should not happen.
|
|
return false;
|
|
}
|
|
|
|
if (c == '\n') {
|
|
break;
|
|
} else if (c == '\r') {
|
|
// CRLF?
|
|
if (_sr->tell() < (_sr->size() - 1)) {
|
|
char d;
|
|
if (!Char1(&d)) {
|
|
// this should not happen.
|
|
return false;
|
|
}
|
|
|
|
if (d == '\n') {
|
|
break;
|
|
}
|
|
|
|
// unwind 1 char
|
|
if (!_sr->seek_from_current(-1)) {
|
|
// this should not happen.
|
|
return false;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
} else {
|
|
// continue
|
|
}
|
|
}
|
|
|
|
_curr_cursor.row++;
|
|
_curr_cursor.col = 0;
|
|
return true;
|
|
}
|
|
|
|
// metadata_opt := string_literal '\n'
|
|
// | var '=' value '\n'
|
|
//
|
|
bool AsciiParser::ParseStageMetaOpt() {
|
|
// Maybe string-only comment.
|
|
// Comment cannot have multiple lines. The last one wins
|
|
{
|
|
value::StringData str;
|
|
if (MaybeTripleQuotedString(&str)) {
|
|
_stage_metas.comment = str;
|
|
return true;
|
|
} else if (MaybeString(&str)) {
|
|
_stage_metas.comment = str;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
std::string varname;
|
|
if (!ReadIdentifier(&varname)) {
|
|
return false;
|
|
}
|
|
|
|
DCOUT("varname = " << varname);
|
|
|
|
if (!IsStageMeta(varname)) {
|
|
std::string msg = "'" + varname + "' is not a Stage Metadata variable.\n";
|
|
PushError(msg);
|
|
return false;
|
|
}
|
|
|
|
if (!Expect('=')) {
|
|
PUSH_ERROR_AND_RETURN("'=' expected in Stage Metadata opt.");
|
|
return false;
|
|
}
|
|
|
|
if (!SkipWhitespace()) {
|
|
return false;
|
|
}
|
|
|
|
const VariableDef &vardef = _supported_stage_metas.at(varname);
|
|
MetaVariable var;
|
|
if (!ParseMetaValue(vardef, &var)) {
|
|
PushError("Failed to parse meta value.\n");
|
|
return false;
|
|
}
|
|
var.set_name(varname);
|
|
|
|
if (varname == "defaultPrim") {
|
|
value::token tok;
|
|
if (var.get_value(&tok)) {
|
|
DCOUT("defaultPrim = " << tok);
|
|
_stage_metas.defaultPrim = tok;
|
|
} else {
|
|
PUSH_ERROR_AND_RETURN("`defaultPrim` isn't a token value.");
|
|
}
|
|
} else if (varname == "subLayers") {
|
|
std::vector<value::AssetPath> paths;
|
|
if (var.get_value(&paths)) {
|
|
DCOUT("subLayers = " << paths);
|
|
for (const auto &item : paths) {
|
|
_stage_metas.subLayers.push_back(item);
|
|
}
|
|
} else {
|
|
PUSH_ERROR_AND_RETURN("`subLayers` isn't an array of asset path");
|
|
}
|
|
} else if (varname == "upAxis") {
|
|
if (auto pv = var.get_value<value::token>()) {
|
|
DCOUT("upAxis = " << pv.value());
|
|
const std::string s = pv.value().str();
|
|
if (s == "X") {
|
|
_stage_metas.upAxis = Axis::X;
|
|
} else if (s == "Y") {
|
|
_stage_metas.upAxis = Axis::Y;
|
|
} else if (s == "Z") {
|
|
_stage_metas.upAxis = Axis::Z;
|
|
} else {
|
|
PUSH_ERROR_AND_RETURN(
|
|
"Invalid `upAxis` value. Must be \"X\", \"Y\" or \"Z\", but got "
|
|
"\"" +
|
|
s + "\"(Note: Case sensitive)");
|
|
}
|
|
} else {
|
|
PUSH_ERROR_AND_RETURN("`upAxis` isn't a token value.");
|
|
}
|
|
} else if ((varname == "doc") || (varname == "documentation")) {
|
|
// `documentation` will be shorten to `doc`
|
|
if (auto pv = var.get_value<value::StringData>()) {
|
|
DCOUT("doc = " << to_string(pv.value()));
|
|
_stage_metas.doc = pv.value();
|
|
} else if (auto pvs = var.get_value<std::string>()) {
|
|
value::StringData sdata;
|
|
sdata.value = pvs.value();
|
|
sdata.is_triple_quoted = false;
|
|
_stage_metas.doc = sdata;
|
|
} else {
|
|
PUSH_ERROR_AND_RETURN(fmt::format("`{}` isn't a string value.", varname));
|
|
}
|
|
} else if (varname == "metersPerUnit") {
|
|
DCOUT("ty = " << var.type_name());
|
|
if (auto pv = var.get_value<float>()) {
|
|
DCOUT("metersPerUnit = " << pv.value());
|
|
_stage_metas.metersPerUnit = double(pv.value());
|
|
} else if (auto pvd = var.get_value<double>()) {
|
|
DCOUT("metersPerUnit = " << pvd.value());
|
|
_stage_metas.metersPerUnit = pvd.value();
|
|
} else {
|
|
PUSH_ERROR_AND_RETURN("`metersPerUnit` isn't a floating-point value.");
|
|
}
|
|
} else if (varname == "timeCodesPerSecond") {
|
|
DCOUT("ty = " << var.type_name());
|
|
if (auto pv = var.get_value<float>()) {
|
|
DCOUT("metersPerUnit = " << pv.value());
|
|
_stage_metas.timeCodesPerSecond = double(pv.value());
|
|
} else if (auto pvd = var.get_value<double>()) {
|
|
DCOUT("metersPerUnit = " << pvd.value());
|
|
_stage_metas.timeCodesPerSecond = pvd.value();
|
|
} else {
|
|
PUSH_ERROR_AND_RETURN(
|
|
"`timeCodesPerSecond` isn't a floating-point value.");
|
|
}
|
|
} else if (varname == "startTimeCode") {
|
|
if (auto pv = var.get_value<float>()) {
|
|
DCOUT("startTimeCode = " << pv.value());
|
|
_stage_metas.startTimeCode = double(pv.value());
|
|
} else if (auto pvd = var.get_value<double>()) {
|
|
DCOUT("startTimeCode = " << pvd.value());
|
|
_stage_metas.startTimeCode = pvd.value();
|
|
}
|
|
} else if (varname == "endTimeCode") {
|
|
if (auto pv = var.get_value<float>()) {
|
|
DCOUT("endTimeCode = " << pv.value());
|
|
_stage_metas.endTimeCode = double(pv.value());
|
|
} else if (auto pvd = var.get_value<double>()) {
|
|
DCOUT("endTimeCode = " << pvd.value());
|
|
_stage_metas.endTimeCode = pvd.value();
|
|
}
|
|
} else if (varname == "framesPerSecond") {
|
|
if (auto pv = var.get_value<float>()) {
|
|
DCOUT("framesPerSecond = " << pv.value());
|
|
_stage_metas.framesPerSecond = double(pv.value());
|
|
} else if (auto pvd = var.get_value<double>()) {
|
|
DCOUT("framesPerSecond = " << pvd.value());
|
|
_stage_metas.framesPerSecond = pvd.value();
|
|
}
|
|
} else if (varname == "apiSchemas") {
|
|
// TODO: ListEdit qualifer check
|
|
if (auto pv = var.get_value<std::vector<value::token>>()) {
|
|
for (auto &item : pv.value()) {
|
|
if (IsSupportedAPISchema(item.str())) {
|
|
// OK
|
|
} else {
|
|
PUSH_ERROR_AND_RETURN("\"" << item.str()
|
|
<< "\" is not supported(at the moment) "
|
|
"for `apiSchemas` in TinyUSDZ.");
|
|
}
|
|
}
|
|
} else {
|
|
PUSH_ERROR_AND_RETURN("`apiSchemas` isn't an `token[]` type.");
|
|
}
|
|
} else if (varname == "customLayerData") {
|
|
if (auto pv = var.get_value<CustomDataType>()) {
|
|
_stage_metas.customLayerData = pv.value();
|
|
} else {
|
|
PUSH_ERROR_AND_RETURN("`customLayerData` isn't a dictionary value.");
|
|
}
|
|
} else if (varname == "comment") {
|
|
if (auto pv = var.get_value<value::StringData>()) {
|
|
DCOUT("comment = " << to_string(pv.value()));
|
|
_stage_metas.comment = pv.value();
|
|
} else if (auto pvs = var.get_value<std::string>()) {
|
|
value::StringData sdata;
|
|
sdata.value = pvs.value();
|
|
sdata.is_triple_quoted = false;
|
|
_stage_metas.comment = sdata;
|
|
} else {
|
|
PUSH_ERROR_AND_RETURN(fmt::format("`{}` isn't a string value.", varname));
|
|
}
|
|
} else {
|
|
DCOUT("TODO: Stage meta: " << varname);
|
|
PUSH_WARN("TODO: Stage meta: " << varname);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Parse Stage meta
|
|
// meta = ( metadata_opt )
|
|
// ;
|
|
bool AsciiParser::ParseStageMetas() {
|
|
if (!Expect('(')) {
|
|
return false;
|
|
}
|
|
|
|
if (!SkipCommentAndWhitespaceAndNewline()) {
|
|
return false;
|
|
}
|
|
|
|
while (!Eof()) {
|
|
char c;
|
|
if (!LookChar1(&c)) {
|
|
return false;
|
|
}
|
|
|
|
if (c == ')') {
|
|
if (!SeekTo(CurrLoc() + 1)) {
|
|
return false;
|
|
}
|
|
|
|
if (!SkipCommentAndWhitespaceAndNewline()) {
|
|
return false;
|
|
}
|
|
|
|
DCOUT("Stage metas end");
|
|
|
|
// end
|
|
return true;
|
|
|
|
} else {
|
|
if (!SkipWhitespace()) {
|
|
// eof
|
|
return false;
|
|
}
|
|
|
|
if (!ParseStageMetaOpt()) {
|
|
// parse error
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!SkipCommentAndWhitespaceAndNewline()) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
DCOUT("ParseStageMetas end");
|
|
return true;
|
|
}
|
|
|
|
// `#` style comment
|
|
bool AsciiParser::ParseSharpComment() {
|
|
char c;
|
|
if (!Char1(&c)) {
|
|
// eol
|
|
return false;
|
|
}
|
|
|
|
if (c != '#') {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Fetch 1 char. Do not change input stream position.
|
|
bool AsciiParser::LookChar1(char *c) {
|
|
if (!Char1(c)) {
|
|
return false;
|
|
}
|
|
|
|
Rewind(1);
|
|
|
|
return true;
|
|
}
|
|
|
|
// Fetch N chars. Do not change input stream position.
|
|
bool AsciiParser::LookCharN(size_t n, std::vector<char> *nc) {
|
|
std::vector<char> buf(n);
|
|
|
|
auto loc = CurrLoc();
|
|
|
|
bool ok = _sr->read(n, n, reinterpret_cast<uint8_t *>(buf.data()));
|
|
if (ok) {
|
|
(*nc) = buf;
|
|
}
|
|
|
|
SeekTo(loc);
|
|
|
|
return ok;
|
|
}
|
|
|
|
bool AsciiParser::Char1(char *c) { return _sr->read1(c); }
|
|
|
|
bool AsciiParser::CharN(size_t n, std::vector<char> *nc) {
|
|
std::vector<char> buf(n);
|
|
|
|
bool ok = _sr->read(n, n, reinterpret_cast<uint8_t *>(buf.data()));
|
|
if (ok) {
|
|
(*nc) = buf;
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
bool AsciiParser::Rewind(size_t offset) {
|
|
if (!_sr->seek_from_current(-int64_t(offset))) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
uint64_t AsciiParser::CurrLoc() { return _sr->tell(); }
|
|
|
|
bool AsciiParser::SeekTo(uint64_t pos) {
|
|
if (!_sr->seek_set(pos)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool AsciiParser::PushParserState() {
|
|
// Stack size must be less than the number of input bytes.
|
|
if (parse_stack.size() >= _sr->size()) {
|
|
PUSH_ERROR_AND_RETURN_TAG(kAscii, "Parser state stack become too deep.");
|
|
}
|
|
|
|
uint64_t loc = _sr->tell();
|
|
|
|
ParseState state;
|
|
state.loc = int64_t(loc);
|
|
parse_stack.push(state);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool AsciiParser::PopParserState(ParseState *state) {
|
|
if (parse_stack.empty()) {
|
|
return false;
|
|
}
|
|
|
|
(*state) = parse_stack.top();
|
|
|
|
parse_stack.pop();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool AsciiParser::SkipWhitespace() {
|
|
while (!Eof()) {
|
|
char c;
|
|
if (!Char1(&c)) {
|
|
// this should not happen.
|
|
return false;
|
|
}
|
|
_curr_cursor.col++;
|
|
|
|
if ((c == ' ') || (c == '\t') || (c == '\f')) {
|
|
// continue
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// unwind 1 char
|
|
if (!_sr->seek_from_current(-1)) {
|
|
return false;
|
|
}
|
|
_curr_cursor.col--;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool AsciiParser::SkipWhitespaceAndNewline(const bool allow_semicolon) {
|
|
// USDA also allow C-style ';' as a newline separator.
|
|
while (!Eof()) {
|
|
char c;
|
|
if (!Char1(&c)) {
|
|
// this should not happen.
|
|
return false;
|
|
}
|
|
|
|
// printf("sws c = %c\n", c);
|
|
|
|
if ((c == ' ') || (c == '\t') || (c == '\f')) {
|
|
_curr_cursor.col++;
|
|
// continue
|
|
} else if (allow_semicolon && (c == ';')) {
|
|
_curr_cursor.col++;
|
|
// continue
|
|
} else if (c == '\n') {
|
|
_curr_cursor.col = 0;
|
|
_curr_cursor.row++;
|
|
// continue
|
|
} else if (c == '\r') {
|
|
// CRLF?
|
|
if (_sr->tell() < (_sr->size() - 1)) {
|
|
char d;
|
|
if (!Char1(&d)) {
|
|
// this should not happen.
|
|
return false;
|
|
}
|
|
|
|
if (d == '\n') {
|
|
// CRLF
|
|
} else {
|
|
// unwind 1 char
|
|
if (!_sr->seek_from_current(-1)) {
|
|
// this should not happen.
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
_curr_cursor.col = 0;
|
|
_curr_cursor.row++;
|
|
// continue
|
|
} else {
|
|
// end loop
|
|
if (!_sr->seek_from_current(-1)) {
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool AsciiParser::SkipCommentAndWhitespaceAndNewline(const bool allow_semicolon) {
|
|
// Skip multiple line of comments.
|
|
while (!Eof()) {
|
|
char c;
|
|
if (!Char1(&c)) {
|
|
// this should not happen.
|
|
return false;
|
|
}
|
|
|
|
// printf("sws c = %c\n", c);
|
|
|
|
if (c == '#') {
|
|
if (!SkipUntilNewline()) {
|
|
return false;
|
|
}
|
|
} else if ((c == ' ') || (c == '\t') || (c == '\f')) {
|
|
_curr_cursor.col++;
|
|
// continue
|
|
} else if (allow_semicolon && (c == ';')) {
|
|
_curr_cursor.col++;
|
|
// continue
|
|
} else if (c == '\n') {
|
|
_curr_cursor.col = 0;
|
|
_curr_cursor.row++;
|
|
// continue
|
|
} else if (c == '\r') {
|
|
// CRLF?
|
|
if (_sr->tell() < (_sr->size() - 1)) {
|
|
char d;
|
|
if (!Char1(&d)) {
|
|
// this should not happen.
|
|
return false;
|
|
}
|
|
|
|
if (d == '\n') {
|
|
// CRLF
|
|
} else {
|
|
// unwind 1 char
|
|
if (!_sr->seek_from_current(-1)) {
|
|
// this should not happen.
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
_curr_cursor.col = 0;
|
|
_curr_cursor.row++;
|
|
// continue
|
|
} else {
|
|
// std::cout << "unwind\n";
|
|
// end loop
|
|
if (!_sr->seek_from_current(-1)) {
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool AsciiParser::Expect(char expect_c) {
|
|
if (!SkipWhitespace()) {
|
|
return false;
|
|
}
|
|
|
|
char c;
|
|
if (!Char1(&c)) {
|
|
// this should not happen.
|
|
return false;
|
|
}
|
|
|
|
bool ret = (c == expect_c);
|
|
|
|
if (!ret) {
|
|
std::string msg = "Expected `" + std::string(&expect_c, 1) + "` but got `" +
|
|
std::string(&c, 1) + "`\n";
|
|
PushError(msg);
|
|
|
|
// unwind
|
|
_sr->seek_from_current(-1);
|
|
} else {
|
|
_curr_cursor.col++;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Parse magic
|
|
// #usda FLOAT
|
|
bool AsciiParser::ParseMagicHeader() {
|
|
if (!SkipWhitespace()) {
|
|
return false;
|
|
}
|
|
|
|
if (Eof()) {
|
|
return false;
|
|
}
|
|
|
|
{
|
|
char magic[6];
|
|
if (!_sr->read(6, 6, reinterpret_cast<uint8_t *>(magic))) {
|
|
// eol
|
|
return false;
|
|
}
|
|
|
|
if ((magic[0] == '#') && (magic[1] == 'u') && (magic[2] == 's') &&
|
|
(magic[3] == 'd') && (magic[4] == 'a') && (magic[5] == ' ')) {
|
|
// ok
|
|
} else {
|
|
PUSH_ERROR_AND_RETURN(
|
|
"Magic header must start with `#usda `(at least single whitespace "
|
|
"after 'a') but got `" +
|
|
std::string(magic, 6));
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!SkipWhitespace()) {
|
|
// eof
|
|
return false;
|
|
}
|
|
|
|
// current we only accept "1.0"
|
|
{
|
|
char ver[3];
|
|
if (!_sr->read(3, 3, reinterpret_cast<uint8_t *>(ver))) {
|
|
return false;
|
|
}
|
|
|
|
if ((ver[0] == '1') && (ver[1] == '.') && (ver[2] == '0')) {
|
|
// ok
|
|
_version = 1.0f;
|
|
} else {
|
|
PUSH_ERROR_AND_RETURN("Version must be `1.0` but got `" +
|
|
std::string(ver, 3) + "`");
|
|
}
|
|
}
|
|
|
|
SkipUntilNewline();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool AsciiParser::ParseCustomMetaValue() {
|
|
// type identifier '=' value
|
|
|
|
// return ParseAttributeMeta();
|
|
PUSH_ERROR_AND_RETURN("TODO");
|
|
}
|
|
|
|
bool AsciiParser::ParseAssetIdentifier(value::AssetPath *out,
|
|
bool *triple_deliminated) {
|
|
// @...@
|
|
// or @@@...@@@ (Triple '@'-deliminated asset identifier.)
|
|
// @@@ = Path containing '@'. '@@@' in Path is encoded as '\@@@'
|
|
//
|
|
// Example:
|
|
// @bora@
|
|
// @@@bora@@@
|
|
// @@@bora\@@@dora@@@
|
|
|
|
// TODO: Correctly support escape characters
|
|
|
|
// look ahead.
|
|
std::vector<char> buf;
|
|
uint64_t curr = _sr->tell();
|
|
bool maybe_triple{false};
|
|
|
|
if (!SkipWhitespaceAndNewline()) {
|
|
return false;
|
|
}
|
|
|
|
if (CharN(3, &buf)) {
|
|
if (buf[0] == '@' && buf[1] == '@' && buf[2] == '@') {
|
|
maybe_triple = true;
|
|
}
|
|
}
|
|
|
|
bool valid{false};
|
|
|
|
if (!maybe_triple) {
|
|
// std::cout << "maybe single-'@' asset reference\n";
|
|
|
|
SeekTo(curr);
|
|
char s;
|
|
if (!Char1(&s)) {
|
|
return false;
|
|
}
|
|
|
|
if (s != '@') {
|
|
std::string sstr{s};
|
|
PUSH_ERROR_AND_RETURN("Reference must start with '@', but got '" + sstr +
|
|
"'");
|
|
}
|
|
|
|
std::string tok;
|
|
|
|
// Read until '@'
|
|
bool found_delimiter = false;
|
|
while (!Eof()) {
|
|
char c;
|
|
|
|
if (!Char1(&c)) {
|
|
return false;
|
|
}
|
|
|
|
if (c == '@') {
|
|
found_delimiter = true;
|
|
break;
|
|
}
|
|
|
|
tok += c;
|
|
}
|
|
|
|
if (found_delimiter) {
|
|
(*out) = tok;
|
|
(*triple_deliminated) = false;
|
|
|
|
valid = true;
|
|
}
|
|
|
|
} else {
|
|
bool found_delimiter{false};
|
|
bool escape_sequence{false};
|
|
int at_cnt{0};
|
|
std::string tok;
|
|
|
|
// Read until '@@@' appears
|
|
// Need to escaped '@@@'("\\@@@")
|
|
while (!Eof()) {
|
|
char c;
|
|
|
|
if (!Char1(&c)) {
|
|
return false;
|
|
}
|
|
|
|
if (c == '\\') {
|
|
escape_sequence = true;
|
|
}
|
|
|
|
if (c == '@') {
|
|
at_cnt++;
|
|
} else {
|
|
at_cnt--;
|
|
if (at_cnt < 0) {
|
|
at_cnt = 0;
|
|
}
|
|
}
|
|
|
|
tok += c;
|
|
|
|
if (at_cnt == 3) {
|
|
if (escape_sequence) {
|
|
// Still in path identifier...
|
|
// Unescape "\\@@@"
|
|
|
|
if (tok.size() > 3) { // this should be true.
|
|
if (endsWith(tok, "\\@@@")) { // this also should be true.
|
|
tok.erase(tok.size() - 4);
|
|
tok.append("@@@");
|
|
}
|
|
}
|
|
at_cnt = 0;
|
|
escape_sequence = false;
|
|
} else {
|
|
// Got it. '@@@'
|
|
found_delimiter = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (found_delimiter) {
|
|
// remote last '@@@'
|
|
(*out) = removeSuffix(tok, "@@@");
|
|
(*triple_deliminated) = true;
|
|
|
|
valid = true;
|
|
}
|
|
}
|
|
|
|
return valid;
|
|
}
|
|
|
|
// TODO: Return Path
|
|
bool AsciiParser::ParseReference(Reference *out, bool *triple_deliminated) {
|
|
/*
|
|
Asset reference = AsssetIdentifier + optially followd by prim path
|
|
|
|
Example:
|
|
@bora@
|
|
@bora@</dora>
|
|
*/
|
|
|
|
value::AssetPath ap;
|
|
if (!ParseAssetIdentifier(&ap, triple_deliminated)) {
|
|
PUSH_ERROR_AND_RETURN_TAG(kAscii, "Failed to parse asset path identifier.");
|
|
}
|
|
out->asset_path = ap;
|
|
|
|
// Parse optional prim_path
|
|
if (!SkipWhitespace()) {
|
|
return false;
|
|
}
|
|
|
|
{
|
|
char c;
|
|
if (!Char1(&c)) {
|
|
return false;
|
|
}
|
|
|
|
if (c == '<') {
|
|
if (!Rewind(1)) {
|
|
return false;
|
|
}
|
|
|
|
std::string path;
|
|
if (!ReadPathIdentifier(&path)) {
|
|
return false;
|
|
}
|
|
|
|
out->prim_path = Path(path, "");
|
|
} else {
|
|
if (!Rewind(1)) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool AsciiParser::ParseMetaValue(const VariableDef &def, MetaVariable *outvar) {
|
|
const std::string vartype = def.type;
|
|
const std::string varname = def.name;
|
|
|
|
MetaVariable var;
|
|
|
|
bool is_array_type{false};
|
|
if (def.allow_array_type) {
|
|
// Seek '['
|
|
char c;
|
|
if (LookChar1(&c)) {
|
|
if (c == '[') {
|
|
is_array_type = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO: Refactor.
|
|
if (vartype == value::kBool) {
|
|
bool value;
|
|
if (!ReadBasicType(&value)) {
|
|
PUSH_ERROR_AND_RETURN("Boolean value expected for `" + varname + "`.");
|
|
}
|
|
DCOUT("bool = " << value);
|
|
|
|
var.set_value(value);
|
|
} else if (vartype == value::kToken) {
|
|
if (is_array_type) {
|
|
std::vector<value::token> value;
|
|
if (!ParseBasicTypeArray(&value)) {
|
|
PUSH_ERROR_AND_RETURN_TAG(
|
|
kAscii, fmt::format("token[] expected for `{}`.", varname));
|
|
}
|
|
DCOUT("token[] = " << value);
|
|
|
|
var.set_value(value);
|
|
} else {
|
|
value::token value;
|
|
if (!ReadBasicType(&value)) {
|
|
std::string msg = "Token expected for `" + varname + "`.\n";
|
|
PushError(msg);
|
|
return false;
|
|
}
|
|
DCOUT("token = " << value);
|
|
|
|
var.set_value(value);
|
|
}
|
|
} else if (vartype == "token[]") {
|
|
std::vector<value::token> value;
|
|
if (!ParseBasicTypeArray(&value)) {
|
|
std::string msg = "Token array expected for `" + varname + "`.\n";
|
|
PushError(msg);
|
|
return false;
|
|
}
|
|
// TODO
|
|
// DCOUT("token[] = " << to_string(value));
|
|
|
|
var.set_value(value);
|
|
} else if (vartype == value::kString) {
|
|
value::StringData sdata;
|
|
if (MaybeTripleQuotedString(&sdata)) {
|
|
var.set_value(sdata);
|
|
} else {
|
|
std::string value;
|
|
if (!ReadStringLiteral(&value)) {
|
|
PUSH_ERROR_AND_RETURN("String literal expected for `" + varname + "`.");
|
|
}
|
|
var.set_value(value);
|
|
}
|
|
|
|
} else if (vartype == "string[]") {
|
|
// TODO: Support multi-line string?
|
|
std::vector<std::string> values;
|
|
if (!ParseBasicTypeArray(&values)) {
|
|
return false;
|
|
}
|
|
|
|
var.set_value(values);
|
|
} else if (vartype == "ref[]") {
|
|
std::vector<Reference> values;
|
|
if (!ParseBasicTypeArray(&values)) {
|
|
PUSH_ERROR_AND_RETURN("Array of Reference expected for `" + varname +
|
|
"`.");
|
|
}
|
|
|
|
var.set_value(values);
|
|
|
|
} else if (vartype == "int[]") {
|
|
std::vector<int32_t> values;
|
|
if (!ParseBasicTypeArray(&values)) {
|
|
// std::string msg = "Array of int values expected for `" + var.name +
|
|
// "`.\n"; PushError(msg);
|
|
return false;
|
|
}
|
|
|
|
for (size_t i = 0; i < values.size(); i++) {
|
|
DCOUT("int[" << i << "] = " << values[i]);
|
|
}
|
|
|
|
var.set_value(values);
|
|
} else if (vartype == "float[]") {
|
|
std::vector<float> values;
|
|
if (!ParseBasicTypeArray(&values)) {
|
|
return false;
|
|
}
|
|
|
|
var.set_value(values);
|
|
} else if (vartype == "float2[]") {
|
|
std::vector<value::float2> values;
|
|
if (!ParseBasicTypeArray(&values)) {
|
|
return false;
|
|
}
|
|
|
|
var.set_value(values);
|
|
} else if (vartype == "float3[]") {
|
|
std::vector<value::float3> values;
|
|
if (!ParseBasicTypeArray(&values)) {
|
|
return false;
|
|
}
|
|
|
|
var.set_value(values);
|
|
} else if (vartype == "float4[]") {
|
|
std::vector<value::float4> values;
|
|
if (!ParseBasicTypeArray(&values)) {
|
|
return false;
|
|
}
|
|
|
|
var.set_value(values);
|
|
} else if (vartype == "double[]") {
|
|
std::vector<double> values;
|
|
if (!ParseBasicTypeArray(&values)) {
|
|
return false;
|
|
}
|
|
|
|
var.set_value(values);
|
|
} else if (vartype == "double2[]") {
|
|
std::vector<value::double2> values;
|
|
if (!ParseBasicTypeArray(&values)) {
|
|
return false;
|
|
}
|
|
|
|
var.set_value(values);
|
|
} else if (vartype == "double3[]") {
|
|
std::vector<value::double3> values;
|
|
if (!ParseBasicTypeArray(&values)) {
|
|
return false;
|
|
}
|
|
|
|
var.set_value(values);
|
|
} else if (vartype == "double4[]") {
|
|
std::vector<value::double4> values;
|
|
if (!ParseBasicTypeArray(&values)) {
|
|
return false;
|
|
}
|
|
|
|
var.set_value(values);
|
|
} else if (vartype == value::kFloat) {
|
|
float value;
|
|
if (!ReadBasicType(&value)) {
|
|
return false;
|
|
}
|
|
var.set_value(value);
|
|
} else if (vartype == value::kDouble) {
|
|
double value;
|
|
if (!ReadBasicType(&value)) {
|
|
return false;
|
|
}
|
|
var.set_value(value);
|
|
} else if (vartype == "int2") {
|
|
value::int2 value;
|
|
if (!ReadBasicType(&value)) {
|
|
return false;
|
|
}
|
|
|
|
var.set_value(value);
|
|
|
|
} else if (vartype == "int3") {
|
|
value::int3 value;
|
|
if (!ReadBasicType(&value)) {
|
|
return false;
|
|
}
|
|
|
|
var.set_value(value);
|
|
} else if (vartype == "int4") {
|
|
value::int4 value;
|
|
if (!ReadBasicType(&value)) {
|
|
return false;
|
|
}
|
|
|
|
var.set_value(value);
|
|
} else if (vartype == value::kPath) {
|
|
if (is_array_type) {
|
|
std::vector<Path> paths;
|
|
if (!ParseBasicTypeArray(&paths)) {
|
|
PUSH_ERROR_AND_RETURN_TAG(
|
|
kAscii,
|
|
fmt::format("Failed to parse `{}` in Prim metadatum.", def.name));
|
|
}
|
|
var.set_value(paths);
|
|
|
|
} else {
|
|
Path path;
|
|
if (!ReadBasicType(&path)) {
|
|
PUSH_ERROR_AND_RETURN_TAG(
|
|
kAscii,
|
|
fmt::format("Failed to parse `{}` in Prim metadatum.", def.name));
|
|
}
|
|
var.set_value(path);
|
|
}
|
|
|
|
} else if (vartype == value::kAssetPath) {
|
|
if (is_array_type) {
|
|
std::vector<value::AssetPath> paths;
|
|
if (!ParseBasicTypeArray(&paths)) {
|
|
PUSH_ERROR_AND_RETURN_TAG(
|
|
kAscii,
|
|
fmt::format("Failed to parse `{}` in Prim metadataum.", def.name));
|
|
}
|
|
var.set_value(paths);
|
|
} else {
|
|
value::AssetPath asset_path;
|
|
if (!ReadBasicType(&asset_path)) {
|
|
PUSH_ERROR_AND_RETURN_TAG(
|
|
kAscii,
|
|
fmt::format("Failed to parse `{}` in Prim metadataum.", def.name));
|
|
}
|
|
var.set_value(asset_path);
|
|
}
|
|
} else if (vartype == "Reference") {
|
|
if (is_array_type) {
|
|
std::vector<Reference> refs;
|
|
if (!ParseBasicTypeArray(&refs)) {
|
|
PUSH_ERROR_AND_RETURN_TAG(
|
|
kAscii,
|
|
fmt::format("Failed to parse `{}` in Prim metadataum.", def.name));
|
|
}
|
|
var.set_value(refs);
|
|
} else {
|
|
nonstd::optional<Reference> ref;
|
|
if (!ReadBasicType(&ref)) {
|
|
PUSH_ERROR_AND_RETURN_TAG(
|
|
kAscii,
|
|
fmt::format("Failed to parse `{}` in Prim metadataum.", def.name));
|
|
}
|
|
if (ref) {
|
|
var.set_value(ref.value());
|
|
} else {
|
|
// None
|
|
var.set_value(value::ValueBlock());
|
|
}
|
|
}
|
|
} else if (vartype == value::kDictionary) {
|
|
DCOUT("Parse dict in meta.");
|
|
CustomDataType dict;
|
|
if (!ParseDict(&dict)) {
|
|
PUSH_ERROR_AND_RETURN("Failed to parse `dictonary` data in metadataum.");
|
|
}
|
|
var.set_value(dict);
|
|
} else {
|
|
PUSH_ERROR_AND_RETURN("TODO: vartype = " + vartype);
|
|
}
|
|
|
|
#if 0
|
|
if (is_array_type) {
|
|
var.type = vartype + "[]";
|
|
} else {
|
|
var.type = vartype;
|
|
}
|
|
#endif
|
|
|
|
(*outvar) = var;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool AsciiParser::LexFloat(std::string *result) {
|
|
// FLOATVAL : ('+' or '-')? FLOAT
|
|
// FLOAT
|
|
// : ('0'..'9')+ '.' ('0'..'9')* EXPONENT?
|
|
// | '.' ('0'..'9')+ EXPONENT?
|
|
// | ('0'..'9')+ EXPONENT
|
|
// ;
|
|
// EXPONENT : ('e'|'E') ('+'|'-')? ('0'..'9')+ ;
|
|
|
|
std::stringstream ss;
|
|
|
|
bool has_sign{false};
|
|
bool leading_decimal_dots{false};
|
|
{
|
|
char sc;
|
|
if (!Char1(&sc)) {
|
|
return false;
|
|
}
|
|
_curr_cursor.col++;
|
|
|
|
ss << sc;
|
|
|
|
// sign, '.' or [0-9]
|
|
if ((sc == '+') || (sc == '-')) {
|
|
has_sign = true;
|
|
|
|
char c;
|
|
if (!Char1(&c)) {
|
|
return false;
|
|
}
|
|
|
|
if (c == '.') {
|
|
// ok. something like `+.7`, `-.53`
|
|
leading_decimal_dots = true;
|
|
_curr_cursor.col++;
|
|
ss << c;
|
|
|
|
} else {
|
|
// unwind and continue
|
|
_sr->seek_from_current(-1);
|
|
}
|
|
|
|
} else if ((sc >= '0') && (sc <= '9')) {
|
|
// ok
|
|
} else if (sc == '.') {
|
|
// ok
|
|
leading_decimal_dots = true;
|
|
} else {
|
|
PUSH_ERROR_AND_RETURN("Sign or `.` or 0-9 expected.");
|
|
}
|
|
}
|
|
|
|
(void)has_sign;
|
|
|
|
// 1. Read the integer part
|
|
char curr;
|
|
if (!leading_decimal_dots) {
|
|
// std::cout << "1 read int part: ss = " << ss.str() << "\n";
|
|
|
|
while (!Eof()) {
|
|
if (!Char1(&curr)) {
|
|
return false;
|
|
}
|
|
|
|
// std::cout << "1 curr = " << curr << "\n";
|
|
if ((curr >= '0') && (curr <= '9')) {
|
|
// continue
|
|
ss << curr;
|
|
} else {
|
|
_sr->seek_from_current(-1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Eof()) {
|
|
(*result) = ss.str();
|
|
return true;
|
|
}
|
|
|
|
if (!Char1(&curr)) {
|
|
return false;
|
|
}
|
|
|
|
// std::cout << "before 2: ss = " << ss.str() << ", curr = " << curr <<
|
|
// "\n";
|
|
|
|
// 2. Read the decimal part
|
|
if (curr == '.') {
|
|
ss << curr;
|
|
|
|
while (!Eof()) {
|
|
if (!Char1(&curr)) {
|
|
return false;
|
|
}
|
|
|
|
if ((curr >= '0') && (curr <= '9')) {
|
|
ss << curr;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
} else if ((curr == 'e') || (curr == 'E')) {
|
|
// go to 3.
|
|
} else {
|
|
// end
|
|
(*result) = ss.str();
|
|
_sr->seek_from_current(-1);
|
|
return true;
|
|
}
|
|
|
|
if (Eof()) {
|
|
(*result) = ss.str();
|
|
return true;
|
|
}
|
|
|
|
// 3. Read the exponent part
|
|
bool has_exp_sign{false};
|
|
if ((curr == 'e') || (curr == 'E')) {
|
|
ss << curr;
|
|
|
|
if (!Char1(&curr)) {
|
|
return false;
|
|
}
|
|
|
|
if ((curr == '+') || (curr == '-')) {
|
|
// exp sign
|
|
ss << curr;
|
|
has_exp_sign = true;
|
|
|
|
} else if ((curr >= '0') && (curr <= '9')) {
|
|
// ok
|
|
ss << curr;
|
|
} else {
|
|
// Empty E is not allowed.
|
|
PUSH_ERROR_AND_RETURN("Empty `E' is not allowed.");
|
|
}
|
|
|
|
while (!Eof()) {
|
|
if (!Char1(&curr)) {
|
|
return false;
|
|
}
|
|
|
|
if ((curr >= '0') && (curr <= '9')) {
|
|
// ok
|
|
ss << curr;
|
|
|
|
} else if ((curr == '+') || (curr == '-')) {
|
|
if (has_exp_sign) {
|
|
// No multiple sign characters
|
|
PUSH_ERROR_AND_RETURN("No multiple exponential sign characters.");
|
|
return false;
|
|
}
|
|
|
|
ss << curr;
|
|
has_exp_sign = true;
|
|
} else {
|
|
// end
|
|
_sr->seek_from_current(-1);
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
_sr->seek_from_current(-1);
|
|
}
|
|
|
|
(*result) = ss.str();
|
|
return true;
|
|
}
|
|
|
|
nonstd::optional<AsciiParser::VariableDef> AsciiParser::GetStageMetaDefinition(
|
|
const std::string &name) {
|
|
if (_supported_stage_metas.count(name)) {
|
|
return _supported_stage_metas.at(name);
|
|
}
|
|
|
|
return nonstd::nullopt;
|
|
}
|
|
|
|
nonstd::optional<AsciiParser::VariableDef> AsciiParser::GetPrimMetaDefinition(
|
|
const std::string &name) {
|
|
if (_supported_prim_metas.count(name)) {
|
|
return _supported_prim_metas.at(name);
|
|
}
|
|
|
|
return nonstd::nullopt;
|
|
}
|
|
|
|
nonstd::optional<AsciiParser::VariableDef> AsciiParser::GetPropMetaDefinition(
|
|
const std::string &name) {
|
|
if (_supported_prop_metas.count(name)) {
|
|
return _supported_prop_metas.at(name);
|
|
}
|
|
|
|
return nonstd::nullopt;
|
|
}
|
|
|
|
bool AsciiParser::ParseStageMeta(std::pair<ListEditQual, MetaVariable> *out) {
|
|
if (!SkipCommentAndWhitespaceAndNewline()) {
|
|
return false;
|
|
}
|
|
|
|
tinyusdz::ListEditQual qual{ListEditQual::ResetToExplicit};
|
|
if (!MaybeListEditQual(&qual)) {
|
|
return false;
|
|
}
|
|
|
|
DCOUT("list-edit qual: " << tinyusdz::to_string(qual));
|
|
|
|
if (!SkipWhitespaceAndNewline()) {
|
|
return false;
|
|
}
|
|
|
|
std::string varname;
|
|
if (!ReadIdentifier(&varname)) {
|
|
return false;
|
|
}
|
|
|
|
// std::cout << "varname = `" << varname << "`\n";
|
|
|
|
if (!IsStageMeta(varname)) {
|
|
PUSH_ERROR_AND_RETURN("Unsupported or invalid/empty variable name `" +
|
|
varname + "` for Stage metadatum");
|
|
}
|
|
|
|
if (!SkipWhitespaceAndNewline()) {
|
|
return false;
|
|
}
|
|
|
|
if (!Expect('=')) {
|
|
PushError("`=` expected.");
|
|
return false;
|
|
}
|
|
|
|
if (!SkipWhitespaceAndNewline()) {
|
|
return false;
|
|
}
|
|
|
|
auto pvardef = GetStageMetaDefinition(varname);
|
|
if (!pvardef) {
|
|
// This should not happen though;
|
|
return false;
|
|
}
|
|
|
|
auto vardef = (*pvardef);
|
|
|
|
MetaVariable var;
|
|
if (!ParseMetaValue(vardef, &var)) {
|
|
return false;
|
|
}
|
|
var.set_name(varname);
|
|
|
|
std::get<0>(*out) = qual;
|
|
std::get<1>(*out) = var;
|
|
|
|
return true;
|
|
}
|
|
|
|
nonstd::optional<std::pair<ListEditQual, MetaVariable>>
|
|
AsciiParser::ParsePrimMeta() {
|
|
if (!SkipCommentAndWhitespaceAndNewline()) {
|
|
return nonstd::nullopt;
|
|
}
|
|
|
|
tinyusdz::ListEditQual qual{ListEditQual::ResetToExplicit};
|
|
|
|
// May be string only(varname is "comment")
|
|
// For some reason, string-only data is just stored in `MetaVariable` and
|
|
// reconstructed in ReconstructPrimMeta in usda-reader.cc later
|
|
//
|
|
{
|
|
value::StringData sdata;
|
|
if (MaybeTripleQuotedString(&sdata)) {
|
|
MetaVariable var;
|
|
// empty name
|
|
var.set_value("comment", sdata);
|
|
|
|
return std::make_pair(qual, var);
|
|
|
|
} else if (MaybeString(&sdata)) {
|
|
MetaVariable var;
|
|
var.set_value("comment", sdata);
|
|
|
|
return std::make_pair(qual, var);
|
|
}
|
|
}
|
|
|
|
if (!MaybeListEditQual(&qual)) {
|
|
return nonstd::nullopt;
|
|
}
|
|
|
|
DCOUT("list-edit qual: " << tinyusdz::to_string(qual));
|
|
|
|
if (!SkipWhitespaceAndNewline()) {
|
|
return nonstd::nullopt;
|
|
}
|
|
|
|
std::string varname;
|
|
if (!ReadIdentifier(&varname)) {
|
|
return nonstd::nullopt;
|
|
}
|
|
|
|
DCOUT("Identifier = " << varname);
|
|
|
|
if (!IsPrimMeta(varname)) {
|
|
std::string msg = "'" + varname + "' is not a Prim Metadata variable.\n";
|
|
PushError(msg);
|
|
return nonstd::nullopt;
|
|
}
|
|
|
|
if (!Expect('=')) {
|
|
PushError("'=' expected in Prim Metadata line.\n");
|
|
return nonstd::nullopt;
|
|
}
|
|
SkipWhitespace();
|
|
|
|
if (auto pv = GetPrimMetaDefinition(varname)) {
|
|
MetaVariable var;
|
|
const auto vardef = pv.value();
|
|
if (!ParseMetaValue(vardef, &var)) {
|
|
PushError("Failed to parse Prim meta value.\n");
|
|
return nonstd::nullopt;
|
|
}
|
|
var.set_name(varname);
|
|
|
|
return std::make_pair(qual, var);
|
|
} else {
|
|
PushError(fmt::format("Unsupported/unimplemented PrimSpec metadata {}", varname));
|
|
return nonstd::nullopt;
|
|
}
|
|
|
|
}
|
|
|
|
bool AsciiParser::ParsePrimMetas(
|
|
PrimMetaMap *args) {
|
|
// '(' args ')'
|
|
// args = list of argument, separated by newline.
|
|
|
|
if (!Expect('(')) {
|
|
return false;
|
|
}
|
|
|
|
if (!SkipCommentAndWhitespaceAndNewline()) {
|
|
// std::cout << "skip comment/whitespace/nl failed\n";
|
|
DCOUT("SkipCommentAndWhitespaceAndNewline failed.");
|
|
return false;
|
|
}
|
|
|
|
while (!Eof()) {
|
|
if (!SkipCommentAndWhitespaceAndNewline()) {
|
|
// std::cout << "2: skip comment/whitespace/nl failed\n";
|
|
return false;
|
|
}
|
|
|
|
char s;
|
|
if (!Char1(&s)) {
|
|
return false;
|
|
}
|
|
|
|
if (s == ')') {
|
|
DCOUT("Prim meta end");
|
|
// End
|
|
break;
|
|
}
|
|
|
|
Rewind(1);
|
|
|
|
DCOUT("Start PrimMeta parse.");
|
|
|
|
// ty = std::pair<ListEditQual, MetaVariable>;
|
|
if (auto m = ParsePrimMeta()) {
|
|
DCOUT("PrimMeta: list-edit qual = "
|
|
<< tinyusdz::to_string(std::get<0>(m.value()))
|
|
<< ", name = " << std::get<1>(m.value()).get_name());
|
|
|
|
if (std::get<1>(m.value()).get_name().empty()) {
|
|
PUSH_ERROR_AND_RETURN("[InternalError] Metadataum name is empty.");
|
|
}
|
|
|
|
(*args)[std::get<1>(m.value()).get_name()] = m.value();
|
|
} else {
|
|
PUSH_ERROR_AND_RETURN("Failed to parse Meta value.");
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool AsciiParser::ParseAttrMeta(AttrMeta *out_meta) {
|
|
// '(' metas ')'
|
|
//
|
|
// currently we only support 'interpolation', 'elementSize' and 'cutomData'
|
|
|
|
if (!SkipWhitespace()) {
|
|
return false;
|
|
}
|
|
|
|
// The first character.
|
|
{
|
|
char c;
|
|
if (!Char1(&c)) {
|
|
// this should not happen.
|
|
return false;
|
|
}
|
|
|
|
if (c == '(') {
|
|
// ok
|
|
} else {
|
|
_sr->seek_from_current(-1);
|
|
|
|
// Still ok. No meta
|
|
DCOUT("No attribute meta.");
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (!SkipWhitespaceAndNewline()) {
|
|
return false;
|
|
}
|
|
|
|
while (!Eof()) {
|
|
char c;
|
|
if (!Char1(&c)) {
|
|
return false;
|
|
}
|
|
|
|
if (c == ')') {
|
|
// end meta
|
|
break;
|
|
} else {
|
|
if (!Rewind(1)) {
|
|
return false;
|
|
}
|
|
|
|
// May be string only
|
|
{
|
|
value::StringData sdata;
|
|
if (MaybeTripleQuotedString(&sdata)) {
|
|
out_meta->stringData.push_back(sdata);
|
|
|
|
DCOUT("Add triple-quoted string to attr meta:" << to_string(sdata));
|
|
if (!SkipWhitespaceAndNewline()) {
|
|
return false;
|
|
}
|
|
continue;
|
|
} else if (MaybeString(&sdata)) {
|
|
out_meta->stringData.push_back(sdata);
|
|
|
|
DCOUT("Add string to attr meta:" << to_string(sdata));
|
|
if (!SkipWhitespaceAndNewline()) {
|
|
return false;
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
|
|
std::string varname;
|
|
if (!ReadIdentifier(&varname)) {
|
|
return false;
|
|
}
|
|
|
|
DCOUT("Property/Attribute meta name: " << varname);
|
|
|
|
bool supported = _supported_prop_metas.count(varname);
|
|
if (!supported) {
|
|
PUSH_ERROR_AND_RETURN_TAG(kAscii,
|
|
fmt::format("Unsupported Property metadatum name: {}", varname));
|
|
}
|
|
|
|
if (!SkipWhitespaceAndNewline()) {
|
|
return false;
|
|
}
|
|
|
|
if (!Expect('=')) {
|
|
return false;
|
|
}
|
|
|
|
if (!SkipWhitespaceAndNewline()) {
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// First-class predefind prop metas.
|
|
//
|
|
if (varname == "interpolation") {
|
|
std::string value;
|
|
if (!ReadStringLiteral(&value)) {
|
|
return false;
|
|
}
|
|
|
|
DCOUT("Got `interpolation` meta : " << value);
|
|
out_meta->interpolation = InterpolationFromString(value);
|
|
} else if (varname == "elementSize") {
|
|
uint32_t value;
|
|
if (!ReadBasicType(&value)) {
|
|
PUSH_ERROR_AND_RETURN("Failed to parse `elementSize`");
|
|
}
|
|
|
|
DCOUT("Got `elementSize` meta : " << value);
|
|
out_meta->elementSize = value;
|
|
} else if (varname == "colorSpace") {
|
|
value::token tok;
|
|
if (!ReadBasicType(&tok)) {
|
|
PUSH_ERROR_AND_RETURN("Failed to parse `colorSpace`");
|
|
}
|
|
// Add as custom meta value.
|
|
MetaVariable metavar;
|
|
metavar.set_value("colorSpace", tok);
|
|
out_meta->meta.emplace("colorSpace", metavar);
|
|
} else if (varname == "customData") {
|
|
CustomDataType dict;
|
|
|
|
if (!ParseDict(&dict)) {
|
|
return false;
|
|
}
|
|
|
|
DCOUT("Got `customData` meta");
|
|
out_meta->customData = dict;
|
|
|
|
} else {
|
|
if (auto pv = GetPropMetaDefinition(varname)) {
|
|
// Parse as generic metadata variable
|
|
MetaVariable metavar;
|
|
const auto &vardef = pv.value();
|
|
|
|
if (!ParseMetaValue(vardef, &metavar)) {
|
|
return false;
|
|
}
|
|
metavar.set_name(varname);
|
|
|
|
// add to custom meta
|
|
out_meta->meta.emplace(varname, metavar);
|
|
|
|
} else {
|
|
// This should not happen though.
|
|
PUSH_ERROR_AND_RETURN_TAG(kAscii, fmt::format("[InternalErrror] Failed to parse Property metadataum `{}`", varname));
|
|
}
|
|
}
|
|
|
|
if (!SkipWhitespaceAndNewline()) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool IsUSDA(const std::string &filename, size_t max_filesize) {
|
|
// TODO: Read only first N bytes
|
|
std::vector<uint8_t> data;
|
|
std::string err;
|
|
|
|
if (!io::ReadWholeFile(&data, &err, filename, max_filesize)) {
|
|
return false;
|
|
}
|
|
|
|
tinyusdz::StreamReader sr(data.data(), data.size(), /* swap endian */ false);
|
|
tinyusdz::ascii::AsciiParser parser(&sr);
|
|
|
|
return parser.CheckHeader();
|
|
}
|
|
|
|
//
|
|
// -- Impl
|
|
//
|
|
|
|
///
|
|
/// Parse rel string
|
|
///
|
|
bool AsciiParser::ParseRelationship(Relationship *result) {
|
|
char c;
|
|
if (!LookChar1(&c)) {
|
|
return false;
|
|
}
|
|
|
|
if (c == '"') {
|
|
// string
|
|
std::string value;
|
|
if (!ReadBasicType(&value)) {
|
|
PUSH_ERROR_AND_RETURN("Failed to parse String.");
|
|
}
|
|
result->set(value);
|
|
|
|
} else if (c == '<') {
|
|
// Path
|
|
Path value;
|
|
if (!ReadBasicType(&value)) {
|
|
PUSH_ERROR_AND_RETURN("Failed to parse Path.");
|
|
}
|
|
result->set(value);
|
|
} else if (c == '[') {
|
|
// PathVector
|
|
std::vector<Path> value;
|
|
if (!ParseBasicTypeArray(&value)) {
|
|
PUSH_ERROR_AND_RETURN("Failed to parse PathVector.");
|
|
}
|
|
result->set(value);
|
|
} else {
|
|
PUSH_ERROR_AND_RETURN("Unexpected char \"" + std::to_string(c) +
|
|
"\" found. Expects string, Path or PathVector.");
|
|
}
|
|
|
|
if (!SkipWhitespaceAndNewline()) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
template <typename T>
|
|
bool AsciiParser::ParseBasicPrimAttr(bool array_qual,
|
|
const std::string &primattr_name,
|
|
Attribute *out_attr) {
|
|
Attribute attr;
|
|
primvar::PrimVar var;
|
|
bool blocked{false};
|
|
|
|
if (array_qual) {
|
|
if (MaybeNone()) {
|
|
} else {
|
|
std::vector<T> value;
|
|
if (!ParseBasicTypeArray(&value)) {
|
|
PUSH_ERROR_AND_RETURN("Failed to parse " +
|
|
std::string(value::TypeTraits<T>::type_name()) +
|
|
" array.");
|
|
return false;
|
|
}
|
|
|
|
// Empty array allowed.
|
|
DCOUT("Got it: ty = " + std::string(value::TypeTraits<T>::type_name()) +
|
|
", sz = " + std::to_string(value.size()));
|
|
var.set_value(value);
|
|
}
|
|
|
|
} else if (hasConnect(primattr_name)) {
|
|
std::string value; // TODO: Path
|
|
if (!ReadPathIdentifier(&value)) {
|
|
PushError("Failed to parse path identifier for `token`.\n");
|
|
return false;
|
|
}
|
|
|
|
var.set_value(value);
|
|
} else {
|
|
nonstd::optional<T> value;
|
|
if (!ReadBasicType(&value)) {
|
|
PUSH_ERROR_AND_RETURN("Failed to parse " +
|
|
std::string(value::TypeTraits<T>::type_name()));
|
|
return false;
|
|
}
|
|
|
|
if (value) {
|
|
DCOUT("ParseBasicPrimAttr: " << value::TypeTraits<T>::type_name() << " = "
|
|
<< (*value));
|
|
|
|
var.set_value(value.value());
|
|
|
|
} else {
|
|
blocked = true;
|
|
// std::cout << "ParseBasicPrimAttr: " <<
|
|
// value::TypeTraits<T>::type_name()
|
|
// << " = None\n";
|
|
}
|
|
}
|
|
|
|
// optional: attribute meta.
|
|
AttrMeta meta;
|
|
if (!ParseAttrMeta(&meta)) {
|
|
PUSH_ERROR_AND_RETURN("Failed to parse Attribute meta.");
|
|
}
|
|
attr.metas() = meta;
|
|
|
|
if (blocked) {
|
|
// There is still have a type for ValueBlock.
|
|
value::ValueBlock noneval;
|
|
attr.set_value(noneval);
|
|
attr.set_blocked(true);
|
|
if (array_qual) {
|
|
attr.set_type_name(value::TypeTraits<T>::type_name() + "[]");
|
|
} else {
|
|
attr.set_type_name(value::TypeTraits<T>::type_name());
|
|
}
|
|
} else {
|
|
attr.set_var(std::move(var));
|
|
}
|
|
|
|
(*out_attr) = std::move(attr);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool AsciiParser::ParsePrimProps(std::map<std::string, Property> *props) {
|
|
// prim_prop : (custom?) uniform type (array_qual?) name '=' value
|
|
// | (custom?) type (array_qual?) name '=' value interpolation?
|
|
// | (custom?) uniform type (array_qual?) name interpolation?
|
|
// | (custom?) (listeditqual?) rel attr_name = None
|
|
// | (custom?) (listeditqual?) rel attr_name = string meta
|
|
// | (custom?) (listeditqual?) rel attr_name = path meta
|
|
// | (custom?) (listeditqual?) rel attr_name = pathvector meta
|
|
// | (custom?) (listeditqual?) rel attr_name meta
|
|
// ;
|
|
|
|
// Skip comment
|
|
if (!SkipCommentAndWhitespaceAndNewline()) {
|
|
return false;
|
|
}
|
|
|
|
// Parse `custom`
|
|
bool custom_qual = MaybeCustom();
|
|
|
|
if (!SkipWhitespace()) {
|
|
return false;
|
|
}
|
|
|
|
// List editing only applicable for Relation for Primitive attributes.
|
|
ListEditQual listop_qual;
|
|
if (!MaybeListEditQual(&listop_qual)) {
|
|
return false;
|
|
}
|
|
|
|
bool uniform_qual{false};
|
|
std::string type_name;
|
|
|
|
if (!ReadIdentifier(&type_name)) {
|
|
return false;
|
|
}
|
|
|
|
if (!SkipWhitespace()) {
|
|
return false;
|
|
}
|
|
|
|
DCOUT("type_name = " << type_name);
|
|
|
|
// Relation('rel')
|
|
if (type_name == kRel) {
|
|
DCOUT("relation");
|
|
|
|
// - prim_identifier
|
|
// - prim_identifier, '(' metadataum ')'
|
|
// - prim_identifier, '=', (None|string|path|pathvector)
|
|
// NOTE: There should be no 'uniform rel'
|
|
|
|
std::string attr_name;
|
|
|
|
if (!ReadPrimAttrIdentifier(&attr_name)) {
|
|
PUSH_ERROR_AND_RETURN(
|
|
"Attribute name(Identifier) expected but got non-identifier.");
|
|
}
|
|
|
|
if (!SkipWhitespace()) {
|
|
return false;
|
|
}
|
|
|
|
char c;
|
|
if (!LookChar1(&c)) {
|
|
return false;
|
|
}
|
|
|
|
nonstd::optional<AttrMeta> metap;
|
|
|
|
if (c == '(') {
|
|
// FIXME: Implement Relation specific metadatum parser?
|
|
AttrMeta meta;
|
|
if (!ParseAttrMeta(&meta)) {
|
|
PUSH_ERROR_AND_RETURN("Failed to parse metadataum.");
|
|
}
|
|
|
|
metap = meta;
|
|
|
|
if (!LookChar1(&c)) {
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
if (c != '=') {
|
|
DCOUT("Relationship with no target: " << attr_name);
|
|
|
|
// No targets. Define only.
|
|
Property p(type_name, custom_qual);
|
|
p.set_property_type(Property::Type::NoTargetsRelation);
|
|
p.set_listedit_qual(listop_qual);
|
|
|
|
if (metap) {
|
|
// TODO: metadataum for Rel
|
|
p.attribute().metas() = metap.value();
|
|
}
|
|
|
|
(*props)[attr_name] = p;
|
|
|
|
return true;
|
|
}
|
|
|
|
// has targets
|
|
if (!Expect('=')) {
|
|
return false;
|
|
}
|
|
|
|
if (metap) {
|
|
PUSH_ERROR_AND_RETURN_TAG(kAscii, "Syntax error. Property metadatum must be defined after `=` and relationship target(s).");
|
|
}
|
|
|
|
if (!SkipWhitespaceAndNewline()) {
|
|
return false;
|
|
}
|
|
|
|
if (MaybeNone()) {
|
|
PUSH_ERROR_AND_RETURN("TODO: Support `None` for property.");
|
|
}
|
|
|
|
Relationship rel;
|
|
if (!ParseRelationship(&rel)) {
|
|
PUSH_ERROR_AND_RETURN("Failed to parse `rel` property.");
|
|
}
|
|
|
|
if (!SkipWhitespace()) {
|
|
}
|
|
|
|
if (!LookChar1(&c)) {
|
|
return false;
|
|
}
|
|
|
|
// TODO: Parse Meta.
|
|
if (c == '(') {
|
|
|
|
if (metap) {
|
|
PUSH_ERROR_AND_RETURN_TAG(kAscii, "[InternalError] parser error.");
|
|
}
|
|
|
|
AttrMeta meta;
|
|
|
|
// FIXME: Implement Relation specific metadatum parser?
|
|
if (!ParseAttrMeta(&meta)) {
|
|
PUSH_ERROR_AND_RETURN("Failed to parse metadataum.");
|
|
}
|
|
|
|
metap = meta;
|
|
|
|
}
|
|
|
|
DCOUT("Relationship with target: " << attr_name);
|
|
Property p(rel, custom_qual);
|
|
p.set_listedit_qual(listop_qual);
|
|
|
|
if (metap) {
|
|
p.attribute().metas() = metap.value();
|
|
}
|
|
|
|
(*props)[attr_name] = p;
|
|
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// Attrib.
|
|
//
|
|
|
|
if (listop_qual != ListEditQual::ResetToExplicit) {
|
|
PUSH_ERROR_AND_RETURN_TAG(
|
|
kAscii, "List editing qualifier is not allowed for Attribute.");
|
|
}
|
|
|
|
if (type_name == "uniform") {
|
|
uniform_qual = true;
|
|
|
|
// next token should be type
|
|
if (!ReadIdentifier(&type_name)) {
|
|
PushError("`type` identifier expected but got non-identifier\n");
|
|
return false;
|
|
}
|
|
|
|
// `type_name` is then overwritten.
|
|
}
|
|
|
|
if (!IsSupportedPrimAttrType(type_name)) {
|
|
PUSH_ERROR_AND_RETURN("Unknown or unsupported primtive attribute type `" +
|
|
type_name + "`\n");
|
|
}
|
|
|
|
// Has array qualifier? `[]`
|
|
bool array_qual = false;
|
|
{
|
|
char c0, c1;
|
|
if (!Char1(&c0)) {
|
|
return false;
|
|
}
|
|
|
|
if (c0 == '[') {
|
|
if (!Char1(&c1)) {
|
|
return false;
|
|
}
|
|
|
|
if (c1 == ']') {
|
|
array_qual = true;
|
|
} else {
|
|
// Invalid syntax
|
|
PushError("Invalid syntax found.\n");
|
|
return false;
|
|
}
|
|
|
|
} else {
|
|
if (!Rewind(1)) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!SkipWhitespace()) {
|
|
return false;
|
|
}
|
|
|
|
std::string primattr_name;
|
|
if (!ReadPrimAttrIdentifier(&primattr_name)) {
|
|
PUSH_ERROR_AND_RETURN("Failed to parse primAttr identifier.");
|
|
}
|
|
|
|
if (!SkipWhitespace()) {
|
|
return false;
|
|
}
|
|
|
|
// output node?
|
|
if (type_name == "token" && hasOutputs(primattr_name) &&
|
|
!hasConnect(primattr_name)) {
|
|
// ok
|
|
return true;
|
|
}
|
|
|
|
bool isTimeSample = endsWith(primattr_name, kTimeSamplesSuffix);
|
|
bool isConnection = endsWith(primattr_name, kConnectSuffix);
|
|
|
|
// Remove suffix
|
|
std::string attr_name = primattr_name;
|
|
if (isTimeSample) {
|
|
attr_name = removeSuffix(primattr_name, kTimeSamplesSuffix);
|
|
}
|
|
if (isConnection) {
|
|
attr_name = removeSuffix(primattr_name, kConnectSuffix);
|
|
}
|
|
|
|
bool define_only = false;
|
|
{
|
|
char c;
|
|
if (!Char1(&c)) {
|
|
return false;
|
|
}
|
|
|
|
if (c != '=') {
|
|
// Define only(e.g. output variable)
|
|
define_only = true;
|
|
}
|
|
}
|
|
|
|
DCOUT("define only:" << define_only);
|
|
|
|
if (define_only) {
|
|
#if 0
|
|
if (isConnection || isTimeSample) {
|
|
PUSH_ERROR_AND_RETURN("`.connect` or `.timeSamples` suffix provided, but no target/values provided.");
|
|
}
|
|
#endif
|
|
|
|
Rewind(1);
|
|
|
|
// optional: attribute meta.
|
|
AttrMeta meta;
|
|
if (!ParseAttrMeta(&meta)) {
|
|
PUSH_ERROR_AND_RETURN("Failed to parse Attribute meta.");
|
|
}
|
|
|
|
DCOUT("Define only property = " + primattr_name);
|
|
|
|
// Empty Attribute. type info only
|
|
Property p(type_name, custom_qual);
|
|
|
|
if (uniform_qual) {
|
|
p.attribute().variability() = Variability::Uniform;
|
|
}
|
|
p.attribute().metas() = meta;
|
|
|
|
(*props)[attr_name] = p;
|
|
|
|
return true;
|
|
}
|
|
|
|
// Continue to parse argument
|
|
if (!SkipWhitespace()) {
|
|
return false;
|
|
}
|
|
|
|
if (isConnection) {
|
|
DCOUT("isConnection");
|
|
|
|
// Target Must be Path
|
|
Path path;
|
|
if (!ReadBasicType(&path)) {
|
|
PUSH_ERROR_AND_RETURN("Path expected for .connect target.");
|
|
}
|
|
|
|
Property p(path, /* value typename */ type_name, custom_qual);
|
|
|
|
(*props)[attr_name] = p;
|
|
|
|
DCOUT(fmt::format("Added {} as a attribute connection.", primattr_name));
|
|
|
|
return true;
|
|
|
|
} else if (isTimeSample) {
|
|
//
|
|
// TODO(syoyo): Refactror and implement value parser dispatcher.
|
|
//
|
|
if (array_qual) {
|
|
DCOUT("timeSample data. type = " << type_name << "[]");
|
|
} else {
|
|
DCOUT("timeSample data. type = " << type_name);
|
|
}
|
|
|
|
value::TimeSamples ts;
|
|
if (array_qual) {
|
|
if (!ParseTimeSamplesOfArray(type_name, &ts)) {
|
|
PUSH_ERROR_AND_RETURN_TAG(kAscii, fmt::format("Failed to parse TimeSamples of type {}[]", type_name));
|
|
}
|
|
} else {
|
|
if (!ParseTimeSamples(type_name, &ts)) {
|
|
PUSH_ERROR_AND_RETURN_TAG(kAscii, fmt::format("Failed to parse TimeSamples of type {}", type_name));
|
|
}
|
|
}
|
|
|
|
//std::string varname = removeSuffix(primattr_name, ".timeSamples");
|
|
Attribute attr;
|
|
primvar::PrimVar var;
|
|
var.set_timesamples(ts);
|
|
|
|
attr.name() = attr_name;
|
|
attr.set_var(std::move(var));
|
|
|
|
DCOUT("timeSamples primattr: type = " << type_name
|
|
<< ", name = " << attr_name);
|
|
|
|
Property p(attr, custom_qual);
|
|
p.set_property_type(Property::Type::Attrib);
|
|
(*props)[attr_name] = p;
|
|
|
|
return true;
|
|
|
|
} else {
|
|
Attribute attr;
|
|
|
|
// TODO: Refactor. ParseAttrMeta is currently called inside
|
|
// ParseBasicPrimAttr()
|
|
if (type_name == value::kBool) {
|
|
if (!ParseBasicPrimAttr<bool>(array_qual, primattr_name, &attr)) {
|
|
return false;
|
|
}
|
|
} else if (type_name == value::kInt) {
|
|
if (!ParseBasicPrimAttr<int>(array_qual, primattr_name, &attr)) {
|
|
return false;
|
|
}
|
|
} else if (type_name == value::kUInt) {
|
|
if (!ParseBasicPrimAttr<uint32_t>(array_qual, primattr_name, &attr)) {
|
|
return false;
|
|
}
|
|
} else if (type_name == value::kInt64) {
|
|
if (!ParseBasicPrimAttr<int64_t>(array_qual, primattr_name, &attr)) {
|
|
return false;
|
|
}
|
|
} else if (type_name == value::kUInt64) {
|
|
if (!ParseBasicPrimAttr<uint64_t>(array_qual, primattr_name, &attr)) {
|
|
return false;
|
|
}
|
|
} else if (type_name == value::kDouble) {
|
|
if (!ParseBasicPrimAttr<double>(array_qual, primattr_name, &attr)) {
|
|
return false;
|
|
}
|
|
} else if (type_name == value::kString) {
|
|
if (!ParseBasicPrimAttr<value::StringData>(array_qual, primattr_name, &attr)) {
|
|
return false;
|
|
}
|
|
} else if (type_name == value::kToken) {
|
|
if (!ParseBasicPrimAttr<value::token>(array_qual, primattr_name, &attr)) {
|
|
return false;
|
|
}
|
|
} else if (type_name == value::kHalf) {
|
|
if (!ParseBasicPrimAttr<value::half>(array_qual, primattr_name, &attr)) {
|
|
return false;
|
|
}
|
|
} else if (type_name == value::kHalf2) {
|
|
if (!ParseBasicPrimAttr<value::half2>(array_qual, primattr_name, &attr)) {
|
|
return false;
|
|
}
|
|
} else if (type_name == value::kHalf3) {
|
|
if (!ParseBasicPrimAttr<value::half3>(array_qual, primattr_name, &attr)) {
|
|
return false;
|
|
}
|
|
} else if (type_name == value::kHalf4) {
|
|
if (!ParseBasicPrimAttr<value::half4>(array_qual, primattr_name, &attr)) {
|
|
return false;
|
|
}
|
|
} else if (type_name == value::kFloat) {
|
|
if (!ParseBasicPrimAttr<float>(array_qual, primattr_name, &attr)) {
|
|
return false;
|
|
}
|
|
} else if (type_name == value::kFloat2) {
|
|
if (!ParseBasicPrimAttr<value::float2>(array_qual, primattr_name,
|
|
&attr)) {
|
|
return false;
|
|
}
|
|
} else if (type_name == value::kFloat3) {
|
|
if (!ParseBasicPrimAttr<value::float3>(array_qual, primattr_name,
|
|
&attr)) {
|
|
return false;
|
|
}
|
|
} else if (type_name == value::kFloat4) {
|
|
if (!ParseBasicPrimAttr<value::float4>(array_qual, primattr_name,
|
|
&attr)) {
|
|
return false;
|
|
}
|
|
} else if (type_name == value::kDouble2) {
|
|
if (!ParseBasicPrimAttr<value::double2>(array_qual, primattr_name,
|
|
&attr)) {
|
|
return false;
|
|
}
|
|
} else if (type_name == value::kDouble3) {
|
|
if (!ParseBasicPrimAttr<value::double3>(array_qual, primattr_name,
|
|
&attr)) {
|
|
return false;
|
|
}
|
|
} else if (type_name == value::kDouble4) {
|
|
if (!ParseBasicPrimAttr<value::double4>(array_qual, primattr_name,
|
|
&attr)) {
|
|
return false;
|
|
}
|
|
} else if (type_name == value::kPoint3f) {
|
|
if (!ParseBasicPrimAttr<value::point3f>(array_qual, primattr_name,
|
|
&attr)) {
|
|
return false;
|
|
}
|
|
} else if (type_name == value::kColor3f) {
|
|
if (!ParseBasicPrimAttr<value::color3f>(array_qual, primattr_name,
|
|
&attr)) {
|
|
return false;
|
|
}
|
|
} else if (type_name == value::kColor4f) {
|
|
if (!ParseBasicPrimAttr<value::color4f>(array_qual, primattr_name,
|
|
&attr)) {
|
|
return false;
|
|
}
|
|
} else if (type_name == value::kPoint3d) {
|
|
if (!ParseBasicPrimAttr<value::point3d>(array_qual, primattr_name,
|
|
&attr)) {
|
|
return false;
|
|
}
|
|
} else if (type_name == value::kNormal3f) {
|
|
if (!ParseBasicPrimAttr<value::normal3f>(array_qual, primattr_name,
|
|
&attr)) {
|
|
return false;
|
|
}
|
|
} else if (type_name == value::kNormal3d) {
|
|
if (!ParseBasicPrimAttr<value::normal3d>(array_qual, primattr_name,
|
|
&attr)) {
|
|
return false;
|
|
}
|
|
} else if (type_name == value::kVector3f) {
|
|
if (!ParseBasicPrimAttr<value::vector3f>(array_qual, primattr_name,
|
|
&attr)) {
|
|
return false;
|
|
}
|
|
} else if (type_name == value::kVector3d) {
|
|
if (!ParseBasicPrimAttr<value::vector3d>(array_qual, primattr_name,
|
|
&attr)) {
|
|
return false;
|
|
}
|
|
} else if (type_name == value::kColor3d) {
|
|
if (!ParseBasicPrimAttr<value::color3d>(array_qual, primattr_name,
|
|
&attr)) {
|
|
return false;
|
|
}
|
|
} else if (type_name == value::kColor4d) {
|
|
if (!ParseBasicPrimAttr<value::color4d>(array_qual, primattr_name,
|
|
&attr)) {
|
|
return false;
|
|
}
|
|
} else if (type_name == value::kMatrix2d) {
|
|
if (!ParseBasicPrimAttr<value::matrix2d>(array_qual, primattr_name,
|
|
&attr)) {
|
|
return false;
|
|
}
|
|
} else if (type_name == value::kMatrix3d) {
|
|
if (!ParseBasicPrimAttr<value::matrix3d>(array_qual, primattr_name,
|
|
&attr)) {
|
|
return false;
|
|
}
|
|
} else if (type_name == value::kMatrix4d) {
|
|
if (!ParseBasicPrimAttr<value::matrix4d>(array_qual, primattr_name,
|
|
&attr)) {
|
|
return false;
|
|
}
|
|
|
|
} else if (type_name == value::kTexCoord2f) {
|
|
if (!ParseBasicPrimAttr<value::texcoord2f>(array_qual, primattr_name,
|
|
&attr)) {
|
|
PUSH_ERROR_AND_RETURN("Failed to parse texCoord2f data.");
|
|
}
|
|
|
|
} else if (type_name == value::kAssetPath) {
|
|
Reference asset_ref;
|
|
bool triple_deliminated{false};
|
|
if (!ParseReference(&asset_ref, &triple_deliminated)) {
|
|
PUSH_ERROR_AND_RETURN("Failed to parse `asset` data.");
|
|
}
|
|
|
|
DCOUT("Asset path = " << asset_ref.asset_path);
|
|
value::AssetPath assetp(asset_ref.asset_path);
|
|
primvar::PrimVar var;
|
|
var.set_value(assetp);
|
|
attr.set_var(std::move(var));
|
|
|
|
// optional: attribute meta.
|
|
AttrMeta meta;
|
|
if (!ParseAttrMeta(&meta)) {
|
|
PUSH_ERROR_AND_RETURN("Failed to parse Attribute meta.");
|
|
}
|
|
attr.metas() = meta;
|
|
|
|
} else {
|
|
PUSH_ERROR_AND_RETURN("TODO: type = " + type_name);
|
|
}
|
|
|
|
if (uniform_qual) {
|
|
attr.variability() = Variability::Uniform;
|
|
}
|
|
attr.set_name(primattr_name);
|
|
|
|
DCOUT("primattr: type = " << type_name << ", name = " << primattr_name);
|
|
|
|
Property p(attr, custom_qual);
|
|
|
|
(*props)[primattr_name] = p;
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool AsciiParser::ParseProperty(std::map<std::string, Property> *props) {
|
|
// property : primm_attr
|
|
// | 'rel' name '=' path
|
|
// ;
|
|
|
|
if (!SkipWhitespace()) {
|
|
return false;
|
|
}
|
|
|
|
// rel?
|
|
{
|
|
uint64_t loc = CurrLoc();
|
|
std::string tok;
|
|
|
|
if (!ReadIdentifier(&tok)) {
|
|
return false;
|
|
}
|
|
|
|
if (tok == "rel") {
|
|
PUSH_ERROR_AND_RETURN("TODO: Parse rel");
|
|
} else {
|
|
SeekTo(loc);
|
|
}
|
|
}
|
|
|
|
// attribute
|
|
return ParsePrimProps(props);
|
|
}
|
|
|
|
std::string AsciiParser::GetCurrentPath() {
|
|
if (_path_stack.empty()) {
|
|
return "/";
|
|
}
|
|
|
|
return _path_stack.top();
|
|
}
|
|
|
|
//
|
|
// -- ctor, dtor
|
|
//
|
|
|
|
AsciiParser::AsciiParser() { Setup(); }
|
|
|
|
AsciiParser::AsciiParser(StreamReader *sr) : _sr(sr) { Setup(); }
|
|
|
|
void AsciiParser::Setup() {
|
|
RegisterStageMetas(_supported_stage_metas);
|
|
RegisterPrimMetas(_supported_prim_metas);
|
|
RegisterPropMetas(_supported_prop_metas);
|
|
RegisterPrimAttrTypes(_supported_prim_attr_types);
|
|
RegisterPrimTypes(_supported_prim_types);
|
|
RegisterAPISchemas(_supported_api_schemas);
|
|
}
|
|
|
|
AsciiParser::~AsciiParser() {}
|
|
|
|
bool AsciiParser::CheckHeader() { return ParseMagicHeader(); }
|
|
|
|
bool AsciiParser::IsPrimMeta(const std::string &name) {
|
|
return _supported_prim_metas.count(name) ? true : false;
|
|
}
|
|
|
|
bool AsciiParser::IsStageMeta(const std::string &name) {
|
|
return _supported_stage_metas.count(name) ? true : false;
|
|
}
|
|
|
|
bool AsciiParser::ParseVariantSet(const int64_t primIdx,
|
|
const int64_t parentPrimIdx,
|
|
const uint32_t depth) {
|
|
// {
|
|
// "variantName0" ( metas ) { ... }
|
|
// "variantName1" ( metas ) { ... }
|
|
// ...
|
|
// }
|
|
if (!Expect('{')) {
|
|
return false;
|
|
}
|
|
|
|
if (!SkipCommentAndWhitespaceAndNewline()) {
|
|
return false;
|
|
}
|
|
|
|
std::map<std::string, VariantContent> variantContentMap;
|
|
|
|
// for each variantStatement
|
|
while (!Eof()) {
|
|
{
|
|
char c;
|
|
if (!Char1(&c)) {
|
|
return false;
|
|
}
|
|
|
|
if (c == '}') {
|
|
// end
|
|
break;
|
|
}
|
|
|
|
Rewind(1);
|
|
}
|
|
|
|
// string
|
|
std::string variantName;
|
|
if (!ReadBasicType(&variantName)) {
|
|
PUSH_ERROR_AND_RETURN_TAG(
|
|
kAscii, "Failed to parse variant name for `variantSet` statement.");
|
|
}
|
|
|
|
if (!SkipWhitespace()) {
|
|
return false;
|
|
}
|
|
|
|
// Optional: PrimSpec meta
|
|
PrimMetaMap metas;
|
|
{
|
|
char mc;
|
|
if (!LookChar1(&mc)) {
|
|
return false;
|
|
}
|
|
|
|
if (mc == '(') {
|
|
if (!ParsePrimMetas(&metas)) {
|
|
PUSH_ERROR_AND_RETURN_TAG(kAscii, "Failed to parse PrimSpec metas in variant statement.");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!Expect('{')) {
|
|
return false;
|
|
}
|
|
|
|
if (!SkipCommentAndWhitespaceAndNewline()) {
|
|
return false;
|
|
}
|
|
|
|
|
|
VariantContent variantContent;
|
|
|
|
while (!Eof()) {
|
|
{
|
|
char c;
|
|
if (!Char1(&c)) {
|
|
return false;
|
|
}
|
|
|
|
if (c == '}') {
|
|
DCOUT("End block in variantSet stmt.");
|
|
// end block
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!Rewind(1)) {
|
|
return false;
|
|
}
|
|
|
|
DCOUT("Read first token in VariantSet stmt");
|
|
Identifier tok;
|
|
if (!ReadBasicType(&tok)) {
|
|
PUSH_ERROR_AND_RETURN(
|
|
"Failed to parse an identifier in variantSet block statement.");
|
|
}
|
|
|
|
if (!Rewind(tok.size())) {
|
|
return false;
|
|
}
|
|
|
|
if (tok == "variantSet") {
|
|
PUSH_ERROR_AND_RETURN("Nested `variantSet` is not supported yet.");
|
|
}
|
|
|
|
Specifier child_spec{Specifier::Invalid};
|
|
if (tok == "def") {
|
|
child_spec = Specifier::Def;
|
|
} else if (tok == "over") {
|
|
child_spec = Specifier::Over;
|
|
} else if (tok == "class") {
|
|
child_spec = Specifier::Class;
|
|
}
|
|
|
|
if (child_spec != Specifier::Invalid) {
|
|
// FIXME: Assign idx dedicated for variant.
|
|
int64_t idx = _prim_idx_assign_fun(parentPrimIdx);
|
|
DCOUT("enter parseBlock in variantSet. spec = " << to_string(child_spec) << ", idx = "
|
|
<< idx << ", rootIdx = " << primIdx);
|
|
|
|
// recusive call
|
|
if (!ParseBlock(child_spec, idx, primIdx, depth + 1, /* in_variantStmt */true)) {
|
|
PUSH_ERROR_AND_RETURN(
|
|
fmt::format("`{}` block parse failed.", to_string(child_spec)));
|
|
}
|
|
DCOUT(fmt::format("Done parse `{}` block.", to_string(child_spec)));
|
|
|
|
DCOUT(fmt::format("Add primIdx {} to variant {}", idx, variantName));
|
|
variantContent.primIndices.push_back(idx);
|
|
|
|
} else {
|
|
DCOUT("Enter ParsePrimProps.");
|
|
if (!ParsePrimProps(&variantContent.props)) {
|
|
PUSH_ERROR_AND_RETURN("Failed to parse Prim attribute.");
|
|
}
|
|
DCOUT(fmt::format("Done parse ParsePrimProps."));
|
|
}
|
|
|
|
if (!SkipWhitespaceAndNewline()) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!SkipWhitespaceAndNewline()) {
|
|
return false;
|
|
}
|
|
|
|
DCOUT(fmt::format("variantSet item {} parsed.", variantName));
|
|
|
|
variantContentMap.emplace(variantName, variantContent);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
///
|
|
/// Parse block.
|
|
///
|
|
/// block = spec prim_type? token metas? { ... }
|
|
/// metas = '(' args ')'
|
|
///
|
|
/// spec = `def`, `over` or `class`
|
|
///
|
|
///
|
|
bool AsciiParser::ParseBlock(const Specifier spec, const int64_t primIdx,
|
|
const int64_t parentPrimIdx,
|
|
const uint32_t depth,
|
|
const bool in_variantStaement) {
|
|
DCOUT("ParseBlock");
|
|
|
|
if (!SkipCommentAndWhitespaceAndNewline()) {
|
|
DCOUT("SkipCommentAndWhitespaceAndNewline failed");
|
|
return false;
|
|
}
|
|
|
|
Identifier def;
|
|
if (!ReadIdentifier(&def)) {
|
|
DCOUT("ReadIdentifier failed");
|
|
return false;
|
|
}
|
|
DCOUT("spec = " << def);
|
|
|
|
if ((def == "def") || (def == "over") || (def == "class")) {
|
|
// ok
|
|
} else {
|
|
PUSH_ERROR_AND_RETURN("Invalid specifier.");
|
|
}
|
|
|
|
// Ensure spec and def is same.
|
|
if (def == "def") {
|
|
if (spec != Specifier::Def) {
|
|
PUSH_ERROR_AND_RETURN_TAG(
|
|
kAscii, "Internal error. Invalid Specifier token combination. def = " << def << ", spec = " << to_string(spec));
|
|
}
|
|
} else if (def == "over") {
|
|
if (spec != Specifier::Over) {
|
|
PUSH_ERROR_AND_RETURN_TAG(
|
|
kAscii, "Internal error. Invalid Specifier token combination. def = " << def << ", spec = " << to_string(spec));
|
|
}
|
|
} else if (def == "class") {
|
|
if (spec != Specifier::Class) {
|
|
PUSH_ERROR_AND_RETURN_TAG(
|
|
kAscii, "Internal error. Invalid Specifier token combination. def = " << def << ", spec = " << to_string(spec));
|
|
}
|
|
}
|
|
|
|
if (!SkipWhitespaceAndNewline()) {
|
|
return false;
|
|
}
|
|
|
|
// look ahead
|
|
bool has_primtype = false;
|
|
{
|
|
char c;
|
|
if (!Char1(&c)) {
|
|
return false;
|
|
}
|
|
|
|
if (!Rewind(1)) {
|
|
return false;
|
|
}
|
|
|
|
if (c == '"') {
|
|
// token
|
|
has_primtype = false;
|
|
} else {
|
|
has_primtype = true;
|
|
}
|
|
}
|
|
|
|
Identifier prim_type;
|
|
|
|
DCOUT("has_primtype = " << has_primtype);
|
|
|
|
if (has_primtype) {
|
|
if (!ReadIdentifier(&prim_type)) {
|
|
return false;
|
|
}
|
|
|
|
if (!IsSupportedPrimType(prim_type)) {
|
|
std::string msg =
|
|
"`" + prim_type +
|
|
"` is not a defined Prim type(or not supported in TinyUSDZ)\n";
|
|
PushError(msg);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!SkipWhitespaceAndNewline()) {
|
|
return false;
|
|
}
|
|
|
|
std::string prim_name;
|
|
if (!ReadBasicType(&prim_name)) {
|
|
return false;
|
|
}
|
|
|
|
DCOUT("prim name = " << prim_name);
|
|
if (!ValidatePrimName(prim_name)) {
|
|
PUSH_ERROR_AND_RETURN_TAG(kAscii, "Prim name contains invalid chacracter.");
|
|
}
|
|
|
|
if (!SkipWhitespaceAndNewline()) {
|
|
return false;
|
|
}
|
|
|
|
std::map<std::string, std::pair<ListEditQual, MetaVariable>> in_metas;
|
|
{
|
|
// look ahead
|
|
char c;
|
|
if (!LookChar1(&c)) {
|
|
return false;
|
|
}
|
|
|
|
if (c == '(') {
|
|
// meta
|
|
|
|
if (!ParsePrimMetas(&in_metas)) {
|
|
DCOUT("Parse Prim metas failed.");
|
|
return false;
|
|
}
|
|
|
|
if (!SkipWhitespaceAndNewline()) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!SkipCommentAndWhitespaceAndNewline()) {
|
|
return false;
|
|
}
|
|
|
|
if (!Expect('{')) {
|
|
return false;
|
|
}
|
|
|
|
if (!SkipWhitespaceAndNewline()) {
|
|
return false;
|
|
}
|
|
|
|
std::map<std::string, Property> props;
|
|
|
|
{
|
|
std::string full_path = GetCurrentPath();
|
|
if (full_path == "/") {
|
|
full_path += prim_name;
|
|
} else {
|
|
full_path += "/" + prim_name;
|
|
}
|
|
PushPath(full_path);
|
|
}
|
|
|
|
// expect = '}'
|
|
// | def_block
|
|
// | prim_attr+
|
|
// | variantSet '{' ... '}'
|
|
while (!Eof()) {
|
|
if (!SkipCommentAndWhitespaceAndNewline()) {
|
|
return false;
|
|
}
|
|
|
|
char c;
|
|
if (!Char1(&c)) {
|
|
return false;
|
|
}
|
|
|
|
if (c == '}') {
|
|
// end block
|
|
break;
|
|
} else {
|
|
if (!Rewind(1)) {
|
|
return false;
|
|
}
|
|
|
|
DCOUT("Read stmt token");
|
|
Identifier tok;
|
|
if (!ReadBasicType(&tok)) {
|
|
// maybe ';'?
|
|
|
|
if (LookChar1(&c)) {
|
|
if (c == ';') {
|
|
PUSH_ERROR_AND_RETURN(
|
|
"Semicolon is not allowd in `def` block statement.");
|
|
}
|
|
}
|
|
PUSH_ERROR_AND_RETURN(
|
|
"Failed to parse an identifier in `def` block statement.");
|
|
}
|
|
|
|
if (tok == "variantSet") {
|
|
if (!SkipWhitespace()) {
|
|
return false;
|
|
}
|
|
|
|
std::string variantName;
|
|
if (!ReadBasicType(&variantName)) {
|
|
PUSH_ERROR_AND_RETURN("Failed to parse `variantSet` statement.");
|
|
}
|
|
|
|
DCOUT("variantName = " << variantName);
|
|
|
|
if (!SkipWhitespace()) {
|
|
return false;
|
|
}
|
|
|
|
if (!Expect('=')) {
|
|
return false;
|
|
}
|
|
|
|
if (!SkipWhitespace()) {
|
|
return false;
|
|
}
|
|
|
|
if (!ParseVariantSet(primIdx, parentPrimIdx, depth)) {
|
|
PUSH_ERROR_AND_RETURN("Failed to parse `variantSet` statement.");
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
if (!Rewind(tok.size())) {
|
|
return false;
|
|
}
|
|
|
|
Specifier child_spec{Specifier::Invalid};
|
|
if (tok == "def") {
|
|
child_spec = Specifier::Def;
|
|
} else if (tok == "over") {
|
|
child_spec = Specifier::Over;
|
|
} else if (tok == "class") {
|
|
child_spec = Specifier::Class;
|
|
}
|
|
|
|
if (child_spec != Specifier::Invalid) {
|
|
int64_t idx = _prim_idx_assign_fun(parentPrimIdx);
|
|
DCOUT("enter parseDef. spec = " << to_string(child_spec) << ", idx = "
|
|
<< idx << ", rootIdx = " << primIdx);
|
|
|
|
// recusive call
|
|
if (!ParseBlock(child_spec, idx, primIdx, depth + 1)) {
|
|
PUSH_ERROR_AND_RETURN(
|
|
fmt::format("`{}` block parse failed.", to_string(child_spec)));
|
|
}
|
|
DCOUT(fmt::format("Done parse `{}` block.", to_string(child_spec)));
|
|
} else {
|
|
DCOUT("Enter ParsePrimProps.");
|
|
// Assume PrimAttr
|
|
if (!ParsePrimProps(&props)) {
|
|
PUSH_ERROR_AND_RETURN("Failed to parse Prim attribute.");
|
|
}
|
|
}
|
|
|
|
if (!SkipWhitespaceAndNewline()) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string pTy = prim_type;
|
|
|
|
if (IsToplevel()) {
|
|
|
|
if (prim_type.empty()) {
|
|
// No Prim type specified. Treat it as Model
|
|
|
|
pTy = "Model";
|
|
}
|
|
|
|
if (_prim_construct_fun_map.count(pTy)) {
|
|
auto construct_fun = _prim_construct_fun_map[pTy];
|
|
|
|
Path fullpath(GetCurrentPath(), "");
|
|
Path pname(prim_name, "");
|
|
nonstd::expected<bool, std::string> ret = construct_fun(
|
|
fullpath, spec, pname, primIdx, parentPrimIdx, props, in_metas);
|
|
|
|
if (!ret) {
|
|
// construction failed.
|
|
PUSH_ERROR_AND_RETURN("Constructing Prim type `" + pTy +
|
|
"` failed: " + ret.error());
|
|
}
|
|
} else {
|
|
PUSH_WARN(fmt::format(
|
|
"TODO: Unsupported/Unimplemented Prim type: `{}`. Skipping parsing.",
|
|
pTy));
|
|
}
|
|
} else {
|
|
// Load scene as PrimSpec tree
|
|
if (_primspec_fun) {
|
|
Path fullpath(GetCurrentPath(), "");
|
|
Path pname(prim_name, "");
|
|
|
|
// pass prim_type as is(empty = empty string)
|
|
nonstd::expected<bool, std::string> ret = _primspec_fun(
|
|
fullpath, spec, prim_type, pname, primIdx, parentPrimIdx, props, in_metas);
|
|
|
|
if (!ret) {
|
|
// construction failed.
|
|
PUSH_ERROR_AND_RETURN(fmt::format("Constructing PrimSpec typeName `{}`, elementName `{}` failed: {}", prim_type, prim_name, ret.error()));
|
|
}
|
|
} else {
|
|
PUSH_ERROR_AND_RETURN_TAG(kAscii, "[Internal Error] PrimSpec handler is not found.");
|
|
}
|
|
}
|
|
|
|
PopPath();
|
|
|
|
return true;
|
|
}
|
|
|
|
///
|
|
/// Parser entry point
|
|
/// TODO: Refactor and use unified code path regardless of LoadState.
|
|
///
|
|
bool AsciiParser::Parse(LoadState state) {
|
|
_sub_layered = (state == LoadState::Sublayer);
|
|
_referenced = (state == LoadState::Reference);
|
|
_payloaded = (state == LoadState::Payload);
|
|
|
|
bool header_ok = ParseMagicHeader();
|
|
if (!header_ok) {
|
|
PushError("Failed to parse USDA magic header.\n");
|
|
return false;
|
|
}
|
|
|
|
SkipCommentAndWhitespaceAndNewline();
|
|
|
|
if (Eof()) {
|
|
// Empty USDA
|
|
return true;
|
|
}
|
|
|
|
{
|
|
char c;
|
|
if (!LookChar1(&c)) {
|
|
return false;
|
|
}
|
|
|
|
if (c == '(') {
|
|
// stage meta.
|
|
// TODO: We could skip parsing stage meta in flatten(composition) mode.
|
|
if (!ParseStageMetas()) {
|
|
PUSH_ERROR_AND_RETURN("Failed to parse Stage metas.");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (IsToplevel() && _stage_meta_process_fun) {
|
|
DCOUT("StageMeta callback.");
|
|
bool ret = _stage_meta_process_fun(_stage_metas);
|
|
if (!ret) {
|
|
PUSH_ERROR_AND_RETURN("Failed to reconstruct Stage metas.");
|
|
}
|
|
}
|
|
|
|
PushPath("/");
|
|
|
|
// parse blocks
|
|
while (!Eof()) {
|
|
if (!SkipCommentAndWhitespaceAndNewline()) {
|
|
return false;
|
|
}
|
|
|
|
if (Eof()) {
|
|
// Whitespaces in the end of line.
|
|
break;
|
|
}
|
|
|
|
// Look ahead token
|
|
auto curr_loc = _sr->tell();
|
|
|
|
Identifier tok;
|
|
if (!ReadBasicType(&tok)) {
|
|
PushError("Identifier expected.\n");
|
|
return false;
|
|
}
|
|
|
|
// Rewind
|
|
if (!SeekTo(curr_loc)) {
|
|
return false;
|
|
}
|
|
|
|
Specifier spec{Specifier::Invalid};
|
|
if (tok == "def") {
|
|
spec = Specifier::Def;
|
|
} else if (tok == "over") {
|
|
spec = Specifier::Over;
|
|
} else if (tok == "class") {
|
|
spec = Specifier::Class;
|
|
} else {
|
|
PushError("Invalid specifier token '" + tok + "'");
|
|
return false;
|
|
}
|
|
|
|
int64_t primIdx = _prim_idx_assign_fun(-1);
|
|
DCOUT("Enter parseDef. primIdx = " << primIdx
|
|
<< ", parentPrimIdx = root(-1)");
|
|
bool block_ok = ParseBlock(spec, primIdx, /* parent */ -1, /* depth */0, /* in_variantStmt */false);
|
|
if (!block_ok) {
|
|
PushError("Failed to parse `def` block.\n");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
} // namespace ascii
|
|
} // namespace tinyusdz
|
|
|
|
#else // TINYUSDZ_DISABLE_MODULE_USDA_READER
|
|
|
|
#endif // TINYUSDZ_DISABLE_MODULE_USDA_READER
|