/* * $Id: byte-compile.c,v 1.12 2000/11/22 19:33:00 danny Exp $ * * Copyright © 1990, 1992, 1993 Free Software Foundation, Inc. * * This file is part of Oleo, the GNU Spreadsheet. * * Oleo 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, or (at your option) * any later version. * * Oleo 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 Oleo; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef WITH_DMALLOC #include #endif /* FIXME #include "funcdef.h" */ #include #include "sysdef.h" #ifdef HAVE_MALLOC_H #include #endif #ifdef _DEBUG_MALLOC_INC static void local_free (p) void * p; { free (p); } #define obstack_chunk_free local_free #else /* ndef _DEBUG_MALLOC_INC */ #define obstack_chunk_free free #endif /* ndef _DEBUG_MALLOC_INC */ #define obstack_chunk_alloc ck_malloc #include "obstack.h" #include "global.h" #include "node.h" #include "eval.h" #include "hash.h" #include "ref.h" extern int yyparse (void); extern struct function date_funs[]; extern struct function busi_funs[]; extern struct function string_funs[]; extern struct function cells_funs[]; extern struct function mysql_functions[]; extern struct function gsl_functions[]; extern char *instr; extern int parse_error; extern struct node *parse_return; extern void sort (); static void add_backpatch (unsigned, unsigned); struct backpatch { unsigned from, to; }; static struct backpatch *patches; static int patches_allocated; static int patches_used; static void *fn_stack; static void *str_stack; struct obstack tmp_mem; void *tmp_mem_start; #define V (void(*)()) /* These have to go in some file or other, so it is stuck in here (for now). */ struct function the_funs[] = { {0, X_A0, "", 0, ""}, {0, X_A0, "", 0, ""}, {C_IF | R | INF (1), X_A1 | X_J, "D", 0, "?"}, {C_IF | R | INF (1), X_A1 | X_JL, "D", 0, "?"}, {C_IF, X_A1 | X_J, "D", 0, "if"}, {C_IF, X_A1 | X_JL, "D", 0, "if"}, {C_ANDOR, X_A1 | X_J, "D", 0, "and"}, /* { C_ANDOR|L|INF(3), X_A1, "DD", 0, "&" }, */ {C_ANDOR, X_A1 | X_JL, "D", 0, "and"}, /* { C_ANDOR|L|INF(3), X_A1, "DD", 0, "&" }, */ {C_ANDOR, X_A1 | X_J, "D", 0, "or"}, /* { C_ANDOR|L|INF(2), X_A1, "DD", 0, "|" }, */ {C_ANDOR, X_A1 | X_JL, "D", 0, "or"}, /* { C_ANDOR|L|INF(2), X_A1, "DD", 0, "|" }, */ {C_STR, X_A0 | X_J, "", 0, "\"%s\""}, {C_STR, X_A0 | X_JL, "", 0, "\"%s\""}, {C_CELL, X_A0, "", 0, "$%s$%u"}, {C_CELL, X_A0, "", 0, "$%s%u"}, {C_CELL, X_A0, "", 0, "%s$%u"}, {C_CELL, X_A0, "", 0, "%s%u"}, {C_RANGE, X_A0, "", 0, "$%s$%u:$%s$%u"}, {C_RANGE, X_A0, "", 0, "$%s%u:$%s$%u"}, {C_RANGE, X_A0, "", 0, "$%s$%u:$%s%u"}, {C_RANGE, X_A0, "", 0, "$%s%u:$%s%u"}, {C_RANGE, X_A0, "", 0, "%s$%u:$%s$%u"}, {C_RANGE, X_A0, "", 0, "%s%u:$%s$%u"}, {C_RANGE, X_A0, "", 0, "%s$%u:$%s%u"}, {C_RANGE, X_A0, "", 0, "%s%u:$%s%u"}, {C_RANGE, X_A0, "", 0, "$%s$%u:%s$%u"}, {C_RANGE, X_A0, "", 0, "$%s%u:%s$%u"}, {C_RANGE, X_A0, "", 0, "$%s$%u:%s%u"}, {C_RANGE, X_A0, "", 0, "$%s%u:%s%u"}, {C_RANGE, X_A0, "", 0, "%s$%u:%s$%u"}, {C_RANGE, X_A0, "", 0, "%s%u:%s$%u"}, {C_RANGE, X_A0, "", 0, "%s$%u:%s%u"}, {C_RANGE, X_A0, "", 0, "%s%u:%s%u"}, {C_CONST, X_A0, "", 0, tname}, {C_CONST, X_A0, "", 0, fname}, {C_CONST, X_A0, "", 0, iname}, {C_CONST, X_A0, "", 0, mname}, {C_CONST, X_A0, "", 0, nname}, {C_ERR, X_A0 | X_J, "", 0, "%s"}, {C_FLT, X_A0, "", 0, "%.15g"}, {C_INT, X_A0, "", 0, "%ld"}, {C_VAR, X_A0, "", 0, "%s"}, {C_UNA, X_A1, "F", 0, "-"}, {C_UNA, X_A1, "B", 0, "!"}, {C_INF | L | INF (6), X_A2, "NN", 0, "-"}, {C_INF | L | INF (7), X_A2, "NN", 0, "/"}, {C_INF | L | INF (7), X_A2, "NN", 0, "%"}, {C_INF | L | INF (7), X_A2, "NN", 0, "*"}, {C_INF | L | INF (6), X_A2, "NN", 0, "+"}, {C_INF | L | INF (2), X_A2, "SS", 0, "&"}, {C_INF | N | INF (4), X_A2, "AA", 0, "="}, {C_INF | N | INF (5), X_A2, "AA", 0, ">="}, {C_INF | N | INF (5), X_A2, "AA", 0, ">"}, {C_INF | N | INF (5), X_A2, "AA", 0, "<"}, {C_INF | N | INF (5), X_A2, "AA", 0, "<="}, {C_INF | N | INF (4), X_A2, "AA", 0, "!="}, {C_INF | R | INF (8), X_A2, "FF", V pow, "^"}, {C_FN0, X_A0, "", 0, "pi"}, {C_FN0X, X_A0, "", 0, "row"}, {C_FN0X, X_A0, "", 0, "col"}, {C_FN0 | C_T, X_A0, "", 0, "now"}, {C_FN1, X_A1, "F", V fabs, "abs"}, {C_FN1, X_A1, "F", V acos, "acos"}, {C_FN1, X_A1, "F", V asin, "asin"}, {C_FN1, X_A1, "F", V atan, "atan"}, {C_FN1, X_A1, "F", V ceil, "ceil"}, {C_FN1, X_A1, "F", V to_int, "int"}, {C_FN1, X_A1, "F", V floor, "floor"}, {C_FN1, X_A1, "F", V cos, "cos"}, {C_FN1, X_A1, "F", V dtr, "dtr"}, {C_FN1, X_A1, "F", V exp, "exp"}, {C_FN1, X_A1, "F", V log, "log"}, {C_FN1, X_A1, "F", V log10, "log10"}, {C_FN1, X_A1, "F", V rtd, "rtd"}, {C_FN1, X_A1, "F", V sin, "sin"}, {C_FN1, X_A1, "F", V sqrt, "sqrt"}, {C_FN1, X_A1, "F", V tan, "tan"}, {C_FN1, X_A1, "I", 0, "ctime"}, {C_FN1, X_A1, "A", 0, "negate"}, {C_FN1, X_A1, "A", 0, "not"}, {C_FN1, X_A1, "A", 0, "iserr"}, {C_FN1, X_A1, "A", 0, "isnum"}, {C_FN1 | C_T, X_A1, "I", 0, "rnd"}, {C_FN1, X_A1, "R", 0, "rows"}, {C_FN1, X_A1, "R", 0, "cols"}, {C_FN2, X_A2, "FF", V atan2, "atan2"}, #ifdef HAVE_HYPOT {C_FN2, X_A2, "FF", V hypot, "hypot"}, #else {C_FN2, X_A2, "FF", 0, "*&%$%*"}, #endif {C_FN2, X_A2, "FI", 0, "fixed"}, {C_FN2, X_A2, "AA", 0, "iferr"}, {C_FN2, X_A2, "RI", 0, "index"}, {C_FN3, X_A3, "RII", 0, "index"}, {C_FNN, X_AN, "IAAA", 0, "oneof"}, {C_FNN, X_AN, "SIIA", 0, "file"}, {C_FNN, X_AN, "EEEE", 0, "sum"}, {C_FNN, X_AN, "EEEE", 0, "prod"}, {C_FNN, X_AN, "EEEE", 0, "avg"}, {C_FNN, X_AN, "EEEE", 0, "std"}, {C_FNN, X_AN, "EEEE", 0, "max"}, {C_FNN, X_AN, "EEEE", 0, "min"}, {C_FNN, X_AN, "EEEE", 0, "count"}, {C_FNN, X_AN, "EEEE", 0, "var"}, }; /* * This is the place where you can add extra functions */ static struct function *__usr_funs[] = { date_funs, busi_funs, string_funs, cells_funs, mysql_functions, gsl_functions, /* Add something here */ }; /* * A small function in each module tells us how many functions * it defines. */ extern int init_date_function_count(void); extern int init_busi_function_count(void); extern int init_string_function_count(void); extern int init_cells_function_count(void); extern int init_mysql_function_count(void); extern int init_gsl_function_count(void); /* Add something here */ typedef int (*init_function_count)(void); static init_function_count init_function_counts[] = { &init_date_function_count, &init_busi_function_count, &init_string_function_count, &init_cells_function_count, &init_mysql_function_count, &init_gsl_function_count /* Add something here */ }; /* Determine how many groups of functions we have */ int n_usr_funs = sizeof(init_function_counts) / sizeof(init_function_count); /* * This is an array containing the number of functions in each group. * It is allocated and initialised in init_mem() below. * Previous versions of Oleo required manual maintenance of this. */ int *usr_n_funs = NULL; struct function **usr_funs = __usr_funs; /* ... A whole huge empty space, then ... */ struct function skip_funs[] = { {C_SKIP, X_A0 | X_J, "", 0, ""}, {C_SKIP, X_A0 | X_JL, "", 0, ""}, }; /* The memory allocated here is used for several things, but byte_compile is a small file, so it might as well be here */ void init_mem () { int n, i; /* Initialise counters */ usr_n_funs = (int *)calloc(n_usr_funs, sizeof(int)); for (i=0; i n1) { patches[n2] = patches[n2 - 1]; --n2; } patches[n2] = tmp; } /* This takes an ascii string and returns a pointer to the byte-compiled result. It calls yyparse() to do the actual parsing. This is complicated only because yyparse returns a parse tree which needs to be turned into postfix compiled bytes. This is further complicated by the presence of forward branches in the byte-compiled code. That's what the backpatch stuff is for. It'd be nice if oneof() could compile into arg1 ONEOF n_possibilities JUMP poss1 JUMP poss2 JUMP poss3 ... JUMP error {poss 1} JUMP end {poss 2} JUMP end ... end: {rest of expression} instead of the simplistic (and slow-to-execute) version currently used It'd also be nice if byte-compiled expressions could have *BIG* subexpressions, instead of silently failing as they do now. Error checking and a way to encode longer branches would be a *good* idea. */ unsigned char * parse_and_compile (string) char *string; { struct node *new_node; struct node *node; const struct function *f; unsigned char *ret; int n; unsigned buf_siz; int need_relax; int byte; instr = string; parse_error = 0; patches_used = 0; if (yyparse () || parse_error) { ret = ck_malloc (strlen (string) + 5); ret[0] = CONST_ERR; ret[1] = 2; ret[2] = parse_error; ret[3] = ENDCOMP; strcpy ((char *) &ret[4], string); (void) obstack_free (&tmp_mem, tmp_mem_start); return ret; } node = parse_return; if (!node) return 0; loop: if (node->comp_value < USR1) { f = &the_funs[node->comp_value]; } else if (node->comp_value < SKIP) { n = node->sub_value; f = &usr_funs[node->comp_value - USR1][n]; } else { f = &skip_funs[node->comp_value - SKIP]; } byte = node->comp_value; #ifdef TEST if (!f) panic ("f is zero in byte_compile!"); #endif switch (GET_COMP (f->fn_comptype)) { case C_IF: /* if compiles to test-code IF amt-to-skip-on-false true-code SKIP amt-to-skip-on-true false-code */ if (node->n_x.v_subs[0]) { if (node->n_x.v_subs[0]->n_x.v_subs[0]) { /* Put out the test-code */ push_stack (fn_stack, node); new_node = node->n_x.v_subs[0]->n_x.v_subs[0]; node->n_x.v_subs[0]->n_x.v_subs[0] = 0; node = new_node; goto loop; } /* Put out IF, null-byte to backpatch */ (void) obstack_1grow (&tmp_mem, byte); node->add_byte = obstack_object_size (&tmp_mem); (void) obstack_1grow (&tmp_mem, 0); /* put out true-code */ push_stack (fn_stack, node); new_node = node->n_x.v_subs[0]->n_x.v_subs[1]; node->n_x.v_subs[0] = 0; node = new_node; goto loop; } if (node->n_x.v_subs[1]) { (void) obstack_1grow (&tmp_mem, (char)SKIP); (void) obstack_1grow (&tmp_mem, 0); add_backpatch (node->add_byte, obstack_object_size (&tmp_mem)); node->add_byte = obstack_object_size (&tmp_mem) - 1; push_stack (fn_stack, node); new_node = node->n_x.v_subs[1]; node->n_x.v_subs[1] = 0; node = new_node; goto loop; } add_backpatch (node->add_byte, obstack_object_size (&tmp_mem)); break; case C_ANDOR: if (node->n_x.v_subs[0]) { push_stack (fn_stack, node); new_node = node->n_x.v_subs[0]; node->n_x.v_subs[0] = 0; node = new_node; goto loop; } if (node->n_x.v_subs[1]) { (void) obstack_1grow (&tmp_mem, byte); node->add_byte = obstack_object_size (&tmp_mem); (void) obstack_1grow (&tmp_mem, 0); /* for backpatching */ push_stack (fn_stack, node); new_node = node->n_x.v_subs[1]; node->n_x.v_subs[1] = 0; node = new_node; goto loop; } add_backpatch (node->add_byte, obstack_object_size (&tmp_mem)); break; case C_ERR: (void) obstack_1grow (&tmp_mem, byte); node->add_byte = obstack_object_size (&tmp_mem); (void) obstack_1grow (&tmp_mem, 0); (void) obstack_1grow (&tmp_mem, node->n_x.v_int); node->n_x.v_string = ename[node->n_x.v_int]; push_stack (str_stack, node); break; case C_FLT: (void) obstack_1grow (&tmp_mem, byte); (void) obstack_grow (&tmp_mem, &(node->n_x.v_float), sizeof (double)); break; case C_INT: (void) obstack_1grow (&tmp_mem, byte); (void) obstack_grow (&tmp_mem, &(node->n_x.v_int), sizeof (long)); break; case C_STR: (void) obstack_1grow (&tmp_mem, byte); node->add_byte = obstack_object_size (&tmp_mem); (void) obstack_1grow (&tmp_mem, 0); push_stack (str_stack, node); break; case C_VAR: add_ref_to (obstack_object_size (&tmp_mem)); add_var_ref (node->n_x.v_var); (void) obstack_1grow (&tmp_mem, byte); (void) obstack_grow (&tmp_mem, &(node->n_x.v_var), sizeof (struct var *)); break; case C_CELL: add_ref_to (obstack_object_size (&tmp_mem)); add_ref (node->n_x.v_rng.lr, node->n_x.v_rng.lc); (void) obstack_1grow (&tmp_mem, byte); #if BITS_PER_CELLREF==16 (void) obstack_1grow (&tmp_mem, node->n_x.v_rng.lr >> 8); (void) obstack_1grow (&tmp_mem, node->n_x.v_rng.lr); (void) obstack_1grow (&tmp_mem, node->n_x.v_rng.lc >> 8); (void) obstack_1grow (&tmp_mem, node->n_x.v_rng.lc); #else #if BITS_PER_CELLREF==8 (void) obstack_1grow (&tmp_mem, node->n_x.v_rng.lr); (void) obstack_1grow (&tmp_mem, node->n_x.v_rng.lc); #else Insert appropriate code here #endif #endif break; case C_RANGE: add_ref_to (obstack_object_size (&tmp_mem)); add_range_ref (&(node->n_x.v_rng)); (void) obstack_1grow (&tmp_mem, byte); (void) obstack_grow (&tmp_mem, &(node->n_x.v_rng), sizeof (struct rng)); break; case C_FN0X: add_ref_to (obstack_object_size (&tmp_mem)); /* FALLTHROUGH */ case C_FN0: case C_CONST: add_byte: if (f->fn_comptype & C_T) add_timer_ref (obstack_object_size (&tmp_mem)); (void) obstack_1grow (&tmp_mem, byte); if (byte >= USR1 && byte < SKIP) (void) obstack_1grow (&tmp_mem, (int) node->sub_value); break; case C_FN1: case C_UNA: if (node->n_x.v_subs[0]) { push_stack (fn_stack, node); new_node = node->n_x.v_subs[0]; node->n_x.v_subs[0] = 0; node = new_node; goto loop; } goto add_byte; case C_FN2: case C_INF: if (node->n_x.v_subs[0]) { push_stack (fn_stack, node); new_node = node->n_x.v_subs[0]; node->n_x.v_subs[0] = 0; node = new_node; goto loop; } if (node->n_x.v_subs[1]) { push_stack (fn_stack, node); new_node = node->n_x.v_subs[1]; node->n_x.v_subs[1] = 0; node = new_node; goto loop; } goto add_byte; case C_FN3: if (node->n_x.v_subs[0]) { if (node->n_x.v_subs[0]->n_x.v_subs[0]) { push_stack (fn_stack, node); new_node = node->n_x.v_subs[0]->n_x.v_subs[0]; node->n_x.v_subs[0]->n_x.v_subs[0] = 0; node = new_node; goto loop; } push_stack (fn_stack, node); new_node = node->n_x.v_subs[0]->n_x.v_subs[1]; node->n_x.v_subs[0] = 0; node = new_node; goto loop; } if (node->n_x.v_subs[1]) { push_stack (fn_stack, node); new_node = node->n_x.v_subs[1]; node->n_x.v_subs[1] = 0; node = new_node; goto loop; } goto add_byte; case C_FN4: if (node->n_x.v_subs[0]) { if (node->n_x.v_subs[0]->n_x.v_subs[0]) { push_stack (fn_stack, node); new_node = node->n_x.v_subs[0]->n_x.v_subs[0]; node->n_x.v_subs[0]->n_x.v_subs[0] = 0; node = new_node; goto loop; } push_stack (fn_stack, node); new_node = node->n_x.v_subs[0]->n_x.v_subs[1]; node->n_x.v_subs[0] = 0; node = new_node; goto loop; } if (node->n_x.v_subs[1]) { if (node->n_x.v_subs[1]->n_x.v_subs[0]) { push_stack (fn_stack, node); new_node = node->n_x.v_subs[1]->n_x.v_subs[0]; node->n_x.v_subs[1]->n_x.v_subs[0] = 0; node = new_node; goto loop; } push_stack (fn_stack, node); new_node = node->n_x.v_subs[1]->n_x.v_subs[1]; node->n_x.v_subs[1] = 0; node = new_node; goto loop; } goto add_byte; case C_FNN: if (node->n_x.v_subs[1]) { if (node->add_byte == 0) for (new_node = node; new_node->n_x.v_subs[1]; new_node = new_node->n_x.v_subs[1]) node->add_byte++; for (new_node = node; new_node->n_x.v_subs[1]->n_x.v_subs[1]; new_node = new_node->n_x.v_subs[1]) ; push_stack (fn_stack, node); node = new_node->n_x.v_subs[1]->n_x.v_subs[0]; new_node->n_x.v_subs[1] = 0; goto loop; } (void) obstack_1grow (&tmp_mem, byte); if (byte >= USR1 && byte < SKIP) (void) obstack_1grow (&tmp_mem, (int) node->sub_value); (void) obstack_1grow (&tmp_mem, node->add_byte); break; default: panic ("Bad comptype %d", f->fn_comptype); } node = (struct node *) pop_stack (fn_stack); if (node) goto loop; (void) obstack_1grow (&tmp_mem, 0); while ((node = (pop_stack (str_stack)))) { add_backpatch (node->add_byte, obstack_object_size (&tmp_mem)); (void) obstack_grow (&tmp_mem, node->n_x.v_string, strlen (node->n_x.v_string) + 1); } buf_siz = obstack_object_size (&tmp_mem); ret = (unsigned char *) ck_malloc (buf_siz); bcopy (obstack_finish (&tmp_mem), ret, buf_siz); need_relax = 0; for (n = 0; n < patches_used; n++) { long offset; offset = (patches[n].to - patches[n].from) - 1; if (offset < 0 || offset > 255) need_relax++; else ret[patches[n].from] = offset; } if (need_relax) { int n_lo; long offset; int start; /* ... Sort the patches list ... */ sort (patches_used, cmp_patch, swp_patch, rot_patch); while (need_relax) { ret = ck_realloc (ret, buf_siz + need_relax); for (n_lo = 0; n_lo < patches_used; n_lo++) { offset = (patches[n_lo].to - patches[n_lo].from) - 1; if (offset < 0 || offset > 255 - need_relax) break; } /* n_lo points to the first jump that may need to be relaxed */ for (n = n_lo; n < patches_used; n++) { offset = (patches[n].to - patches[n].from) - 1; if (offset < 0 || offset > 255) { int nn; start = patches[n].from; ret[start - 1]++; /* Translate insn to LONG */ ret[start] = offset; bcopy (&ret[start + 1], &ret[start + 2], buf_siz - start); ret[start + 1] = offset >> 8; need_relax--; buf_siz++; for (nn = 0; nn < patches_used; nn++) { if (patches[nn].from > start) patches[nn].from++; if (patches[nn].to > start) patches[nn].to++; if (patches[nn].from < start && patches[nn].to > start && ret[patches[nn].from]++ == 255) { if (ret[patches[nn].from - 1] & 01) ret[patches[nn].from + 1]++; else need_relax++; } } } } } } (void) obstack_free (&tmp_mem, tmp_mem_start); patches_used = 0; return ret; } /* Back when strings stored a char*, they needed to be freed when a byte-compiled expression was freed. Now that they're appended to the end, they don't need to be specially freed anymore. */ void byte_free (form) unsigned char *form; { /* no longer needed unsigned char *f; for(f=form;*f;f++) { switch(*f) { case IF: case F_IF: case SKIP: case AND: case OR: case CONST_STR: f++; break; case CONST_INT: f+=sizeof(long); break; case CONST_FLT: f+=sizeof(double); break; case VAR: f+=sizeof(struct var *); break; case R_CELL: case R_CELL|ROWREL: case R_CELL|COLREL: case R_CELL|ROWREL|COLREL: f+=EXP_ADD; break; case RANGE: case RANGE|LRREL: case RANGE|LRREL|LCREL: case RANGE|LRREL|LCREL|HCREL: case RANGE|LRREL|HCREL: case RANGE|LRREL|HRREL: case RANGE|LRREL|HRREL|LCREL: case RANGE|LRREL|HRREL|LCREL|HCREL: case RANGE|LRREL|HRREL|HCREL: case RANGE|HRREL: case RANGE|HRREL|LCREL: case RANGE|HRREL|LCREL|HCREL: case RANGE|HRREL|HCREL: case RANGE|LCREL: case RANGE|LCREL|HCREL: case RANGE|HCREL: f+=EXP_ADD_RNG; break; case F_PRINTF: case F_CONCAT: case F_ONEOF: case F_STRSTR: case F_EDIT: case AREA_SUM: case AREA_PROD: case AREA_AVG: case AREA_STD: case AREA_MAX: case AREA_MIN: case AREA_CNT: case AREA_VAR: f++; break; default: break; } } */ free (form); } /* This tries to tell if a byte-compiled expression is a constant. If it is a constant, we can free it, and never try to recompute its value. This returns non-zero if the expression is constant.*/ int is_constant (bytes) unsigned char *bytes; { /* It's constant, but it's already been dealt with. Pretend it isn't. */ if (!bytes) return 0; switch (bytes[0]) { case CONST_ERR: return (bytes[3] == 0 && !strcmp ((char *) bytes + 4, ename[bytes[2]])); case CONST_INT: return bytes[sizeof (long) + 1] == ENDCOMP; case CONST_FLT: return bytes[sizeof (double) + 1] == ENDCOMP; case CONST_STR: return bytes[2] == ENDCOMP; case F_TRUE: case F_FALSE: case CONST_INF: case CONST_NINF: case CONST_NAN: return bytes[1] == ENDCOMP; default: return 0; } }