Be a little more correct by setting CMDF_WRITE flag, in preparation
[comedilib.git] / lib / calib_yacc.y
index 1e1bf5d0bd74d209aa6c2b8eb0577165fc755981..ede8e99073823e3e7c55db59ca9fcf5d6546e186 100644 (file)
@@ -3,7 +3,7 @@
     lib/calib_yacc.y
     code for parsing calibration file, generated by bison
 
-    Copyright (C) 2003 Frank Mori Hess <fmhess@users.sourceforge.net
+    Copyright (C) 2003 Frank Mori Hess <fmhess@users.sourceforge.net>
 
     This library is free software; you can redistribute it and/or
     modify it under the terms of the GNU Lesser General Public
     USA.
 */
 
+#define _GNU_SOURCE
+
 #include <stdio.h>
 #include "libinternal.h"
+#include <math.h>
 #include <string.h>
 #include <stdlib.h>
+#include "calib_yacc.h"
+#include "calib_lex.h"
 
 #define YYERROR_VERBOSE
+#define YYPARSE_PARAM parse_arg
+#define YYLEX_PARAM priv(YYPARSE_PARAM)->yyscanner
+
+enum polynomial_direction
+{
+       POLYNOMIAL_TO_PHYS,
+       POLYNOMIAL_FROM_PHYS
+};
+
+typedef struct
+{
+       yyscan_t yyscanner;
+       comedi_calibration_t *parsed_file;
+       comedi_caldac_t caldac;
+       int cal_index;
+       unsigned num_coefficients;
+       comedi_polynomial_t polynomial;
+} calib_yyparse_private_t;
+
+YY_DECL;
 
-struct calibration_file_contents *parsed_file;
-static struct caldac_setting caldac;
-static int cal_index;
-FILE *calib_yyin;
+static inline calib_yyparse_private_t* priv( calib_yyparse_private_t *parse_arg)
+{
+       return parse_arg;
+}
 
-static void free_calibration_setting( struct calibration_setting *setting )
+static void free_calibration_setting( comedi_calibration_setting_t *setting )
 {
        if( setting->channels );
        {
@@ -54,83 +79,105 @@ static void free_calibration_setting( struct calibration_setting *setting )
                setting->caldacs = NULL;
                setting->num_caldacs = 0;
        }
+       if(setting->soft_calibration.to_phys)
+       {
+               free(setting->soft_calibration.to_phys);
+               setting->soft_calibration.to_phys = NULL;
+       }
+       if(setting->soft_calibration.from_phys)
+       {
+               free(setting->soft_calibration.from_phys);
+               setting->soft_calibration.from_phys = NULL;
+       }
 }
 
-static void free_calibrations( struct calibration_file_contents *file_contents )
+static void free_settings( comedi_calibration_t *file_contents )
 {
        int i;
 
-       if( file_contents->calibrations == NULL ) return;
+       if( file_contents->settings == NULL ) return;
 
-       for( i = 0; i < file_contents->num_calibrations; i++ )
+       for( i = 0; i < file_contents->num_settings; i++ )
        {
-               free_calibration_setting( &file_contents->calibrations[ i ] );
+               free_calibration_setting( &file_contents->settings[ i ] );
        }
-       file_contents->calibrations = NULL;
+       file_contents->settings = NULL;
 }
 
-static int add_calibration_setting( struct calibration_file_contents *file_contents )
+static int add_calibration_setting( comedi_calibration_t *file_contents )
 {
-       struct calibration_setting *temp;
+       comedi_calibration_setting_t *temp;
 
-       temp = realloc( file_contents->calibrations,
-               ( file_contents->num_calibrations + 1 ) * sizeof( struct calibration_setting ) );
-       if( temp == NULL ) return -1;
-       file_contents->calibrations = temp;
-       memset( &file_contents->calibrations[ file_contents->num_calibrations ],
-               0, sizeof( struct calibration_setting ) );
+       temp = realloc( file_contents->settings,
+               ( file_contents->num_settings + 1 ) * sizeof( comedi_calibration_setting_t ) );
+       if( temp == NULL )
+       {
+               fprintf(stderr, "%s: realloc failed to allocate memory.\n", __FUNCTION__);
+               return -1;
+       }
+       file_contents->settings = temp;
+       memset( &file_contents->settings[ file_contents->num_settings ],
+               0, sizeof( comedi_calibration_setting_t ) );
 
-       file_contents->num_calibrations++;
+       file_contents->num_settings++;
        return 0;
 }
 
-static struct calibration_setting* current_setting( struct calibration_file_contents *file_contents )
+static comedi_calibration_setting_t* current_setting( calib_yyparse_private_t *priv )
 {
        int retval;
 
-       while( cal_index >= file_contents->num_calibrations )
+       while( priv->cal_index >= priv->parsed_file->num_settings )
        {
-               retval = add_calibration_setting( file_contents );
+               retval = add_calibration_setting( priv->parsed_file );
                if( retval < 0 ) return NULL;
        }
-       return &file_contents->calibrations[ cal_index ];
+       return &priv->parsed_file->settings[ priv->cal_index ];
 }
 
-static int add_channel( struct calibration_file_contents *file_contents, int channel )
+static int add_channel( calib_yyparse_private_t *priv, int channel )
 {
-       int *temp;
-       struct calibration_setting *setting;
+       unsigned *temp;
+       comedi_calibration_setting_t *setting;
 
-       setting = current_setting( file_contents );
+       setting = current_setting( priv );
        if( setting == NULL ) return -1;
 
-       temp = realloc( setting->channels, ( setting->num_channels + 1 ) * sizeof( int ) );
-       if( temp == NULL ) return -1;
+       temp = realloc( setting->channels, ( setting->num_channels + 1 ) * sizeof(unsigned) );
+       if( temp == NULL )
+       {
+               fprintf(stderr, "%s: realloc failed to allocate memory.\n", __FUNCTION__);
+               return -1;
+       }
        setting->channels = temp;
        setting->channels[ setting->num_channels++ ] = channel;
        return 0;
 }
 
-static int add_range( struct calibration_file_contents *file_contents, int range )
+static int add_range( calib_yyparse_private_t *priv, int range )
 {
-       int *temp;
-       struct calibration_setting *setting;
+       unsigned *temp;
+       comedi_calibration_setting_t *setting;
 
-       setting = current_setting( file_contents );
+       setting = current_setting( priv );
        if( setting == NULL ) return -1;
 
-       temp = realloc( setting->ranges, ( setting->num_ranges + 1 ) * sizeof( int ) );
-       if( temp == NULL ) return -1;
+       temp = realloc( setting->ranges, ( setting->num_ranges + 1 ) * sizeof(unsigned) );
+       if( temp == NULL )
+       {
+               fprintf(stderr, "%s: realloc failed to allocate memory.\n", __FUNCTION__);
+               return -1;
+       }
        setting->ranges = temp;
        setting->ranges[ setting->num_ranges++ ] = range;
        return 0;
 }
 
-static int add_aref( struct calibration_file_contents *file_contents, int aref )
+static int add_aref( calib_yyparse_private_t *priv, int aref )
 {
-       struct calibration_setting *setting;
+       comedi_calibration_setting_t *setting;
 
-       setting = current_setting( file_contents );
+       setting = current_setting( priv );
        if( setting == NULL ) return -1;
 
        if( setting->num_arefs >= sizeof( setting->arefs ) /
@@ -140,33 +187,81 @@ static int add_aref( struct calibration_file_contents *file_contents, int aref )
        return 0;
 }
 
-static int add_caldac( struct calibration_file_contents *file_contents,
-       struct caldac_setting caldac )
+static int add_caldac( calib_yyparse_private_t *priv,
+       comedi_caldac_t caldac )
 {
-       struct caldac_setting *temp;
-       struct calibration_setting *setting;
+       comedi_caldac_t *temp;
+       comedi_calibration_setting_t *setting;
 
-       setting = current_setting( file_contents );
+       setting = current_setting( priv );
        if( setting == NULL ) return -1;
 
        temp = realloc( setting->caldacs, ( setting->num_caldacs + 1 ) *
-               sizeof( struct caldac_setting ) );
-       if( temp == NULL ) return -1;
+               sizeof( comedi_caldac_t ) );
+       if( temp == NULL )
+       {
+               fprintf(stderr, "%s: realloc failed to allocate memory.\n", __FUNCTION__);
+               return -1;
+       }
        setting->caldacs = temp;
        setting->caldacs[ setting->num_caldacs++ ] = caldac;
        return 0;
 }
 
-static struct calibration_file_contents* alloc_calib_parse( void )
+static int add_polynomial(calib_yyparse_private_t *priv, enum polynomial_direction polynomial_direction)
+{
+       comedi_calibration_setting_t *setting;
+
+       setting = current_setting( priv );
+       if( setting == NULL )
+       {
+               fprintf(stderr, "%s: current_setting returned NULL\n", __FUNCTION__);
+               return -1;
+       }
+       if(priv->num_coefficients < 1)
+       {
+               fprintf(stderr, "%s: polynomial has no coefficients.\n", __FUNCTION__);
+               return -1;
+       }
+       if(polynomial_direction == POLYNOMIAL_TO_PHYS)
+       {
+               if(setting->soft_calibration.to_phys) return -1;
+               setting->soft_calibration.to_phys = malloc(sizeof(comedi_polynomial_t));
+               *setting->soft_calibration.to_phys = priv->polynomial;
+       }else
+       {
+               if(setting->soft_calibration.from_phys) return -1;
+               setting->soft_calibration.from_phys = malloc(sizeof(comedi_polynomial_t));
+               *setting->soft_calibration.from_phys = priv->polynomial;
+       }
+       return 0;
+}
+
+static int add_polynomial_coefficient(calib_yyparse_private_t *priv, double coefficient)
+{
+       if(priv->num_coefficients >= COMEDI_MAX_NUM_POLYNOMIAL_COEFFICIENTS)
+       {
+               fprintf(stderr, "too many coefficients for polynomial,\n");
+               fprintf(stderr, "num_coefficients=%i, max is %i .\n", priv->num_coefficients, COMEDI_MAX_NUM_POLYNOMIAL_COEFFICIENTS);
+               return -1;
+       }
+       priv->polynomial.order = priv->num_coefficients;
+       priv->polynomial.coefficients[priv->num_coefficients++] = coefficient;
+       return 0;
+}
+
+static comedi_calibration_t* alloc_calib_parse( void )
 {
-       struct calibration_file_contents *file_contents;
+       comedi_calibration_t *file_contents;
+
        file_contents = malloc( sizeof( *file_contents ) );
        if( file_contents == NULL ) return file_contents;
        memset( file_contents, 0, sizeof( *file_contents ) );
        return file_contents;
 }
 
-extern void cleanup_calibration_parse( struct calibration_file_contents *file_contents )
+EXPORT_ALIAS_DEFAULT(_comedi_cleanup_calibration,comedi_cleanup_calibration,0.7.20);
+extern void _comedi_cleanup_calibration( comedi_calibration_t *file_contents )
 {
        if( file_contents->driver_name )
        {
@@ -178,47 +273,105 @@ extern void cleanup_calibration_parse( struct calibration_file_contents *file_co
                free( file_contents->board_name );
                file_contents->board_name = NULL;
        }
-       free_calibrations( file_contents );
+       free_settings( file_contents );
        free( file_contents );
-       file_contents = NULL;
 }
 
-extern struct calibration_file_contents* parse_calibration_file( FILE *file )
+static comedi_polynomial_t* alloc_inverse_linear_polynomial(const comedi_polynomial_t *polynomial)
+{
+       if(polynomial->order != 1) return NULL;
+       comedi_polynomial_t *inverse = malloc(sizeof(comedi_polynomial_t));
+       memset(inverse, 0, sizeof(comedi_polynomial_t));
+       inverse->order = 1;
+       inverse->expansion_origin = polynomial->coefficients[0];
+       inverse->coefficients[0] = polynomial->expansion_origin;
+       inverse->coefficients[1] = 1. / polynomial->coefficients[1];
+       if(isfinite(inverse->coefficients[1]) == 0)
+       {
+               free(inverse);
+               return NULL;
+       }
+       return inverse;
+}
+
+static void fill_inverse_linear_polynomials(comedi_calibration_t *calibration)
+{
+       unsigned i;
+       for(i = 0; i < calibration->num_settings; ++i)
+       {
+               if(calibration->settings[i].soft_calibration.to_phys)
+               {
+                       if(calibration->settings[i].soft_calibration.from_phys == NULL)
+                       {
+                               calibration->settings[i].soft_calibration.from_phys =
+                                       alloc_inverse_linear_polynomial(calibration->settings[i].soft_calibration.to_phys);
+                       }
+               }else if(calibration->settings[i].soft_calibration.from_phys)
+               {
+                       calibration->settings[i].soft_calibration.to_phys =
+                               alloc_inverse_linear_polynomial(calibration->settings[i].soft_calibration.from_phys);
+               }
+       }
+}
+
+EXPORT_ALIAS_DEFAULT(_comedi_parse_calibration_file,comedi_parse_calibration_file,0.7.20);
+extern comedi_calibration_t* _comedi_parse_calibration_file( const char *cal_file_path )
 {
-       calib_yyin = file;
-       parsed_file = alloc_calib_parse();
-       if( parsed_file == NULL ) return parsed_file;
-       cal_index = 0;
-       if( calib_yyparse() )
+       calib_yyparse_private_t priv;
+       FILE *file;
+
+       if( cal_file_path == NULL ) return NULL;
+       memset(&priv, 0, sizeof(calib_yyparse_private_t));
+       priv.parsed_file = alloc_calib_parse();
+       if( priv.parsed_file == NULL ) return NULL;
+
+       file = fopen( cal_file_path, "r" );
+       if( file == NULL )
        {
-               cleanup_calibration_parse( parsed_file );
+               COMEDILIB_DEBUG( 3, "failed to open file\n" );
                return NULL;
        }
-       return parsed_file;
+       calib_yylex_init(&priv.yyscanner);
+       calib_yyrestart(file, priv.yyscanner);
+       if( calib_yyparse( &priv ) )
+       {
+               comedi_cleanup_calibration( priv.parsed_file );
+               priv.parsed_file = NULL;
+       }
+       calib_yylex_destroy(priv.yyscanner);
+       fclose( file );
+       fill_inverse_linear_polynomials(priv.parsed_file);
+       return priv.parsed_file;
 }
 
 %}
 
+%pure_parser
+
 %union
 {
        int  ival;
+       double dval;
        char *sval;
 }
 
 %token T_DRIVER_NAME T_BOARD_NAME T_CALIBRATIONS T_SUBDEVICE T_CHANNELS
 %token T_RANGES T_AREFS T_CALDACS T_CHANNEL T_VALUE T_NUMBER T_STRING
-%token T_ASSIGN
+%token T_COEFFICIENTS T_EXPANSION_ORIGIN T_SOFTCAL_TO_PHYS T_SOFTCAL_FROM_PHYS
+%token T_ASSIGN T_FLOAT
 
 %type <ival> T_NUMBER
 %type <sval> T_STRING
+%type <dval> T_FLOAT
 
 %%
 
        input: '{' hash '}'
                | error
                        {
-                               fprintf(stderr, "input error on line %i\n", @1.first_line );
-                               return -1;
+                               fprintf(stderr, "input error on line %i\n", calib_yyget_lineno(priv(parse_arg)->yyscanner));
+//                             fprintf(stderr, "input error on line %i\n", @1.first_line );
+                               YYABORT;
                        }
                ;
 
@@ -229,13 +382,13 @@ extern struct calibration_file_contents* parse_calibration_file( FILE *file )
 
        hash_element: T_DRIVER_NAME T_ASSIGN T_STRING
                {
-                       if( parsed_file->driver_name != NULL ) YYABORT;
-                       parsed_file->driver_name = strdup( $3 );
+                       if( priv(parse_arg)->parsed_file->driver_name != NULL ) YYABORT;
+                       priv(parse_arg)->parsed_file->driver_name = strdup( $3 );
                }
                | T_BOARD_NAME T_ASSIGN T_STRING
                {
-                       if( parsed_file->board_name != NULL ) YYABORT;
-                       parsed_file->board_name = strdup( $3 );
+                       if( priv(parse_arg)->parsed_file->board_name != NULL ) YYABORT;
+                       priv(parse_arg)->parsed_file->board_name = strdup( $3 );
                }
                | T_CALIBRATIONS T_ASSIGN '[' calibrations_array ']'
                ;
@@ -245,15 +398,15 @@ extern struct calibration_file_contents* parse_calibration_file( FILE *file )
                | '{' calibration_setting '}' ',' calibrations_array
                ;
 
-       calibration_setting: /* empty */ { cal_index++; }
-               | calibration_setting_element { cal_index++; }
+       calibration_setting: /* empty */ { priv(parse_arg)->cal_index++; }
+               | calibration_setting_element { priv(parse_arg)->cal_index++; }
                | calibration_setting_element ',' calibration_setting
                ;
 
        calibration_setting_element: T_SUBDEVICE T_ASSIGN T_NUMBER
                {
-                       struct calibration_setting *setting;
-                       setting = current_setting( parsed_file );
+                       comedi_calibration_setting_t *setting;
+                       setting = current_setting( parse_arg );
                        if( setting == NULL ) YYABORT;
                        setting->subdevice = $3;
                }
@@ -261,6 +414,16 @@ extern struct calibration_file_contents* parse_calibration_file( FILE *file )
                | T_RANGES T_ASSIGN '[' ranges_array ']'
                | T_AREFS T_ASSIGN '[' arefs_array ']'
                | T_CALDACS T_ASSIGN '[' caldacs_array ']'
+               | T_SOFTCAL_TO_PHYS T_ASSIGN '{' polynomial '}'
+               {
+                       if(add_polynomial(parse_arg, POLYNOMIAL_TO_PHYS) < 0) YYERROR;
+                       priv(parse_arg)->num_coefficients = 0;
+               }
+               | T_SOFTCAL_FROM_PHYS T_ASSIGN '{' polynomial '}'
+               {
+                       if(add_polynomial(parse_arg, POLYNOMIAL_FROM_PHYS) < 0) YYERROR;
+                       priv(parse_arg)->num_coefficients = 0;
+               }
                ;
 
        channels_array: /* empty */
@@ -268,7 +431,7 @@ extern struct calibration_file_contents* parse_calibration_file( FILE *file )
                | channel ',' channels_array
                ;
 
-       channel: T_NUMBER { add_channel( parsed_file, $1 ); }
+       channel: T_NUMBER { if(add_channel( parse_arg, $1 ) < 0) YYERROR; }
                ;
 
        ranges_array: /* empty */
@@ -276,7 +439,7 @@ extern struct calibration_file_contents* parse_calibration_file( FILE *file )
                | range ',' ranges_array
                ;
 
-       range: T_NUMBER { add_range( parsed_file, $1 ); }
+       range: T_NUMBER { if(add_range( parse_arg, $1 ) < 0) YYERROR; }
                ;
 
        arefs_array: /* empty */
@@ -284,7 +447,7 @@ extern struct calibration_file_contents* parse_calibration_file( FILE *file )
                | aref ',' arefs_array
                ;
 
-       aref: T_NUMBER { add_aref( parsed_file, $1 ); }
+       aref: T_NUMBER { if(add_aref( parse_arg, $1 ) < 0) YYERROR; }
                ;
 
        caldacs_array: /* empty */
@@ -292,14 +455,48 @@ extern struct calibration_file_contents* parse_calibration_file( FILE *file )
                | '{' caldac '}' ',' caldacs_array
                ;
 
-       caldac: /* empty */ { add_caldac( parsed_file, caldac ); }
-               | caldac_element { add_caldac( parsed_file, caldac ); }
+       caldac: /* empty */ { if(add_caldac( parse_arg, priv(parse_arg)->caldac ) < 0) YYERROR; }
+               | caldac_element { if(add_caldac( parse_arg, priv(parse_arg)->caldac ) < 0) YYERROR; }
                | caldac_element ',' caldac
                ;
 
-       caldac_element: T_SUBDEVICE T_ASSIGN T_NUMBER { caldac.subdevice = $3; }
-               | T_CHANNEL T_ASSIGN T_NUMBER { caldac.channel = $3; }
-               | T_VALUE T_ASSIGN T_NUMBER { caldac.value = $3; }
+       caldac_element: T_SUBDEVICE T_ASSIGN T_NUMBER { priv(parse_arg)->caldac.subdevice = $3; }
+               | T_CHANNEL T_ASSIGN T_NUMBER { priv(parse_arg)->caldac.channel = $3; }
+               | T_VALUE T_ASSIGN T_NUMBER { priv(parse_arg)->caldac.value = $3; }
+               ;
+
+       polynomial: /* empty */
+               | polynomial_element
+               | polynomial_element ',' polynomial
+               ;
+
+       polynomial_element: T_COEFFICIENTS T_ASSIGN '[' coefficient_array ']'
+               | T_EXPANSION_ORIGIN T_ASSIGN expansion_origin
+               ;
+
+       coefficient_array: /* empty */
+               | coefficient
+               | coefficient ',' coefficient_array
+               ;
+
+       coefficient: T_FLOAT
+               {
+                       if(add_polynomial_coefficient(parse_arg, $1) < 0) YYERROR;
+               }
+               | T_NUMBER
+               {
+                       if(add_polynomial_coefficient(parse_arg, $1) < 0) YYERROR;
+               }
+               ;
+
+       expansion_origin: T_FLOAT
+               {
+                       priv(parse_arg)->polynomial.expansion_origin = $1;
+               }
+               | T_NUMBER
+               {
+                       priv(parse_arg)->polynomial.expansion_origin = $1;
+               }
                ;
 
 %%