Ian Jauslin
summaryrefslogtreecommitdiff
blob: 08cae88018a491aeb0a1fa1a7d64b5118b500311 (plain)
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
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
/*
Copyright 2015-2022 Ian Jauslin

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#include "symbolic_parser.h"
#include <stdlib.h>
#include <stdio.h>
#include "tree.h"
#include "definitions.cpp"
#include "array.h"
#include "istring.h"
#include "fields.h"
#include "polynomial.h"

#define SP_NULL_MODE 0
#define SP_FUNCTION_MODE 1

// parse a symbolic expression from a char_array
int parse_symbolic_expression(Char_Array str, Fields_Table fields, Variables variables, Polynomial* polynomial){
  Tree symbol_tree;
  char_array_to_symbol_tree(str, &symbol_tree);
  resolve_symbol_tree(symbol_tree, fields, variables, polynomial);
  free_Tree(symbol_tree);
  return(0);
}
// from char*
int parse_symbolic_expression_str(char* str, Fields_Table fields, Variables variables, Polynomial* polynomial){
  Char_Array char_array;
  str_to_char_array(str,&char_array);
  parse_symbolic_expression(char_array, fields, variables, polynomial);
  free_Char_Array(char_array);
  return(0);
}

// compute the symbol tree from a string
int char_array_to_symbol_tree(Char_Array str, Tree* symbol_tree){
  // buffer
  char* buffer=calloc(str.length+1,sizeof(char));
  char* buffer_ptr=buffer;
  Tree child;
  int match;
  Char_Array nodestr;
  Char_Array label;
  Char_Array str_clean;
  int mode;
  int comment=0;
  int j;
  int gotanode=0;

  // allocate memory
  init_Tree(symbol_tree,SYMBOL_TREE_SIZE, SYMBOL_TREE_LABEL_SIZE);

  // remove comments, ' ' and '\n'
  init_Char_Array(&str_clean,str.length);
  for(j=0;j<str.length;j++){
    if(comment==1){
      if(str.str[j]=='\n'){
	comment=0;
      }
    }
    else{
      switch(str.str[j]){
      case ' ':break;
      case '\n':break;
      // comments
      case '#':
	comment=1;
	break;
      default:
	char_array_append(str.str[j],&str_clean);
	break;
      }
    }
  }

  // if the string contains no '<', then trivial tree
  for(j=0;j<str_clean.length;j++){
    if(str_clean.str[j]=='<'){
      break;
    }
  }
  // no '<': trivial tree
  if(j==str_clean.length){
    tree_set_label(str_clean, symbol_tree);
    free(buffer);
    free_Char_Array(str_clean);
    return(0);
  }

  *buffer_ptr='\0';
  // loop over the input string
  // start in null mode
  mode=SP_NULL_MODE;
  for(j=0;j<str_clean.length;j++){
    switch(str_clean.str[j]){
    // new node
    case '<':
      // find matching bracket
      match=matching_bracket(str_clean,j);
      // check whether it exists
      if(match<0){
	fprintf(stderr,"syntax error: unmatched brackets in %s\n",char_array_to_str_noinit(&str));
	exit(-1);
      }
      // extract substring until bracket
      char_array_substring(str_clean,j+1,match-1,&nodestr);

      // check whether node is trivial
      if(j==0 && match==str_clean.length-1){
	free_Tree(*symbol_tree);
	char_array_to_symbol_tree(nodestr, symbol_tree);
	free_Char_Array(nodestr);
	j=match;
	break;
      }

      // parse subexpression
      char_array_to_symbol_tree(nodestr, &child);
      free_Char_Array(nodestr);
      // add child to tree
      tree_append_child_noinit(child, symbol_tree);
      // boolean indicating a node has been found
      gotanode=1;
      // set next position after the node
      j=match;

      // if function mode, then check that the match is at the end of the node
      if(mode==SP_FUNCTION_MODE){
	if(match<str_clean.length-1){
	  fprintf(stderr,"syntax error: functions must occupy an entire node (e.g. <%%exp<...>>), got %s\n",char_array_to_str_noinit(&str));
	  exit(-1);
	}
	else{
	  // set label
	  str_to_char_array(buffer,&label);
	  tree_set_label(label,symbol_tree);
	  free_Char_Array(label);
	}
      }
      break;

    // function
    case '%':
      if(j>0){
	fprintf(stderr,"syntax error: functions must occupy an entire node (e.g. <%%exp<...>>), got %s\n",char_array_to_str_noinit(&str));
	exit(-1);
      }
      mode=SP_FUNCTION_MODE;
      break;

    // product
    case '*':
      if(gotanode==0){
	fprintf(stderr,"syntax error: '*' is not preceded by a node in %s\n",char_array_to_str_noinit(&str));
	exit(-1);
      }
      if(j>=str_clean.length-1){
	fprintf(stderr,"syntax error: '*' cannot be at the end of an expression, got %s\n",char_array_to_str_noinit(&str));
	exit(-1);
      }
      // set label
      init_Char_Array(&label,1);
      char_array_append('*',&label);
      tree_set_label(label,symbol_tree);
      free_Char_Array(label);
      // next child
      char_array_substring(str_clean,j+1,str_clean.length-1,&nodestr);
      // parse subexpression
      char_array_to_symbol_tree(nodestr, &child);
      free_Char_Array(nodestr);
      // append next child
      tree_append_child_noinit(child, symbol_tree);

      // make it stop
      j=str_clean.length-1;
      break;

    // sum
    case '+':
      if(gotanode==0){
	fprintf(stderr,"syntax error: '+' is not preceded by a node in %s\n",char_array_to_str_noinit(&str));
	exit(-1);
      }
      if(j>=str_clean.length-1){
	fprintf(stderr,"syntax error: '+' cannot be at the end of an expression, got %s\n",char_array_to_str_noinit(&str));
	exit(-1);
      }
      // set label
      init_Char_Array(&label,1);
      char_array_append('+',&label);
      tree_set_label(label,symbol_tree);
      free_Char_Array(label);
      // next child
      char_array_substring(str_clean,j+1,str_clean.length-1,&nodestr);
      // parse subexpression
      char_array_to_symbol_tree(nodestr, &child);
      free_Char_Array(nodestr);
      // append next child
      tree_append_child_noinit(child, symbol_tree);

      // make it stop
      j=str_clean.length-1;
      break;

    default:
      if(mode!=SP_NULL_MODE){
	// write to buffer
	buffer_ptr=str_addchar(buffer_ptr,str_clean.str[j]);
      }
      break;
    }
  }

  free_Char_Array(str_clean);
  free(buffer);
  return(0);
}
// from char*
int str_to_symbol_tree(char* str, Tree* symbol_tree){
  Char_Array char_array;
  str_to_char_array(str,&char_array);
  char_array_to_symbol_tree(char_array,symbol_tree);
  free_Char_Array(char_array);
  return(0);
}


// find matching '<' and '>'
int matching_bracket(Char_Array str, int start){
  int bracket_count=0;
  int i;
  for(i=start;i<str.length;i++){
    if(str.str[i]=='<'){
      bracket_count++;
    }
    else if(str.str[i]=='>'){
      bracket_count--;
      if(bracket_count==0){
	return(i);
      }
    }
  }
  // if the function has not returned, then no matching bracket
  return(-1);
}


// resolve a symbol tree to its corresponding polynomial
int resolve_symbol_tree(Tree symbol_tree, Fields_Table fields, Variables variables, Polynomial* output){
  Polynomial poly;
  Tree variable_tree;

  // trivial tree
  if(symbol_tree.length==0){
    // variable
    if(symbol_tree.root_label.length>0 && symbol_tree.root_label.str[0]=='$'){
      variables_find_var(symbol_tree.root_label, variables, &variable_tree);
      resolve_symbol_tree(variable_tree, fields, variables, output);
      free_Tree(variable_tree);
    }
    //polynomial
    else{
      Char_Array_to_Polynomial(symbol_tree.root_label, output);
    }
  }

  // exp
  else if (char_array_cmp_str(symbol_tree.root_label,"exp")==1){
    if(symbol_tree.length!=1){
      fprintf(stderr,"syntax error: exp must have 1 argument\n");
      exit(-1);
    }
    resolve_symbol_tree(symbol_tree.children[0], fields, variables, &poly);
    polynomial_exponential(poly, output, fields);
    free_Polynomial(poly);
  }

  // log
  else if (char_array_cmp_str(symbol_tree.root_label,"log_1")==1){
    if(symbol_tree.length!=1){
      fprintf(stderr,"syntax error: log_1 must have 1 argument\n");
      exit(-1);
    }
    resolve_symbol_tree(symbol_tree.children[0], fields, variables, &poly);
    polynomial_logarithm(poly, output, fields);
    free_Polynomial(poly);
  }

  // product
  else if (char_array_cmp_str(symbol_tree.root_label,"*")==1){
    if(symbol_tree.length!=2){
      fprintf(stderr,"syntax error: '*' must have 2 arguments\n");
      exit(-1);
    }
    resolve_symbol_tree(symbol_tree.children[0], fields, variables, output);
    resolve_symbol_tree(symbol_tree.children[1], fields, variables, &poly);
    polynomial_prod_chain(poly, output, fields);
    free_Polynomial(poly);
  }

  // sum
  else if (char_array_cmp_str(symbol_tree.root_label,"+")==1){
    if(symbol_tree.length!=2){
      fprintf(stderr,"syntax error: '+' must have 2 arguments\n");
      exit(-1);
    }
    resolve_symbol_tree(symbol_tree.children[0], fields, variables, output);
    resolve_symbol_tree(symbol_tree.children[1], fields, variables, &poly);
    polynomial_add_chain_noinit(poly, output, fields);
  }

  else{
    fprintf(stderr,"syntax error: unrecognized operation '%s'\n",char_array_to_str_noinit(&(symbol_tree.root_label)));
    exit(-1);
  }

  return(0);
}