Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
V
varnish-cache
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Commits
Open sidebar
varnishcache
varnish-cache
Commits
530f0b20
Commit
530f0b20
authored
Sep 20, 2013
by
Martin Blix Grydeland
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Rename tag -> lhs and val -> rhs in the parser.
parent
b6f7816a
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
117 additions
and
112 deletions
+117
-112
vsl_query.c
lib/libvarnishapi/vsl_query.c
+37
-37
vxp.h
lib/libvarnishapi/vxp.h
+13
-8
vxp_parse.c
lib/libvarnishapi/vxp_parse.c
+67
-67
No files found.
lib/libvarnishapi/vsl_query.c
View file @
530f0b20
...
...
@@ -56,7 +56,7 @@ struct vslq_query {
static
int
vslq_test_rec
(
const
struct
vex
*
vex
,
const
struct
VSLC_ptr
*
rec
)
{
const
struct
vex_
val
*
val
;
const
struct
vex_
rhs
*
rhs
;
int
reclen
;
const
char
*
recdata
;
long
long
recint
;
...
...
@@ -65,8 +65,8 @@ vslq_test_rec(const struct vex *vex, const struct VSLC_ptr *rec)
AN
(
vex
);
AN
(
rec
);
val
=
vex
->
val
;
AN
(
val
);
rhs
=
vex
->
rhs
;
AN
(
rhs
);
reclen
=
VSL_LEN
(
rec
->
ptr
);
recdata
=
VSL_CDATA
(
rec
->
ptr
);
...
...
@@ -80,7 +80,7 @@ vslq_test_rec(const struct vex *vex, const struct VSLC_ptr *rec)
case
T_LEQ
:
/* <= */
case
T_GEQ
:
/* >= */
/* Numerical comparison */
switch
(
val
->
type
)
{
switch
(
rhs
->
type
)
{
case
VEX_INT
:
recint
=
strtoll
(
recdata
,
&
endptr
,
0
);
if
(
*
endptr
==
'\0'
||
isspace
(
*
endptr
))
...
...
@@ -102,93 +102,93 @@ vslq_test_rec(const struct vex *vex, const struct VSLC_ptr *rec)
/* Compare */
switch
(
vex
->
tok
)
{
case
T_EQ
:
/* == */
switch
(
val
->
type
)
{
switch
(
rhs
->
type
)
{
case
VEX_INT
:
if
(
recint
==
val
->
val_int
)
if
(
recint
==
rhs
->
val_int
)
return
(
1
);
return
(
0
);
case
VEX_FLOAT
:
if
(
recfloat
==
val
->
val_float
)
if
(
recfloat
==
rhs
->
val_float
)
return
(
1
);
return
(
0
);
default:
WRONG
(
"Wrong
value
type"
);
WRONG
(
"Wrong
RHS
type"
);
}
case
T_NEQ
:
/* != */
switch
(
val
->
type
)
{
switch
(
rhs
->
type
)
{
case
VEX_INT
:
if
(
recint
!=
val
->
val_int
)
if
(
recint
!=
rhs
->
val_int
)
return
(
1
);
return
(
0
);
case
VEX_FLOAT
:
if
(
recfloat
!=
val
->
val_float
)
if
(
recfloat
!=
rhs
->
val_float
)
return
(
1
);
return
(
0
);
default:
WRONG
(
"Wrong
value
type"
);
WRONG
(
"Wrong
RHS
type"
);
}
case
'<'
:
/* < */
switch
(
val
->
type
)
{
switch
(
rhs
->
type
)
{
case
VEX_INT
:
if
(
recint
<
val
->
val_int
)
if
(
recint
<
rhs
->
val_int
)
return
(
1
);
return
(
0
);
case
VEX_FLOAT
:
if
(
recfloat
<
val
->
val_float
)
if
(
recfloat
<
rhs
->
val_float
)
return
(
1
);
return
(
0
);
default:
WRONG
(
"Wrong
value
type"
);
WRONG
(
"Wrong
RHS
type"
);
}
case
'>'
:
switch
(
val
->
type
)
{
switch
(
rhs
->
type
)
{
case
VEX_INT
:
if
(
recint
>
val
->
val_int
)
if
(
recint
>
rhs
->
val_int
)
return
(
1
);
return
(
0
);
case
VEX_FLOAT
:
if
(
recfloat
>
val
->
val_float
)
if
(
recfloat
>
rhs
->
val_float
)
return
(
1
);
return
(
0
);
default:
WRONG
(
"Wrong
value
type"
);
WRONG
(
"Wrong
RHS
type"
);
}
case
T_LEQ
:
/* <= */
switch
(
val
->
type
)
{
switch
(
rhs
->
type
)
{
case
VEX_INT
:
if
(
recint
<=
val
->
val_int
)
if
(
recint
<=
rhs
->
val_int
)
return
(
1
);
return
(
0
);
case
VEX_FLOAT
:
if
(
recfloat
<=
val
->
val_float
)
if
(
recfloat
<=
rhs
->
val_float
)
return
(
1
);
return
(
0
);
default:
WRONG
(
"Wrong
value
type"
);
WRONG
(
"Wrong
RHS
type"
);
}
case
T_GEQ
:
/* >= */
switch
(
val
->
type
)
{
switch
(
rhs
->
type
)
{
case
VEX_INT
:
if
(
recint
>=
val
->
val_int
)
if
(
recint
>=
rhs
->
val_int
)
return
(
1
);
return
(
0
);
case
VEX_FLOAT
:
if
(
recfloat
>=
val
->
val_float
)
if
(
recfloat
>=
rhs
->
val_float
)
return
(
1
);
return
(
0
);
default:
WRONG
(
"Wrong
value
type"
);
WRONG
(
"Wrong
RHS
type"
);
}
case
T_SEQ
:
/* eq */
assert
(
val
->
type
==
VEX_STRING
);
if
(
reclen
==
val
->
val_stringlen
+
1
&&
!
strncmp
(
recdata
,
val
->
val_string
,
reclen
))
assert
(
rhs
->
type
==
VEX_STRING
);
if
(
reclen
==
rhs
->
val_stringlen
+
1
&&
!
strncmp
(
recdata
,
rhs
->
val_string
,
reclen
))
return
(
1
);
return
(
0
);
case
T_SNEQ
:
/* ne */
assert
(
val
->
type
==
VEX_STRING
);
if
(
reclen
!=
val
->
val_stringlen
+
1
||
strncmp
(
recdata
,
val
->
val_string
,
reclen
))
assert
(
rhs
->
type
==
VEX_STRING
);
if
(
reclen
!=
rhs
->
val_stringlen
+
1
||
strncmp
(
recdata
,
rhs
->
val_string
,
reclen
))
return
(
1
);
return
(
0
);
default:
...
...
@@ -205,8 +205,8 @@ vslq_test(const struct vex *vex, struct VSL_transaction * const ptrans[])
int
i
;
CHECK_OBJ_NOTNULL
(
vex
,
VEX_MAGIC
);
CHECK_OBJ_NOTNULL
(
vex
->
tag
,
VEX_TAG
_MAGIC
);
CHECK_OBJ_NOTNULL
(
vex
->
val
,
VEX_VAL
_MAGIC
);
CHECK_OBJ_NOTNULL
(
vex
->
lhs
,
VEX_LHS
_MAGIC
);
CHECK_OBJ_NOTNULL
(
vex
->
rhs
,
VEX_RHS
_MAGIC
);
for
(
t
=
ptrans
[
0
];
t
!=
NULL
;
t
=
*++
ptrans
)
{
AZ
(
VSL_ResetCursor
(
t
->
c
));
...
...
@@ -219,7 +219,7 @@ vslq_test(const struct vex *vex, struct VSL_transaction * const ptrans[])
assert
(
i
==
1
);
AN
(
t
->
c
->
rec
.
ptr
);
if
(
vex
->
tag
->
tag
!=
VSL_TAG
(
t
->
c
->
rec
.
ptr
))
if
(
vex
->
lhs
->
tag
!=
VSL_TAG
(
t
->
c
->
rec
.
ptr
))
continue
;
i
=
vslq_test_rec
(
vex
,
&
t
->
c
->
rec
);
...
...
lib/libvarnishapi/vxp.h
View file @
530f0b20
...
...
@@ -76,16 +76,19 @@ struct vxp {
struct
vex
;
struct
vex_tag
{
struct
vex_lhs
{
/* Left-hand-side of a vex expression. Stores the information
about which records and what parts of those records the
expression should be applied to */
unsigned
magic
;
#define VEX_
TAG
_MAGIC 0x1AD3D78D
#define VEX_
LHS
_MAGIC 0x1AD3D78D
int
tag
;
int
field
;
int
level_min
;
int
level_max
;
};
enum
vex_
val
_e
{
enum
vex_
rhs
_e
{
VEX__UNSET
,
VEX_INT
,
VEX_FLOAT
,
...
...
@@ -93,10 +96,12 @@ enum vex_val_e {
VEX_REGEX
,
};
struct
vex_val
{
struct
vex_rhs
{
/* Right-hand-side of a vex expression. Stores the value that the
records from LHS should be matched against */
unsigned
magic
;
#define VEX_
VAL
_MAGIC 0x3F109965
enum
vex_
val
_e
type
;
#define VEX_
RHS
_MAGIC 0x3F109965
enum
vex_
rhs
_e
type
;
long
long
val_int
;
double
val_float
;
char
*
val_string
;
...
...
@@ -109,8 +114,8 @@ struct vex {
#define VEX_MAGIC 0xC7DB792D
unsigned
tok
;
struct
vex
*
a
,
*
b
;
struct
vex_
tag
*
tag
;
struct
vex_
val
*
val
;
struct
vex_
lhs
*
lhs
;
struct
vex_
rhs
*
rhs
;
};
/* VXP internals */
...
...
lib/libvarnishapi/vxp_parse.c
View file @
530f0b20
...
...
@@ -48,26 +48,26 @@
static
void
vxp_expr_or
(
struct
vxp
*
vxp
,
struct
vex
**
pvex
);
static
void
vxp_expr_
tag
(
struct
vxp
*
vxp
,
struct
vex_tag
**
ptag
)
vxp_expr_
lhs
(
struct
vxp
*
vxp
,
struct
vex_lhs
**
plhs
)
{
/* XXX: Tag wildcards */
AN
(
p
tag
);
AZ
(
*
p
tag
);
AN
(
p
lhs
);
AZ
(
*
p
lhs
);
if
(
vxp
->
t
->
tok
!=
VAL
)
{
VSB_printf
(
vxp
->
sb
,
"Expected VSL tag got '%.*s' "
,
PF
(
vxp
->
t
));
vxp_ErrWhere
(
vxp
,
vxp
->
t
,
-
1
);
return
;
}
ALLOC_OBJ
(
*
p
tag
,
VEX_TAG
_MAGIC
);
AN
(
*
p
tag
);
(
*
p
tag
)
->
tag
=
VSL_Name2Tag
(
vxp
->
t
->
dec
,
-
1
);
if
((
*
p
tag
)
->
tag
==
-
1
)
{
ALLOC_OBJ
(
*
p
lhs
,
VEX_LHS
_MAGIC
);
AN
(
*
p
lhs
);
(
*
p
lhs
)
->
tag
=
VSL_Name2Tag
(
vxp
->
t
->
dec
,
-
1
);
if
((
*
p
lhs
)
->
tag
==
-
1
)
{
VSB_printf
(
vxp
->
sb
,
"Could not match '%.*s' to any tag "
,
PF
(
vxp
->
t
));
vxp_ErrWhere
(
vxp
,
vxp
->
t
,
-
1
);
return
;
}
else
if
((
*
p
tag
)
->
tag
==
-
2
)
{
}
else
if
((
*
p
lhs
)
->
tag
==
-
2
)
{
VSB_printf
(
vxp
->
sb
,
"'%.*s' matches multiple tags "
,
PF
(
vxp
->
t
));
vxp_ErrWhere
(
vxp
,
vxp
->
t
,
-
1
);
...
...
@@ -75,27 +75,27 @@ vxp_expr_tag(struct vxp *vxp, struct vex_tag **ptag)
}
vxp_NextToken
(
vxp
);
/* XXX:
Tag
limiting operators ([], {}) */
/* XXX:
Lhs
limiting operators ([], {}) */
}
static
void
vxp_expr_num
(
struct
vxp
*
vxp
,
struct
vex_
val
**
pval
)
vxp_expr_num
(
struct
vxp
*
vxp
,
struct
vex_
rhs
**
prhs
)
{
char
*
endptr
;
AN
(
p
val
);
AZ
(
*
p
val
);
AN
(
p
rhs
);
AZ
(
*
p
rhs
);
if
(
vxp
->
t
->
tok
!=
VAL
)
{
VSB_printf
(
vxp
->
sb
,
"Expected number got '%.*s' "
,
PF
(
vxp
->
t
));
vxp_ErrWhere
(
vxp
,
vxp
->
t
,
-
1
);
return
;
}
AN
(
vxp
->
t
->
dec
);
ALLOC_OBJ
(
*
p
val
,
VEX_VAL
_MAGIC
);
AN
(
*
p
val
);
ALLOC_OBJ
(
*
p
rhs
,
VEX_RHS
_MAGIC
);
AN
(
*
p
rhs
);
if
(
strchr
(
vxp
->
t
->
dec
,
'.'
))
{
(
*
p
val
)
->
type
=
VEX_FLOAT
;
(
*
p
val
)
->
val_float
=
strtod
(
vxp
->
t
->
dec
,
&
endptr
);
(
*
p
rhs
)
->
type
=
VEX_FLOAT
;
(
*
p
rhs
)
->
val_float
=
strtod
(
vxp
->
t
->
dec
,
&
endptr
);
while
(
isspace
(
*
endptr
))
endptr
++
;
if
(
*
endptr
!=
'\0'
)
{
...
...
@@ -104,8 +104,8 @@ vxp_expr_num(struct vxp *vxp, struct vex_val **pval)
return
;
}
}
else
{
(
*
p
val
)
->
type
=
VEX_INT
;
(
*
p
val
)
->
val_int
=
strtoll
(
vxp
->
t
->
dec
,
&
endptr
,
0
);
(
*
p
rhs
)
->
type
=
VEX_INT
;
(
*
p
rhs
)
->
val_int
=
strtoll
(
vxp
->
t
->
dec
,
&
endptr
,
0
);
while
(
isspace
(
*
endptr
))
endptr
++
;
if
(
*
endptr
!=
'\0'
)
{
...
...
@@ -118,36 +118,36 @@ vxp_expr_num(struct vxp *vxp, struct vex_val **pval)
}
static
void
vxp_expr_str
(
struct
vxp
*
vxp
,
struct
vex_
val
**
pval
)
vxp_expr_str
(
struct
vxp
*
vxp
,
struct
vex_
rhs
**
prhs
)
{
AN
(
p
val
);
AZ
(
*
p
val
);
AN
(
p
rhs
);
AZ
(
*
p
rhs
);
if
(
vxp
->
t
->
tok
!=
VAL
)
{
VSB_printf
(
vxp
->
sb
,
"Expected string got '%.*s' "
,
PF
(
vxp
->
t
));
vxp_ErrWhere
(
vxp
,
vxp
->
t
,
-
1
);
return
;
}
AN
(
vxp
->
t
->
dec
);
ALLOC_OBJ
(
*
p
val
,
VEX_VAL
_MAGIC
);
AN
(
*
p
val
);
(
*
p
val
)
->
type
=
VEX_STRING
;
(
*
p
val
)
->
val_string
=
strdup
(
vxp
->
t
->
dec
);
AN
((
*
p
val
)
->
val_string
);
(
*
p
val
)
->
val_stringlen
=
strlen
((
*
pval
)
->
val_string
);
ALLOC_OBJ
(
*
p
rhs
,
VEX_RHS
_MAGIC
);
AN
(
*
p
rhs
);
(
*
p
rhs
)
->
type
=
VEX_STRING
;
(
*
p
rhs
)
->
val_string
=
strdup
(
vxp
->
t
->
dec
);
AN
((
*
p
rhs
)
->
val_string
);
(
*
p
rhs
)
->
val_stringlen
=
strlen
((
*
prhs
)
->
val_string
);
vxp_NextToken
(
vxp
);
}
static
void
vxp_expr_regex
(
struct
vxp
*
vxp
,
struct
vex_
val
**
pval
)
vxp_expr_regex
(
struct
vxp
*
vxp
,
struct
vex_
rhs
**
prhs
)
{
const
char
*
errptr
;
int
erroff
;
/* XXX: Caseless option */
AN
(
p
val
);
AZ
(
*
p
val
);
AN
(
p
rhs
);
AZ
(
*
p
rhs
);
if
(
vxp
->
t
->
tok
!=
VAL
)
{
VSB_printf
(
vxp
->
sb
,
"Expected regular expression got '%.*s' "
,
PF
(
vxp
->
t
));
...
...
@@ -155,12 +155,12 @@ vxp_expr_regex(struct vxp *vxp, struct vex_val **pval)
return
;
}
AN
(
vxp
->
t
->
dec
);
ALLOC_OBJ
(
*
p
val
,
VEX_VAL
_MAGIC
);
AN
(
*
p
val
);
(
*
p
val
)
->
type
=
VEX_REGEX
;
(
*
p
val
)
->
val_string
=
strdup
(
vxp
->
t
->
dec
);
(
*
p
val
)
->
val_regex
=
VRE_compile
(
vxp
->
t
->
dec
,
0
,
&
errptr
,
&
erroff
);
if
((
*
p
val
)
->
val_regex
==
NULL
)
{
ALLOC_OBJ
(
*
p
rhs
,
VEX_RHS
_MAGIC
);
AN
(
*
p
rhs
);
(
*
p
rhs
)
->
type
=
VEX_REGEX
;
(
*
p
rhs
)
->
val_string
=
strdup
(
vxp
->
t
->
dec
);
(
*
p
rhs
)
->
val_regex
=
VRE_compile
(
vxp
->
t
->
dec
,
0
,
&
errptr
,
&
erroff
);
if
((
*
p
rhs
)
->
val_regex
==
NULL
)
{
AN
(
errptr
);
VSB_printf
(
vxp
->
sb
,
"Regular expression error: %s "
,
errptr
);
vxp_ErrWhere
(
vxp
,
vxp
->
t
,
erroff
);
...
...
@@ -172,8 +172,8 @@ vxp_expr_regex(struct vxp *vxp, struct vex_val **pval)
/*
* SYNTAX:
* expr_cmp:
*
tag
*
tag
<operator> num|str|regex
*
lhs
*
lhs
<operator> num|str|regex
*/
static
void
...
...
@@ -184,13 +184,13 @@ vxp_expr_cmp(struct vxp *vxp, struct vex **pvex)
AZ
(
*
pvex
);
ALLOC_OBJ
(
*
pvex
,
VEX_MAGIC
);
AN
(
*
pvex
);
vxp_expr_
tag
(
vxp
,
&
(
*
pvex
)
->
tag
);
vxp_expr_
lhs
(
vxp
,
&
(
*
pvex
)
->
lhs
);
ERRCHK
(
vxp
);
/* Test operator */
switch
(
vxp
->
t
->
tok
)
{
/* Single
tag
expressions don't take any more tokens */
/* Single
lhs
expressions don't take any more tokens */
case
EOI
:
case
T_AND
:
case
T_OR
:
...
...
@@ -231,15 +231,15 @@ vxp_expr_cmp(struct vxp *vxp, struct vex **pvex)
case
T_GEQ
:
/* >= */
case
T_LEQ
:
/* <= */
case
T_NEQ
:
/* != */
vxp_expr_num
(
vxp
,
&
(
*
pvex
)
->
val
);
vxp_expr_num
(
vxp
,
&
(
*
pvex
)
->
rhs
);
break
;
case
T_SEQ
:
/* eq */
case
T_SNEQ
:
/* ne */
vxp_expr_str
(
vxp
,
&
(
*
pvex
)
->
val
);
vxp_expr_str
(
vxp
,
&
(
*
pvex
)
->
rhs
);
break
;
case
'~'
:
/* ~ */
case
T_NOMATCH
:
/* !~ */
vxp_expr_regex
(
vxp
,
&
(
*
pvex
)
->
val
);
vxp_expr_regex
(
vxp
,
&
(
*
pvex
)
->
rhs
);
break
;
default:
INCOMPL
();
...
...
@@ -401,14 +401,14 @@ void
vex_Free
(
struct
vex
**
pvex
)
{
if
((
*
pvex
)
->
tag
!=
NULL
)
FREE_OBJ
((
*
pvex
)
->
tag
);
if
((
*
pvex
)
->
val
!=
NULL
)
{
if
((
*
pvex
)
->
val
->
val_string
)
free
((
*
pvex
)
->
val
->
val_string
);
if
((
*
pvex
)
->
val
->
val_regex
)
VRE_free
(
&
(
*
pvex
)
->
val
->
val_regex
);
FREE_OBJ
((
*
pvex
)
->
val
);
if
((
*
pvex
)
->
lhs
!=
NULL
)
FREE_OBJ
((
*
pvex
)
->
lhs
);
if
((
*
pvex
)
->
rhs
!=
NULL
)
{
if
((
*
pvex
)
->
rhs
->
val_string
)
free
((
*
pvex
)
->
rhs
->
val_string
);
if
((
*
pvex
)
->
rhs
->
val_regex
)
VRE_free
(
&
(
*
pvex
)
->
rhs
->
val_regex
);
FREE_OBJ
((
*
pvex
)
->
rhs
);
}
if
((
*
pvex
)
->
a
!=
NULL
)
{
vex_Free
(
&
(
*
pvex
)
->
a
);
...
...
@@ -425,28 +425,28 @@ vex_Free(struct vex **pvex)
#ifdef VXP_DEBUG
static
void
vex_print_
val
(
const
struct
vex_val
*
val
)
vex_print_
rhs
(
const
struct
vex_rhs
*
rhs
)
{
CHECK_OBJ_NOTNULL
(
val
,
VEX_VAL
_MAGIC
);
switch
(
val
->
type
)
{
CHECK_OBJ_NOTNULL
(
rhs
,
VEX_RHS
_MAGIC
);
switch
(
rhs
->
type
)
{
case
VEX_INT
:
fprintf
(
stderr
,
"INT=%jd"
,
(
intmax_t
)
val
->
val_int
);
fprintf
(
stderr
,
"INT=%jd"
,
(
intmax_t
)
rhs
->
val_int
);
break
;
case
VEX_FLOAT
:
fprintf
(
stderr
,
"FLOAT=%f"
,
val
->
val_float
);
fprintf
(
stderr
,
"FLOAT=%f"
,
rhs
->
val_float
);
break
;
case
VEX_STRING
:
AN
(
val
->
val_string
);
fprintf
(
stderr
,
"STRING='%s'"
,
val
->
val_string
);
AN
(
rhs
->
val_string
);
fprintf
(
stderr
,
"STRING='%s'"
,
rhs
->
val_string
);
break
;
case
VEX_REGEX
:
AN
(
val
->
val_string
);
AN
(
val
->
val_regex
);
fprintf
(
stderr
,
"REGEX='%s'"
,
val
->
val_string
);
AN
(
rhs
->
val_string
);
AN
(
rhs
->
val_regex
);
fprintf
(
stderr
,
"REGEX='%s'"
,
rhs
->
val_string
);
break
;
default:
WRONG
(
"
value
type"
);
WRONG
(
"
rhs
type"
);
break
;
}
}
...
...
@@ -457,13 +457,13 @@ vex_print(const struct vex *vex, int indent)
CHECK_OBJ_NOTNULL
(
vex
,
VEX_MAGIC
);
fprintf
(
stderr
,
"%*s%s"
,
indent
,
""
,
vxp_tnames
[
vex
->
tok
]);
if
(
vex
->
tag
!=
NULL
)
{
CHECK_OBJ_NOTNULL
(
vex
->
tag
,
VEX_TAG
_MAGIC
);
fprintf
(
stderr
,
" tag=%s"
,
VSL_tags
[
vex
->
tag
->
tag
]);
if
(
vex
->
lhs
!=
NULL
)
{
CHECK_OBJ_NOTNULL
(
vex
->
lhs
,
VEX_LHS
_MAGIC
);
fprintf
(
stderr
,
" tag=%s"
,
VSL_tags
[
vex
->
lhs
->
tag
]);
}
if
(
vex
->
val
!=
NULL
)
{
if
(
vex
->
rhs
!=
NULL
)
{
fprintf
(
stderr
,
" "
);
vex_print_
val
(
vex
->
val
);
vex_print_
rhs
(
vex
->
rhs
);
}
fprintf
(
stderr
,
"
\n
"
);
if
(
vex
->
a
!=
NULL
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment