#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "INLINE.h"
// The transition. There are no explicit states, just linked
// lists of transitions.
typedef struct TRANS *trans;
typedef struct TRANS {
char splitchar;
trans next_trans;
trans next_state;
} Trans;
typedef struct FSM *fsm;
typedef struct FSM {
trans root;
int terminals;
int transitions;
int states;
int maxpath;
char* ignore;
char* boundary;
char* inclboundary ;
char* charclasses;
char* wild; // all chars which match a wildcard
AV* found_keys;
AV* found_offsets;
AV* found_vals;
int position;
bool use_wildcards;
bool squeezeblanks;
char* s; // string on which to match
} Fsm;
// function declarations (prototypes) necessary for
// mutually recursive functions
int _eat_wild_chars(fsm this, int matchlen, trans p);
int _find_match(fsm this, int matchlen, trans p);
// For vector records
#define BIT_ON(vec, offset, pos) \
( *(vec + \
((unsigned char) offset*32) + \
(unsigned char)pos/8) |= \
(1 << ((unsigned char)pos % 8)) \
)
#define IS_BIT_ON(vec, offset, pos) \
( *(vec+((unsigned char) offset*32) + \
(unsigned char)pos/8) & \
(1 << ((unsigned char)pos % 8)) \
)
_malloc(fsm m) {
av_clear(m->found_keys);
av_clear(m->found_offsets);
av_clear(m->found_vals);
}
// Place the top transition where it belongs (in the order \0,*,[others])
trans _demote(trans parent){
trans child, top = parent;
if( !(child = parent->next_trans) ) return top;
if( !parent->splitchar ) return top;
if( !child->splitchar ){
parent->next_trans = child->next_trans;
child->next_trans = parent;
top = child;
}
if( !(child = parent->next_trans) ) return top;
if( child->splitchar == '*' ){
parent->next_trans = child->next_trans;
child->next_trans = parent;
if( top != parent ) top->next_trans = child;
else top = child;
}
return top;
}
trans _insert_(fsm m, trans p, char *s, SV* val) {
trans t = p;
// going to be a new state with one transition.
//if (p == 0 && *s) m->states++;
if(p == 0) m->states++;
// search for *s in transition list (state) t
while(t){
// if(*s == t->splitchar) break;
if(IS_BIT_ON(m->charclasses, *s, t->splitchar)) break;
else t = t->next_trans;
}
// *s transition not in current state? Make a new one, place
// it at the top of the list. (but keep terminals, wilds at top)
if(!t){
m->transitions++;
t = (trans) malloc(sizeof(Trans));
t->splitchar = *s;
t->next_state = 0;
t->next_trans = p;
// flip terminal state and/or wildcard p to top
p = _demote(t);
}
// continue inserting the rest of the string. If there is no more
// string, place the SV* val into this termination transition.
if(*s){
t->next_state = _insert_(m, t->next_state, ++s, val);
}
else {
if(t->next_state)
sv_2mortal((SV*)t->next_state);
else
m->terminals++;
t->next_state = (trans) val;
}
return p;
}
// unused, too slow
/*
void _cleanup_(trans p) {
if (p) {
_cleanup_(p->next_trans);
if(!p->splitchar){
sv_2mortal( (SV*) p->next_state);
}
else
_cleanup_(p->next_state);
}
//free(p);
}
*/
int _search(trans root, char *s) {
trans p = root;
while (p) {
while(p)
if(p->splitchar == *s) break;
else p = p->next_trans;
if(!p) return 0;
if(!p->splitchar) return 1;
p = p->next_state;
s++;
}
return 0;
}
// Return the node representing the char s, if it exists, from this list.
trans _bsearch( char* vec, char s, trans q ){
while(q){
if(IS_BIT_ON(vec, s, q->splitchar))
break;
else
q = q->next_trans;
}
return q;
}
void _record_match(fsm this, int matchlen, trans p){
SV* val = (SV*) p->next_state;
av_push(this->found_keys, newSVpvn(this->s,matchlen+1));
av_push(this->found_offsets, newSViv(this->position));
av_push(this->found_vals, val);
SvREFCNT_inc(val);
}
int _eat_wild_chars(fsm this, int matchlen, trans p){
char* t = this->s + matchlen;
int wildlength = 0;
while( IS_BIT_ON( this->wild, 0, *t ) ){
t++;
wildlength++;
if(wildlength > 256) return 0;
}
matchlen += wildlength;
p = p->next_state;
return _find_match(this, matchlen, p);
}
int _find_match(fsm this, int matchlen, trans p){
// These items are invariant through a complete recursive call.
//this->s (document to match)
//this->position (position in document where s starts)
//this->found_keys (perl list of found text)
//this->found_offsets (perl list of offsets for found text)
//this->found_vals (perl list of found values stored in fsm)
int depth = matchlen;
char* t = this->s + matchlen; //starting point for this match
while(p){
// if this is a termination state
if(!p->splitchar){
if( IS_BIT_ON( this->boundary, 0, *t )
|| IS_BIT_ON( this->inclboundary,0,*t)
){
matchlen = depth - 1;
_record_match(this, matchlen, p);
}
p = p->next_trans;
}
// ignore irrelevant chars
while( IS_BIT_ON(this->ignore, 0, *t) ){
t++;
depth += 1;
}
// squeeze spaces cough cough
if(this->squeezeblanks && IS_BIT_ON(this->charclasses, ' ', *t))
while(IS_BIT_ON(this->charclasses, ' ', *(t+1))){
t++;
depth++;
}
// find wildcard matches
if(p && p->splitchar == '*' && this->use_wildcards)
matchlen = _eat_wild_chars(this, depth, p);
// search for t
p = _bsearch( this->charclasses, *t, p );
if(p){
t++;
depth++;
p = p->next_state;
}
}
return matchlen;
}
void _scan(fsm this, char *s) {
int match = 0;
int position = 0;
int cue = 0;
while(*s){
this->s = s;
this->position = position;
match = _find_match(this, 0, this->root);
// truncate s by single char to match everything possible
if(match){ s++; position++; }
// cue up the first possible match:
// [boundary][ignore]*[firstmatchchar]/
if( cue = _cue(this, s) ){
position += cue;
s += cue; }
else
break;
match = 0;
}
}
int _cue( fsm this, char *s ){
int position = 0;
while(*s){
//Move to the first boundary char
//or stop in case the char is an inclboundary
// When we are in a word, we dont want to skip the chars who
// could be included boundary
// But we still want to skip at least to the next char,
while( ! IS_BIT_ON(this->boundary, 0, *s) ) {
s++; position++;
if ( IS_BIT_ON(this->inclboundary,0,*s) ) { break ;}
}
// chop off the first boundary only if not an included boundary
if(*s != 0
&& ! IS_BIT_ON(this->inclboundary,0,*s)
) { s++; position++; }
// move past any irrelevant chars
while( IS_BIT_ON(this->ignore, 0, *s) ) { s++; position++; }
// found a starting match point?
if( (int) _bsearch(this->charclasses, *s, this->root) ){
return position;
}
}
return 0;
}
void _dump(fsm m, trans p, char* k, int depth) {
if (!p) return;
_dump(m, p->next_trans, k, depth);
if (p->splitchar){
*(k+depth) = p->splitchar;
_dump(m, p->next_state, k, depth+1);
}
else {
av_push(m->found_keys, newSVpvn(k, depth));
av_push(m->found_vals, (SV*)p->next_state);
SvREFCNT_inc((SV*)p->next_state);
}
}
void _init_charclasses(char* vecs){
int i;
for(i=0;i<256;i++){
// For the ith 256-bit span, turn on bit i
BIT_ON( vecs, i, i );
}
}
// Default pattern boundary is EOS (null) and space (' ')
void _init_boundary(char* vec){
BIT_ON( vec, 0, 0 );
BIT_ON( vec, 0, (int) ' ' );
}
void _init_inclboundary(char* vec ){
}
void _init_wild(char* vec){
int i;
for(i=1;i<256;i++){
// All non-space chars match wilds by default
if( !isspace(i) )
BIT_ON( vec, 0, i );
}
}
SV* new(char* class){
fsm m = (fsm) malloc( sizeof(Fsm) );
SV* obj_ref = newSViv(0);
SV* obj = newSVrv(obj_ref, class);
m->root = 0;
m->terminals = 0;
m->transitions = 0;
m->states = 0;
m->maxpath = 0;
m->ignore = (char*) calloc(256/sizeof(char), sizeof(char));
m->boundary = (char*) calloc(256/sizeof(char), sizeof(char));
m->wild = (char*) calloc(256/sizeof(char), sizeof(char));
m->inclboundary = (char*) calloc(256/sizeof(char), sizeof(char));
m->charclasses = (char*) calloc((256*256)/sizeof(char), sizeof(char));
_init_inclboundary(m->inclboundary);
_init_boundary(m->boundary);
_init_charclasses(m->charclasses);
_init_wild(m->wild);
m->found_keys = (AV*) newAV();
m->found_offsets = (AV*) newAV();
m->found_vals = (AV*) newAV();
m->use_wildcards = FALSE;
m->squeezeblanks = FALSE;
sv_setiv(obj, (IV)m);
SvREADONLY_on(obj);
return obj_ref;
}
void DESTROY(SV* obj){
fsm m = (fsm)SvIV(SvRV(obj));
// takes *far* too long compared to OS garbage collection.
// _cleanup_(m->root);
// free(m->charclasses);
// free(m->ignore);
// free(m);
}
void usewild(SV* obj){
fsm m = (fsm)SvIV(SvRV(obj));
m->use_wildcards = TRUE;
}
void squeezeblanks(SV* obj){
fsm m = (fsm)SvIV(SvRV(obj));
m->squeezeblanks = TRUE;
}
// This must be called before any insert(), but may be called
// any number of times.
void charclass(SV* obj, char* vecstring){
fsm m = (fsm)SvIV(SvRV(obj));
char* i = vecstring;
char* j = vecstring;
char* vec = m->charclasses;
while(*i){
while(*j){
// For the ith 256-bit span, turn on the jth bit
BIT_ON( vec, *i, *j);
j++;
}
j = vecstring;
i++;
}
}
void ignore(SV* obj, char* vecstring){
fsm m = (fsm)SvIV(SvRV(obj));
char* i = vecstring;
for(; *i; i++ )
BIT_ON( m->ignore, 0, *i);
// "ignore" chars also count as boundaries
i = vecstring;
for(; *i; i++ )
BIT_ON( m->boundary, 0, *i );
// "ignore" chars also match wildcards
i = vecstring;
for(; *i; i++ )
BIT_ON( m->wild, 0, *i );
}
void ignorecase(SV* obj){
charclass(obj, "Aa");
charclass(obj, "Bb");
charclass(obj, "Cc");
charclass(obj, "Dd");
charclass(obj, "Ee");
charclass(obj, "Ff");
charclass(obj, "Gg");
charclass(obj, "Hh");
charclass(obj, "Ii");
charclass(obj, "Jj");
charclass(obj, "Kk");
charclass(obj, "Ll");
charclass(obj, "Mm");
charclass(obj, "Nn");
charclass(obj, "Oo");
charclass(obj, "Pp");
charclass(obj, "Qq");
charclass(obj, "Rr");
charclass(obj, "Ss");
charclass(obj, "Tt");
charclass(obj, "Uu");
charclass(obj, "Vv");
charclass(obj, "Ww");
charclass(obj, "Xx");
charclass(obj, "Yy");
charclass(obj, "Zz");
}
// define class of chars that qualify as beginning/ending of patterns
// meaning, what is allowed to occur right before or after pattern
void boundary(SV* obj, char* b){
fsm m = (fsm)SvIV(SvRV(obj));
int i;
// Reset boundary to none
for( i=0; i<(256/sizeof(char)); i++ )
*(m->boundary + i) = 0;
BIT_ON( m->boundary, 0, 0 );
// Special case: if null string specified, all chars are boundary
if(!*b)
for( i=0; i<256; i++ )
BIT_ON( m->boundary, 0, i );
// Otherwise set the specified chars as boundary
else
for(; *b; b++ )
BIT_ON( m->boundary, 0, *b );
}
void inclboundary(SV* obj, char* b){
fsm m = (fsm)SvIV(SvRV(obj));
int i;
// Reset boundary to none
for( i=0; i<(256/sizeof(char)); i++ )
*(m->inclboundary + i) = 0;
if(!*b){ return ; }
for(; *b; b++ )
BIT_ON( m->inclboundary, 0, *b );
}
int insert(SV* obj, SV* key, SV* val) {
fsm m = (fsm)SvIV(SvRV(obj));
//Don't make a copy of the key, but do make one of the value
SV* v = newSVsv( val );
char* s = SvPV_nolen( key );
int keylen = strlen(s);
if(keylen > m->maxpath) m->maxpath = keylen;
if(keylen == 0) return 1;
m->root = _insert_(m, m->root, s, v);
return 1;
}
int has(SV* obj, char *s) {
fsm m = (fsm)SvIV(SvRV(obj));
return _search(m->root, s);
}
SV* val(SV* obj, char *s) {
fsm m = (fsm)SvIV(SvRV(obj));
trans p = m->root;
while (p) {
while(p)
if(p->splitchar == *s) break;
else p = p->next_trans;
if(!p) return &PL_sv_undef;
if(!p->splitchar) return newSVsv((SV*) p->next_state);
p = p->next_state;
s++;
}
return &PL_sv_undef;
}
void dump(SV* obj){
fsm m = (fsm)SvIV(SvRV(obj));
int i;
SV** ptr;
char *k;
INLINE_STACK_VARS;
k = (char*) malloc(sizeof(char) * m->maxpath);
_malloc(m);
_dump(m, m->root, k, 0);
free(k);
INLINE_STACK_RESET;
for (i = 0; i <= av_len(m->found_keys); i++) {
ptr = av_fetch(m->found_keys, i, 0);
INLINE_STACK_PUSH(sv_2mortal(newSVsv(*ptr)));
ptr = av_fetch(m->found_vals, i, 0);
INLINE_STACK_PUSH(sv_2mortal(newSVsv(*ptr)));
}
INLINE_STACK_DONE;
}
void keys(SV* obj){
fsm m = (fsm)SvIV(SvRV(obj));
int i;
SV** ptr;
char *k;
INLINE_STACK_VARS;
k = (char*) malloc(sizeof(char) * m->maxpath);
_malloc(m);
_dump(m, m->root, k, 0);
free(k);
INLINE_STACK_RESET;
for (i = 0; i <= av_len(m->found_keys); i++) {
ptr = av_fetch(m->found_keys, i, 0);
INLINE_STACK_PUSH(sv_2mortal(newSVsv(*ptr)));
}
INLINE_STACK_DONE;
}
void values(SV* obj){
fsm m = (fsm)SvIV(SvRV(obj));
int i;
SV** ptr;
char *k;
INLINE_STACK_VARS;
k = (char*) malloc(sizeof(char) * m->maxpath);
_malloc(m);
_dump(m, m->root, k, 0);
free(k);
INLINE_STACK_RESET;
for (i = 0; i <= av_len(m->found_vals); i++) {
ptr = av_fetch(m->found_vals, i, 0);
INLINE_STACK_PUSH(sv_2mortal(newSVsv(*ptr)));
}
INLINE_STACK_DONE;
}
int states(SV* obj){
fsm m = (fsm)SvIV(SvRV(obj));
return m->states;
}
int transitions(SV* obj){
fsm m = (fsm)SvIV(SvRV(obj));
return m->transitions;
}
int terminals(SV* obj){
fsm m = (fsm)SvIV(SvRV(obj));
return m->terminals;
}
void scan(SV* obj, char *s) {
fsm m = (fsm)SvIV(SvRV(obj));
int i;
SV** ptr;
INLINE_STACK_VARS;
_malloc(m);
_scan(m, s);
INLINE_STACK_RESET;
for (i = 0; i <= av_len(m->found_keys); i++) {
ptr = av_fetch(m->found_keys, i, 0);
INLINE_STACK_PUSH(sv_2mortal(newSVsv(*ptr)));
ptr = av_fetch(m->found_vals, i, 0);
INLINE_STACK_PUSH(sv_2mortal(newSVsv(*ptr)));
}
INLINE_STACK_DONE;
}
void mindex(SV* obj, char *s) {
fsm m = (fsm)SvIV(SvRV(obj));
int i;
SV** ptr;
INLINE_STACK_VARS;
_malloc(m);
_scan(m, s);
INLINE_STACK_RESET;
for (i = 0; i <= av_len(m->found_keys); i++) {
ptr = av_fetch(m->found_keys, i, 0);
INLINE_STACK_PUSH(sv_2mortal(newSVsv(*ptr)));
ptr = av_fetch(m->found_offsets, i, 0);
INLINE_STACK_PUSH(sv_2mortal(newSVsv(*ptr)));
}
INLINE_STACK_DONE;
}
void multiscan(SV* obj, char *s) {
fsm m = (fsm)SvIV(SvRV(obj));
int i;
SV** ptr;
AV* result; // holds one result (3-item array)
INLINE_STACK_VARS;
_malloc(m);
_scan(m, s);
INLINE_STACK_RESET;
for (i = 0; i <= av_len(m->found_keys); i++) {
result = (AV*) newAV();
ptr = av_fetch(m->found_keys, i, 0);
av_push(result, (SV*) newSVsv(*ptr));
ptr = av_fetch(m->found_offsets, i, 0);
av_push(result, (SV*) newSVsv(*ptr));
ptr = av_fetch(m->found_vals, i, 0);
av_push(result, (SV*) newSVsv(*ptr));
INLINE_STACK_PUSH(sv_2mortal(newRV_noinc((SV*)result)));
}
INLINE_STACK_DONE;
}
// This inline uses variables in context
#define RECORD_STATE \
i = 1; \
if(front->splitchar == 0){ \
s = (char *)SvPV_nolen( (SV*)front->next_state ); \
len = strlen(s); \
PerlIO_write( valfp, &len, sizeof(unsigned int)); \
PerlIO_write( valfp, s, sizeof(char) * len); \
} \
while(front->next_trans){ \
*(tlist+i) = front->splitchar; \
i++; \
front = front->next_trans; \
pos++; \
} \
*(tlist+i) = front->splitchar; \
*tlist = i; \
PerlIO_write(statefp, tlist, sizeof(char) * (size_t) i+1); \
// Record the trie and its values to disk, to be reloaded
// at another time. For this to work, all values must be either
// numbers or strings, ie Perl scalars but no references.
// tlist - String to record the transition list for each state
// tvector - bit vector of transition positions, to record the
// end-of-state position so the trie can be recreated.
int _serialize(SV* obj, char *triename, char *valsname){
fsm m = (fsm)SvIV(SvRV(obj));
trans front, back;
PerlIO *statefp, *valfp;
int pos, len, i;
char *tlist = (char*) malloc(sizeof(char) * 255);
char *tvector =
(char*) calloc((size_t)ceil(m->transitions/8), sizeof(char));
char *s;
if( !(statefp = PerlIO_open(triename, "wb"))){ return errno; }
if( !(valfp = PerlIO_open(valsname, "wb"))){ return errno; }
PerlIO_write( statefp, &m->terminals, sizeof(int));
PerlIO_write( statefp, &m->transitions, sizeof(int));
PerlIO_write( statefp, &m->states, sizeof(int));
PerlIO_write( statefp, &m->maxpath, sizeof(int));
PerlIO_write( statefp, &m->use_wildcards, sizeof(bool));
// execute breadth-first traversal of the trie
// recording the positions of state-ending transitions
// in the bit vector for later reconstruction.
front = back = m->root;
pos = 0;
while( front ){
// record state [and value if present] at front,
// move front to end of state. Increment pos by len of state
RECORD_STATE;
BIT_ON(tvector, 0, pos);
if(!back){ break; } //the end
front->next_trans = back->next_state;
front = front->next_trans;
pos++;
back = back->next_trans;
while(back && back->splitchar == 0){
back = back->next_trans;
}
}
// Now repair the trie, severing horizontal links between states
front = m->root;
for( pos=0; pos < m->transitions; pos++ ){
if(IS_BIT_ON(tvector, 0, pos)){
back = front;
front = front->next_trans;
back->next_trans = 0;
}
else {
front = front->next_trans;
}
}
PerlIO_close(statefp);
PerlIO_close(valfp);
return 0;
}
// Add a value back into the trie. Called from restore()
void _restore_val(trans t, PerlIO* valfp){
unsigned int len = 0;
char* s;
PerlIO_read(valfp, &len, sizeof(unsigned int));
s = (char*) malloc(len * sizeof(char));
PerlIO_read(valfp, s, sizeof(char) * len);
t->next_state = (trans) newSVpvn(s, len);
}
// Restore the serialized trie.
int _restore( SV* obj, char *triename, char *valsname ){
fsm m = (fsm)SvIV(SvRV(obj));
trans front, back, last, linker, restorer;
PerlIO *statefp, *valfp;
int j, transitions = 0, states = 0, terminals = 0;
char len;
if( !(statefp = PerlIO_open(triename, "rb")) ){ return errno; }
if( !(valfp = PerlIO_open(valsname, "rb")) ){ return errno; }
/* Read in metadata */
PerlIO_read( statefp, &m->terminals, sizeof(int));
PerlIO_read( statefp, &m->transitions, sizeof(int));
PerlIO_read( statefp, &m->states, sizeof(int));
PerlIO_read( statefp, &m->maxpath, sizeof(int));
PerlIO_read( statefp, &m->use_wildcards, sizeof(bool));
// create transitions
m->root = (trans) malloc(sizeof(Trans));
front = m->root;
while( !PerlIO_eof(statefp) ){
states++;
PerlIO_read( statefp, &len, sizeof(char) );
for( j=0; j < len; j++ ){
front->splitchar = (char) PerlIO_getc(statefp);
front->next_state = 0;
front->next_trans = (trans) malloc(sizeof(Trans));
transitions++;
last = front;
front = front->next_trans;
}
front->next_trans = 0;
front->next_state = 0;
last->next_state = front;
last->next_trans = 0;
}
last->next_state = 0;
free(front);
// link transitions appropriately
front = back = m->root;
while(back){
linker = back;
while(front->next_trans)
front = front->next_trans;
front = front->next_state;
back = back->next_trans ? back->next_trans : back->next_state;
while(back && !back->splitchar){
restorer = back;
back = back->next_trans ? back->next_trans : back->next_state;
_restore_val(restorer, valfp);
terminals++;
}
linker->next_state = front;
}
m->transitions = transitions - 1;
m->states = states - 1;
m->terminals = terminals;
PerlIO_close(valfp);
PerlIO_close(statefp);
return 0;
}
MODULE = Text::Scan PACKAGE = Text::Scan
PROTOTYPES: DISABLE
void
_init_charclasses (vecs)
char * vecs
PREINIT:
I32* temp;
PPCODE:
temp = PL_markstack_ptr++;
_init_charclasses(vecs);
if (PL_markstack_ptr != temp) {
/* truly void, because dXSARGS not invoked */
PL_markstack_ptr = temp;
XSRETURN_EMPTY; /* return empty stack */
}
/* must have used dXSARGS; list context implied */
return; /* assume stack size is correct */
void
_init_boundary (vec)
char * vec
PREINIT:
I32* temp;
PPCODE:
temp = PL_markstack_ptr++;
_init_boundary(vec);
if (PL_markstack_ptr != temp) {
/* truly void, because dXSARGS not invoked */
PL_markstack_ptr = temp;
XSRETURN_EMPTY; /* return empty stack */
}
/* must have used dXSARGS; list context implied */
return; /* assume stack size is correct */
void
_init_inclboundary (vec)
char * vec
PREINIT:
I32* temp;
PPCODE:
temp = PL_markstack_ptr++;
_init_inclboundary(vec);
if (PL_markstack_ptr != temp) {
/* truly void, because dXSARGS not invoked */
PL_markstack_ptr = temp;
XSRETURN_EMPTY; /* return empty stack */
}
/* must have used dXSARGS; list context implied */
return; /* assume stack size is correct */
void
_init_wild (vec)
char * vec
PREINIT:
I32* temp;
PPCODE:
temp = PL_markstack_ptr++;
_init_wild(vec);
if (PL_markstack_ptr != temp) {
/* truly void, because dXSARGS not invoked */
PL_markstack_ptr = temp;
XSRETURN_EMPTY; /* return empty stack */
}
/* must have used dXSARGS; list context implied */
return; /* assume stack size is correct */
SV *
new (class)
char * class
void
DESTROY (obj)
SV * obj
PREINIT:
I32* temp;
PPCODE:
temp = PL_markstack_ptr++;
DESTROY(obj);
if (PL_markstack_ptr != temp) {
/* truly void, because dXSARGS not invoked */
PL_markstack_ptr = temp;
XSRETURN_EMPTY; /* return empty stack */
}
/* must have used dXSARGS; list context implied */
return; /* assume stack size is correct */
void
usewild (obj)
SV * obj
PREINIT:
I32* temp;
PPCODE:
temp = PL_markstack_ptr++;
usewild(obj);
if (PL_markstack_ptr != temp) {
/* truly void, because dXSARGS not invoked */
PL_markstack_ptr = temp;
XSRETURN_EMPTY; /* return empty stack */
}
/* must have used dXSARGS; list context implied */
return; /* assume stack size is correct */
void
squeezeblanks (obj)
SV * obj
PREINIT:
I32* temp;
PPCODE:
temp = PL_markstack_ptr++;
squeezeblanks(obj);
if (PL_markstack_ptr != temp) {
/* truly void, because dXSARGS not invoked */
PL_markstack_ptr = temp;
XSRETURN_EMPTY; /* return empty stack */
}
/* must have used dXSARGS; list context implied */
return; /* assume stack size is correct */
void
charclass (obj, vecstring)
SV * obj
char * vecstring
PREINIT:
I32* temp;
PPCODE:
temp = PL_markstack_ptr++;
charclass(obj, vecstring);
if (PL_markstack_ptr != temp) {
/* truly void, because dXSARGS not invoked */
PL_markstack_ptr = temp;
XSRETURN_EMPTY; /* return empty stack */
}
/* must have used dXSARGS; list context implied */
return; /* assume stack size is correct */
void
ignore (obj, vecstring)
SV * obj
char * vecstring
PREINIT:
I32* temp;
PPCODE:
temp = PL_markstack_ptr++;
ignore(obj, vecstring);
if (PL_markstack_ptr != temp) {
/* truly void, because dXSARGS not invoked */
PL_markstack_ptr = temp;
XSRETURN_EMPTY; /* return empty stack */
}
/* must have used dXSARGS; list context implied */
return; /* assume stack size is correct */
void
ignorecase (obj)
SV * obj
PREINIT:
I32* temp;
PPCODE:
temp = PL_markstack_ptr++;
ignorecase(obj);
if (PL_markstack_ptr != temp) {
/* truly void, because dXSARGS not invoked */
PL_markstack_ptr = temp;
XSRETURN_EMPTY; /* return empty stack */
}
/* must have used dXSARGS; list context implied */
return; /* assume stack size is correct */
void
boundary (obj, b)
SV * obj
char * b
PREINIT:
I32* temp;
PPCODE:
temp = PL_markstack_ptr++;
boundary(obj, b);
if (PL_markstack_ptr != temp) {
/* truly void, because dXSARGS not invoked */
PL_markstack_ptr = temp;
XSRETURN_EMPTY; /* return empty stack */
}
/* must have used dXSARGS; list context implied */
return; /* assume stack size is correct */
void
inclboundary (obj, b)
SV * obj
char * b
PREINIT:
I32* temp;
PPCODE:
temp = PL_markstack_ptr++;
inclboundary(obj, b);
if (PL_markstack_ptr != temp) {
/* truly void, because dXSARGS not invoked */
PL_markstack_ptr = temp;
XSRETURN_EMPTY; /* return empty stack */
}
/* must have used dXSARGS; list context implied */
return; /* assume stack size is correct */
int
insert (obj, key, val)
SV * obj
SV * key
SV * val
int
has (obj, s)
SV * obj
char * s
SV *
val (obj, s)
SV * obj
char * s
void
dump (obj)
SV * obj
PREINIT:
I32* temp;
PPCODE:
temp = PL_markstack_ptr++;
dump(obj);
if (PL_markstack_ptr != temp) {
/* truly void, because dXSARGS not invoked */
PL_markstack_ptr = temp;
XSRETURN_EMPTY; /* return empty stack */
}
/* must have used dXSARGS; list context implied */
return; /* assume stack size is correct */
void
keys (obj)
SV * obj
PREINIT:
I32* temp;
PPCODE:
temp = PL_markstack_ptr++;
keys(obj);
if (PL_markstack_ptr != temp) {
/* truly void, because dXSARGS not invoked */
PL_markstack_ptr = temp;
XSRETURN_EMPTY; /* return empty stack */
}
/* must have used dXSARGS; list context implied */
return; /* assume stack size is correct */
void
values (obj)
SV * obj
PREINIT:
I32* temp;
PPCODE:
temp = PL_markstack_ptr++;
values(obj);
if (PL_markstack_ptr != temp) {
/* truly void, because dXSARGS not invoked */
PL_markstack_ptr = temp;
XSRETURN_EMPTY; /* return empty stack */
}
/* must have used dXSARGS; list context implied */
return; /* assume stack size is correct */
int
states (obj)
SV * obj
int
transitions (obj)
SV * obj
int
terminals (obj)
SV * obj
void
scan (obj, s)
SV * obj
char * s
PREINIT:
I32* temp;
PPCODE:
temp = PL_markstack_ptr++;
scan(obj, s);
if (PL_markstack_ptr != temp) {
/* truly void, because dXSARGS not invoked */
PL_markstack_ptr = temp;
XSRETURN_EMPTY; /* return empty stack */
}
/* must have used dXSARGS; list context implied */
return; /* assume stack size is correct */
void
mindex (obj, s)
SV * obj
char * s
PREINIT:
I32* temp;
PPCODE:
temp = PL_markstack_ptr++;
mindex(obj, s);
if (PL_markstack_ptr != temp) {
/* truly void, because dXSARGS not invoked */
PL_markstack_ptr = temp;
XSRETURN_EMPTY; /* return empty stack */
}
/* must have used dXSARGS; list context implied */
return; /* assume stack size is correct */
void
multiscan (obj, s)
SV * obj
char * s
PREINIT:
I32* temp;
PPCODE:
temp = PL_markstack_ptr++;
multiscan(obj, s);
if (PL_markstack_ptr != temp) {
/* truly void, because dXSARGS not invoked */
PL_markstack_ptr = temp;
XSRETURN_EMPTY; /* return empty stack */
}
/* must have used dXSARGS; list context implied */
return; /* assume stack size is correct */
int
_serialize (obj, triename, valsname)
SV * obj
char * triename
char * valsname
int
_restore (obj, triename, valsname)
SV * obj
char * triename
char * valsname