/**********************************************************************
 
	Copyright (C) 2003 Hirohisa MORI <joshua@nichibun.ac.jp>
 
	This program is free software; you can redistribute it 
	and/or modify it under the terms of the GLOBALBASE 
	Library General Public License (G-LGPL) as published by 

	http://www.globalbase.org/
 
	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.

**********************************************************************/



#include	<stdio.h>
#include	"memory_debug.h"
#include	"regular.h"

typedef struct at_header {
	int		type;
#define ATT_CHAR	1
#define ATT_STAR	2
#define ATT_PLUS	3
#define ATT_QST		4
#define ATT_OR		5
#define ATT_AND		6
} AT_HEADER;

typedef struct at_char {
	AT_HEADER	h;
	L_CHAR		from;
	L_CHAR		to;
} AT_CHAR;

typedef struct at_star {
	AT_HEADER	h;
	union a_tree *	target;
} AT_STAR;

typedef struct at_plus {
	AT_HEADER	h;
	union a_tree *	target;
} AT_PLUS;

typedef struct at_qst {
	AT_HEADER	h;
	union a_tree *	target;
} AT_QST;

typedef struct at_or {
	AT_HEADER	h;
	union a_tree *	a;
	union a_tree *	b;
} AT_OR;

typedef struct at_and {
	AT_HEADER	h;
	union a_tree *	a;
	union a_tree *	b;
} AT_AND;

typedef union a_tree {
	AT_HEADER		h;
	AT_CHAR			ch;
	AT_STAR			s;
	AT_STAR			q;
	AT_PLUS			p;
	AT_OR			or;
	AT_AND			and;
} A_TREE;

typedef struct a_input {
	struct a_input *	next;
	_L_CHAR			from;
	_L_CHAR			to;
} A_INPUT;

AUTOMATON *
make_nfa(A_TREE * at);


void
free_a_tree(A_TREE * a)
{
A_TREE * _a;
	switch ( a->h.type ) {
	case ATT_CHAR:
		break;
	case ATT_STAR:
		free_a_tree(a->s.target);
		break;
	case ATT_PLUS:
		free_a_tree(a->p.target);
		break;
	case ATT_QST:
		free_a_tree(a->q.target);
		break;
	case ATT_OR:
		for ( _a = a; _a->h.type == ATT_OR ; _a = _a->or.a ) {
			free_a_tree(_a->or.b);
		}
		free_a_tree(_a);
		break;
	case ATT_AND:
		for ( _a = a ; _a->h.type == ATT_AND ; _a = _a->and.a )
			free_a_tree(_a->and.b);
		free_a_tree(_a);
		break;
	default:
		er_panic("free_a_type");
	}
	d_f_ree(a);
}


int
skip_punc(L_CHAR * rex,int * ip,L_CHAR ch1,L_CHAR ch2)
{
int depth;
	depth = 0;
	for ( ; ; (*ip) ++ ) {
		if ( rex[*ip] == 0 )
			break;
		else if ( rex[*ip] == '\\' )
			(*ip)++;
		else if ( rex[*ip] == ch1 )
			depth ++;
		else if ( rex[*ip] == ch2 ) {
			depth --;
			if ( depth == 0 ) {
				(*ip) ++;
				return 0;
			}
		}
	}
	if ( depth )
		return -1;
	return 0;
}


A_TREE *
char_a_tree(L_CHAR from,L_CHAR to)
{
A_TREE * at;
	at = d_alloc(sizeof(*at));
	at->h.type = ATT_CHAR;
	at->ch.from = from;
	at->ch.to = to;
	return at;
}


A_TREE *
star_a_tree(A_TREE * a)
{
A_TREE * ret;
	if ( a == 0 )
		return 0;
	ret = d_alloc(sizeof(*ret));
	ret->h.type = ATT_STAR;
	ret->s.target = a;
	return ret;
}

A_TREE *
q_a_tree(A_TREE * a)
{
A_TREE * ret;
	if ( a == 0 )
		return 0;
	ret = d_alloc(sizeof(*ret));
	ret->h.type = ATT_QST;
	ret->q.target = a;
	return ret;
}

A_TREE *
plus_a_tree(A_TREE * a)
{
A_TREE * ret;
	if ( a == 0 )
		return 0;
	ret = d_alloc(sizeof(*ret));
	ret->h.type = ATT_PLUS;
	ret->p.target = a;
	return ret;
}

A_TREE *
and_a_tree(A_TREE * a,A_TREE * b)
{
A_TREE * ret;
	if ( a == 0 )
		return b;
	if ( b == 0 ) {
		free_a_tree(a);
		return 0;
	}
	ret = d_alloc(sizeof(*ret));
	ret->h.type = ATT_AND;
	ret->and.a = a;
	ret->and.b = b;
	return ret;
}

A_TREE * 
or_a_tree(A_TREE * a,A_TREE * b)
{
A_TREE * ret;
	if ( b == 0 ) {
		free_a_tree(a);
		return 0;
	}
	if ( a == 0 )
		return b;
	ret = d_alloc(sizeof(*ret));
	ret->h.type = ATT_OR;
	ret->or.a = a;
	ret->or.b = b;
	return ret;
}

A_TREE *
char_class_a_tree(L_CHAR * rex)
{
int i;
A_TREE * at;
	at = 0;

	for ( i = 0 ; ; ) {
		if ( rex[i] == 0 )
			break;
		switch ( rex[i+1] ) {
		case 0:
			return or_a_tree(at,
				char_a_tree(rex[i],rex[i]));
		case '-':
			if ( rex[i+2] == 0 )
				goto err;
			at = or_a_tree(at,
				char_a_tree(rex[i],rex[i+2]));
			i += 3;
			break;
		default:
			at = or_a_tree(at,
				char_a_tree(rex[i],rex[i]));
			i ++;
			break;
		}
	}
	return at;
err:
	free_a_tree(at);
	return 0;
}

A_TREE *
make_a_tree(L_CHAR * rex)
{
L_CHAR * buf;
int i,st,len;
A_TREE * at,* at2;
	len = l_strlen(rex);
	at = 0;
	for ( i = 0 ; rex[i] ; ) {
		switch ( rex[i] ) {
		case '\\':
			if ( i+1 >= len )
				goto err;
			switch ( rex[i+1] ) {
			case 'n':
				at = and_a_tree(at,
					char_a_tree('\n','\n'));
				break;
			case 'r':
				at = and_a_tree(at,
					char_a_tree('\r','\r'));
				break;
			case 't':
				at = and_a_tree(at,
					char_a_tree('\t','\t'));
				break;
			default:
				at = and_a_tree(at,
					char_a_tree(rex[i+1],rex[i+1]));
			}
			if ( at == 0 )
				goto err;
			i += 2;
			break;
		case '|':
			if ( i == 0 )
				goto err;
			if ( i+1 >= len )
				goto err;
			return or_a_tree(at,
				make_a_tree(&rex[i+1]));
		case '[':
			st = i;
			if ( skip_punc(rex,&i,'[',']') < 0 )
				goto err;
			buf = d_alloc(sizeof(L_CHAR)*(i-st));
			memcpy(buf,&rex[st+1],sizeof(L_CHAR)*(i-st-2));
			buf[i-st-2] = 0;
			at = and_a_tree(at,char_class_a_tree(buf));
			d_f_ree(buf);
			break;
		case '(':
			st = i;
			if ( skip_punc(rex,&i,'(',')') < 0 )
				goto err;
			buf = d_alloc(sizeof(L_CHAR)*(i-st));
			memcpy(buf,&rex[st+1],sizeof(L_CHAR)*(i-st-2));
			buf[i-st-2] = 0;
			at = and_a_tree(at,make_a_tree(buf));
			break;
		case '.':
			at = and_a_tree(at,
				or_a_tree(
				or_a_tree(
				char_a_tree(0x80000000,0xdffeffff),
				char_a_tree(0xe0000000,0xffffffff)),
				char_a_tree(0x20,0x7fffffff)));
			i ++;
			break;
		case '*':
			if ( at == 0 )
				goto def;
			if ( at->h.type == ATT_AND )
				at->and.b = star_a_tree(at->and.b);
			else	at = star_a_tree(at);
			i ++;
			break;
		case '?':
			if ( at == 0 )
				goto def;
			if ( at->h.type == ATT_AND )
				at->and.b = q_a_tree(at->and.b);
			else	at = q_a_tree(at);
			i ++;
			break;
		case '+':
			if ( at == 0 )
				goto def;
			if ( at->h.type == ATT_AND )
				at->and.b = plus_a_tree(at->and.b);
			else	at = plus_a_tree(at);
			i ++;
			break;
		default:
		def:
			at = and_a_tree(at,
					char_a_tree(rex[i],rex[i]));
			i ++;
			break;
		}
	}
	return at;
err:
	if ( at )
		free_a_tree(at);
	return 0;
}


void
print_a_tree(A_TREE * at)
{
	if ( at == 0 ) {
		printf(".");
		return;
	}
	switch ( at->h.type ) {
	case ATT_CHAR:
		printf("[%c(%x)-%c(%x)]",
			at->ch.from,at->ch.from,
			at->ch.to,at->ch.to);
		break;
	case ATT_STAR:
		printf("(");
		print_a_tree(at->s.target);
		printf(")*");
		break;
	case ATT_PLUS:
		printf("(");
		print_a_tree(at->p.target);
		printf(")+");
		break;
	case ATT_AND:
		printf("(");
		print_a_tree(at->and.a);
		print_a_tree(at->and.b);
		printf(")");
		break;
	case ATT_OR:
		printf("(");
		print_a_tree(at->and.a);
		printf("|");
		print_a_tree(at->and.b);
		printf(")");
		break;
	default:
		er_panic("print_a_tree");
	}
}

void
print_ss(A_STATE_SET * ss)
{
	printf("{");
	for ( ; ss ; ss = ss->next )
		printf("%x:",ss->state);
	printf("}");
}

void
print_automaton(AUTOMATON * a)
{
A_STATE * s;
int size;
A_GOTO * g;
	printf("AUTOMATON\n");
	for ( s = a->state_list ; s ; s = s->next ) {
		printf(" %x %x",s,s->flags);
		if ( s->state_set )
			print_ss(s->state_set);
		printf("[");
		size = s->table_size;
		g = s->table;
		for ( ; size ; size -- , g ++ )
			printf("('%c'(%x)-'%c'(%x) : %x)",
				g->from,g->from,
				g->to,g->to,
				g->next_state);
		printf("]\n");
	}
	printf("AUTOMATON END\n");
}

AUTOMATON * 
new_automaton()
{
AUTOMATON * ret;
	ret = d_alloc(sizeof(*ret));
	ret->state_list = 0;
	return ret;
}


A_STATE_SET *
new_state_set(A_STATE * s)
{
A_STATE_SET * ret;
	ret = d_alloc(sizeof(*ret));
	ret->state = s;
	ret->next = 0;
	return ret;
}

void
free_state_set(A_STATE_SET * ss)
{
A_STATE_SET * ss1;
	for ( ; ss ; ) {
		ss1 = ss->next;
		d_f_ree(ss);
		ss = ss1;
	}
}

A_STATE *
new_state(AUTOMATON * a)
{
A_STATE * ret;
	ret = d_alloc(sizeof(*ret));
	ret->table_size = 0;
	ret->table = 0;
	ret->flags = 0;
	ret->work_state = 0;
	ret->state_set = 0;
	ret->next = a->state_list;
	a->state_list = ret;
	return ret;
}

A_STATE *
search_state(AUTOMATON * a,int flags)
{
A_STATE * ret;
	for ( ret = a->state_list ; ret ; ret = ret->next )
		if ( ret->flags & flags )
			return ret;
	return 0;
}

AUTOMATON *
copy_automaton(AUTOMATON * a)
{
AUTOMATON * ret;
A_STATE * s,* s1;
int size;
A_GOTO * g;
	ret = new_automaton();
	for ( s = a->state_list ; s ; s = s->next ) {
		s1 = d_alloc(sizeof(*s1));
		s1->state_set = 0;
		s1->flags = s->flags;
		s1->table_size = s->table_size;
		s1->table = d_alloc(sizeof(A_GOTO)*s->table_size);
		memcpy(s1->table,s->table,sizeof(A_GOTO)*s->table_size);
		s->work_state = s1;

		s1->next = ret->state_list;
		ret->state_list = s1;
	}
	for ( s = ret->state_list ; s ; s = s->next ) {
		g = s->table;
		for ( size = s->table_size ; size ; size -- , g ++ )
			g->next_state = g->next_state->work_state;
	}
	return ret;
}

void
set_goto(A_STATE * s1,L_CHAR from,L_CHAR to,A_STATE * s2)
{
A_GOTO * g;
	if ( s1->table_size == 0 ) {
		s1->table_size = 1;
		g = s1->table = d_alloc(sizeof(*g));
	}
	else {
		s1->table_size ++;
		s1->table = d_re_alloc(s1->table,
				sizeof(*g)*s1->table_size);
		g = &s1->table[s1->table_size-1];
	}
	g->from = from;
	g->to = to;
	g->next_state = s2;
}


void
marge_automaton(AUTOMATON * a1,AUTOMATON * a2)
{
A_STATE * s1;
	if ( a1->state_list == 0 ) {
		a1->state_list = a2->state_list;
		return;
	}
	for ( s1 = a1->state_list ; s1->next ; s1 = s1->next );
	s1->next = a2->state_list;
	d_f_ree(a2);
	return;
}

void
free_automaton(AUTOMATON * a)
{
A_STATE * s, * s2;
	if ( a == 0 )
		return; 
	for ( s = a->state_list ; s ;) {
		s2 = s->next;
		if ( s->table )
			d_f_ree(s->table);
		free_state_set(s->state_set);
		d_f_ree(s);
		s = s2;
	}
	d_f_ree(a);
}

AUTOMATON *
make_a_char(A_TREE * at)
{
AUTOMATON * a;
A_STATE * s1, * s2;
	a = new_automaton();
	s1 = new_state(a);
	s1->flags = AF_INIT;
	s2 = new_state(a);
	s2->flags = AF_ACCEPT;
	set_goto(s1,at->ch.from,at->ch.to,s2);
	return a;
}


AUTOMATON *
make_a_star(A_TREE * at)
{
AUTOMATON * a;
A_STATE * s1, * s2;
	a = make_nfa(at->s.target);
	if ( a == 0 )
		return 0;
	s1 = search_state(a,AF_INIT);
	s2 = search_state(a,AF_ACCEPT);
	set_goto(s2,0,0,s1);
	set_goto(s1,0,0,s2);
	return a;
}

AUTOMATON *
make_a_q(A_TREE * at)
{
AUTOMATON * a;
A_STATE * s1, * s2;
	a = make_nfa(at->q.target);
	if ( a == 0 )
		return 0;
	s1 = search_state(a,AF_INIT);
	s2 = search_state(a,AF_ACCEPT);
	set_goto(s1,0,0,s2);
	return a;
}

AUTOMATON *
make_a_plus(A_TREE * at)
{
AUTOMATON * a1, * a2;
A_STATE * s1, * s2, * s3;
	a1 = make_nfa(at->p.target);
	if ( a1 == 0 )
		return 0;
	a2 = copy_automaton(a1);
	s1 = search_state(a2,AF_INIT);
	s2 = search_state(a2,AF_ACCEPT);
	set_goto(s2,0,0,s1);
	set_goto(s1,0,0,s2);

	s3 = search_state(a1,AF_ACCEPT);
	set_goto(s3,0,0,s1);
	s3->flags = 0;
	s1->flags = 0;

	marge_automaton(a1,a2);
	return a1;
}

AUTOMATON *
make_a_and(A_TREE * at)
{
AUTOMATON * a1, * a2;
A_STATE * s1, * s2;
	a1 = make_nfa(at->and.a);
	if ( a1 == 0 )
		return 0;
	a2 = make_nfa(at->and.b);
	if ( a2 == 0 ) {
		free_automaton(a1);
		return 0;
	}
	s1 = search_state(a1,AF_ACCEPT);
	s2 = search_state(a2,AF_INIT);
	set_goto(s1,0,0,s2);
	s1->flags = 0;
	s2->flags = 0;

	marge_automaton(a1,a2);
	return a1;
}

AUTOMATON *
make_a_or(A_TREE * at)
{
AUTOMATON * a1, * a2;
A_STATE * s1, * s2, * s3, * s4;
A_STATE * init, * acc;
	a1 = make_nfa(at->and.a);
	if ( a1 == 0 )
		return 0;
	a2 = make_nfa(at->and.b);
	if ( a2 == 0 ) {
		free_automaton(a1);
		return 0;
	}

	s1 = search_state(a1,AF_INIT);
	s2 = search_state(a1,AF_ACCEPT);
	s3 = search_state(a2,AF_INIT);
	s4 = search_state(a2,AF_ACCEPT);

	s1->flags = 0;
	s2->flags = 0;
	s3->flags = 0;
	s4->flags = 0;

	marge_automaton(a1,a2);

	init = new_state(a1);
	init->flags = AF_INIT;
	set_goto(init,0,0,s1);
	set_goto(init,0,0,s3);

	acc = new_state(a1);
	acc->flags = AF_ACCEPT;
	set_goto(s2,0,0,acc);
	set_goto(s4,0,0,acc);

	return a1;
}

AUTOMATON *
make_nfa(A_TREE * at)
{
	switch ( at->h.type ) {
	case ATT_CHAR:
		return make_a_char(at);
	case ATT_STAR:
		return make_a_star(at);
	case ATT_QST:
		return make_a_q(at);
	case ATT_PLUS:
		return make_a_plus(at);
	case ATT_AND:
		return make_a_and(at);
	case ATT_OR:
		return make_a_or(at);
	default:
		er_panic("make_automaton(1)");
	}
	return 0;
}


void
init_flags(AUTOMATON *nfa,A_STATE_SET * ss)
{
A_STATE * s;
	for ( s = nfa->state_list ; s ; s = s->next )
		s->flags &= ~(AF_CHECK1|AF_CHECK2);
	for ( ; ss ; ss = ss->next )
		ss->state->flags |= AF_CHECK1;
}

A_STATE_SET *
get_state_set(AUTOMATON * nfa,int flags)
{
A_STATE_SET * ret, * ss1;
A_STATE * s;
	ret = 0;
	for ( s = nfa->state_list ; s ; s = s->next ) {
		if ( !(s->flags & flags ) )
			continue;
		ss1 = d_alloc(sizeof(*ss1));
		ss1->state = s;
		ss1->next = ret;
		ret = ss1;
	}
	return ret;
}

A_STATE_SET *
get_empty_closure(AUTOMATON * nfa,A_STATE_SET * ss)
{
A_STATE * s;
int f;
A_GOTO * g;
A_STATE_SET * ret, * ss1;
int size;
	init_flags(nfa,ss);
	f = 1;
	for ( ; f ; ) {
		f = 0;
		for ( s = nfa->state_list ; s ; s = s->next ) {
			if ( !(s->flags & AF_CHECK1) )
				continue;
			g = s->table;
			for ( size = s->table_size ; size ; size -- ,
					g ++ ) {
				if ( g->to || g->from )
					continue;
				if ( g->next_state->flags & AF_CHECK1 )
					continue;
				g->next_state->flags |= AF_CHECK1;
				f = 1;
			}
		}
	}
	return get_state_set(nfa,AF_CHECK1);
}

A_STATE_SET *
get_goto(AUTOMATON * nfa,A_STATE_SET * ss,_L_CHAR from,_L_CHAR to)
{
int f;
int size;
A_GOTO * g;
A_STATE_SET * ret, * ss1;
A_STATE * s;
	init_flags(nfa,ss);
	for ( s = nfa->state_list ; s ; s = s->next ) {
		if ( !(s->flags & AF_CHECK1) )
			continue;
		g = s->table;
		size = s->table_size;
		for ( ; size ; size -- , g ++ ) {
			if ( to < g->from )
				continue;
			if ( from > g->to )
				continue;
			g->next_state->flags |= AF_CHECK2;
		}
	}
	ret = get_state_set(nfa,AF_CHECK2);
	return ret;
}


_L_CHAR
get_from(AUTOMATON * nfa,_L_CHAR to)
{
A_STATE * s;
A_GOTO * g;
int size;
_L_CHAR from;
int f;
	from = 0;
	f = 0;
	for ( s = nfa->state_list ; s ; s = s->next ) {
		size = s->table_size;
		g = s->table;
		for ( ; size ; g ++ , size -- ) {
			if ( g->to == 0 && g->from == 0 )
				continue;
			if ( g->to < to ) {
				if ( f == 0 || from < g->to+1 ) {
					from = g->to+1;
					f = 1;
				}
			}
			if ( g->from <= to ) {
				if ( f == 0 || from < g->from ) {
					from = g->from;
					f = 1;
				}
			}
		}
	}
	if ( f == 0 )
		return 1;
	return from;
}

A_INPUT *
get_input_list(AUTOMATON * nfa)
{
A_INPUT * ret, * inp;
_L_CHAR from,to;
	ret = 0;
	to = LC_MAX;
	for ( ; ; ) {
		inp = d_alloc(sizeof(*inp));
		inp->to = to;
		inp->from = get_from(nfa,to);
		inp->next = ret;
		ret = inp;
		to = inp->from-1;
		if ( inp->from == 1 )
			break;
	}
	return ret;
}

void
free_input_list(A_INPUT * inp)
{
A_INPUT * i1;
	for ( ; inp ; ) {
		i1 = inp->next;
		d_f_ree(inp);
		inp = i1;
	}
}

A_STATE *
exist_check(AUTOMATON * a,A_STATE_SET * ss)
{
A_STATE * s;
A_STATE_SET * ss1, * ss2;
	for ( s = a->state_list ; s ; s = s->next ) {
		for ( ss1 = ss , ss2 = s->state_set ;
			ss1 && ss2 ; ss1 = ss1->next , ss2 = ss2->next )
			if ( ss1->state != ss2->state )
				goto next;
		if ( ss1 || ss2 )
			goto next;
		return s;
	next:	;
	}
	return 0;
}

void
print_input(A_INPUT * inp)
{
	for ( ; inp ; inp = inp->next )
		printf("(%x-%x)",inp->from,inp->to);
	printf("\n");
}

AUTOMATON *
get_dfa(AUTOMATON * nfa)
{
A_INPUT * inp, * i1;
A_STATE * init, * s1, * s2;
A_STATE_SET * ss;
AUTOMATON * ret;
int f;

int cnt;
	inp = get_input_list(nfa);

	ret = new_automaton();
	s1 = new_state(ret);

	init = search_state(nfa,AF_INIT);
	ss = new_state_set(init);
	s1->state_set = get_empty_closure(nfa,ss);
	s1->flags = AF_INIT;
	free_state_set(ss);
	f = 1;
	for ( ; f ; ) {
		f = 0;
		for ( s1 = ret->state_list ; s1 ; s1 = s1->next ) {
			if ( s1->flags & AF_CHECK1 )
				continue;
			s1->flags |= AF_CHECK1;
			for ( i1 = inp ; i1 ; i1 = i1->next ) {
				ss = get_empty_closure(nfa,
					get_goto(nfa,
						s1->state_set,
						i1->from,
						i1->to));
				if ( ss == 0 )
					continue;
				s2 = exist_check(ret,ss);
				if ( s2 == 0 ) {
					s2 = new_state(ret);
					s2->state_set = ss;
				}
			next:
				set_goto(s1,i1->from,i1->to,s2);
			}
			f = 1;
		}
	}
	for ( s1 = ret->state_list ; s1 ; s1 = s1->next ) {
		for ( ss = s1->state_set ; ss ; ss = ss->next ) {
			if ( ss->state->flags & AF_ACCEPT ) {
				s1->flags |= AF_ACCEPT;
				break;
			}
		}
	}
	free_input_list(inp);
	return ret;
}

AUTOMATON *
get_fa(L_CHAR * str)
{
A_TREE * at;
AUTOMATON * a1, * a2;

	at = make_a_tree(str);
	if ( at == 0 )
		return 0;
	a1 = make_nfa(at);
	free_a_tree(at);
	if ( a1 == 0 )
		return 0;
	a2 = get_dfa(a1);
	free_automaton(a1);
	return a2;
}


A_STATE *
get_next_state(A_STATE * s,_L_CHAR ch)
{
A_GOTO * g;
int size;
	g = s->table;
	size = s->table_size;
	for (  ; size ; g ++ , size -- ) {
		if ( g->from <= ch && ch <= g->to )
			return g->next_state;
	}
	return 0;
}

int
parse_dfa_str(int * min_ptr,int * max_ptr,AUTOMATON * dfa,L_CHAR * str,
		int min_match_flag)
{
A_STATE * s;
int p;

	*min_ptr = -1;
	*max_ptr = -1;
	s = search_state(dfa,AF_INIT);
	if ( s == 0 )
		er_panic("parse_dfa_str");
	for ( p = 0 ; str[p] ; p ++ ) {
		if ( s->flags & AF_ACCEPT ) {
			if ( *min_ptr < 0 )
				*min_ptr = p;
			*max_ptr = p;
			if ( min_match_flag )
				return p;
		}
		s = get_next_state(s,str[p]);
		if ( s == 0 )
			return -1;
	}
	if ( s->flags & AF_ACCEPT ) {
		if ( *min_ptr < 0 )
			*min_ptr = p;
		*max_ptr = p;
		return p;
	}
	return -1;
}
