/* // Dao Standard Modules // http://www.daovm.net // // Copyright (c) 2014-2016, Limin Fu // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES // OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT // SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT // OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // 2014-01: Danilov Aleksey, initial implementation. #include"dao_scanner.h" DaoScanner* DaoScanner_New( DaoType *type ) { DaoCstruct *cstruct = DaoCstruct_New( type, sizeof(DaoScanner) ); DaoScanner *self = (DaoScanner*) cstruct; self->regex = NULL; self->context = NULL; self->pos = 0; self->start = -1; self->end = -1; return self; } void DaoScanner_Delete( DaoScanner *self ) { if ( self->context ) DString_Delete( self->context ); DaoCstruct_Delete( (DaoCstruct*) self ); } static void DaoScanner_Create( DaoProcess *proc, DaoValue *p[], int N ) { DaoType *retype = DaoProcess_GetReturnType( proc ); DaoScanner *self = DaoScanner_New( retype ); DString *str = p[0]->xString.value; dao_integer pos = p[1]->xInteger.value; if ( pos < 0 ) pos = str->size + pos; self->pos = ( pos > str->size )? str->size : pos; self->context = DString_New(); DString_Assign( self->context, str ); DaoProcess_PutValue( proc, (DaoValue*) self ); } static void DaoScanner_Context( DaoProcess *proc, DaoValue *p[], int N ) { DaoScanner *self = (DaoScanner*) p[0]; DaoProcess_PutString( proc, self->context ); } static void DaoScanner_Position( DaoProcess *proc, DaoValue *p[], int N ) { DaoScanner *self = (DaoScanner*) p[0]; DaoProcess_PutInteger( proc, self->pos ); } static void DaoScanner_SetPos( DaoProcess *proc, DaoValue *p[], int N ) { DaoScanner *self = (DaoScanner*) p[0]; dao_integer pos = p[1]->xInteger.value; if ( pos < 0 ) pos = self->context->size + pos; self->pos = ( pos > self->context->size )? self->context->size : pos; } static void DaoScanner_Rest( DaoProcess *proc, DaoValue *p[], int N ) { DaoScanner *self = (DaoScanner*) p[0]; DaoProcess_PutInteger( proc, self->context->size - self->pos ); } static void DaoScanner_Append( DaoProcess *proc, DaoValue *p[], int N ) { DaoScanner *self = (DaoScanner*) p[0]; DString_Append( self->context, p[1]->xString.value ); } static void DaoScanner_FetchPeek( DaoProcess *proc, DaoValue *p[], int fetch ) { DaoScanner *self = (DaoScanner*) p[0]; dao_integer count = p[1]->xInteger.value; DString *sub; if ( count < 0 ){ DaoProcess_RaiseError( proc, "Param", "Invalid number of bytes" ); return; } sub = DString_New(); if ( self->pos < self->context->size ){ if ( self->pos + count > self->context->size ) count = self->context->size - self->pos; DString_SubString( self->context, sub, self->pos, count ); if ( fetch ) self->pos += count; } DaoProcess_PutString( proc, sub ); DString_Delete( sub ); } static void DaoScanner_Fetch( DaoProcess *proc, DaoValue *p[], int N ) { DaoScanner_FetchPeek( proc, p, 1 ); } static void DaoScanner_Peek( DaoProcess *proc, DaoValue *p[], int N ) { DaoScanner_FetchPeek( proc, p, 0 ); } static void DaoScanner_ScanSeek( DaoProcess *proc, DaoValue *p[], int seek ) { DaoScanner *self = (DaoScanner*) p[0]; DString *pt = p[1]->xString.value; int del = 0; daoint res = 0; if ( !seek && pt->size ){ int i; for ( i = 0; i < pt->size && isspace( pt->chars[i] ); i++ ); if ( i >= pt->size || pt->chars[i] != '^' ){ pt = DString_NewChars( "^" ); DString_Append( pt, p[1]->xString.value ); del = 1; } } self->regex = DaoProcess_MakeRegex( proc, pt ); if ( del ) DString_Delete( pt ); if ( self->regex == NULL ) return; if ( self->pos < self->context->size ){ self->start = self->pos; self->end = self->context->size - 1; if ( DaoRegex_Match( self->regex, self->context, &self->start, &self->end ) ){ res = self->end - self->pos + 1; self->pos = self->end + 1; } } else { self->start = -1; self->end = -1; } DaoProcess_PutInteger( proc, res ); } static void DaoScanner_Scan( DaoProcess *proc, DaoValue *p[], int N ) { DaoScanner_ScanSeek( proc, p, 0 ); } static void DaoScanner_Seek( DaoProcess *proc, DaoValue *p[], int N ) { DaoScanner_ScanSeek( proc, p, 1 ); } static void DaoScanner_Matched( DaoProcess *proc, DaoValue *p[], int N ) { DaoScanner *self = (DaoScanner*) p[0]; dao_integer group = p[1]->xInteger.value; DString *res = DString_New(); if ( self->regex && self->start >= 0 ){ if ( group ){ daoint start = self->start, end = self->end; if ( DaoRegex_SubMatch( self->regex, group, &start, &end ) ) DString_SubString( self->context, res, start, end - start + 1 ); } else DString_SubString( self->context, res, self->start, self->end - self->start + 1 ); } DaoProcess_PutString( proc, res ); DString_Delete( res ); } static void DaoScanner_MatchedAt( DaoProcess *proc, DaoValue *p[], int N ) { DaoScanner *self = (DaoScanner*) p[0]; dao_integer group = p[1]->xInteger.value; if ( self->regex && self->start >= 0 ){ DaoTuple *res = DaoProcess_PutTuple( proc, 2 ); res->values[0]->xInteger.value = -1; res->values[1]->xInteger.value = self->context->size; if ( group ){ daoint start = self->start, end = self->end; if ( DaoRegex_SubMatch( self->regex, group, &start, &end ) ){ res->values[0]->xInteger.value = start; res->values[1]->xInteger.value = end; } } else { res->values[0]->xInteger.value = self->start; res->values[1]->xInteger.value = self->end; } } else DaoProcess_PutNone( proc ); } static void DaoScanner_Line( DaoProcess *proc, DaoValue *p[], int N ) { DaoScanner *self = (DaoScanner*) p[0]; daoint res = 1, i; for ( i = 0; i < self->pos; i++ ) if ( self->context->chars[i] == '\n' ) res++; DaoProcess_PutInteger( proc, res ); } static void DaoScanner_Follows( DaoProcess *proc, DaoValue *p[], int N ) { DaoScanner *self = (DaoScanner*) p[0]; DaoRegex *reg; DString *pt = p[1]->xString.value; int del = 0; daoint res = 0; if ( pt->size ){ int i; for ( i = pt->size - 1; i >= 0 && isspace( pt->chars[i] ); i-- ); if ( i < 0 || pt->chars[i] != '$' ){ pt = DString_Copy( pt ); DString_AppendChar( pt, '$' ); del = 1; } } reg = DaoProcess_MakeRegex( proc, pt ); if ( del ) DString_Delete( pt ); if ( self->regex == NULL ) return; if ( self->pos > 0 || self->start > 0 ){ daoint start = 0, end = ( self->start <= 0 ? self->pos : self->start ) - 1; if ( DaoRegex_Match( reg, self->context, &start, &end ) ) res = 1; } DaoProcess_PutBoolean( proc, res ); } static void DaoScanner_Precedes( DaoProcess *proc, DaoValue *p[], int N ) { DaoScanner *self = (DaoScanner*) p[0]; DaoRegex *reg; DString *pt = p[1]->xString.value; int del = 0; daoint res = 0; if ( pt->size ){ int i; for ( i = 0; i < pt->size && isspace( pt->chars[i] ); i++ ); if ( i >= pt->size || pt->chars[i] != '^' ){ pt = DString_NewChars( "^" ); DString_Append( pt, p[1]->xString.value ); del = 1; } } reg = DaoProcess_MakeRegex( proc, pt ); if ( del ) DString_Delete( pt ); if ( self->regex == NULL ) return; if ( self->end >= 0 || self->pos < self->context->size ){ daoint start = self->end >= 0? self->end + 1 : self->pos, end = self->context->size - 1; if ( DaoRegex_Match( reg, self->context, &start, &end ) ) res = 1; } DaoProcess_PutBoolean( proc, res ); } static DaoFunctionEntry daoScannerMeths[] = { /*! Constructs scanner operating on string \a context starting at position \a pos */ { DaoScanner_Create, "Scanner(context: string, pos = 0) => Scanner" }, /*! String being scanned */ { DaoScanner_Context, ".context(invar self: Scanner) => string" }, /*! Current position */ { DaoScanner_Position, ".pos(invar self: Scanner) => int" }, { DaoScanner_SetPos, ".pos=(self: Scanner, value: int)" }, /*! Number of bytes left to scan (from the current position to the end of the context) */ { DaoScanner_Rest, ".rest(invar self: Scanner) => int" }, /*! Appends \a str to the context */ { DaoScanner_Append, "append(self: Scanner, str: string)" }, /*! Returns \a count bytes starting from the current position and advances the scanner */ { DaoScanner_Fetch, "fetch(self: Scanner, count: int) => string" }, /*! Returns \a count bytes starting from the current position without advancing the scanner */ { DaoScanner_Peek, "peek(invar self: Scanner, count: int) => string" }, /*! Mathes \a pattern immediately at the current position; on success, the scanner is advanced and its last match information is updated. * Returns the number of bytes the scanner has advanced through */ { DaoScanner_Scan, "scan(self: Scanner, pattern: string) => int" }, /*! Mathes \a pattern anywhere in the string after the current position; on success, the scanner is advanced and its last match * information is updated. Returns the number of bytes the scanner has advanced through */ { DaoScanner_Seek, "seek(self: Scanner, pattern: string) => int" }, /*! The last matched sub-string or its group \a group (if \a group is greater then zero) */ { DaoScanner_Matched, "matched(invar self: Scanner, group = 0) => string" }, /*! Position of the last matched sub-string or its group \a group (if \a group is greater then zero) */ { DaoScanner_MatchedAt, "matchedPos(invar self: Scanner, group = 0) => tuple|none" }, /*! Line number at the current position */ { DaoScanner_Line, "line(invar self: Scanner) => int" }, /*! Matches \a pattern immediately before the current position without affecting the state of the scanner */ { DaoScanner_Follows, "follows(invar self: Scanner, pattern: string) => bool" }, /*! Matches \a pattern immediately after the current position without affecting the state of the scanner */ { DaoScanner_Precedes, "precedes(invar self: Scanner, pattern: string) => bool" }, { NULL, NULL } }; /*! Provides way to successively process textual data using Dao string patterns */ DaoTypeCore daoScannerCore = { "Scanner", /* name */ sizeof(DaoScanner), /* size */ { NULL }, /* bases */ { NULL }, /* casts */ NULL, /* numbers */ daoScannerMeths, /* methods */ DaoCstruct_CheckGetField, DaoCstruct_DoGetField, /* GetField */ DaoCstruct_CheckSetField, DaoCstruct_DoSetField, /* SetField */ NULL, NULL, /* GetItem */ NULL, NULL, /* SetItem */ NULL, NULL, /* Unary */ NULL, NULL, /* Binary */ NULL, NULL, /* Conversion */ NULL, NULL, /* ForEach */ NULL, /* Print */ NULL, /* Slice */ NULL, /* Compare */ NULL, /* Hash */ NULL, /* Create */ NULL, /* Copy */ (DaoDeleteFunction) DaoScanner_Delete, /* Delete */ NULL /* HandleGC */ }; DAO_DLL int DaoScanner_OnLoad( DaoVmSpace *vmSpace, DaoNamespace *ns ) { DaoNamespace *strns = DaoVmSpace_GetNamespace( vmSpace, "str" ); DaoNamespace_AddConstValue( ns, "str", (DaoValue*)strns ); DaoNamespace_WrapType( strns, & daoScannerCore, DAO_CSTRUCT, 0 ); return 0; }