La siguiente solución no es exactamente lo que ha pedido, pero espero que a pesar de no ser una simple respuesta C cubra sus necesidades. Reinventar la rueda no es un camino por recorrer, así que usemos date.js en C ejecutándolo con SpiderMonkey, el motor de JavaScript de Mozilla.
Así es como lo hice. Empecé descargando date.js y traduciéndolo a const char*
llamado code
definido en date.js.h
.
(\
echo 'const char *code =' ; \
curl https://datejs.googlecode.com/files/date.js | \
sed -e 's/\\/\\\\/g; s/"/\\"/g; s/^/"/; s/\r\?$/\\n"/'; \
echo ';' \
) > date.js.h
Luego tomé el JSAPI's Hello, World! como punto de partida.
#include "jsapi.h"
#include "date.js.h"
static JSClass global_class = { "global", JSCLASS_GLOBAL_FLAGS,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
JSCLASS_NO_OPTIONAL_MEMBERS };
void reportError(JSContext *cx, const char *message, JSErrorReport *report) {
fprintf(stderr, "%s:%u:%s\n",
report->filename ? report->filename : "<no filename>",
(unsigned int) report->lineno, message);
}
int main(int argc, const char *argv[]) {
JSRuntime *rt;
JSContext *cx;
JSObject *global;
rt = JS_NewRuntime(8L * 1024L * 1024L);
if (rt == NULL) return 1;
cx = JS_NewContext(rt, 8192);
if (cx == NULL) return 1;
JS_SetOptions(cx, JSOPTION_VAROBJFIX | JSOPTION_JIT | JSOPTION_METHODJIT);
JS_SetVersion(cx, JSVERSION_LATEST);
JS_SetErrorReporter(cx, reportError);
global = JS_NewCompartmentAndGlobalObject(cx, &global_class, NULL);
if (global == NULL) return 1;
if (!JS_InitStandardClasses(cx, global)) return 1;
/* Here's where the interesting stuff is starting to take place.
* Begin by evaluating sources of date.js */
jsval out;
if (!JS_EvaluateScript(cx, global, code, strlen(code), "code", 1, &out))
return 1;
/* Now create a call to Date.parse and evaluate it. The return value should
* be a timestamp of a given date. If no errors occur convert the timestamp
* to a double and print it. */
const int buflen = 1024;
char parse[buflen + 1];
snprintf(parse, buflen, "Date.parse(\"%s\").getTime();", argv[1]);
if (!JS_EvaluateScript(cx, global, parse, strlen(parse), "parse", 1, &out))
return 1;
double val;
JS_ValueToNumber(cx, out, &val);
printf("%i\n", (int) (val/1000));
/* Finally, clean everything up. */
JS_DestroyContext(cx);
JS_DestroyRuntime(rt);
JS_ShutDown();
return 0;
}
Así es como funciona en la práctica.
$ time ./parse "week ago"
1331938800
0.01user 0.00system 0:00.02elapsed 92%CPU (0avgtext+0avgdata 6168maxresident)k
0inputs+0outputs (0major+1651minor)pagefaults 0swaps
$ time ./parse yesterday
1332457200
0.01user 0.00system 0:00.02elapsed 84%CPU (0avgtext+0avgdata 6168maxresident)k
0inputs+0outputs (0major+1653minor)pagefaults 0swaps
Como se puede ver que es bastante rápido y que podría aumentar significativamente su rendimiento mediante la reutilización del contexto creado inicialmente para todas las llamadas posteriores a Date.parse
.
Hablando de problemas de licencia, date.js está disponible bajo los términos de MIT y SpiderMonkey está disponible bajo MPL 1.1, GPL 2.0 o LGPL 2.1. Vincularlo dinámicamente satisface el requisito de no GPL.
TL; DR:git clone https://gist.github.com/2180739.git && cd 2180739 && make && ./parse yesterday
Por lo que vale la pena, date.js tiene licencia MIT. Entonces, si el objetivo aquí es obtener algo que pueda vincular con el código de propiedad, debería poder usar date.js como un punto de partida seguro si tiene que implementar el suyo propio. Aunque una reescritura javascript-to-C podría no ser una caminata en el parque. –
Es exactamente por eso que hago esta pregunta en lugar de seguir adelante para escribir el código :-) –
Si le preocupa la complejidad de la fuente para escribir su propio analizador, ¿puede usar las herramientas lex/yacc? – Jerry