301 lines
8.9 KiB
C
301 lines
8.9 KiB
C
/***********************************************************************/
|
|
/* */
|
|
/* OCaml */
|
|
/* */
|
|
/* Xavier Leroy, projet Cristal, INRIA Rocquencourt */
|
|
/* */
|
|
/* Copyright 1996 Institut National de Recherche en Informatique et */
|
|
/* en Automatique. All rights reserved. This file is distributed */
|
|
/* under the terms of the GNU Library General Public License, with */
|
|
/* the special exception on linking described in file ../LICENSE. */
|
|
/* */
|
|
/***********************************************************************/
|
|
|
|
/* The PDA automaton for parsers generated by camlyacc */
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include "caml/config.h"
|
|
#include "caml/mlvalues.h"
|
|
#include "caml/memory.h"
|
|
#include "caml/alloc.h"
|
|
|
|
#define ERRCODE 256
|
|
|
|
struct parser_tables { /* Mirrors parse_tables in ../stdlib/parsing.mli */
|
|
value actions;
|
|
value transl_const;
|
|
value transl_block;
|
|
char * lhs;
|
|
char * len;
|
|
char * defred;
|
|
char * dgoto;
|
|
char * sindex;
|
|
char * rindex;
|
|
char * gindex;
|
|
value tablesize;
|
|
char * table;
|
|
char * check;
|
|
value error_function;
|
|
char * names_const;
|
|
char * names_block;
|
|
};
|
|
|
|
struct parser_env { /* Mirrors parser_env in ../stdlib/parsing.ml */
|
|
value s_stack;
|
|
value v_stack;
|
|
value symb_start_stack;
|
|
value symb_end_stack;
|
|
value stacksize;
|
|
value stackbase;
|
|
value curr_char;
|
|
value lval;
|
|
value symb_start;
|
|
value symb_end;
|
|
value asp;
|
|
value rule_len;
|
|
value rule_number;
|
|
value sp;
|
|
value state;
|
|
value errflag;
|
|
};
|
|
|
|
#if defined(ARCH_BIG_ENDIAN) || SIZEOF_SHORT != 2
|
|
#define Short(tbl,n) \
|
|
(*((unsigned char *)((tbl) + (n) * 2)) + \
|
|
(*((signed char *)((tbl) + (n) * 2 + 1)) << 8))
|
|
#else
|
|
#define Short(tbl,n) (((short *)(tbl))[n])
|
|
#endif
|
|
|
|
int caml_parser_trace = 0;
|
|
|
|
/* Input codes */
|
|
/* Mirrors parser_input in ../stdlib/parsing.ml */
|
|
#define START 0
|
|
#define TOKEN_READ 1
|
|
#define STACKS_GROWN_1 2
|
|
#define STACKS_GROWN_2 3
|
|
#define SEMANTIC_ACTION_COMPUTED 4
|
|
#define ERROR_DETECTED 5
|
|
|
|
/* Output codes */
|
|
/* Mirrors parser_output in ../stdlib/parsing.ml */
|
|
#define READ_TOKEN Val_int(0)
|
|
#define RAISE_PARSE_ERROR Val_int(1)
|
|
#define GROW_STACKS_1 Val_int(2)
|
|
#define GROW_STACKS_2 Val_int(3)
|
|
#define COMPUTE_SEMANTIC_ACTION Val_int(4)
|
|
#define CALL_ERROR_FUNCTION Val_int(5)
|
|
|
|
/* To preserve local variables when communicating with the ML code */
|
|
|
|
#define SAVE \
|
|
env->sp = Val_int(sp), \
|
|
env->state = Val_int(state), \
|
|
env->errflag = Val_int(errflag)
|
|
|
|
#define RESTORE \
|
|
sp = Int_val(env->sp), \
|
|
state = Int_val(env->state), \
|
|
errflag = Int_val(env->errflag)
|
|
|
|
/* Auxiliary for printing token just read */
|
|
|
|
static char * token_name(char * names, int number)
|
|
{
|
|
for (/*nothing*/; number > 0; number--) {
|
|
if (names[0] == 0) return "<unknown token>";
|
|
names += strlen(names) + 1;
|
|
}
|
|
return names;
|
|
}
|
|
|
|
static void print_token(struct parser_tables *tables, int state, value tok)
|
|
{
|
|
value v;
|
|
|
|
if (Is_long(tok)) {
|
|
fprintf(stderr, "State %d: read token %s\n",
|
|
state, token_name(tables->names_const, Int_val(tok)));
|
|
} else {
|
|
fprintf(stderr, "State %d: read token %s(",
|
|
state, token_name(tables->names_block, Tag_val(tok)));
|
|
v = Field(tok, 0);
|
|
if (Is_long(v))
|
|
fprintf(stderr, "%" ARCH_INTNAT_PRINTF_FORMAT "d", Long_val(v));
|
|
else if (Tag_val(v) == String_tag)
|
|
fprintf(stderr, "%s", String_val(v));
|
|
else if (Tag_val(v) == Double_tag)
|
|
fprintf(stderr, "%g", Double_val(v));
|
|
else
|
|
fprintf(stderr, "_");
|
|
fprintf(stderr, ")\n");
|
|
}
|
|
}
|
|
|
|
/* The pushdown automata */
|
|
|
|
CAMLprim value caml_parse_engine(struct parser_tables *tables,
|
|
struct parser_env *env, value cmd, value arg)
|
|
{
|
|
int state;
|
|
mlsize_t sp, asp;
|
|
int errflag;
|
|
int n, n1, n2, m, state1;
|
|
|
|
switch(Int_val(cmd)) {
|
|
|
|
case START:
|
|
state = 0;
|
|
sp = Int_val(env->sp);
|
|
errflag = 0;
|
|
|
|
loop:
|
|
n = Short(tables->defred, state);
|
|
if (n != 0) goto reduce;
|
|
if (Int_val(env->curr_char) >= 0) goto testshift;
|
|
SAVE;
|
|
return READ_TOKEN;
|
|
/* The ML code calls the lexer and updates */
|
|
/* symb_start and symb_end */
|
|
case TOKEN_READ:
|
|
RESTORE;
|
|
if (Is_block(arg)) {
|
|
env->curr_char = Field(tables->transl_block, Tag_val(arg));
|
|
caml_modify(&env->lval, Field(arg, 0));
|
|
} else {
|
|
env->curr_char = Field(tables->transl_const, Int_val(arg));
|
|
caml_modify(&env->lval, Val_long(0));
|
|
}
|
|
if (caml_parser_trace) print_token(tables, state, arg);
|
|
|
|
testshift:
|
|
n1 = Short(tables->sindex, state);
|
|
n2 = n1 + Int_val(env->curr_char);
|
|
if (n1 != 0 && n2 >= 0 && n2 <= Int_val(tables->tablesize) &&
|
|
Short(tables->check, n2) == Int_val(env->curr_char)) goto shift;
|
|
n1 = Short(tables->rindex, state);
|
|
n2 = n1 + Int_val(env->curr_char);
|
|
if (n1 != 0 && n2 >= 0 && n2 <= Int_val(tables->tablesize) &&
|
|
Short(tables->check, n2) == Int_val(env->curr_char)) {
|
|
n = Short(tables->table, n2);
|
|
goto reduce;
|
|
}
|
|
if (errflag > 0) goto recover;
|
|
SAVE;
|
|
return CALL_ERROR_FUNCTION;
|
|
/* The ML code calls the error function */
|
|
case ERROR_DETECTED:
|
|
RESTORE;
|
|
recover:
|
|
if (errflag < 3) {
|
|
errflag = 3;
|
|
while (1) {
|
|
state1 = Int_val(Field(env->s_stack, sp));
|
|
n1 = Short(tables->sindex, state1);
|
|
n2 = n1 + ERRCODE;
|
|
if (n1 != 0 && n2 >= 0 && n2 <= Int_val(tables->tablesize) &&
|
|
Short(tables->check, n2) == ERRCODE) {
|
|
if (caml_parser_trace)
|
|
fprintf(stderr, "Recovering in state %d\n", state1);
|
|
goto shift_recover;
|
|
} else {
|
|
if (caml_parser_trace){
|
|
fprintf(stderr, "Discarding state %d\n", state1);
|
|
}
|
|
if (sp <= Int_val(env->stackbase)) {
|
|
if (caml_parser_trace){
|
|
fprintf(stderr, "No more states to discard\n");
|
|
}
|
|
return RAISE_PARSE_ERROR; /* The ML code raises Parse_error */
|
|
}
|
|
sp--;
|
|
}
|
|
}
|
|
} else {
|
|
if (Int_val(env->curr_char) == 0)
|
|
return RAISE_PARSE_ERROR; /* The ML code raises Parse_error */
|
|
if (caml_parser_trace) fprintf(stderr, "Discarding last token read\n");
|
|
env->curr_char = Val_int(-1);
|
|
goto loop;
|
|
}
|
|
|
|
shift:
|
|
env->curr_char = Val_int(-1);
|
|
if (errflag > 0) errflag--;
|
|
shift_recover:
|
|
if (caml_parser_trace)
|
|
fprintf(stderr, "State %d: shift to state %d\n",
|
|
state, Short(tables->table, n2));
|
|
state = Short(tables->table, n2);
|
|
sp++;
|
|
if (sp < Long_val(env->stacksize)) goto push;
|
|
SAVE;
|
|
return GROW_STACKS_1;
|
|
/* The ML code resizes the stacks */
|
|
case STACKS_GROWN_1:
|
|
RESTORE;
|
|
push:
|
|
Field(env->s_stack, sp) = Val_int(state);
|
|
caml_modify(&Field(env->v_stack, sp), env->lval);
|
|
Store_field (env->symb_start_stack, sp, env->symb_start);
|
|
Store_field (env->symb_end_stack, sp, env->symb_end);
|
|
goto loop;
|
|
|
|
reduce:
|
|
if (caml_parser_trace)
|
|
fprintf(stderr, "State %d: reduce by rule %d\n", state, n);
|
|
m = Short(tables->len, n);
|
|
env->asp = Val_int(sp);
|
|
env->rule_number = Val_int(n);
|
|
env->rule_len = Val_int(m);
|
|
sp = sp - m + 1;
|
|
m = Short(tables->lhs, n);
|
|
state1 = Int_val(Field(env->s_stack, sp - 1));
|
|
n1 = Short(tables->gindex, m);
|
|
n2 = n1 + state1;
|
|
if (n1 != 0 && n2 >= 0 && n2 <= Int_val(tables->tablesize) &&
|
|
Short(tables->check, n2) == state1) {
|
|
state = Short(tables->table, n2);
|
|
} else {
|
|
state = Short(tables->dgoto, m);
|
|
}
|
|
if (sp < Long_val(env->stacksize)) goto semantic_action;
|
|
SAVE;
|
|
return GROW_STACKS_2;
|
|
/* The ML code resizes the stacks */
|
|
case STACKS_GROWN_2:
|
|
RESTORE;
|
|
semantic_action:
|
|
SAVE;
|
|
return COMPUTE_SEMANTIC_ACTION;
|
|
/* The ML code calls the semantic action */
|
|
case SEMANTIC_ACTION_COMPUTED:
|
|
RESTORE;
|
|
Field(env->s_stack, sp) = Val_int(state);
|
|
caml_modify(&Field(env->v_stack, sp), arg);
|
|
asp = Int_val(env->asp);
|
|
Store_field (env->symb_end_stack, sp, Field(env->symb_end_stack, asp));
|
|
if (sp > asp) {
|
|
/* This is an epsilon production. Take symb_start equal to symb_end. */
|
|
Store_field (env->symb_start_stack, sp, Field(env->symb_end_stack, asp));
|
|
}
|
|
goto loop;
|
|
|
|
default: /* Should not happen */
|
|
Assert(0);
|
|
return RAISE_PARSE_ERROR; /* Keeps gcc -Wall happy */
|
|
}
|
|
|
|
}
|
|
|
|
/* Control printing of debugging info */
|
|
|
|
CAMLprim value caml_set_parser_trace(value flag)
|
|
{
|
|
value oldflag = Val_bool(caml_parser_trace);
|
|
caml_parser_trace = Bool_val(flag);
|
|
return oldflag;
|
|
}
|