Text syntax parser/loader
Added support for list x y; Uses a mechanism to parse as unknown XML body and post-parsing replace with list keys Fixed example and test to work with new TEXT syntax
This commit is contained in:
parent
0c79298e76
commit
b6bfcb69f7
14 changed files with 227 additions and 377 deletions
|
|
@ -274,6 +274,69 @@ clixon_txt2file(FILE *f,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Look for YANG lists nodes and convert bodies to keys
|
||||
*
|
||||
* This is a compromise between making the text parser (1) YANG aware or (2) not.
|
||||
* (1) The reason for is that some constructs such as "list <keyval1> {" does not
|
||||
* contain enough info (eg name of XML tag for <keyval1>)
|
||||
* (2) The reason against is of principal of making the parser design simpler in a bottom-up mode
|
||||
* The compromise between (1) and (2) is to first parse without YANG (2) and then call a special
|
||||
* function after YANG binding to populate key tags properly.
|
||||
* @param[in] x XML node
|
||||
* @see TEXT_LIST_KEYS which controls output/save, whereas this is parsing/input
|
||||
* @see text_mark_bodies where marking of bodies made transformed here
|
||||
*/
|
||||
static int
|
||||
text_populate_list(cxobj *xn)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_stmt *yn;
|
||||
yang_stmt *yc;
|
||||
cxobj *xc;
|
||||
cxobj *xb;
|
||||
cvec *cvk; /* vector of index keys */
|
||||
cg_var *cvi = NULL;
|
||||
char *namei;
|
||||
|
||||
if ((yn = xml_spec(xn)) == NULL)
|
||||
goto ok;
|
||||
if (yang_keyword_get(yn) == Y_LIST){
|
||||
cvk = yang_cvec_get(yn);
|
||||
/* Loop over bodies and keys and create key leafs
|
||||
*/
|
||||
cvi = NULL;
|
||||
xb = NULL;
|
||||
while ((xb = xml_find_type(xn, NULL, NULL, CX_BODY)) != NULL) {
|
||||
if (!xml_flag(xb, XML_FLAG_BODYKEY))
|
||||
continue;
|
||||
xml_flag_reset(xb, XML_FLAG_BODYKEY);
|
||||
if ((cvi = cvec_next(cvk, cvi)) == NULL){
|
||||
clicon_err(OE_XML, 0, "text parser, key and body mismatch");
|
||||
goto done;
|
||||
}
|
||||
namei = cv_string_get(cvi);
|
||||
if ((xc = xml_new(namei, xn, CX_ELMNT)) == NULL)
|
||||
goto done;
|
||||
yc = yang_find(yn, Y_LEAF, namei);
|
||||
xml_spec_set(xc, yc);
|
||||
if ((xml_addsub(xc, xb)) < 0)
|
||||
goto done;
|
||||
|
||||
}
|
||||
if (xml_sort(xn) < 0)
|
||||
goto done;
|
||||
}
|
||||
xc = NULL;
|
||||
while ((xc = xml_child_each(xn, xc, CX_ELMNT)) != NULL) {
|
||||
if (text_populate_list(xc) < 0)
|
||||
goto done;
|
||||
}
|
||||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Parse a string containing text syntax and return an XML tree
|
||||
*
|
||||
|
||||
|
|
@ -283,11 +346,11 @@ clixon_txt2file(FILE *f,
|
|||
* @param[in] yspec Yang specification (if rfc 7951)
|
||||
* @param[out] xt XML top of tree typically w/o children on entry (but created)
|
||||
* @param[out] xerr Reason for invalid returned as netconf err msg
|
||||
*
|
||||
* @see _xml_parse for XML variant
|
||||
* @retval 1 OK and valid
|
||||
* @retval 0 Invalid (only if yang spec)
|
||||
* @retval -1 Error with clicon_err called
|
||||
* @see _xml_parse for XML variant
|
||||
* @note Parsing requires YANG, which means yb must be YB_MODULE/_NEXT
|
||||
*/
|
||||
static int
|
||||
_text_syntax_parse(char *str,
|
||||
|
|
@ -302,8 +365,13 @@ _text_syntax_parse(char *str,
|
|||
cxobj *x;
|
||||
cbuf *cberr = NULL;
|
||||
int failed = 0; /* yang assignment */
|
||||
cxobj *xc;
|
||||
|
||||
clicon_debug(1, "%s %d %s", __FUNCTION__, yb, str);
|
||||
if (yb != YB_MODULE && yb != YB_MODULE_NEXT){
|
||||
clicon_err(OE_YANG, EINVAL, "yb must be YB_MODULE or YB_MODULE_NEXT");
|
||||
return -1;
|
||||
}
|
||||
ts.ts_parse_string = str;
|
||||
ts.ts_linenum = 1;
|
||||
ts.ts_xtop = xt;
|
||||
|
|
@ -322,17 +390,6 @@ _text_syntax_parse(char *str,
|
|||
/* Populate, ie associate xml nodes with yang specs
|
||||
*/
|
||||
switch (yb){
|
||||
case YB_NONE:
|
||||
break;
|
||||
case YB_PARENT:
|
||||
/* xt:n Has spec
|
||||
* x: <a> <-- populate from parent
|
||||
*/
|
||||
if ((ret = xml_bind_yang0(x, YB_PARENT, NULL, xerr)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
failed++;
|
||||
break;
|
||||
case YB_MODULE_NEXT:
|
||||
if ((ret = xml_bind_yang(x, YB_MODULE, yspec, xerr)) < 0)
|
||||
goto done;
|
||||
|
|
@ -348,19 +405,18 @@ _text_syntax_parse(char *str,
|
|||
if (ret == 0)
|
||||
failed++;
|
||||
break;
|
||||
case YB_RPC:
|
||||
if ((ret = xml_bind_yang_rpc(x, yspec, xerr)) < 0)
|
||||
goto done;
|
||||
if (ret == 0){ /* Add message-id */
|
||||
if (*xerr && clixon_xml_attr_copy(x, *xerr, "message-id") < 0)
|
||||
goto done;
|
||||
failed++;
|
||||
}
|
||||
break;
|
||||
default: /* shouldnt happen */
|
||||
break;
|
||||
} /* switch */
|
||||
/*! Look for YANG lists nodes and convert bodies to keys */
|
||||
xc = NULL;
|
||||
while ((xc = xml_child_each(x, xc, CX_ELMNT)) != NULL)
|
||||
if (text_populate_list(xc) < 0)
|
||||
goto done;
|
||||
}
|
||||
if (failed)
|
||||
goto fail;
|
||||
|
||||
/* Sort the complete tree after parsing. Sorting is not really meaningful if Yang
|
||||
not bound */
|
||||
if (yb != YB_NONE)
|
||||
|
|
@ -439,6 +495,7 @@ clixon_text_syntax_parse_string(char *str,
|
|||
* @note you need to free the xml parse tree after use, using xml_free()
|
||||
* @note, If xt empty, a top-level symbol will be added so that <tree../> will be: <top><tree.../></tree></top>
|
||||
* @note May block on file I/O
|
||||
* @note Parsing requires YANG, which means yb must be YB_MODULE/_NEXT
|
||||
*
|
||||
* @retval 1 OK and valid
|
||||
* @retval 0 Invalid (only if yang spec) w xerr set
|
||||
|
|
@ -462,7 +519,7 @@ clixon_text_syntax_parse_file(FILE *fp,
|
|||
char ch;
|
||||
int len = 0;
|
||||
|
||||
if (xt==NULL){
|
||||
if (xt == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "xt is NULL");
|
||||
return -1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,7 +45,8 @@
|
|||
%type <stack> stmt
|
||||
%type <stack> id
|
||||
%type <string> value
|
||||
%type <stack> leaflist
|
||||
%type <stack> values
|
||||
%type <string> substr
|
||||
|
||||
%start top
|
||||
|
||||
|
|
@ -117,6 +118,8 @@ text_add_value(cxobj *xn,
|
|||
return xb;
|
||||
}
|
||||
|
||||
/*! Create XML node prefix:id
|
||||
*/
|
||||
static cxobj*
|
||||
text_create_node(clixon_text_syntax_yacc *ts,
|
||||
char *name)
|
||||
|
|
@ -158,7 +161,7 @@ strjoin(char *str0,
|
|||
size_t len0;
|
||||
size_t len;
|
||||
|
||||
len0 = strlen(str0);
|
||||
len0 = str0?strlen(str0):0;
|
||||
len = len0 + strlen(str1) + 1;
|
||||
if ((str0 = realloc(str0, len)) == NULL){
|
||||
clicon_err(OE_YANG, errno, "realloc");
|
||||
|
|
@ -168,10 +171,10 @@ strjoin(char *str0,
|
|||
return str0;
|
||||
}
|
||||
|
||||
/*! Given a vector of XML bodies, transform it to a vector of ELEMENT entries
|
||||
/*! Given a vector of XML bodies, transform it to a vector of ELEMENT entries copied from x1
|
||||
*/
|
||||
static int
|
||||
text_leaflist_create(clixon_xvec *xvec0,
|
||||
text_element_create(clixon_xvec *xvec0,
|
||||
cxobj *x1,
|
||||
clixon_xvec *xvec1)
|
||||
{
|
||||
|
|
@ -193,20 +196,34 @@ text_leaflist_create(clixon_xvec *xvec0,
|
|||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Special mechanism to mark bodies so they will not be filtered as whitespace
|
||||
* @see strip_body_objects text_populate_list
|
||||
*/
|
||||
static int
|
||||
text_mark_bodies(clixon_xvec *xv)
|
||||
{
|
||||
int i;
|
||||
cxobj *xb;
|
||||
|
||||
for (i=0; i<clixon_xvec_len(xv); i++){
|
||||
xb = clixon_xvec_i(xv, i);
|
||||
xml_flag_set(xb, XML_FLAG_BODYKEY);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
%%
|
||||
|
||||
top : stmt MY_EOF { _PARSE_DEBUG("top->stmt");
|
||||
// if (xml_addsub(_TS->ts_xtop, $1) < 0) YYERROR;
|
||||
if (clixon_child_xvec_append(_TS->ts_xtop, $1) < 0) YYERROR;
|
||||
clixon_xvec_free($1);
|
||||
YYACCEPT; }
|
||||
;
|
||||
|
||||
stmts : stmts stmt { _PARSE_DEBUG("stmts->stmts stmt");
|
||||
// if (clixon_xvec_append($1, $2) < 0) YYERROR;
|
||||
if (clixon_xvec_merge($1, $2) < 0) YYERROR;
|
||||
clixon_xvec_free($2);
|
||||
$$ = $1;
|
||||
|
|
@ -216,28 +233,26 @@ stmts : stmts stmt { _PARSE_DEBUG("stmts->stmts stmt");
|
|||
}
|
||||
;
|
||||
|
||||
stmt : id value ';' { _PARSE_DEBUG("stmt-> id value ;");
|
||||
if (text_add_value($1, $2) == NULL) YYERROR;
|
||||
free($2);
|
||||
stmt : id values ';' { _PARSE_DEBUG("stmt-> id value ;");
|
||||
text_mark_bodies($2);
|
||||
if (($$ = clixon_xvec_new()) == NULL) YYERROR;
|
||||
if (clixon_xvec_append($$, $1) < 0) YYERROR;
|
||||
if (text_element_create($$, $1, $2) < 0) YYERROR;
|
||||
xml_free($1);
|
||||
clixon_xvec_free($2);
|
||||
}
|
||||
| id '"' value '"' ';' { _PARSE_DEBUG("stmt-> id \" value \" ;");
|
||||
if (text_add_value($1, $3) == NULL) YYERROR;
|
||||
free($3);
|
||||
if (($$ = clixon_xvec_new()) == NULL) YYERROR;
|
||||
if (clixon_xvec_append($$, $1) < 0) YYERROR;
|
||||
}
|
||||
| id '{' stmts '}' { _PARSE_DEBUG("stmt-> id { stmts }");
|
||||
if (clixon_child_xvec_append($1, $3) < 0) YYERROR;
|
||||
clixon_xvec_free($3);
|
||||
| id values '{' stmts '}' { _PARSE_DEBUG("stmt-> id values { stmts }");
|
||||
text_mark_bodies($2);
|
||||
if (clixon_child_xvec_append($1, $2) < 0) YYERROR;
|
||||
clixon_xvec_free($2);
|
||||
if (clixon_child_xvec_append($1, $4) < 0) YYERROR;
|
||||
clixon_xvec_free($4);
|
||||
if (($$ = clixon_xvec_new()) == NULL) YYERROR;
|
||||
if (clixon_xvec_append($$, $1) < 0) YYERROR;
|
||||
}
|
||||
| id '[' leaflist ']'
|
||||
{ _PARSE_DEBUG("stmt-> id [ leaflist ]");
|
||||
}
|
||||
| id '[' values ']'
|
||||
{ _PARSE_DEBUG("stmt-> id [ values ]");
|
||||
if (($$ = clixon_xvec_new()) == NULL) YYERROR;
|
||||
if (text_leaflist_create($$, $1, $3) < 0) YYERROR;
|
||||
if (text_element_create($$, $1, $3) < 0) YYERROR;
|
||||
xml_free($1);
|
||||
clixon_xvec_free($3);
|
||||
}
|
||||
|
|
@ -249,25 +264,34 @@ id : TOKEN { _PARSE_DEBUG("id->TOKEN");
|
|||
}
|
||||
;
|
||||
|
||||
value : value TOKEN { _PARSE_DEBUG("value->value TOKEN");
|
||||
$$ = strjoin($1, $2); free($2);}
|
||||
| TOKEN { _PARSE_DEBUG("value->TOKEN");
|
||||
$$ = $1; }
|
||||
;
|
||||
|
||||
leaflist : leaflist TOKEN { _PARSE_DEBUG("leaflist->leaflist TOKEN");
|
||||
/* Array of body objects, possibly empty */
|
||||
values : values value { _PARSE_DEBUG("values->values value");
|
||||
cxobj* x;
|
||||
if ((x = text_add_value(NULL, $2)) == NULL) YYERROR;;
|
||||
free($2);
|
||||
if (clixon_xvec_append($1, x) < 0) YYERROR;
|
||||
$$ = $1;
|
||||
}
|
||||
| { _PARSE_DEBUG("leaflist->");
|
||||
}
|
||||
| { _PARSE_DEBUG("values->value");
|
||||
if (($$ = clixon_xvec_new()) == NULL) YYERROR;
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
/* Returns single string either as a single token or contained by double quotes */
|
||||
value : TOKEN { _PARSE_DEBUG("value->TOKEN");
|
||||
$$=$1;
|
||||
}
|
||||
| '"' substr '"' { _PARSE_DEBUG("value-> \" substr \"");
|
||||
$$=$2;
|
||||
}
|
||||
;
|
||||
|
||||
|
||||
/* Value within quotes merged to single string, has separate lexical scope */
|
||||
substr : substr TOKEN { _PARSE_DEBUG("substr->substr TOKEN");
|
||||
$$ = strjoin($1, $2); free($2);}
|
||||
| { _PARSE_DEBUG("substr->");
|
||||
$$ = NULL; }
|
||||
;
|
||||
|
||||
%%
|
||||
|
||||
|
|
|
|||
|
|
@ -1734,8 +1734,8 @@ xml_find_type(cxobj *xt,
|
|||
enum cxobj_type type)
|
||||
{
|
||||
cxobj *x = NULL;
|
||||
int pmatch; /* prefix match */
|
||||
char *xprefix; /* xprefix */
|
||||
int pmatch; /* prefix match */
|
||||
char *xprefix; /* xprefix */
|
||||
|
||||
if (!is_element(xt))
|
||||
return NULL;
|
||||
|
|
@ -1746,7 +1746,7 @@ xml_find_type(cxobj *xt,
|
|||
}
|
||||
else
|
||||
pmatch = 1;
|
||||
if (pmatch && strcmp(name, xml_name(x)) == 0)
|
||||
if (pmatch && (name==NULL || strcmp(name, xml_name(x)) == 0))
|
||||
return x;
|
||||
}
|
||||
return NULL;
|
||||
|
|
@ -2010,7 +2010,7 @@ xml_dup(cxobj *x0)
|
|||
* free(xvec);
|
||||
* @endcode
|
||||
* @see cxvec_prepend
|
||||
* @see clixon_cxvec_append which is its own encapsulated xml vector datatype
|
||||
* @see clixon_xvec_append which is its own encapsulated xml vector datatype
|
||||
*/
|
||||
int
|
||||
cxvec_append(cxobj *x,
|
||||
|
|
@ -2045,8 +2045,8 @@ cxvec_append(cxobj *x,
|
|||
* if (xvec)
|
||||
* free(xvec);
|
||||
* @endcode
|
||||
* @see cxvec_prepend
|
||||
* @see clixon_cxvec_prepend which is its own encapsulated xml vector datatype
|
||||
* @see cxvec_append
|
||||
* @see clixon_xvec_prepend which is its own encapsulated xml vector datatype
|
||||
*/
|
||||
int
|
||||
cxvec_prepend(cxobj *x,
|
||||
|
|
|
|||
|
|
@ -107,22 +107,28 @@ xml_bind_netconf_message_id_optional(int val)
|
|||
|
||||
/*! After yang binding, bodies of containers and lists are stripped from XML bodies
|
||||
* May apply to other nodes?
|
||||
* Exception for bodies marked with XML_FLAG_BODYKEY, see text syntax parsing
|
||||
* @see text_mark_bodies
|
||||
*/
|
||||
static int
|
||||
strip_whitespace(cxobj *xt)
|
||||
strip_body_objects(cxobj *xt)
|
||||
{
|
||||
yang_stmt *yt;
|
||||
enum rfc_6020 keyword;
|
||||
cxobj *xc;
|
||||
cxobj *xb;
|
||||
|
||||
if ((yt = xml_spec(xt)) != NULL){
|
||||
keyword = yang_keyword_get(yt);
|
||||
if (keyword == Y_LIST || keyword == Y_CONTAINER){
|
||||
xc = NULL;
|
||||
while ((xc = xml_find_type(xt, NULL, "body", CX_BODY)) != NULL)
|
||||
xml_purge(xc);
|
||||
xb = NULL;
|
||||
/* Quits if marked object, assume all are same */
|
||||
while ((xb = xml_find_type(xt, NULL, "body", CX_BODY)) != NULL &&
|
||||
!xml_flag(xb, XML_FLAG_BODYKEY)){
|
||||
xml_purge(xb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -367,7 +373,7 @@ xml_bind_yang(cxobj *xt,
|
|||
cxobj *xc; /* xml child */
|
||||
int ret;
|
||||
|
||||
strip_whitespace(xt);
|
||||
strip_body_objects(xt);
|
||||
xc = NULL; /* Apply on children */
|
||||
while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL) {
|
||||
if ((ret = xml_bind_yang0(xc, yb, yspec, xerr)) < 0)
|
||||
|
|
@ -414,7 +420,7 @@ xml_bind_yang0_opt(cxobj *xt,
|
|||
goto fail;
|
||||
else if (ret == 2) /* ret=2 for anyxml from parent^ */
|
||||
goto ok;
|
||||
strip_whitespace(xt);
|
||||
strip_body_objects(xt);
|
||||
xc = NULL; /* Apply on children */
|
||||
while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL) {
|
||||
/* It is xml2ns in populate_self_parent that needs improvement */
|
||||
|
|
@ -493,7 +499,7 @@ xml_bind_yang0(cxobj *xt,
|
|||
goto fail;
|
||||
else if (ret == 2) /* ret=2 for anyxml from parent^ */
|
||||
goto ok;
|
||||
strip_whitespace(xt);
|
||||
strip_body_objects(xt);
|
||||
xc = NULL; /* Apply on children */
|
||||
while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL) {
|
||||
if ((ret = xml_bind_yang0_opt(xc, YB_PARENT, NULL, xerr)) < 0)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue