lib/calib_yacc.y: Move comedi_parse_calibration_file()
[comedilib.git] / lib / calib_yacc.y
1 %{
2 /*
3     lib/calib_yacc.y
4     code for parsing calibration file, generated by bison
5
6     Copyright (C) 2003 Frank Mori Hess <fmhess@users.sourceforge.net>
7
8     This library is free software; you can redistribute it and/or
9     modify it under the terms of the GNU Lesser General Public
10     License as published by the Free Software Foundation, version 2.1
11     of the License.
12
13     This library is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16     Lesser General Public License for more details.
17
18     You should have received a copy of the GNU Lesser General Public
19     License along with this library; if not, write to the Free Software
20     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
21     USA.
22 */
23
24 #define _GNU_SOURCE
25
26 #include <stdio.h>
27 #include "libinternal.h"
28 #include <math.h>
29 #include <string.h>
30 #include <stdlib.h>
31
32 #define YYERROR_VERBOSE
33 #define YYPARSE_PARAM parse_arg
34 #define YYLEX_PARAM priv(YYPARSE_PARAM)->yyscanner
35
36 #include "calib_yacc.h"
37 #include "calib_lex.h"
38
39 enum polynomial_direction
40 {
41         POLYNOMIAL_TO_PHYS,
42         POLYNOMIAL_FROM_PHYS
43 };
44
45 typedef struct
46 {
47         yyscan_t yyscanner;
48         comedi_calibration_t *parsed_file;
49         comedi_caldac_t caldac;
50         int cal_index;
51         unsigned num_coefficients;
52         comedi_polynomial_t polynomial;
53 } calib_yyparse_private_t;
54
55 YY_DECL;
56
57 static inline calib_yyparse_private_t* priv( calib_yyparse_private_t *parse_arg)
58 {
59         return parse_arg;
60 }
61
62 static void free_calibration_setting( comedi_calibration_setting_t *setting )
63 {
64         if( setting->channels );
65         {
66                 free( setting->channels );
67                 setting->channels = NULL;
68                 setting->num_channels = 0;
69         }
70         if( setting->ranges );
71         {
72                 free( setting->ranges );
73                 setting->ranges = NULL;
74                 setting->num_ranges = 0;
75         }
76         setting->num_arefs = 0;
77         if( setting->caldacs );
78         {
79                 free( setting->caldacs );
80                 setting->caldacs = NULL;
81                 setting->num_caldacs = 0;
82         }
83         if(setting->soft_calibration.to_phys)
84         {
85                 free(setting->soft_calibration.to_phys);
86                 setting->soft_calibration.to_phys = NULL;
87         }
88         if(setting->soft_calibration.from_phys)
89         {
90                 free(setting->soft_calibration.from_phys);
91                 setting->soft_calibration.from_phys = NULL;
92         }
93 }
94
95 static void free_settings( comedi_calibration_t *file_contents )
96 {
97         int i;
98
99         if( file_contents->settings == NULL ) return;
100
101         for( i = 0; i < file_contents->num_settings; i++ )
102         {
103                 free_calibration_setting( &file_contents->settings[ i ] );
104         }
105         free(file_contents->settings);
106         file_contents->settings = NULL;
107 }
108
109 static int add_calibration_setting( comedi_calibration_t *file_contents )
110 {
111         comedi_calibration_setting_t *temp;
112
113         temp = realloc( file_contents->settings,
114                 ( file_contents->num_settings + 1 ) * sizeof( comedi_calibration_setting_t ) );
115         if( temp == NULL )
116         {
117                 fprintf(stderr, "%s: realloc failed to allocate memory.\n", __FUNCTION__);
118                 return -1;
119         }
120         file_contents->settings = temp;
121         memset( &file_contents->settings[ file_contents->num_settings ],
122                 0, sizeof( comedi_calibration_setting_t ) );
123
124         file_contents->num_settings++;
125         return 0;
126 }
127
128 static comedi_calibration_setting_t* current_setting( calib_yyparse_private_t *priv )
129 {
130         int retval;
131
132         while( priv->cal_index >= priv->parsed_file->num_settings )
133         {
134                 retval = add_calibration_setting( priv->parsed_file );
135                 if( retval < 0 ) return NULL;
136         }
137         return &priv->parsed_file->settings[ priv->cal_index ];
138 }
139
140 static int add_channel( calib_yyparse_private_t *priv, int channel )
141 {
142         unsigned *temp;
143         comedi_calibration_setting_t *setting;
144
145         setting = current_setting( priv );
146         if( setting == NULL ) return -1;
147
148         temp = realloc( setting->channels, ( setting->num_channels + 1 ) * sizeof(unsigned) );
149         if( temp == NULL )
150         {
151                 fprintf(stderr, "%s: realloc failed to allocate memory.\n", __FUNCTION__);
152                 return -1;
153         }
154         setting->channels = temp;
155         setting->channels[ setting->num_channels++ ] = channel;
156         return 0;
157 }
158
159 static int add_range( calib_yyparse_private_t *priv, int range )
160 {
161         unsigned *temp;
162         comedi_calibration_setting_t *setting;
163
164         setting = current_setting( priv );
165         if( setting == NULL ) return -1;
166
167         temp = realloc( setting->ranges, ( setting->num_ranges + 1 ) * sizeof(unsigned) );
168         if( temp == NULL )
169         {
170                 fprintf(stderr, "%s: realloc failed to allocate memory.\n", __FUNCTION__);
171                 return -1;
172         }
173         setting->ranges = temp;
174         setting->ranges[ setting->num_ranges++ ] = range;
175         return 0;
176 }
177
178 static int add_aref( calib_yyparse_private_t *priv, int aref )
179 {
180         comedi_calibration_setting_t *setting;
181
182         setting = current_setting( priv );
183         if( setting == NULL ) return -1;
184
185         if( setting->num_arefs >= sizeof( setting->arefs ) /
186                 sizeof( setting->arefs[ 0 ] ) )
187                 return -1;
188         setting->arefs[ setting->num_arefs++ ] = aref;
189         return 0;
190 }
191
192 static int add_caldac( calib_yyparse_private_t *priv,
193         comedi_caldac_t caldac )
194 {
195         comedi_caldac_t *temp;
196         comedi_calibration_setting_t *setting;
197
198         setting = current_setting( priv );
199         if( setting == NULL ) return -1;
200
201         temp = realloc( setting->caldacs, ( setting->num_caldacs + 1 ) *
202                 sizeof( comedi_caldac_t ) );
203         if( temp == NULL )
204         {
205                 fprintf(stderr, "%s: realloc failed to allocate memory.\n", __FUNCTION__);
206                 return -1;
207         }
208         setting->caldacs = temp;
209         setting->caldacs[ setting->num_caldacs++ ] = caldac;
210         return 0;
211 }
212
213 static int add_polynomial(calib_yyparse_private_t *priv, enum polynomial_direction polynomial_direction)
214 {
215         comedi_calibration_setting_t *setting;
216
217         setting = current_setting( priv );
218         if( setting == NULL )
219         {
220                 fprintf(stderr, "%s: current_setting returned NULL\n", __FUNCTION__);
221                 return -1;
222         }
223         if(priv->num_coefficients < 1)
224         {
225                 fprintf(stderr, "%s: polynomial has no coefficients.\n", __FUNCTION__);
226                 return -1;
227         }
228         if(polynomial_direction == POLYNOMIAL_TO_PHYS)
229         {
230                 if(setting->soft_calibration.to_phys) return -1;
231                 setting->soft_calibration.to_phys = malloc(sizeof(comedi_polynomial_t));
232                 *setting->soft_calibration.to_phys = priv->polynomial;
233         }else
234         {
235                 if(setting->soft_calibration.from_phys) return -1;
236                 setting->soft_calibration.from_phys = malloc(sizeof(comedi_polynomial_t));
237                 *setting->soft_calibration.from_phys = priv->polynomial;
238         }
239         return 0;
240 }
241
242 static int add_polynomial_coefficient(calib_yyparse_private_t *priv, double coefficient)
243 {
244         if(priv->num_coefficients >= COMEDI_MAX_NUM_POLYNOMIAL_COEFFICIENTS)
245         {
246                 fprintf(stderr, "too many coefficients for polynomial,\n");
247                 fprintf(stderr, "num_coefficients=%i, max is %i .\n", priv->num_coefficients, COMEDI_MAX_NUM_POLYNOMIAL_COEFFICIENTS);
248                 return -1;
249         }
250         priv->polynomial.order = priv->num_coefficients;
251         priv->polynomial.coefficients[priv->num_coefficients++] = coefficient;
252         return 0;
253 }
254
255 static comedi_calibration_t* alloc_calib_parse( void )
256 {
257         comedi_calibration_t *file_contents;
258
259         file_contents = malloc( sizeof( *file_contents ) );
260         if( file_contents == NULL ) return file_contents;
261         memset( file_contents, 0, sizeof( *file_contents ) );
262         return file_contents;
263 }
264
265 EXPORT_ALIAS_DEFAULT(_comedi_cleanup_calibration,comedi_cleanup_calibration,0.7.20);
266 extern void _comedi_cleanup_calibration( comedi_calibration_t *file_contents )
267 {
268         if(file_contents == NULL) return;
269         if( file_contents->driver_name )
270         {
271                 free( file_contents->driver_name );
272                 file_contents->driver_name = NULL;
273         }
274         if( file_contents->board_name )
275         {
276                 free( file_contents->board_name );
277                 file_contents->board_name = NULL;
278         }
279         free_settings( file_contents );
280         free( file_contents );
281 }
282
283 static comedi_polynomial_t* alloc_inverse_linear_polynomial(const comedi_polynomial_t *polynomial)
284 {
285         comedi_polynomial_t *inverse;
286         if(polynomial->order != 1) return NULL;
287         inverse = malloc(sizeof(comedi_polynomial_t));
288         memset(inverse, 0, sizeof(comedi_polynomial_t));
289         inverse->order = 1;
290         inverse->expansion_origin = polynomial->coefficients[0];
291         inverse->coefficients[0] = polynomial->expansion_origin;
292         inverse->coefficients[1] = 1. / polynomial->coefficients[1];
293         if(isfinite(inverse->coefficients[1]) == 0)
294         {
295                 free(inverse);
296                 return NULL;
297         }
298         return inverse;
299 }
300
301 static void fill_inverse_linear_polynomials(comedi_calibration_t *calibration)
302 {
303         unsigned i;
304         for(i = 0; i < calibration->num_settings; ++i)
305         {
306                 if(calibration->settings[i].soft_calibration.to_phys)
307                 {
308                         if(calibration->settings[i].soft_calibration.from_phys == NULL)
309                         {
310                                 calibration->settings[i].soft_calibration.from_phys =
311                                         alloc_inverse_linear_polynomial(calibration->settings[i].soft_calibration.to_phys);
312                         }
313                 }else if(calibration->settings[i].soft_calibration.from_phys)
314                 {
315                         calibration->settings[i].soft_calibration.to_phys =
316                                 alloc_inverse_linear_polynomial(calibration->settings[i].soft_calibration.from_phys);
317                 }
318         }
319 }
320
321 static void yyerror(const char *s)
322 {
323         fprintf(stderr, "%s\n", s);
324 }
325
326 %}
327
328 %pure_parser
329
330 %union
331 {
332         int  ival;
333         double dval;
334         char *sval;
335 }
336
337 %token T_DRIVER_NAME T_BOARD_NAME T_CALIBRATIONS T_SUBDEVICE T_CHANNELS
338 %token T_RANGES T_AREFS T_CALDACS T_CHANNEL T_VALUE T_NUMBER T_STRING
339 %token T_COEFFICIENTS T_EXPANSION_ORIGIN T_SOFTCAL_TO_PHYS T_SOFTCAL_FROM_PHYS
340 %token T_ASSIGN T_FLOAT
341
342 %type <ival> T_NUMBER
343 %type <sval> T_STRING
344 %type <dval> T_FLOAT
345
346 %%
347
348         input: '{' hash '}'
349                 | error
350                         {
351                                 fprintf(stderr, "input error on line %i\n", calib_yyget_lineno(priv(parse_arg)->yyscanner));
352 //                              fprintf(stderr, "input error on line %i\n", @1.first_line );
353                                 YYABORT;
354                         }
355                 ;
356
357         hash: /* empty */
358                 | hash_element
359                 | hash_element ',' hash
360                 ;
361
362         hash_element: T_DRIVER_NAME T_ASSIGN T_STRING
363                 {
364                         if( priv(parse_arg)->parsed_file->driver_name != NULL ) YYABORT;
365                         priv(parse_arg)->parsed_file->driver_name = strdup( $3 );
366                 }
367                 | T_BOARD_NAME T_ASSIGN T_STRING
368                 {
369                         if( priv(parse_arg)->parsed_file->board_name != NULL ) YYABORT;
370                         priv(parse_arg)->parsed_file->board_name = strdup( $3 );
371                 }
372                 | T_CALIBRATIONS T_ASSIGN '[' calibrations_array ']'
373                 ;
374
375         calibrations_array: /* empty */
376                 | '{' calibration_setting '}'
377                 | '{' calibration_setting '}' ',' calibrations_array
378                 ;
379
380         calibration_setting: /* empty */ { priv(parse_arg)->cal_index++; }
381                 | calibration_setting_element { priv(parse_arg)->cal_index++; }
382                 | calibration_setting_element ',' calibration_setting
383                 ;
384
385         calibration_setting_element: T_SUBDEVICE T_ASSIGN T_NUMBER
386                 {
387                         comedi_calibration_setting_t *setting;
388                         setting = current_setting( parse_arg );
389                         if( setting == NULL ) YYABORT;
390                         setting->subdevice = $3;
391                 }
392                 | T_CHANNELS T_ASSIGN '[' channels_array ']'
393                 | T_RANGES T_ASSIGN '[' ranges_array ']'
394                 | T_AREFS T_ASSIGN '[' arefs_array ']'
395                 | T_CALDACS T_ASSIGN '[' caldacs_array ']'
396                 | T_SOFTCAL_TO_PHYS T_ASSIGN '{' polynomial '}'
397                 {
398                         if(add_polynomial(parse_arg, POLYNOMIAL_TO_PHYS) < 0) YYERROR;
399                         priv(parse_arg)->num_coefficients = 0;
400                 }
401                 | T_SOFTCAL_FROM_PHYS T_ASSIGN '{' polynomial '}'
402                 {
403                         if(add_polynomial(parse_arg, POLYNOMIAL_FROM_PHYS) < 0) YYERROR;
404                         priv(parse_arg)->num_coefficients = 0;
405                 }
406                 ;
407
408         channels_array: /* empty */
409                 | channel
410                 | channel ',' channels_array
411                 ;
412
413         channel: T_NUMBER { if(add_channel( parse_arg, $1 ) < 0) YYERROR; }
414                 ;
415
416         ranges_array: /* empty */
417                 | range
418                 | range ',' ranges_array
419                 ;
420
421         range: T_NUMBER { if(add_range( parse_arg, $1 ) < 0) YYERROR; }
422                 ;
423
424         arefs_array: /* empty */
425                 | aref
426                 | aref ',' arefs_array
427                 ;
428
429         aref: T_NUMBER { if(add_aref( parse_arg, $1 ) < 0) YYERROR; }
430                 ;
431
432         caldacs_array: /* empty */
433                 | '{' caldac '}'
434                 | '{' caldac '}' ',' caldacs_array
435                 ;
436
437         caldac: /* empty */ { if(add_caldac( parse_arg, priv(parse_arg)->caldac ) < 0) YYERROR; }
438                 | caldac_element { if(add_caldac( parse_arg, priv(parse_arg)->caldac ) < 0) YYERROR; }
439                 | caldac_element ',' caldac
440                 ;
441
442         caldac_element: T_SUBDEVICE T_ASSIGN T_NUMBER { priv(parse_arg)->caldac.subdevice = $3; }
443                 | T_CHANNEL T_ASSIGN T_NUMBER { priv(parse_arg)->caldac.channel = $3; }
444                 | T_VALUE T_ASSIGN T_NUMBER { priv(parse_arg)->caldac.value = $3; }
445                 ;
446
447         polynomial: /* empty */
448                 | polynomial_element
449                 | polynomial_element ',' polynomial
450                 ;
451
452         polynomial_element: T_COEFFICIENTS T_ASSIGN '[' coefficient_array ']'
453                 | T_EXPANSION_ORIGIN T_ASSIGN expansion_origin
454                 ;
455
456         coefficient_array: /* empty */
457                 | coefficient
458                 | coefficient ',' coefficient_array
459                 ;
460
461         coefficient: T_FLOAT
462                 {
463                         if(add_polynomial_coefficient(parse_arg, $1) < 0) YYERROR;
464                 }
465                 | T_NUMBER
466                 {
467                         if(add_polynomial_coefficient(parse_arg, $1) < 0) YYERROR;
468                 }
469                 ;
470
471         expansion_origin: T_FLOAT
472                 {
473                         priv(parse_arg)->polynomial.expansion_origin = $1;
474                 }
475                 | T_NUMBER
476                 {
477                         priv(parse_arg)->polynomial.expansion_origin = $1;
478                 }
479                 ;
480
481 %%
482
483
484 EXPORT_ALIAS_DEFAULT(_comedi_parse_calibration_file,comedi_parse_calibration_file,0.7.20);
485 extern comedi_calibration_t* _comedi_parse_calibration_file( const char *cal_file_path )
486 {
487         calib_yyparse_private_t priv;
488         FILE *file;
489
490         if( cal_file_path == NULL ) return NULL;
491         memset(&priv, 0, sizeof(calib_yyparse_private_t));
492         priv.parsed_file = alloc_calib_parse();
493         if( priv.parsed_file == NULL ) return NULL;
494
495         file = fopen( cal_file_path, "r" );
496         if( file == NULL )
497         {
498                 COMEDILIB_DEBUG( 3, "failed to open file\n" );
499                 return NULL;
500         }
501         calib_yylex_init(&priv.yyscanner);
502         calib_yyrestart(file, priv.yyscanner);
503         if( calib_yyparse( &priv ) )
504         {
505                 comedi_cleanup_calibration( priv.parsed_file );
506                 priv.parsed_file = NULL;
507         }
508         calib_yylex_destroy(priv.yyscanner);
509         fclose( file );
510         fill_inverse_linear_polynomials(priv.parsed_file);
511         return priv.parsed_file;
512 }
513
514