/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: 2021-11-16 12:44:52 UTC
  • Revision ID: gustav.hartvigsson@gmail.com-20211116124452-g9245bvzwcyy9wk9
Added licencing information.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
using Vee;
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 int32 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
200
 
                                   (context.get_variable (key)));
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
 
     */
255
 
    protected void add_node (Stack<Token> operand_stack,
256
 
                             Token operator) throws Error {
257
 
      if (operator is FunctionToken) {
258
 
        FunctionToken funk = (FunctionToken) operator;
259
 
        int32 param_num = funk.next_child_num;
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
 
        }
266
 
      } else if (operator is UnaryOperator &&
267
 
                 ((UnaryOperator) operator).is_unary) {
268
 
        if (operand_stack.elements < 1) {
269
 
          throw new OperandError.MISSING_OPERAND
270
 
                    (@"Missing operand. Position: $(operator.position)");
271
 
        }
272
 
      } else {
273
 
        if (operand_stack.elements < 2) {
274
 
          throw new OperandError.MISSING_OPERAND
275
 
                    (@"Missing operand. Position: $(operator.position)");
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
283
 
}