/** * Copyright - See the COPYRIGHT that is included with this distribution. * pvxs is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. */ #include #include #include #include #include #include "dataimpl.h" #include "utilpvt.h" namespace pvxs { namespace client { namespace detail { struct CommonBase::Req { Value pvRequest; Member fields; std::map options; Req() :fields(TypeCode::Struct, "field") {} }; CommonBase::~CommonBase() {} void CommonBase::_rawRequest(const Value& raw) { if(!req) req = std::make_shared(); req->pvRequest = raw; } void CommonBase::_field(const std::string& s) { if(!req) req = std::make_shared(); size_t idx=0u; decltype (req->fields) *cur = &req->fields; while(idxchildren.size(); for(auto x : range(cur->children.size())) { auto& c = cur->children[x]; if(c.name==child) { idx = x; break; } } if(idx==cur->children.size()) { cur->addChild(Member(TypeCode::Struct, child)); } cur = &cur->children[idx]; } } void CommonBase::_record(const std::string& key, const void* value, StoreType vtype) { if(!req) req = std::make_shared(); req->options[key] = Value::Helper::build(value, vtype); } struct PVRParser { /* Grammar * * PVR : * | ENT * | ENT PVR * ENT : FIELD | RECORD | name * FIELD : "field" '(' FIELD_LIST ')' * RECORD : "record" '[' OPTIONS '}' * FIELD_LIST : * | name * | FIELD_LIST ',' name * OPTIONS -> * | name '=' name * | OPTIONS ',' name '=' name */ enum token_t { // terminals comma = ',', lp = '(', rp = ')', lb = '[', rb = ']', eq = '=', field = 'f', record = 'r', name = 'n', tEOF = -1, }; token_t lextok = tEOF; std::string lexval; const char* input; CommonBase& target; PVRParser(CommonBase& target, const char* input) :input(input) ,target(target) {} void lex() { lexval.clear(); if(!*input) { lextok = tEOF; return; } // skip leading whitespace while(' '==*input) input++; switch(*input) { case '[': case ']': case '(': case ')': case ',': case '=': lextok = token_t(*input++); return; default: break; } auto isname = [](char c) { return ((c>='a' && c<='z')) || ((c>='A' && c<='Z')) || ((c>='0' && c<='9')) || c=='.' || c=='_'; }; auto start = input; while(isname(*input)) input++; if(start==input) throw std::runtime_error("invalid character near: "+std::string(start)); lexval = std::string(start, input-start); if(lexval=="field") { lextok = field; } else if(lexval=="record") { lextok = record; } else { lextok = name; } } void parse() { while(input) { auto start = input; lex(); if(lextok==tEOF) { break; } else if(lextok==field) { lex(); if(lextok!=lp) throw std::runtime_error(SB()<<"Expected field( at "<pvRequest) { return req->pvRequest; } else if(!req) { using namespace pvxs::members; return TypeDef(TypeCode::Struct, { Struct("field", {}), }).create(); } else { using namespace pvxs::members; auto def = TypeDef(TypeCode::Struct, { req->fields, }); if(!req->options.empty()) { std::vector opts; for(auto& pair : req->options) { opts.push_back(TypeDef(pair.second).as(pair.first)); } def += {Struct("record", {Struct("_options", opts)})}; } auto inst = def.create(); auto opt = inst["record._options"]; for(auto& pair : req->options) { opt[pair.first].assign(pair.second); } return inst; } } } // namespace detail } // namespace client } // namespace pvxs