/vqdr/trunk

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/vqdr/trunk

« back to all changes in this revision

Viewing changes to src/libvqdr/parser.vala

  • Committer: Gustav Hartvigsson
  • Date: 2022-06-01 12:14:52 UTC
  • Revision ID: gustav.hartvigsson@gmail.com-20220601121452-ntu94w67q3dhhfeq
More work torwards inperementing the parser.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
using Utils;
 
2
 
 
3
namespace VQDR.Expression {
 
4
 
 
5
  public enum CharType {
 
6
    INVALID = 0,
 
7
    NULL = 1,
 
8
    DIGIT,
 
9
    UOP,
 
10
    OP,
 
11
    ALPHA,
 
12
    DOT,
 
13
    POP,
 
14
    PCL,
 
15
    COM,
 
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
 
 
48
  /**
 
49
   * Determine character type.
 
50
   * @param ch Character to be checked.
 
51
   * @return The character type.
 
52
   */
 
53
    public static CharType from_char (char ch) {
 
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
          }
 
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;
 
106
    private int index = -1;
 
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) {
 
199
            variable_cache.insert (key, Variable.copy (context.get_variable (key)));
 
200
          }
 
201
        } catch (Error e) {
 
202
          err_print_ln ("Something went wrong: (%: %)", e.domain, e.message);
 
203
          assert_not_reached ();
 
204
        }
 
205
      }
 
206
    }
 
207
 
 
208
    protected void set_error (Error e) {
 
209
      expression = "";
 
210
 
 
211
      result_string = "Error";
 
212
 
 
213
      set_variable_cach_values ();
 
214
 
 
215
      error = e;
 
216
    }
 
217
 
 
218
    protected void set_result (Token root_token) {
 
219
      // evaluate = true;
 
220
      evaluated_once = true;
 
221
 
 
222
      result_value = root_token.result_value;
 
223
      result_max_value = root_token.result_max_value;
 
224
      result_min_value = root_token.result_min_value;
 
225
      result_string = root_token.result_string;
 
226
 
 
227
      set_variable_cach_values ();
 
228
 
 
229
      error = null;
 
230
    }
 
231
 
 
232
    protected void evaluate () throws Error {
 
233
      try {
 
234
        parse ();
 
235
 
 
236
        if (root_token == null) {
 
237
          root_token = new RootToken (root);
 
238
        }
 
239
 
 
240
        root_token.evaluate (context);
 
241
 
 
242
        set_result (root_token);
 
243
      } catch (Error e) {
 
244
        set_error (e);
 
245
        throw e;
 
246
      }
 
247
    }
 
248
 
 
249
    /**
 
250
     * Add a note (operator) to the stack after popping it's parameters.
 
251
     * @param operand_stack stack
 
252
     * @param operator operator.
 
253
     */
 
254
    protected void add_node (Stack<Token> operand_stack, Token operator) throws Error {
 
255
      if (operator is FunctionToken) {
 
256
        FunctionToken funk = (FunctionToken) operator;
 
257
        int param_num = funk.next_child_num;
 
258
        for (var i = 0; i < param_num; i++) {
 
259
          Token param_child = operand_stack.pop ();
 
260
          funk.set_child (param_num - 1, param_child);
 
261
 
 
262
          operand_stack.push (funk);
 
263
        }
 
264
      } else if (operator is UnaryOperator && ((UnaryOperator) operator).is_unary) {
 
265
        if (operand_stack.elements < 1) {
 
266
          throw new OperandError.MISSING_OPERAND (@"Missing operand. Position: $(operator.position)");
 
267
        }
 
268
      } else {
 
269
        if (operand_stack.elements < 2) {
 
270
          throw new OperandError.MISSING_OPERAND (@"Missing operand. Position: $(operator.position)");
 
271
        }
 
272
        operator.set_right_child (operand_stack.pop ());
 
273
        operator.set_left_child (operand_stack.pop ());
 
274
        operand_stack.push (operator);
 
275
      }
 
276
    }
 
277
  } // end of Parser class
 
278
}