diff options
author | sebres <sebres@users.sourceforge.net> | 2017-01-10 22:15:36 (GMT) |
---|---|---|
committer | sebres <sebres@users.sourceforge.net> | 2017-01-10 22:15:36 (GMT) |
commit | c313bacc9f0d53d7090a7bc98b24b78ecb92d2f4 (patch) | |
tree | 541ebaaa7aa56442851f0531ae4bbaa094b9d5bc /generic | |
parent | 3f06a6ec89b41434fe38bede870563e35b809019 (diff) | |
download | tcl-c313bacc9f0d53d7090a7bc98b24b78ecb92d2f4.zip tcl-c313bacc9f0d53d7090a7bc98b24b78ecb92d2f4.tar.gz tcl-c313bacc9f0d53d7090a7bc98b24b78ecb92d2f4.tar.bz2 |
[temp-commit]: clock scan tokenizer logic ready (still needs many rules)
caching extended (currentYearCentury, yearOfCenturySwitch, lastBaseDate ...)
Diffstat (limited to 'generic')
-rw-r--r-- | generic/tclClock.c | 69 | ||||
-rw-r--r-- | generic/tclClockFmt.c | 218 | ||||
-rw-r--r-- | generic/tclDate.c | 1 | ||||
-rw-r--r-- | generic/tclDate.h | 88 | ||||
-rw-r--r-- | generic/tclGetDate.y | 1 |
5 files changed, 297 insertions, 80 deletions
diff --git a/generic/tclClock.c b/generic/tclClock.c index e00d6a2..2e7b854 100644 --- a/generic/tclClock.c +++ b/generic/tclClock.c @@ -117,6 +117,8 @@ typedef struct ClockClientData { Tcl_Obj **literals; /* Pool of object literals. */ /* Cache for current clock parameters, imparted via "configure" */ unsigned long LastTZEpoch; + int currentYearCentury; + int yearOfCenturySwitch; Tcl_Obj *SystemTimeZone; Tcl_Obj *SystemSetupTZData; Tcl_Obj *GMTSetupTimeZone; @@ -126,6 +128,9 @@ typedef struct ClockClientData { Tcl_Obj *LastUnnormSetupTimeZone; Tcl_Obj *LastSetupTimeZone; Tcl_Obj *LastSetupTZData; + /* Cache for last base (fast convert if base/tz not changed) */ + Tcl_Obj *lastBaseTimeZone; + TclDateFields lastBaseDate; /* /* [SB] TODO: back-port (from tclSE) the same date caching ... * Cache for last date (fast convert if date parsed was the same) * / @@ -325,6 +330,8 @@ TclClockInit( Tcl_IncrRefCount(data->literals[i]); } data->LastTZEpoch = 0; + data->currentYearCentury = -1; + data->yearOfCenturySwitch = -1; data->SystemTimeZone = NULL; data->SystemSetupTZData = NULL; data->GMTSetupTimeZone = NULL; @@ -335,6 +342,8 @@ TclClockInit( data->LastSetupTimeZone = NULL; data->LastSetupTZData = NULL; + data->lastBaseTimeZone = NULL; + /* * Install the commands. */ @@ -368,6 +377,8 @@ ClockConfigureClear( ClockClientData *data) { data->LastTZEpoch = 0; + data->currentYearCentury = -1; + data->yearOfCenturySwitch = -1; Tcl_UnsetObjRef(data->SystemTimeZone); Tcl_UnsetObjRef(data->SystemSetupTZData); Tcl_UnsetObjRef(data->GMTSetupTimeZone); @@ -708,11 +719,16 @@ ClockCurrentYearCentury( { ClockClientData *dataPtr = clientData; Tcl_Obj **literals = dataPtr->literals; - int year = 2000; + int year = dataPtr->currentYearCentury; - Tcl_Obj * yearObj = Tcl_ObjGetVar2(interp, - literals[LIT_CURRENTYEARCENTURY], NULL, TCL_LEAVE_ERR_MSG); - Tcl_GetIntFromObj(NULL, yearObj, &year); + if (year == -1) { + Tcl_Obj * yearObj; + year = 2000; + yearObj = Tcl_ObjGetVar2(interp, + literals[LIT_CURRENTYEARCENTURY], NULL, TCL_LEAVE_ERR_MSG); + Tcl_GetIntFromObj(NULL, yearObj, &year); + dataPtr->currentYearCentury = year; + } return year; } inline int @@ -722,11 +738,16 @@ ClockGetYearOfCenturySwitch( { ClockClientData *dataPtr = clientData; Tcl_Obj **literals = dataPtr->literals; - int year = 37; - - Tcl_Obj * yearObj = Tcl_ObjGetVar2(interp, - literals[LIT_YEAROFCENTURYSWITCH], NULL, TCL_LEAVE_ERR_MSG); - Tcl_GetIntFromObj(NULL, yearObj, &year); + int year = dataPtr->yearOfCenturySwitch; + + if (year == -1) { + Tcl_Obj * yearObj; + year = 37; + yearObj = Tcl_ObjGetVar2(interp, + literals[LIT_YEAROFCENTURYSWITCH], NULL, TCL_LEAVE_ERR_MSG); + Tcl_GetIntFromObj(NULL, yearObj, &year); + dataPtr->yearOfCenturySwitch = year; + } return year; } @@ -2557,12 +2578,27 @@ ClockScanObjCmd( if (yydate.tzData == NULL) { goto done; } - yydate.seconds = baseVal; - if (ClockGetDateFields(interp, &yydate, yydate.tzData, GREGORIAN_CHANGE_DATE) - != TCL_OK) { - goto done; + Tcl_SetObjRef(yydate.tzName, opts.timezoneObj); + + /* check cached */ + if ( dataPtr->lastBaseTimeZone == opts.timezoneObj + && dataPtr->lastBaseDate.seconds == baseVal) { + memcpy(&yydate, &dataPtr->lastBaseDate, ClockCacheableDateFieldsSize); + } else { + /* extact fields from base */ + yydate.seconds = baseVal; + if (ClockGetDateFields(interp, &yydate, yydate.tzData, GREGORIAN_CHANGE_DATE) + != TCL_OK) { + goto done; + } + /* cache last base */ + memcpy(&dataPtr->lastBaseDate, &yydate, ClockCacheableDateFieldsSize); + dataPtr->lastBaseTimeZone = opts.timezoneObj; } + /* seconds are in localSeconds (relative base date), so reset time here */ + yyHour = 0; yyMinutes = 0; yySeconds = 0; yyMeridian = MER24; + /* If free scan */ if (opts.formatObj == NULL) { /* Use compiled version of FreeScan - */ @@ -2593,7 +2629,7 @@ ClockScanObjCmd( else { /* Use compiled version of Scan - */ - ret = ClockScan(clientData, interp, &yydate, objv[1], &opts); + ret = ClockScan(clientData, interp, info, objv[1], &opts); } if (ret != TCL_OK) { @@ -2710,6 +2746,9 @@ ClockFreeScan( if (yydate.tzData == NULL) { goto done; } + + Tcl_SetObjRef(yydate.tzName, opts->timezoneObj); + } /* on demand (lazy) assemble julianDay using new year, month, etc. */ @@ -2719,8 +2758,6 @@ ClockFreeScan( * Assemble date, time, zone into seconds-from-epoch */ - Tcl_SetObjRef(yydate.tzName, opts->timezoneObj); - if (yyHaveTime == -1) { yySeconds = 0; } diff --git a/generic/tclClockFmt.c b/generic/tclClockFmt.c index 93416af..b074681 100644 --- a/generic/tclClockFmt.c +++ b/generic/tclClockFmt.c @@ -400,35 +400,48 @@ Tcl_GetClockFrmScnFromObj( (tok) = (chain) + (tokCnt); \ (tokCnt) += CLOCK_MIN_TOK_CHAIN_BLOCK_SIZE; \ } \ - *(tok) = NULL; + memset(tok, 0, sizeof(*(tok))); const char *ScnSTokenMapChars = "dmyYHMS"; -static ClockScanToken ScnSTokenMap[] = { - {CTOKT_DIGIT, 1, 2, 0}, - {CTOKT_DIGIT, 1, 2, 0}, - {CTOKT_DIGIT, 1, 2, 0}, - {CTOKT_DIGIT, 1, 4, 0}, - {CTOKT_DIGIT, 1, 2, 0}, - {CTOKT_DIGIT, 1, 2, 0}, - {CTOKT_DIGIT, 1, 2, 0}, +static ClockScanTokenMap ScnSTokenMap[] = { + {CTOKT_DIGIT, CLF_DATE, 1, 2, TclOffset(DateInfo, date.dayOfMonth), + NULL}, + {CTOKT_DIGIT, CLF_DATE, 1, 2, TclOffset(DateInfo, date.month), + NULL}, + {CTOKT_DIGIT, CLF_DATE, 1, 2, TclOffset(DateInfo, date.year), + NULL}, + {CTOKT_DIGIT, CLF_DATE, 1, 4, TclOffset(DateInfo, date.year), + NULL}, + {CTOKT_DIGIT, CLF_TIME, 1, 2, TclOffset(DateInfo, date.hour), + NULL}, + {CTOKT_DIGIT, CLF_TIME, 1, 2, TclOffset(DateInfo, date.minutes), + NULL}, + {CTOKT_DIGIT, CLF_TIME, 1, 2, TclOffset(DateInfo, date.secondOfDay), + NULL}, }; const char *ScnSpecTokenMapChars = - " %"; -static ClockScanToken ScnSpecTokenMap[] = { - {CTOKT_SPACE, 1, 0xffff, 0}, + " "; +static ClockScanTokenMap ScnSpecTokenMap[] = { + {CTOKT_SPACE, 0, 1, 0xffff, 0, + NULL}, +}; + +static ClockScanTokenMap ScnWordTokenMap = { + CTOKT_WORD, 0, 1, 0, 0, + NULL }; /* *---------------------------------------------------------------------- */ -ClockScanToken ** +ClockScanToken * ClockGetOrParseScanFormat( Tcl_Interp *interp, /* Tcl interpreter */ Tcl_Obj *formatObj) /* Format container */ { ClockFmtScnStorage *fss; - ClockScanToken **tok; + ClockScanToken *tok; if (formatObj->typePtr != &ClockFmtObjType) { if (ClockFmtObj_SetFromAny(interp, formatObj) != TCL_OK) { @@ -448,27 +461,30 @@ ClockGetOrParseScanFormat( /* if first time scanning - tokenize format */ if (fss->scnTok == NULL) { const char *strFmt; - register const char *p, *e, *cp, *word_start = NULL; + register const char *p, *e, *cp; Tcl_MutexLock(&ClockFmtMutex); fss->scnTokC = CLOCK_MIN_TOK_CHAIN_BLOCK_SIZE; fss->scnTok = tok = ckalloc(sizeof(*tok) * CLOCK_MIN_TOK_CHAIN_BLOCK_SIZE); - *tok = NULL; + memset(tok, 0, sizeof(*(tok))); strFmt = TclGetString(formatObj); for (e = p = strFmt, e += formatObj->length; p != e; p++) { switch (*p) { case '%': if (p+1 >= e) { - word_start = p; - continue; + goto word_tok; } p++; /* try to find modifier: */ switch (*p) { case '%': - word_start = p-1; + /* begin new word token - don't join with previous word token, + * because current mapping should be "...%%..." -> "...%..." */ + tok->map = &ScnWordTokenMap; + tok->tokWord.start = tok->tokWord.end = p; + AllocTokenInChain(tok, fss->scnTok, fss->scnTokC); continue; break; case 'E': @@ -480,10 +496,24 @@ ClockGetOrParseScanFormat( default: cp = strchr(ScnSTokenMapChars, *p); if (!cp || *cp == '\0') { - word_start = p-1; - continue; + p--; + goto word_tok; + } + tok->map = &ScnSTokenMap[cp - ScnSTokenMapChars]; + /* calculate look ahead value by standing together tokens */ + if (tok > fss->scnTok) { + ClockScanToken *prevTok = tok - 1; + unsigned int lookAhead = tok->map->minSize; + + while (prevTok >= fss->scnTok) { + if (prevTok->map->type != tok->map->type) { + break; + } + prevTok->lookAhead += lookAhead; + prevTok--; + } } - *tok = &ScnSTokenMap[cp - ScnSTokenMapChars]; + /* next token */ AllocTokenInChain(tok, fss->scnTok, fss->scnTokC); break; } @@ -494,18 +524,33 @@ ClockGetOrParseScanFormat( p--; goto word_tok; } - *tok = &ScnSpecTokenMap[cp - ScnSpecTokenMapChars]; + tok->map = &ScnSpecTokenMap[cp - ScnSpecTokenMapChars]; AllocTokenInChain(tok, fss->scnTok, fss->scnTokC); break; default: word_tok: - - continue; + if (1) { + ClockScanToken *wordTok = tok; + if (tok > fss->scnTok && (tok-1)->map == &ScnWordTokenMap) { + wordTok = tok-1; + } + wordTok->tokWord.end = p; + if (wordTok == tok) { + wordTok->tokWord.start = p; + wordTok->map = &ScnWordTokenMap; + AllocTokenInChain(tok, fss->scnTok, fss->scnTokC); + } + continue; + } } continue; + ext_tok_E: + /*******************/ + continue; + ext_tok_O: /*******************/ @@ -527,23 +572,134 @@ int ClockScan( ClientData clientData, /* Client data containing literal pool */ Tcl_Interp *interp, /* Tcl interpreter */ - TclDateFields *date, /* Date fields used for converting */ + register DateInfo *info, /* Date fields used for parsing & converting */ Tcl_Obj *strObj, /* String containing the time to scan */ ClockFmtScnCmdArgs *opts) /* Command options */ { - ClockScanToken **tok; + ClockScanToken *tok; + ClockScanTokenMap *map; + register const char *p, *x, *end; + unsigned short int flags = 0; + int ret = TCL_ERROR; if ((tok = ClockGetOrParseScanFormat(interp, opts->formatObj)) == NULL) { return TCL_ERROR; } + + /* prepare parsing */ + + yyMeridian = MER24; + + /* bypass spaces at begin of string */ + + p = TclGetString(strObj); + end = p + strObj->length; + while (p < end && isspace(UCHAR(*p))) { + p++; + } + info->dateStart = yyInput = p; + + /* parse string */ + for (; tok->map != NULL; yyInput = p, tok++) { + map = tok->map; + switch (map->type) + { + case CTOKT_DIGIT: + if (1) { + unsigned int val = 0; + int size = map->maxSize; + /* greedy find digits (look forward), corresponding pre-calculated lookAhead */ + size += tok->lookAhead; + x = yyInput + size; + while (isdigit(UCHAR(*p)) && p < x) { p++; }; + /* consider reserved (lookAhead) for next tokens */ + p -= tok->lookAhead; + size = p - yyInput; + if (size < map->minSize) { + /* missing input -> error */ + goto done; + } + /* string 2 number */ + p = yyInput; x = p + size; + while (p < x) { + val = val * 10 + (*p++ - '0'); + } + /* put number into info by offset */ + *(time_t *)(((char *)info) + map->offs) = val; + flags |= map->flags; + } + break; + case CTOKT_SPACE: + while (p < end && isspace(UCHAR(*p))) { + p++; + } + break; + case CTOKT_WORD: + x = tok->tokWord.start; + if (x == tok->tokWord.end) { /* single char word */ + if (*p != *x) { + /* no match -> error */ + goto done; + } + p++; + continue; + } + /* multi-char word */ + while (p < end && x < tok->tokWord.end && *p++ == *x++) {}; + if (x < tok->tokWord.end) { + /* no match -> error */ + goto done; + } + break; + } + } - //*********************************** + /* ignore spaces at end */ + while (p < end && isspace(UCHAR(*p))) { + p++; + } + /* check end was reached */ + if (p < end) { + /* something after last token - wrong format */ + goto done; + } - Tcl_SetObjResult(interp, Tcl_NewWideIntObj((Tcl_WideInt)tok)); - return TCL_OK; + /* invalidate result */ + if (flags & CLF_DATE) { + yydate.julianDay = CL_INVALIDATE; + + if (yyYear < 100) { + if (yyYear >= ClockGetYearOfCenturySwitch(clientData, interp)) { + yyYear -= 100; + } + yyYear += ClockCurrentYearCentury(clientData, interp); + } + yydate.era = CE; + if (!(flags & CLF_TIME)) { + yydate.localSeconds = 0; + } + } + + if (flags & CLF_TIME) { + yySeconds = ToSeconds(yyHour, yyMinutes, + yySeconds, yyMeridian); + } else { + yySeconds = yydate.localSeconds % 86400; + } + + ret = TCL_OK; + +done: + + if (ret != TCL_OK) { + Tcl_SetResult(interp, + "input string does not match supplied format", TCL_STATIC); + Tcl_SetErrorCode(interp, "CLOCK", "badInputString", NULL); + return ret; + } - return TCL_ERROR; + return ret; } diff --git a/generic/tclDate.c b/generic/tclDate.c index a47f43d..97d13b4 100644 --- a/generic/tclDate.c +++ b/generic/tclDate.c @@ -2691,7 +2691,6 @@ TclClockFreeScan( yyHaveDate = 0; yyHaveTime = 0; - yyHour = 0; yyMinutes = 0; yySeconds = 0; yyMeridian = MER24; yyHaveZone = 0; yyTimezone = 0; yyDSTmode = DSTmaybe; diff --git a/generic/tclDate.h b/generic/tclDate.h index 49420a2..4b0b47e 100644 --- a/generic/tclDate.h +++ b/generic/tclDate.h @@ -22,60 +22,64 @@ typedef struct TclDateFields { * epoch */ Tcl_WideInt localSeconds; /* Local time expressed in nominal seconds * from the Posix epoch */ - int tzOffset; /* Time zone offset in seconds east of + time_t tzOffset; /* Time zone offset in seconds east of * Greenwich */ + time_t julianDay; /* Julian Day Number in local time zone */ + enum {BCE=1, CE=0} era; /* Era */ + time_t gregorian; /* Flag == 1 if the date is Gregorian */ + time_t year; /* Year of the era */ + time_t dayOfYear; /* Day of the year (1 January == 1) */ + time_t month; /* Month number */ + time_t dayOfMonth; /* Day of the month */ + time_t iso8601Year; /* ISO8601 week-based year */ + time_t iso8601Week; /* ISO8601 week number */ + time_t dayOfWeek; /* Day of the week */ + time_t hour; /* Hours of day (in-between time only calculation) */ + time_t minutes; /* Minutes of day (in-between time only calculation) */ + time_t secondOfDay; /* Seconds of day (in-between time only calculation) */ + Tcl_Obj *tzName; /* Time zone name (if set the refCount is incremented) */ Tcl_Obj *tzData; /* Time zone data object (internally referenced) */ - int julianDay; /* Julian Day Number in local time zone */ - enum {BCE=1, CE=0} era; /* Era */ - int gregorian; /* Flag == 1 if the date is Gregorian */ - int year; /* Year of the era */ - int dayOfYear; /* Day of the year (1 January == 1) */ - int month; /* Month number */ - int dayOfMonth; /* Day of the month */ - int iso8601Year; /* ISO8601 week-based year */ - int iso8601Week; /* ISO8601 week number */ - int dayOfWeek; /* Day of the week */ - int hour; /* Hours of day (in-between time only calculation) */ - int minutes; /* Minutes of day (in-between time only calculation) */ - int secondOfDay; /* Seconds of day (in-between time only calculation) */ } TclDateFields; +#define ClockCacheableDateFieldsSize \ + TclOffset(TclDateFields, tzName) + /* * Structure contains return parsed fields. */ typedef struct DateInfo { + const char *dateStart; + const char *dateInput; TclDateFields date; - int dateHaveDate; + time_t dateHaveDate; - int dateMeridian; - int dateHaveTime; + time_t dateMeridian; + time_t dateHaveTime; time_t dateTimezone; - int dateDSTmode; - int dateHaveZone; + time_t dateDSTmode; + time_t dateHaveZone; time_t dateRelMonth; time_t dateRelDay; time_t dateRelSeconds; - int dateHaveRel; + time_t dateHaveRel; time_t dateMonthOrdinalIncr; time_t dateMonthOrdinal; - int dateHaveOrdinalMonth; + time_t dateHaveOrdinalMonth; time_t dateDayOrdinal; time_t dateDayNumber; - int dateHaveDay; + time_t dateHaveDay; - const char *dateStart; - const char *dateInput; time_t *dateRelPointer; - int dateDigitCount; + time_t dateDigitCount; Tcl_Obj* messages; /* Error messages */ const char* separatrix; /* String separating messages */ @@ -140,8 +144,19 @@ typedef enum _MERIDIAN { #define CLOCK_MIN_TOK_CHAIN_BLOCK_SIZE 12 +typedef struct ClockScanToken ClockScanToken; + + +typedef int ClockScanTokenProc( + DateInfo *info, + ClockScanToken *tok); + + +#define CLF_DATE (1 << 2) +#define CLF_TIME (1 << 3) + typedef enum _CLCKTOK_TYPE { - CTOKT_EOB=0, CTOKT_DIGIT, CTOKT_SPACE + CTOKT_DIGIT = 1, CTOKT_SPACE, CTOKT_WORD } CLCKTOK_TYPE; typedef struct ClockFmtScnStorage ClockFmtScnStorage; @@ -150,18 +165,29 @@ typedef struct ClockFormatToken { CLCKTOK_TYPE type; } ClockFormatToken; -typedef struct ClockScanToken { +typedef struct ClockScanTokenMap { unsigned short int type; + unsigned short int flags; unsigned short int minSize; unsigned short int maxSize; unsigned short int offs; + ClockScanTokenProc *parser; +} ClockScanTokenMap; + +typedef struct ClockScanToken { + ClockScanTokenMap *map; + unsigned int lookAhead; + struct { + const char *start; + const char *end; + } tokWord; } ClockScanToken; typedef struct ClockFmtScnStorage { int objRefCount; /* Reference count shared across threads */ - ClockScanToken **scnTok; + ClockScanToken *scnTok; unsigned int scnTokC; - ClockFormatToken **fmtTok; + ClockFormatToken *fmtTok; unsigned int fmtTokC; #if CLOCK_FMT_SCN_STORAGE_GC_SIZE > 0 ClockFmtScnStorage *nextPtr; @@ -191,8 +217,8 @@ MODULE_SCOPE ClockFmtScnStorage * Tcl_Obj *objPtr); MODULE_SCOPE int ClockScan(ClientData clientData, Tcl_Interp *interp, - TclDateFields *date, Tcl_Obj *strObj, - ClockFmtScnCmdArgs *opts); + register DateInfo *info, + Tcl_Obj *strObj, ClockFmtScnCmdArgs *opts); /* * Other externals. diff --git a/generic/tclGetDate.y b/generic/tclGetDate.y index 571b7df..54087ca 100644 --- a/generic/tclGetDate.y +++ b/generic/tclGetDate.y @@ -902,7 +902,6 @@ TclClockFreeScan( yyHaveDate = 0; yyHaveTime = 0; - yyHour = 0; yyMinutes = 0; yySeconds = 0; yyMeridian = MER24; yyHaveZone = 0; yyTimezone = 0; yyDSTmode = DSTmaybe; |