Commit 2b8ca739 authored by Jukka-Pekka Iivonen's avatar Jukka-Pekka Iivonen Committed by jpekka
Browse files

Moved the parser part into parser.c.

2002-03-26  Jukka-Pekka Iivonen  <jiivonen@hutcs.cs.hut.fi>

        * mps.c: Moved the parser part into parser.c.

        * mps.h, parser.c, Makefile.am: New files.
parent 08f7446a
2002-03-26 Jukka-Pekka Iivonen <jiivonen@hutcs.cs.hut.fi>
* mps.c: Moved the parser part into parser.c.
* mps.h, parser.c, Makefile.am: New files.
2002-03-24 Jukka-Pekka Iivonen <jiivonen@hutcs.cs.hut.fi>
* mps.c: Major clean up. Does not crach any more if a model
......
......@@ -7,7 +7,7 @@ gnumeric_plugin_mpsdir = $(gnumeric_plugindir)/mps
xmldir = $(gnumeric_plugin_mpsdir)
gnumeric_plugin_mps_LTLIBRARIES = mps.la
mps_la_LDFLAGS = -module -avoid-version
mps_la_SOURCES = mps.c
mps_la_SOURCES = mps.c mps.h parser.c
xml_in_files = plugin.xml.in
xml_DATA = $(xml_in_files:.xml.in=.xml)
......
......@@ -13,6 +13,20 @@
* already quite suitable for testing the solving algorithms.
* See, for example, the Netlib collection of LP problems in MPS format
* (ftp://netlib2.cs.utk.edu/lp/data).
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <gnumeric-config.h>
#include <gnumeric.h>
......@@ -38,148 +52,12 @@
#include "solver.h"
#include "sheet-style.h"
#include "parse-util.h"
#include "mps.h"
#include <libgnome/gnome-i18n.h>
GNUMERIC_MODULE_PLUGIN_INFO_DECL;
#define N_INPUT_LINES_BETWEEN_UPDATES 50
#define MAX_COL 160
/*************************************************************************
*
* Data structures.
*/
/*
* MPS Row type (E, L, G, or N).
*/
typedef enum {
EqualityRow, LessOrEqualRow, GreaterOrEqualRow, ObjectiveRow
} MpsRowType;
/*
* MPS Row.
*/
typedef struct {
MpsRowType type;
gchar *name;
gint index;
} MpsRow;
/*
* MPS Column.
*/
typedef struct {
gchar *name;
MpsRow *row;
gnum_float value;
} MpsCol;
/*
* MPS Range.
*/
typedef struct {
gchar *name;
MpsRow *row;
gnum_float value;
} MpsRange;
/*
* MPS Bound type (LO, UP, FX, FR, MI, BV, LI, or UI).
*/
typedef enum {
LowerBound, UpperBound, FixedVariable, FreeVariable, LowerBoundInf,
BinaryVariable, LowerBoundInt, UpperBoundInt
} MpsBoundType;
/*
* MPS Bound.
*/
typedef struct {
char *name;
gint col_index;
gnum_float value;
MpsBoundType type;
} MpsBound;
/*
* MPS RHS.
*/
typedef struct {
gchar *name;
MpsRow *row;
gnum_float value;
} MpsRhs;
/*
* Column mapping.
*/
typedef struct {
gchar *name;
gint index;
} MpsColInfo;
/*
* Input context.
*/
typedef struct {
IOContext *io_context;
gint data_size;
guchar *data, *cur;
gint line_no;
gchar *line;
gint line_len, alloc_line_len;
Sheet *sheet;
gchar *name;
GSList *rows;
GSList *cols;
GSList *rhs;
GSList *bounds;
gint n_rows, n_cols, n_bounds;
GHashTable *row_hash;
GHashTable *col_hash;
gchar **col_name_tbl;
MpsRow *objective_row;
gnum_float **matrix;
} MpsInputContext;
/*************************************************************************
*
* Constants.
*/
static const int MAIN_INFO_ROW = 1;
static const int MAIN_INFO_COL = 0;
static const int OBJECTIVE_VALUE_COL = 1;
static const int VARIABLE_COL = 1;
static const int VARIABLE_ROW = 5;
static const int CONSTRAINT_COL = 1;
static const int CONSTRAINT_ROW = 10;
/* Error margin in the equiality comparison */
static const gchar *BINDING_LIMIT = "0.00000001";
/*************************************************************************
*
* Public Interface of the module
*/
/* Reads the MPS file in and creates a spreadsheet model of it. */
void
mps_file_open (GnumFileOpener const *fo, IOContext *io_context,
WorkbookView *wbv, char const *file_name);
/*************************************************************************
......@@ -187,9 +65,6 @@ mps_file_open (GnumFileOpener const *fo, IOContext *io_context,
* Sheet creation.
*/
static gboolean
mps_add_row (MpsInputContext *ctxt, MpsRowType type, gchar *txt);
/* Writes a string into a cell. */
static void
mps_set_cell (Sheet *sh, int col, int row, const gchar *str)
......@@ -855,532 +730,6 @@ mps_input_context_destroy (MpsInputContext *ctxt)
/************************************************************************
*
* Parser's low level stuff.
*/
/*---------------------------------------------------------------------*/
/* Read a line from the file. */
static gboolean
mps_get_line (MpsInputContext *ctxt)
{
guchar *p, *p_limit;
try_again:
p_limit = ctxt->data + ctxt->data_size;
if (ctxt->cur >= p_limit) {
ctxt->line[0] = '\0';
ctxt->line_len = 0;
return FALSE;
}
for (p = ctxt->cur; p < p_limit && p[0] != '\n' && p[0] != '\r'; p++);
ctxt->line_len = p - ctxt->cur;
if (ctxt->line_len > ctxt->alloc_line_len) {
g_free (ctxt->line);
ctxt->alloc_line_len = MAX (ctxt->alloc_line_len * 2,
ctxt->line_len);
ctxt->line = g_malloc (ctxt->alloc_line_len + 1);
}
if (ctxt->line_len > 0) {
memcpy (ctxt->line, ctxt->cur, ctxt->line_len);
}
ctxt->line[ctxt->line_len] = '\0';
if (p == p_limit || (p == p_limit - 1 && (p[0] == '\n' ||
p[0] == '\r'))) {
ctxt->cur = p_limit;
} else if ((p[0] == '\n' && p[1] == '\r') || (p[0] == '\r' &&
p[1] == '\n')) {
ctxt->cur = p + 2;
} else {
ctxt->cur = p + 1;
}
if ((++ctxt->line_no % N_INPUT_LINES_BETWEEN_UPDATES) == 0) {
memory_io_progress_update (ctxt->io_context, ctxt->cur);
}
/* Check if a comment line */
if (ctxt->line[0] == '*')
goto try_again;
return TRUE;
}
static gboolean
mps_parse_data (gchar *str, gchar *type, gchar *name1, gchar *name2,
gchar *value1, gchar *name3, gchar *value2)
{
gint i;
gchar *n1 = name1;
gchar *n2 = name2;
gchar *n3 = name3;
for (i=0; i<8; i++)
name1[i] = name2[i] = name3[i] = ' ';
*value2 = *name3 = '\0';
if (!(*str) || *str++ != ' ' || !(*str))
return FALSE;
/* Type field is present */
if (*str != ' ') {
*type++ = *str++;
if (!(*str))
return FALSE;
if (*str != ' ')
*type++ = *str++;
else
str++;
*type = '\0';
} else
str += 2;
/* Label 1 */
if (!(*str) || *str++ != ' ')
return FALSE;
for (i=5; i<=12; i++, str++) {
*name1++ = *str;
if (!(*str))
goto ok_out;
}
*name1 = '\0';
/* Label 2 */
if (*str == '\0')
goto ok_out;
if (*str++ != ' ')
return FALSE;
if (*str == '\0')
goto ok_out;
if (*str++ != ' ')
return FALSE;
for (i=15; i<=22; i++, str++) {
*name2++ = *str;
if (!(*str))
return FALSE;
}
*name2 = '\0';
/* Value 1 */
if (!(*str) || *str++ != ' ' || !(*str) || *str++ != ' ')
return FALSE;
for (i=25; i<=36; i++, str++) {
*value1++ = *str;
if (!(*str))
goto ok_out;
}
*value1 = '\0';
/* Label 3 */
if (!(*str))
goto ok_out;
if (*str++ != ' ')
return FALSE;
if (!(*str))
goto ok_out;
if (*str++ != ' ')
return FALSE;
if (!(*str))
goto ok_out;
if (*str++ != ' ')
return FALSE;
for (i=40; i<=47; i++, str++) {
*name3++ = *str;
if (!(*str))
return FALSE;
}
*name3 = '\0';
/* Value 2 */
if (!(*str) || *str++ != ' ' || !(*str) || *str++ != ' ')
return FALSE;
for (i=50; i<=61; i++, str++) {
*value2++ = *str;
if (!(*str))
goto ok_out;
}
*value2 = '\0';
ok_out:
for (i=7; i>=0; i--)
if (n1[i] != ' ')
break;
n1[i+1] = '\0';
for (i=7; i>=0; i--)
if (n2[i] != ' ')
break;
n2[i+1] = '\0';
for (i=7; i>=0; i--)
if (n3[i] != ' ')
break;
n3[i+1] = '\0';
return TRUE;
}
/************************************************************************
*
* Parser.
*/
/*
* NAME section parsing. Saves the program name into `ctxt->name'.
*
* Returns FALSE on error.
*/
static gboolean
mps_parse_name (MpsInputContext *ctxt)
{
while (1) {
gchar *line;
if (!mps_get_line (ctxt))
return FALSE;
if (strncmp (ctxt->line, "NAME", 4) == 0
&& isspace ((unsigned char)(ctxt->line[4]))) {
line = ctxt->line + 5;
while (isspace ((unsigned char) *line))
line++;
ctxt->name = strcpy (g_malloc (ctxt->line_len -
(line-ctxt->line) + 1),
line);
break;
} else
return FALSE;
}
return TRUE;
}
/* Add one ROW definition. */
static gboolean
mps_add_row (MpsInputContext *ctxt, MpsRowType type, gchar *txt)
{
MpsRow *row;
int len;
while (isspace ((unsigned char) *txt))
txt++;
row = g_new (MpsRow, 1);
len = strlen(txt);
if (len == 0)
return FALSE;
row->name = strcpy (g_malloc (len + 1), txt);
row->type = type;
row->index = ctxt->n_rows;
ctxt->n_rows += 1;
ctxt->rows = g_slist_prepend (ctxt->rows, row);
if (type == ObjectiveRow)
ctxt->objective_row = row;
return TRUE;
}
/*
* ROWS section parsing. Saves the number of rows into `ctxt->n_rows'.
* The rows are saved into `ctxt->rows' which is a GSList containing
* MpsRow elements. These elements get their `name', `type', and
* `index' fields set.
*
* Returns FALSE on error.
*/
static gboolean
mps_parse_rows (MpsInputContext *ctxt)
{
gchar type[3], n1[10], n2[10], n3[10], v1[20], v2[20];
GSList *tmp;
while (1) {
if (!mps_get_line (ctxt))
return FALSE;
if (strncmp (ctxt->line, "ROWS", 4) == 0)
break;
else
return FALSE;
}
while (1) {
if (!mps_get_line (ctxt))
return FALSE;
if (!mps_parse_data (ctxt->line, type, n1, n2, v1, n3, v2)) {
if (ctxt->line[0] != ' ')
goto ok_out;
else
return FALSE;
}
if (strcmp (type, "E") == 0) {
if (!mps_add_row (ctxt, EqualityRow, n1))
return FALSE;
} else if (strcmp (type, "L") == 0) {
if (!mps_add_row (ctxt, LessOrEqualRow, n1))
return FALSE;
} else if (strcmp (type, "G") == 0) {
if (!mps_add_row (ctxt, GreaterOrEqualRow, n1))
return FALSE;
} else if (strcmp (type, "N") == 0) {
if (!mps_add_row (ctxt, ObjectiveRow, n1))
return FALSE;
} else
return FALSE;
}
ok_out:
for (tmp = ctxt->rows; tmp != NULL; tmp = tmp->next) {
MpsRow *row = (MpsRow *) tmp->data;
g_hash_table_insert (ctxt->row_hash,
row->name,
(gpointer) row);
}
return TRUE;
}
/* Add one COLUMN definition. */
static gboolean
mps_add_column (MpsInputContext *ctxt, gchar *row_name, gchar *col_name,
gchar *value_str)
{
MpsCol *col;
MpsRow *row;
MpsColInfo *i;
row = (MpsRow *) g_hash_table_lookup (ctxt->row_hash, row_name);
if (row == NULL)
return FALSE;
col = g_new (MpsCol, 1);
col->row = row;
col->name = g_strdup (col_name);
col->value = atof (value_str);
ctxt->cols = g_slist_prepend (ctxt->cols, col);
i = (MpsColInfo *) g_hash_table_lookup (ctxt->col_hash, col_name);
if (i == NULL) {
i = g_new (MpsColInfo, 1);
i->index = ctxt->n_cols;
i->name = strcpy (g_malloc (strlen (col_name) + 1), col_name);
ctxt->n_cols += 1;
g_hash_table_insert (ctxt->col_hash, col->name, (gpointer) i);
}
return TRUE;
}
/*
* COLUMNS section parsing. Saves the number of columns into `ctxt->n_cols'.
* The columns are saved into `ctxt->cols' which is a GSList containing
* MpsCol elements. Fields `row', `name' and `value' are set of each element.
*
* Keeps track of the column names using `ctxt->col_hash'.
*
* Returns FALSE on error.
*/
static gboolean
mps_parse_columns (MpsInputContext *ctxt)
{
gchar type[3], n1[10], n2[10], n3[10], v1[20], v2[20];
while (1) {
if (strncmp (ctxt->line, "COLUMNS", 7) == 0)
break;
else
return FALSE;
if (!mps_get_line (ctxt))
return FALSE;
}
while (1) {
if (!mps_get_line (ctxt))
return FALSE;
if (!mps_parse_data (ctxt->line, type, n1, n2, v1, n3, v2)) {
if (ctxt->line[0] != ' ')
return TRUE;
else
return FALSE;
}
if (!mps_add_column (ctxt, n2, n1, v1))
return FALSE;
/* Optional second column definition */
if (*v2)
if (!mps_add_column (ctxt, n3, n1, v2))
return FALSE;
}
return TRUE;
}
/* Add one RHS definition. */
static gboolean
mps_add_rhs (MpsInputContext *ctxt, gchar *rhs_name, gchar *row_name,
gchar *value_str)
{
MpsRhs *rhs;
rhs = g_new (MpsRhs, 1);
rhs->name = g_strdup (rhs_name);
rhs->row = (MpsRow *) g_hash_table_lookup (ctxt->row_hash, row_name);
if (rhs->row == NULL)
return FALSE;
rhs->value = atof (value_str);
ctxt->rhs = g_slist_prepend (ctxt->rhs, rhs);
return TRUE;
}
/*
* RHS section parsing. Saves the RHS entries into ctxt->rhs list (GSList).
* MpsRhs is the type of the elements in the list. Fields `name', `row',
* and `value' are stored into each element.
*
* Returns FALSE on error.
*/
static gboolean
mps_parse_rhs (MpsInputContext *ctxt)
{
gchar type[3], rhs_name[10], row_name[10], value[20], n2[10], v2[20];
if (strncmp (ctxt->line, "RHS", 3) != 0 || ctxt->line[3] != '\0')
return FALSE;
while (1) {
if (!mps_get_line (ctxt))
return FALSE;
if (!mps_parse_data (ctxt->line, type, rhs_name, row_name,
value, n2, v2)) {
if (ctxt->line[0] != ' ')
return TRUE;
else
return FALSE;
}
if (!mps_add_rhs (ctxt, rhs_name, row_name, value))
return FALSE;
/* Optional second RHS definition */
if (*v2)