/vqdr/trunk

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/vqdr/trunk
64 by Gustav Hartvigsson
[General] Major refactoring. Utils -> Vee nenaming.
1
using Vee;
54 by Gustav Hartvigsson
More work torwards inperementing the parser.
2
3
namespace VQDR.Expression {
4
5
  public enum CharType {
6
    INVALID = 0,
7
    NULL = 1,
45 by Gustav Hartvigsson
* woopes.
8
    DIGIT,
9
    UOP,
10
    OP,
11
    ALPHA,
12
    DOT,
13
    POP,
14
    PCL,
15
    COM,
54 by Gustav Hartvigsson
More work torwards inperementing the parser.
16
    UNKNOWN,
17
    _NUM_VAL;
18
19
    public string to_string () {
20
      // We add one as we have an invalid case.
21
      static_assert (CharType._NUM_VAL == 10 + 1);
22
      switch (this) {
23
        case (NULL): 
24
          return "NULL";
25
        case (DIGIT):
26
          return "DIGIT";
27
        case (UOP):
28
          return "UOP";
29
        case (OP):
30
          return "OP";
31
        case (ALPHA):
32
          return "ALPHA";
33
        case (DOT):
34
          return "DOT";
35
        case (POP):
36
          return "POP";
37
        case (PCL):
38
          return "PLC";
39
        case (COM):
40
          return "COM";
41
        case (UNKNOWN):
42
          return "UNKNOWN";
43
        default:
44
          assert_not_reached ();
45
      }
46
    }
47
45 by Gustav Hartvigsson
* woopes.
48
  /**
49
   * Determine character type.
50
   * @param ch Character to be checked.
51
   * @return The character type.
52
   */
54 by Gustav Hartvigsson
More work torwards inperementing the parser.
53
    public static CharType from_char (char ch) {
45 by Gustav Hartvigsson
* woopes.
54
      switch (ch) {
55
        case '0': case '1':
56
        case '2': case '3':
57
        case '4': case '5':
58
        case '6': case '7':
59
        case '8': case '9':
60
          return DIGIT;
61
        case '+': case '-':
62
          return UOP;
63
        case '*': case '/':
64
          return OP;
65
        case '.':
66
          return DOT;
67
        case '(':
68
          return POP;
69
        case ')':
70
          return PCL;
71
        case ',':
72
          return COM;
73
        case ' ': case 0:
74
          return NULL;
75
        default:
76
          if ((ch >= 'a' && ch <= 'z') || (ch>= 'A' && ch <= 'Z' )) {
77
            return ALPHA;
78
          }
54 by Gustav Hartvigsson
More work torwards inperementing the parser.
79
          return UNKNOWN;
80
      }
81
    }
82
  }
83
84
  public class Parser {
85
    private static string[] EMPTY_VAR_KEYS = new string[0];
86
    private Context context = null;
87
    /** Contains the used variable names. */
88
    private string[] variable_keys;
89
    /** contains the used variables and their last value. */
90
    private HashTable<string, Variable?> variable_cache; 
91
    /** Will contain the Root of the token tree.
92
     * Asumes that there it has no parents.*/
93
    private Token root_token;
94
    private Token root;
95
    private bool parsed = false;
96
    private bool evaluated_once;
97
    private string internal_experisson;
98
    public string expression {
99
      get {return internal_experisson;}
100
      set {internal_experisson = value;
101
           expression_size = value.length;
102
           reset ();}
103
    }
104
    private size_t expression_size = 0;
105
    private char cur_char = 0;
55 by Gustav Hartvigsson
Merged: lp:~gustav.hartvigsson/vqdr/int64
106
    private int32 index = -1;
54 by Gustav Hartvigsson
More work torwards inperementing the parser.
107
108
    private FastNumber result_value;
109
    private FastNumber result_max_value;
110
    private FastNumber result_min_value;
111
112
    private string result_string;
113
    private Error? error;
114
115
116
    public Parser (string expression) {
117
      this.expression = expression;
118
      this.expression_size = expression.length;
119
      this.cur_char = expression[0];
120
      this.index = 0;
121
    }
122
123
    public Parser.empty () {
124
      expression = "";
125
    }
126
127
    public void reset () {
128
      root_token = null;
129
      root = null;
130
      parsed = false;
131
      evaluated_once = false;
132
      result_value = FastNumber (0);
133
      result_min_value = FastNumber (0);
134
      result_max_value = FastNumber (0);
135
      result_string = "";
136
      variable_cache.remove_all ();
137
      variable_keys = EMPTY_VAR_KEYS;
138
      error = null;
139
      index = 0;
140
    }
141
142
    /**
143
     * Parse the expersson string.
144
     */
145
    public void parse () throws ParseError {
146
      if (parsed) {
147
        throw new ParseError.ALLREADY_PARSED 
148
              ("Data has allparsed been parsed.");
149
      }
150
151
152
      while (this.index <= this.expression_size) {
153
        switch (CharType.from_char (this.cur_char)) {
154
          case CharType.NULL:
155
            this.parse_advance ();
156
            break;
157
          default:
158
            assert_not_reached ();
159
        }
160
      }
161
      this.parsed = true;
162
    } // end parse ()
163
164
    /*
165
     * advance the parser. 
166
     */
167
    private void parse_advance () {
168
      if (this.index < this.expression_size && this.cur_char != '\0') {
169
        this.index++;
170
        this.cur_char = this.expression[this.index];
171
      }
172
    }
173
174
    protected bool valid_bounds () {
175
      if (!evaluated_once) {
176
        return false;
177
      }
178
179
      if (context != null) {
180
        try {
181
          foreach (var key in variable_keys) {
182
            if (!context.has_name (key) ||
183
                !variable_cache.get (key).equals (context.get_variable (key))) {
184
              return false;
185
            }
186
          }
187
        } catch (Error e) {
188
          err_print_ln ("Something went wrong: (%: %)", e.domain, e.message);
189
          assert_not_reached ();
190
        }
191
      }
192
      return true;
193
    }
194
195
    protected void set_variable_cach_values () {
196
      if (context != null) {
197
        try {
198
          foreach (var key in variable_keys) {
57 by Gustav Hartvigsson
Made sure that no text rows exeed 80 columns.
199
            variable_cache.insert (key, Variable.copy
200
                                   (context.get_variable (key)));
54 by Gustav Hartvigsson
More work torwards inperementing the parser.
201
          }
202
        } catch (Error e) {
203
          err_print_ln ("Something went wrong: (%: %)", e.domain, e.message);
204
          assert_not_reached ();
205
        }
206
      }
207
    }
208
209
    protected void set_error (Error e) {
210
      expression = "";
211
212
      result_string = "Error";
213
214
      set_variable_cach_values ();
215
216
      error = e;
217
    }
218
219
    protected void set_result (Token root_token) {
220
      // evaluate = true;
221
      evaluated_once = true;
222
223
      result_value = root_token.result_value;
224
      result_max_value = root_token.result_max_value;
225
      result_min_value = root_token.result_min_value;
226
      result_string = root_token.result_string;
227
228
      set_variable_cach_values ();
229
230
      error = null;
231
    }
232
233
    protected void evaluate () throws Error {
234
      try {
235
        parse ();
236
237
        if (root_token == null) {
238
          root_token = new RootToken (root);
239
        }
240
241
        root_token.evaluate (context);
242
243
        set_result (root_token);
244
      } catch (Error e) {
245
        set_error (e);
246
        throw e;
247
      }
248
    }
249
250
    /**
251
     * Add a note (operator) to the stack after popping it's parameters.
252
     * @param operand_stack stack
253
     * @param operator operator.
254
     */
57 by Gustav Hartvigsson
Made sure that no text rows exeed 80 columns.
255
    protected void add_node (Stack<Token> operand_stack,
256
                             Token operator) throws Error {
54 by Gustav Hartvigsson
More work torwards inperementing the parser.
257
      if (operator is FunctionToken) {
258
        FunctionToken funk = (FunctionToken) operator;
55 by Gustav Hartvigsson
Merged: lp:~gustav.hartvigsson/vqdr/int64
259
        int32 param_num = funk.next_child_num;
54 by Gustav Hartvigsson
More work torwards inperementing the parser.
260
        for (var i = 0; i < param_num; i++) {
261
          Token param_child = operand_stack.pop ();
262
          funk.set_child (param_num - 1, param_child);
263
264
          operand_stack.push (funk);
265
        }
57 by Gustav Hartvigsson
Made sure that no text rows exeed 80 columns.
266
      } else if (operator is UnaryOperator &&
267
                 ((UnaryOperator) operator).is_unary) {
54 by Gustav Hartvigsson
More work torwards inperementing the parser.
268
        if (operand_stack.elements < 1) {
57 by Gustav Hartvigsson
Made sure that no text rows exeed 80 columns.
269
          throw new OperandError.MISSING_OPERAND
270
                    (@"Missing operand. Position: $(operator.position)");
54 by Gustav Hartvigsson
More work torwards inperementing the parser.
271
        }
272
      } else {
273
        if (operand_stack.elements < 2) {
57 by Gustav Hartvigsson
Made sure that no text rows exeed 80 columns.
274
          throw new OperandError.MISSING_OPERAND
275
                    (@"Missing operand. Position: $(operator.position)");
54 by Gustav Hartvigsson
More work torwards inperementing the parser.
276
        }
277
        operator.set_right_child (operand_stack.pop ());
278
        operator.set_left_child (operand_stack.pop ());
279
        operand_stack.push (operator);
280
      }
281
    }
282
  } // end of Parser class
44 by Gustav Hartvigsson
* Added skeleton for upcoming parser.
283
}