File: ctpl-parser.c

package info (click to toggle)
ctpl 0.3.5%2Bdfsg-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 3,500 kB
  • sloc: ansic: 5,892; sh: 5,252; python: 159; makefile: 131; sed: 16
file content (248 lines) | stat: -rw-r--r-- 6,762 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
/* 
 * 
 * Copyright (C) 2009-2011 Colomban Wendling <ban@herbesfolles.org>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 */

#include "ctpl-parser.h"
#include <glib.h>
#include <string.h>
#include "ctpl-i18n.h"
#include "ctpl-eval.h"
#include "ctpl-token.h"
#include "ctpl-token-private.h"
#include "ctpl-output-stream.h"


/**
 * SECTION: parser
 * @short_description: Token tree parser
 * @include: ctpl/ctpl.h
 * 
 * Parses a #CtplToken tree against a #CtplEnviron.
 * 
 * To parse a token tree, use ctpl_parser_parse().
 */

/* The only useful thing is to be able to push or pop variables/constants :
 * 
 * A loop :
 * {for i in items}
 *   ...
 * {end}
 * ->
 *   lookup items
 *   assert items is array
 *   while items:
 *     push i = *items
 *     ...
 *     pop i
 *     ++items
 * 
 * An affectation:
 * {i = 42}
 * ->
 *   pop i
 *   push i = 42
 * That may be simplified, needing one more instruction:
 * {i = 42}
 * ->
 *   set i = 42
 * With a reference to a variable/constant:
 * {i = foo}
 * ->
 *   lookup foo
 *   set i = foo
 * 
 * A test :
 * {if n > 42}
 *   ...
 * {end}
 * ->
 *   lookup n
 *   if eval n > 42:
 *     ...
 * 
 */

/*<standard>*/
GQuark
ctpl_parser_error_quark (void)
{
  static GQuark error_quark = 0;
  
  if (G_UNLIKELY (error_quark == 0)) {
    error_quark = g_quark_from_static_string ("CtplParser");
  }
  
  return error_quark;
}


/* "parses" a data token */
static gboolean
ctpl_parser_parse_token_data (const gchar      *data,
                              CtplOutputStream *output,
                              GError          **error)
{
  return ctpl_output_stream_write (output, data, -1, error);
}

/* Tries to parse a `for` token */
static gboolean
ctpl_parser_parse_token_for (const CtplTokenFor  *token,
                             CtplEnviron         *env,
                             CtplOutputStream    *output,
                             GError             **error)
{
  /* we can safely assume token holds array here */
  CtplValue value;
  gboolean  rv = FALSE;
  
  ctpl_value_init (&value);
  if (ctpl_eval_value (token->array, env, &value, error)) {
    if (! CTPL_VALUE_HOLDS_ARRAY (&value)) {
      gchar *array_name;
      
      array_name = ctpl_value_to_string (&value);
      g_set_error (error, CTPL_PARSER_ERROR, CTPL_PARSER_ERROR_INCOMPATIBLE_SYMBOL,
                   _("Cannot iterate over value '%s'"),
                   array_name);
      g_free (array_name);
    } else {
      const GSList *array_items;
      
      rv = TRUE;
      array_items = ctpl_value_get_array (&value);
      for (; rv && array_items; array_items = array_items->next) {
        ctpl_environ_push (env, token->iter, array_items->data);
        rv = ctpl_parser_parse (token->children, env, output, error);
        ctpl_environ_pop (env, token->iter, NULL);
      }
    }
  }
  ctpl_value_free_value (&value);
  
  return rv;
}

/* Tries to parse an `if` token */
static gboolean
ctpl_parser_parse_token_if (const CtplTokenIf  *token,
                            CtplEnviron        *env,
                            CtplOutputStream   *output,
                            GError            **error)
{
  gboolean  rv = FALSE;
  gboolean  eval;
  
  if (ctpl_eval_bool (token->condition, env, &eval, error)) {
    rv = ctpl_parser_parse (eval ? token->if_children
                                 : token->else_children,
                            env, output, error);
  }
  
  return rv;
}

/* Tries to parse an expression (a variable, a complete expression, ...). */
static gboolean
ctpl_parser_parse_token_expr (CtplTokenExpr    *expr,
                              CtplEnviron      *env,
                              CtplOutputStream *output,
                              GError          **error)
{
  CtplValue eval_value;
  gboolean  rv = FALSE;
  
  ctpl_value_init (&eval_value);
  if (ctpl_eval_value (expr, env, &eval_value, error)) {
    gchar *strval;
    
    strval = ctpl_value_to_string (&eval_value);
    if (! strval) {
      g_set_error (error, CTPL_PARSER_ERROR, CTPL_PARSER_ERROR_FAILED,
                   _("Cannot convert expression to a printable format"));
    } else {
      rv = ctpl_output_stream_write (output, strval, -1, error);
    }
    g_free (strval);
  }
  ctpl_value_free_value (&eval_value);
  
  return rv;
}

/* Tries to parse a token by dispatching calls to specific parsers. */
static gboolean
ctpl_parser_parse_token (const CtplToken   *token,
                         CtplEnviron       *env,
                         CtplOutputStream  *output,
                         GError           **error)
{
  gboolean rv = FALSE;
  
  switch (ctpl_token_get_type (token)) {
    case CTPL_TOKEN_TYPE_DATA:
      rv = ctpl_parser_parse_token_data (token->token.t_data, output, error);
      break;
    
    case CTPL_TOKEN_TYPE_FOR:
      rv = ctpl_parser_parse_token_for (token->token.t_for, env, output, error);
      break;
    
    case CTPL_TOKEN_TYPE_IF:
      rv = ctpl_parser_parse_token_if (token->token.t_if, env, output, error);
      break;
    
    case CTPL_TOKEN_TYPE_EXPR:
      rv = ctpl_parser_parse_token_expr (token->token.t_expr, env, output, error);
      break;
    
    default:
      g_critical ("Invalid/unknown token type %d", ctpl_token_get_type (token));
      g_assert_not_reached ();
  }
  
  return rv;
}

/**
 * ctpl_parser_parse:
 * @tree: A #CtplToken from which start parsing
 * @env: A #CtplEnviron representing the parsing environment
 * @output: A #CtplInputStream in which write parsing output
 * @error: Location where return a #GError or %NULL to ignore errors
 * 
 * Parses a token tree against an environment and outputs the result to @output.
 * 
 * Returns: %TRUE on success, %FALSE otherwise, in which case @error shall be
 *          set to the error that occurred.
 */
gboolean
ctpl_parser_parse (const CtplToken   *tree,
                   CtplEnviron       *env,
                   CtplOutputStream  *output,
                   GError           **error)
{
  gboolean rv = TRUE;
  
  for (; rv && tree; tree = tree->next) {
    rv = ctpl_parser_parse_token (tree, env, output, error);
  }
  
  return rv;
}