aboutsummaryrefslogtreecommitdiffstats
path: root/epan/dfilter
diff options
context:
space:
mode:
authorJoão Valverde <j@v6e.pt>2021-09-26 22:22:50 +0100
committerWireshark GitLab Utility <gerald+gitlab-utility@wireshark.org>2021-10-01 16:04:37 +0000
commitdb18865e5517484ed7ce3aebc2e8695d5bf7bedf (patch)
tree9f26806ce246b0cf4f987c541a196acc4aa784de /epan/dfilter
parent487e2b6bc3c204559fa6502f06c33c2383cd5e90 (diff)
dfilter: Save token value to syntax tree
When parsing we save the token value to the syntax tree. This is useful for better error reporting. Use it to report an invalid entity for the slice operation. Before only the memory location was reported, which is not a good error message. Before: % dftest '"01:02:03:04"[0:3] == foo' Filter: ""01:02:03:04"[0:3] == foo" dftest: Range is not supported for entity <0x7f6c84017740> of type STRING After: % dftest '"01:02:03:04"[0:3] == foo' Filter: ""01:02:03:04"[0:3] == foo" dftest: Range is not supported for entity 01:02:03:04 of type STRING When creating a new node from an old one we need to copy the token value. Simple tokens such as RBRACKET, COMMA and COLON are not part of the AST and don't have an associated semantic value.
Diffstat (limited to 'epan/dfilter')
-rw-r--r--epan/dfilter/dfilter.c2
-rw-r--r--epan/dfilter/grammar.lemon20
-rw-r--r--epan/dfilter/scanner.l62
-rw-r--r--epan/dfilter/semcheck.c53
-rw-r--r--epan/dfilter/syntax-tree.c24
-rw-r--r--epan/dfilter/syntax-tree.h11
6 files changed, 101 insertions, 71 deletions
diff --git a/epan/dfilter/dfilter.c b/epan/dfilter/dfilter.c
index e093668d88..84822b3924 100644
--- a/epan/dfilter/dfilter.c
+++ b/epan/dfilter/dfilter.c
@@ -311,7 +311,7 @@ dfilter_compile(const gchar *text, dfilter_t **dfp, gchar **err_msg)
df_set_extra(&state, scanner);
while (1) {
- df_lval = stnode_new(STTYPE_UNINITIALIZED, NULL);
+ df_lval = stnode_new(STTYPE_UNINITIALIZED, NULL, NULL);
token = df_lex(scanner);
/* Check for scanner failure */
diff --git a/epan/dfilter/grammar.lemon b/epan/dfilter/grammar.lemon
index 814c0e3cc4..66158129be 100644
--- a/epan/dfilter/grammar.lemon
+++ b/epan/dfilter/grammar.lemon
@@ -152,25 +152,25 @@ expr(X) ::= logical_test(L). { X = L; }
/* Logical tests */
logical_test(T) ::= expr(E) TEST_AND expr(F).
{
- T = stnode_new(STTYPE_TEST, NULL);
+ T = stnode_new(STTYPE_TEST, NULL, NULL);
sttype_test_set2(T, TEST_OP_AND, E, F);
}
logical_test(T) ::= expr(E) TEST_OR expr(F).
{
- T = stnode_new(STTYPE_TEST, NULL);
+ T = stnode_new(STTYPE_TEST, NULL, NULL);
sttype_test_set2(T, TEST_OP_OR, E, F);
}
logical_test(T) ::= TEST_NOT expr(E).
{
- T = stnode_new(STTYPE_TEST, NULL);
+ T = stnode_new(STTYPE_TEST, NULL, NULL);
sttype_test_set1(T, TEST_OP_NOT, E);
}
logical_test(T) ::= entity(E).
{
- T = stnode_new(STTYPE_TEST, NULL);
+ T = stnode_new(STTYPE_TEST, NULL, NULL);
sttype_test_set1(T, TEST_OP_EXISTS, E);
}
@@ -190,7 +190,7 @@ range_body(B) ::= range(R). { B = R; }
/* Ranges */
range(R) ::= range_body(B) LBRACKET drnode_list(L) RBRACKET.
{
- R = stnode_new(STTYPE_RANGE, NULL);
+ R = stnode_new(STTYPE_RANGE, NULL, NULL);
sttype_range_set(R, B, L);
/* Delete the list, but not the drange_nodes that
@@ -266,7 +266,7 @@ drnode(D) ::= INTEGER(X).
/* Relational tests */
relation_test(T) ::= entity(E) rel_op2(O) entity(F).
{
- T = stnode_new(STTYPE_TEST, NULL);
+ T = stnode_new(STTYPE_TEST, NULL, NULL);
sttype_test_set2(T, O, E, F);
}
@@ -284,10 +284,10 @@ relation_test(T) ::= entity(E) rel_op2(O) relation_test(R).
sttype_test_get(F, NULL, &F, NULL);
} while (stnode_type_id(F) == STTYPE_TEST);
- L = stnode_new(STTYPE_TEST, NULL);
+ L = stnode_new(STTYPE_TEST, NULL, NULL);
sttype_test_set2(L, O, E, stnode_dup(F));
- T = stnode_new(STTYPE_TEST, NULL);
+ T = stnode_new(STTYPE_TEST, NULL, NULL);
sttype_test_set2(T, TEST_OP_AND, L, R);
}
@@ -304,8 +304,8 @@ rel_op2(O) ::= TEST_MATCHES. { O = TEST_OP_MATCHES; }
relation_test(T) ::= entity(E) TEST_IN LBRACE setnode_list(L) RBRACE.
{
stnode_t *S;
- T = stnode_new(STTYPE_TEST, NULL);
- S = stnode_new(STTYPE_SET, L);
+ T = stnode_new(STTYPE_TEST, NULL, NULL);
+ S = stnode_new(STTYPE_SET, L, NULL);
sttype_test_set2(T, TEST_OP_IN, E, S);
}
diff --git a/epan/dfilter/scanner.l b/epan/dfilter/scanner.l
index 1a50d1200d..0ff0d906be 100644
--- a/epan/dfilter/scanner.l
+++ b/epan/dfilter/scanner.l
@@ -84,10 +84,12 @@ DIAG_OFF_FLEX
/*#undef YY_NO_UNPUT*/
-static int set_lval(int token, gpointer data);
-static int set_lval_int(dfwork_t *dfw, int token, char *s);
+static int set_lval_str(int token, const char *token_value);
+static int set_lval_field(int token, header_field_info *hfinfo, const char *token_value);
+static int set_lval_func(int token, df_func_def_t *func, const char *token_value);
+static int set_lval_int(dfwork_t *dfw, int token, const char *token_value);
static int simple(int token);
-static gboolean str_to_gint32(dfwork_t *dfw, char *s, gint32* pint);
+static gboolean str_to_gint32(dfwork_t *dfw, const char *s, gint32* pint);
/*
* Sleazy hack to suppress compiler warnings in yy_fatal_error().
@@ -269,7 +271,7 @@ static gboolean str_to_gint32(dfwork_t *dfw, char *s, gint32* pint);
/* end quote */
int token;
BEGIN(INITIAL);
- token = set_lval(TOKEN_STRING, yyextra->quoted_string->str);
+ token = set_lval_str(TOKEN_STRING, yyextra->quoted_string->str);
g_string_free(yyextra->quoted_string, TRUE);
yyextra->quoted_string = NULL;
return token;
@@ -371,7 +373,7 @@ static gboolean str_to_gint32(dfwork_t *dfw, char *s, gint32* pint);
int token;
BEGIN(INITIAL);
g_string_append_c(yyextra->quoted_string, '\'');
- token = set_lval(TOKEN_CHARCONST, yyextra->quoted_string->str);
+ token = set_lval_str(TOKEN_CHARCONST, yyextra->quoted_string->str);
g_string_free(yyextra->quoted_string, TRUE);
yyextra->quoted_string = NULL;
return token;
@@ -391,7 +393,7 @@ static gboolean str_to_gint32(dfwork_t *dfw, char *s, gint32* pint);
[-[:alnum:]_\.:]*\/[[:digit:]]+ {
/* CIDR */
- return set_lval(TOKEN_UNPARSED, yytext);
+ return set_lval_str(TOKEN_UNPARSED, yytext);
}
([.][-+[:alnum:]_:]+)+[.]{0,2} |
@@ -409,30 +411,28 @@ static gboolean str_to_gint32(dfwork_t *dfw, char *s, gint32* pint);
hfinfo = proto_registrar_get_byname(yytext);
if (hfinfo) {
/* Yes, it's a field name */
- return set_lval(TOKEN_FIELD, hfinfo);
+ return set_lval_field(TOKEN_FIELD, hfinfo, yytext);
}
hfinfo = proto_registrar_get_byalias(yytext);
if (hfinfo) {
/* Yes, it's an aliased field name */
- /* XXX Do we need to make a copy of yytext? dfilter_compile
- * will dup this later on. */
add_deprecated_token(yyextra->deprecated, yytext);
- return set_lval(TOKEN_FIELD, hfinfo);
+ return set_lval_field(TOKEN_FIELD, hfinfo, yytext);
}
df_func_def = df_func_lookup(yytext);
if (df_func_def) {
/* Yes, it's a dfilter function */
- return set_lval(TOKEN_FUNCTION, df_func_def);
+ return set_lval_func(TOKEN_FUNCTION, df_func_def, yytext);
}
/* No match, so treat it as an unparsed string */
- return set_lval(TOKEN_UNPARSED, yytext);
+ return set_lval_str(TOKEN_UNPARSED, yytext);
}
. {
/* Default */
- return set_lval(TOKEN_UNPARSED, yytext);
+ return set_lval_str(TOKEN_UNPARSED, yytext);
}
@@ -479,9 +479,9 @@ simple(int token)
}
static int
-set_lval(int token, gpointer data)
+set_lval_str(int token, const char *token_value)
{
- sttype_id_t type_id = STTYPE_UNINITIALIZED;
+ sttype_id_t type_id;
switch (token) {
case TOKEN_STRING:
@@ -490,29 +490,39 @@ set_lval(int token, gpointer data)
case TOKEN_CHARCONST:
type_id = STTYPE_CHARCONST;
break;
- case TOKEN_FIELD:
- type_id = STTYPE_FIELD;
- break;
case TOKEN_UNPARSED:
type_id = STTYPE_UNPARSED;
break;
- case TOKEN_FUNCTION:
- type_id = STTYPE_FUNCTION;
- break;
default:
ws_assert_not_reached();
}
- stnode_init(df_lval, type_id, data);
+ stnode_init(df_lval, type_id, (gpointer)token_value, token_value);
+ return token;
+}
+
+static int
+set_lval_field(int token, header_field_info *hfinfo, const char *token_value)
+{
+ ws_assert(token == TOKEN_FIELD);
+ stnode_init(df_lval, STTYPE_FIELD, hfinfo, token_value);
+ return token;
+}
+
+static int
+set_lval_func(int token, df_func_def_t *func, const char *token_value)
+{
+ ws_assert(token == TOKEN_FUNCTION);
+ stnode_init(df_lval, STTYPE_FUNCTION, func, token_value);
return token;
}
static int
-set_lval_int(dfwork_t *dfw, int token, char *s)
+set_lval_int(dfwork_t *dfw, int token, const char *token_value)
{
sttype_id_t type_id = STTYPE_UNINITIALIZED;
gint32 val;
- if (!str_to_gint32(dfw, s, &val)) {
+ if (!str_to_gint32(dfw, token_value, &val)) {
return SCAN_FAILED;
}
@@ -524,13 +534,13 @@ set_lval_int(dfwork_t *dfw, int token, char *s)
ws_assert_not_reached();
}
- stnode_init_int(df_lval, type_id, val);
+ stnode_init_int(df_lval, type_id, val, token_value);
return token;
}
static gboolean
-str_to_gint32(dfwork_t *dfw, char *s, gint32* pint)
+str_to_gint32(dfwork_t *dfw, const char *s, gint32* pint)
{
char *endptr;
long integer;
diff --git a/epan/dfilter/semcheck.c b/epan/dfilter/semcheck.c
index 86d689c7b0..86ab74fb16 100644
--- a/epan/dfilter/semcheck.c
+++ b/epan/dfilter/semcheck.c
@@ -593,7 +593,7 @@ convert_to_bytes(stnode_t *arg)
stnode_t *new_st;
drange_node *rn;
- new_st = stnode_new(STTYPE_RANGE, NULL);
+ new_st = stnode_new(STTYPE_RANGE, NULL, arg->token_value);
rn = drange_node_new();
drange_node_set_start_offset(rn, 0);
@@ -714,7 +714,7 @@ check_relation_LHS_FIELD(dfwork_t *dfw, const char *relation_string,
if (!pcre) {
THROW(TypeError);
}
- new_st = stnode_new(STTYPE_PCRE, pcre);
+ new_st = stnode_new(STTYPE_PCRE, pcre, st_arg2->token_value);
} else {
/* Skip incompatible fields */
while (hfinfo1->same_name_prev_id != -1 &&
@@ -756,7 +756,7 @@ check_relation_LHS_FIELD(dfwork_t *dfw, const char *relation_string,
if (!fvalue) {
THROW(TypeError);
}
- new_st = stnode_new(STTYPE_FVALUE, fvalue);
+ new_st = stnode_new(STTYPE_FVALUE, fvalue, st_arg2->token_value);
}
if (stnode_type_id(st_node) == STTYPE_TEST) {
sttype_test_set2_args(st_node, st_arg1, new_st);
@@ -887,7 +887,7 @@ check_relation_LHS_STRING(dfwork_t *dfw, const char* relation_string,
}
}
- new_st = stnode_new(STTYPE_FVALUE, fvalue);
+ new_st = stnode_new(STTYPE_FVALUE, fvalue, st_arg1->token_value);
sttype_test_set2_args(st_node, new_st, st_arg2);
stnode_free(st_arg1);
}
@@ -906,7 +906,7 @@ check_relation_LHS_STRING(dfwork_t *dfw, const char* relation_string,
if (!fvalue) {
THROW(TypeError);
}
- new_st = stnode_new(STTYPE_FVALUE, fvalue);
+ new_st = stnode_new(STTYPE_FVALUE, fvalue, st_arg1->token_value);
sttype_test_set2_args(st_node, new_st, st_arg2);
stnode_free(st_arg1);
}
@@ -929,7 +929,7 @@ check_relation_LHS_STRING(dfwork_t *dfw, const char* relation_string,
check_function(dfw, st_arg2);
- new_st = stnode_new(STTYPE_FVALUE, fvalue);
+ new_st = stnode_new(STTYPE_FVALUE, fvalue, st_arg1->token_value);
sttype_test_set2_args(st_node, new_st, st_arg2);
stnode_free(st_arg1);
}
@@ -981,7 +981,7 @@ check_relation_LHS_UNPARSED(dfwork_t *dfw, const char* relation_string,
}
}
- new_st = stnode_new(STTYPE_FVALUE, fvalue);
+ new_st = stnode_new(STTYPE_FVALUE, fvalue, st_arg1->token_value);
sttype_test_set2_args(st_node, new_st, st_arg2);
stnode_free(st_arg1);
}
@@ -1000,7 +1000,7 @@ check_relation_LHS_UNPARSED(dfwork_t *dfw, const char* relation_string,
if (!fvalue) {
THROW(TypeError);
}
- new_st = stnode_new(STTYPE_FVALUE, fvalue);
+ new_st = stnode_new(STTYPE_FVALUE, fvalue, st_arg1->token_value);
sttype_test_set2_args(st_node, new_st, st_arg2);
stnode_free(st_arg1);
}
@@ -1023,7 +1023,7 @@ check_relation_LHS_UNPARSED(dfwork_t *dfw, const char* relation_string,
check_function(dfw, st_arg2);
- new_st = stnode_new(STTYPE_FVALUE, fvalue);
+ new_st = stnode_new(STTYPE_FVALUE, fvalue, st_arg1->token_value);
sttype_test_set2_args(st_node, new_st, st_arg2);
stnode_free(st_arg1);
}
@@ -1078,13 +1078,12 @@ check_relation_LHS_RANGE(dfwork_t *dfw, const char *relation_string,
check_function(dfw, entity1);
+ } else if (entity1) {
+ dfilter_fail(dfw, "Range is not supported for entity %s of type %s",
+ stnode_token_value(entity1), stnode_type_name(entity1));
+ THROW(TypeError);
} else {
- if (entity1 == NULL) {
- dfilter_fail(dfw, "Range is not supported, details: " G_STRLOC " entity: NULL");
- } else {
- dfilter_fail(dfw, "Range is not supported, details: " G_STRLOC " entity: %p of type %d",
- (void *)entity1, stnode_type_id(entity1));
- }
+ dfilter_fail(dfw, "Range is not supported, details: " G_STRLOC " entity: NULL");
THROW(TypeError);
}
@@ -1118,13 +1117,13 @@ check_relation_LHS_RANGE(dfwork_t *dfw, const char *relation_string,
if (!pcre) {
THROW(TypeError);
}
- new_st = stnode_new(STTYPE_PCRE, pcre);
+ new_st = stnode_new(STTYPE_PCRE, pcre, st_arg2->token_value);
} else {
fvalue = dfilter_fvalue_from_string(dfw, FT_BYTES, s);
if (!fvalue) {
THROW(TypeError);
}
- new_st = stnode_new(STTYPE_FVALUE, fvalue);
+ new_st = stnode_new(STTYPE_FVALUE, fvalue, st_arg2->token_value);
}
sttype_test_set2_args(st_node, st_arg1, new_st);
stnode_free(st_arg2);
@@ -1139,7 +1138,7 @@ check_relation_LHS_RANGE(dfwork_t *dfw, const char *relation_string,
if (!pcre) {
THROW(TypeError);
}
- new_st = stnode_new(STTYPE_PCRE, pcre);
+ new_st = stnode_new(STTYPE_PCRE, pcre, st_arg2->token_value);
} else {
/*
* The RHS should be FT_BYTES. However, there is a
@@ -1176,7 +1175,7 @@ check_relation_LHS_RANGE(dfwork_t *dfw, const char *relation_string,
if (!fvalue) {
THROW(TypeError);
}
- new_st = stnode_new(STTYPE_FVALUE, fvalue);
+ new_st = stnode_new(STTYPE_FVALUE, fvalue, st_arg2->token_value);
}
sttype_test_set2_args(st_node, st_arg1, new_st);
stnode_free(st_arg2);
@@ -1190,7 +1189,7 @@ check_relation_LHS_RANGE(dfwork_t *dfw, const char *relation_string,
if (!pcre) {
THROW(TypeError);
}
- new_st = stnode_new(STTYPE_PCRE, pcre);
+ new_st = stnode_new(STTYPE_PCRE, pcre, st_arg2->token_value);
} else {
/* The RHS should be FT_BYTES, but a character is just a
* one-byte byte string. */
@@ -1198,7 +1197,7 @@ check_relation_LHS_RANGE(dfwork_t *dfw, const char *relation_string,
if (!fvalue) {
THROW(TypeError);
}
- new_st = stnode_new(STTYPE_FVALUE, fvalue);
+ new_st = stnode_new(STTYPE_FVALUE, fvalue, st_arg2->token_value);
}
sttype_test_set2_args(st_node, st_arg1, new_st);
stnode_free(st_arg2);
@@ -1253,7 +1252,7 @@ check_param_entity(dfwork_t *dfw, stnode_t *st_node)
THROW(TypeError);
}
- new_st = stnode_new(STTYPE_FVALUE, fvalue);
+ new_st = stnode_new(STTYPE_FVALUE, fvalue, st_node->token_value);
stnode_free(st_node);
return new_st;
}
@@ -1322,13 +1321,13 @@ check_relation_LHS_FUNCTION(dfwork_t *dfw, const char *relation_string,
if (!pcre) {
THROW(TypeError);
}
- new_st = stnode_new(STTYPE_PCRE, pcre);
+ new_st = stnode_new(STTYPE_PCRE, pcre, st_arg2->token_value);
} else {
fvalue = dfilter_fvalue_from_string(dfw, ftype1, s);
if (!fvalue) {
THROW(TypeError);
}
- new_st = stnode_new(STTYPE_FVALUE, fvalue);
+ new_st = stnode_new(STTYPE_FVALUE, fvalue, st_arg2->token_value);
}
sttype_test_set2_args(st_node, st_arg1, new_st);
stnode_free(st_arg2);
@@ -1341,13 +1340,13 @@ check_relation_LHS_FUNCTION(dfwork_t *dfw, const char *relation_string,
if (!pcre) {
THROW(TypeError);
}
- new_st = stnode_new(STTYPE_PCRE, pcre);
+ new_st = stnode_new(STTYPE_PCRE, pcre, st_arg2->token_value);
} else {
fvalue = dfilter_fvalue_from_unparsed(dfw, ftype1, s, allow_partial_value);
if (!fvalue) {
THROW(TypeError);
}
- new_st = stnode_new(STTYPE_FVALUE, fvalue);
+ new_st = stnode_new(STTYPE_FVALUE, fvalue, st_arg2->token_value);
}
sttype_test_set2_args(st_node, st_arg1, new_st);
stnode_free(st_arg2);
@@ -1450,7 +1449,7 @@ check_relation(dfwork_t *dfw, const char *relation_string,
* functions will take care of it as if it didn't
* match a protocol string.
*/
- new_st = stnode_new(STTYPE_UNPARSED, s);
+ new_st = stnode_new(STTYPE_UNPARSED, s, st_arg2->token_value);
stnode_free(st_arg2);
st_arg2 = new_st;
sttype_test_set2_args(st_node, st_arg1, new_st);
diff --git a/epan/dfilter/syntax-tree.c b/epan/dfilter/syntax-tree.c
index 09388eb7c0..047aae25d6 100644
--- a/epan/dfilter/syntax-tree.c
+++ b/epan/dfilter/syntax-tree.c
@@ -78,7 +78,7 @@ sttype_lookup(sttype_id_t type_id)
stnode_t*
-stnode_new(sttype_id_t type_id, gpointer data)
+stnode_new(sttype_id_t type_id, gpointer data, const char *token_value)
{
sttype_t *type;
stnode_t *node;
@@ -102,6 +102,7 @@ stnode_new(sttype_id_t type_id, gpointer data)
}
}
+ node->token_value = g_strdup(token_value);
return node;
}
@@ -119,6 +120,7 @@ stnode_dup(const stnode_t *org)
node = g_new(stnode_t, 1);
node->magic = STNODE_MAGIC;
+
node->type = type;
node->flags = org->flags;
@@ -128,17 +130,20 @@ stnode_dup(const stnode_t *org)
node->data = org->data;
node->value = org->value;
+ node->token_value = g_strdup(org->token_value);
+
return node;
}
void
-stnode_init(stnode_t *node, sttype_id_t type_id, gpointer data)
+stnode_init(stnode_t *node, sttype_id_t type_id, gpointer data, const char *token_value)
{
sttype_t *type;
ws_assert_magic(node, STNODE_MAGIC);
ws_assert(!node->type);
ws_assert(!node->data);
+ ws_assert(!node->token_value);
type = sttype_lookup(type_id);
ws_assert(type);
@@ -152,12 +157,13 @@ stnode_init(stnode_t *node, sttype_id_t type_id, gpointer data)
node->data = data;
}
node->value = 0;
+ node->token_value = g_strdup(token_value);
}
void
-stnode_init_int(stnode_t *node, sttype_id_t type_id, gint32 value)
+stnode_init_int(stnode_t *node, sttype_id_t type_id, gint32 value, const char *token_value)
{
- stnode_init(node, type_id, NULL);
+ stnode_init(node, type_id, NULL, token_value);
node->value = value;
}
@@ -173,6 +179,7 @@ stnode_free(stnode_t *node)
else {
ws_assert(!node->data);
}
+ g_free(node->token_value);
g_free(node);
}
@@ -338,6 +345,15 @@ visit_tree(wmem_strbuf_t *buf, stnode_t *node, int level)
}
}
+const char *
+stnode_token_value(stnode_t *node)
+{
+ if (node->token_value) {
+ return node->token_value;
+ }
+ return "<unknown token>";
+}
+
void
log_syntax_tree(enum ws_log_level level, stnode_t *root, const char *msg)
{
diff --git a/epan/dfilter/syntax-tree.h b/epan/dfilter/syntax-tree.h
index 47b9893f41..afae6c0834 100644
--- a/epan/dfilter/syntax-tree.h
+++ b/epan/dfilter/syntax-tree.h
@@ -62,6 +62,8 @@ typedef struct {
* set aside to time to do so. */
gpointer data;
int32_t value;
+
+ char *token_value;
} stnode_t;
/* These are the sttype_t registration function prototypes. */
@@ -83,16 +85,16 @@ void
sttype_register(sttype_t *type);
stnode_t*
-stnode_new(sttype_id_t type_id, gpointer data);
+stnode_new(sttype_id_t type_id, gpointer data, const char *token_value);
stnode_t*
stnode_dup(const stnode_t *org);
void
-stnode_init(stnode_t *node, sttype_id_t type_id, gpointer data);
+stnode_init(stnode_t *node, sttype_id_t type_id, gpointer data, const char *token_value);
void
-stnode_init_int(stnode_t *node, sttype_id_t type_id, gint32 value);
+stnode_init_int(stnode_t *node, sttype_id_t type_id, gint32 value, const char *token_value);
void
stnode_free(stnode_t *node);
@@ -118,6 +120,9 @@ stnode_tostr(stnode_t *node);
gboolean
stnode_inside_parens(stnode_t *node);
+const char *
+stnode_token_value(stnode_t *node);
+
void
stnode_set_inside_parens(stnode_t *node, gboolean inside);