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