42 #ifndef PikaScriptImpl_h
43 #define PikaScriptImpl_h
53 #if !defined(PikaScript_h)
54 #include "PikaScript.h"
59 template<
typename T>
inline T mini(T a, T b) {
return (a < b) ? a : b; }
60 template<
typename T>
inline T maxi(T a, T b) {
return (a < b) ? b : a; }
70 #define TMPL template<class CFG>
71 #define T_TYPE(x) typename Script<CFG>::x
75 inline uint uintChar(
char c) {
return uchar(c); }
76 inline uint uintChar(
wchar_t c) {
return c; }
77 template<
class C> std::basic_ostream<C>& xcout();
78 template<
class C> std::basic_istream<C>& xcin();
79 template<>
inline std::basic_ostream<char>& xcout() {
return std::cout; }
80 template<>
inline std::basic_ostream<wchar_t>& xcout() {
return std::wcout; }
81 template<>
inline std::basic_istream<char>& xcin() {
return std::cin; }
82 template<>
inline std::basic_istream<wchar_t>& xcin() {
return std::wcin; }
83 template<>
inline std::string
toStdString(
const std::string& s) {
return s; }
85 inline ulong shiftRight(ulong l,
int r) {
return l >> r; }
86 inline ulong shiftLeft(ulong l,
int r) {
return l << r; }
87 inline ulong bitAnd(ulong l, ulong r) {
return l & r; }
88 inline ulong bitOr(ulong l, ulong r) {
return l | r; }
89 inline ulong bitXor(ulong l, ulong r) {
return l ^ r; }
91 template<
class C>
inline bool isSymbolChar(C c) {
92 return (c >=
'a' && c <=
'z') || (c >=
'A' && c <= 'Z') || (c >=
'0' && c <=
'9') || c ==
'_' || c ==
'$';
95 template<
class C>
inline bool maybeWhite(C c) {
return (c ==
' ' || c ==
'\t' || c ==
'\r' || c ==
'\n' || c ==
'/'); }
97 template<
class S> std::string
toStdString(
const S& s) {
return std::string(s.begin(), s.end()); }
98 template<> std::string
toStdString(
const std::string& s);
99 inline std::string&
toStdString(std::string& s) {
return s; }
101 template<
class S> ulong
hexToLong(
typename S::const_iterator& p,
const typename S::const_iterator& e) {
104 for (; p < e && ((*p >=
'0' && *p <= '9') || (*p >=
'A' && *p <= 'F') || (*p >=
'a' && *p <=
'f')); ++p)
105 l = (l << 4) + (*p <=
'9' ? *p -
'0' : (*p & ~0x20) - (
'A' - 10));
109 template<
class S>
long stringToLong(
typename S::const_iterator& p,
const typename S::const_iterator& e) {
111 bool negative = (e - p > 1 && ((*p ==
'+' || *p ==
'-') && p[1] >=
'0' && p[1] <=
'9') ? (*p++ ==
'-') :
false);
113 for (; p < e && *p >=
'0' && *p <=
'9'; ++p) l = l * 10 + (*p -
'0');
114 return negative ? -l : l;
117 template<
class S,
class T> S
intToString(T i,
int radix,
int minLength) {
118 assert(2 <= radix && radix <= 16);
119 assert(0 <= minLength && minLength <= static_cast<int>(
sizeof (T) * 8));
120 typename S::value_type buffer[
sizeof (T) * 8 + 1], * p = buffer +
sizeof (T) * 8 + 1, * e = p - minLength;
121 for (T x = i; p > e || x != 0; x /= radix) {
122 assert(p >= buffer + 2);
123 *--p = STR(
"fedcba9876543210123456789abcdef")[15 + x % radix];
125 if (std::numeric_limits<T>::is_signed && i < 0) *--p =
'-';
126 return S(p, buffer +
sizeof (T) * 8 + 1 - p);
129 template<
class S>
double stringToDouble(
typename S::const_iterator& p,
const typename S::const_iterator& e) {
131 double d = 0, sign = (e - p > 1 && (*p ==
'+' || *p ==
'-') ? (*p++ ==
'-' ? -1.0 : 1.0) : 1.0);
132 if (std::numeric_limits<double>::has_infinity && e - p >= 8 && equal(p, p + 8, STR(
"infinity"))) {
134 d = std::numeric_limits<double>::infinity();
135 }
else if (p < e && *p >=
'0' && *p <=
'9') {
136 if (*p ==
'0') ++p;
else do { d = d * 10.0 + (*p -
'0'); }
while (++p < e && *p >=
'0' && *p <=
'9');
137 if (e - p > 1 && *p ==
'.' && p[1] >=
'0' && p[1] <=
'9') {
140 do { d += (*p -
'0') * (f *= 0.1); }
while (++p < e && *p >=
'0' && *p <=
'9');
142 if (e - p > 1 && (*p ==
'E' || *p ==
'e')) {
143 typename S::const_iterator b = p;
144 d *= pow(10,
double(stringToLong<S>(++p, e)));
145 if (p == b + 1) p = b;
152 typename S::const_iterator b = s.begin(), e = s.end(), p = b;
153 d = stringToDouble<S>(p, e);
154 return (p != b && p >= e);
158 assert(1 <= precision && precision <= 24);
159 const double EPSILON = 1.0e-300, SMALL = 1.0e-5, LARGE = 1.0e+10;
160 double x = fabs(d), y = x;
161 if (y <= EPSILON)
return S(STR(
"0"));
162 else if (precision >= 12 && y < LARGE &&
long(d) == d)
return intToString<S, long>(long(d));
163 else if (std::numeric_limits<double>::has_infinity && x == std::numeric_limits<double>::infinity())
164 return d < 0 ? S(STR(
"-infinity")) : S(STR(
"+infinity"));
165 typename S::value_type buffer[32], * bp = buffer + 2, * dp = bp, * pp = dp + 1, * ep = pp + precision;
166 for (; x >= 10.0 && pp < ep; x *= 0.1) ++pp;
167 if (pp >= ep || y <= SMALL || y >= LARGE) {
168 double e = floor(log10(y) + 1.0e-10);
169 S exps(e >= 0 ? S(STR(
"e+")) : S(STR(
"e")));
170 exps += intToString<S, int>(int(e));
172 for (
double f = fabs(e); f >= 8; f /= 10) --maxp;
173 return (doubleToString<S>(d * pow(0.1, e), mini(maxp, precision)) += exps);
175 for (; x < 1.0 && dp < buffer + 32; ++ep, x *= 10.0) {
177 if (dp == pp) *dp++ =
'9';
182 if (dp == pp) *dp++ =
'9';
186 while (dp[-1] ==
'9') *--dp =
'0';
187 if (dp == bp) *--bp =
'1';
else dp[-1]++;
190 if (ep > pp)
while (ep[-1] ==
'0') --ep;
191 if (ep - 1 == pp) --ep;
192 if (d < 0) *--bp =
'-';
193 return S(bp, ep - bp);
196 const int ESCAPE_CODE_COUNT = 10;
198 template<
class S> S
unescape(
typename S::const_iterator& p,
const typename S::const_iterator& e) {
200 typedef typename S::value_type CHAR;
201 static const CHAR ESCAPE_CHARS[ESCAPE_CODE_COUNT] = {
'\\',
'\"',
'\'',
'a',
'b',
'f',
'n',
'r',
't',
'v' };
202 static const CHAR ESCAPE_CODES[ESCAPE_CODE_COUNT] = {
'\\',
'\"',
'\'',
'\a',
'\b',
'\f',
'\n',
'\r',
'\t',
'\v' };
203 if (p >= e || (*p !=
'"' && *p !=
'\''))
throw Exception<S>(STR(
"Invalid string literal"));
205 typename S::const_iterator b = ++p;
206 if (p[-1] ==
'\'')
while (e - (p = std::find(p, e,
'\'')) > 1 && p[1] ==
'\'') { d += S(b, ++p); b = ++p; }
207 else while (p < e && *p !=
'\"') {
208 if (*p ==
'\\' && p + 1 < e) {
210 const CHAR* f = std::find(ESCAPE_CHARS, ESCAPE_CHARS + ESCAPE_CODE_COUNT, *++p);
212 if (f != ESCAPE_CHARS + ESCAPE_CODE_COUNT) { ++p; l = ESCAPE_CODES[f - ESCAPE_CHARS]; }
213 else if (*p ==
'x') { b = ++p; l = hexToLong<S>(p, (e - p > 2 ? p + 2 : e)); }
214 else if (*p ==
'u') { b = ++p; l = hexToLong<S>(p, (e - p > 4 ? p + 4 : e)); }
215 else { b = p; l = stringToLong<S>(p, e); }
216 if (p == b)
throw Exception<S>(STR(
"Invalid escape character"));
221 if (p >= e)
throw Exception<S>(STR(
"Unterminated string"));
222 return (d += S(b, p++));
225 template<
class S> S
escape(
const S& s) {
226 typedef typename S::value_type CHAR;
227 static const CHAR ESCAPE_CHARS[ESCAPE_CODE_COUNT] = {
'\\',
'\"',
'\'',
'a',
'b',
'f',
'n',
'r',
't',
'v' };
228 static const CHAR ESCAPE_CODES[ESCAPE_CODE_COUNT] = {
'\\',
'\"',
'\'',
'\a',
'\b',
'\f',
'\n',
'\r',
'\t',
'\v' };
229 typename S::const_iterator b = s.begin(), e = s.end();
230 bool needToBackUp =
false;
231 for (; b < e && *b >= 32 && *b <= 126 && *b !=
'\''; ++b) needToBackUp = needToBackUp || (*b ==
'\\' || *b ==
'\"');
232 if (b >= e)
return ((S(STR(
"'")) += s) +=
'\'');
233 if (needToBackUp) b = s.begin();
234 typename S::const_iterator l = s.begin();
237 while (b < e && *b >= 32 && *b <= 126 && *b !=
'\\' && *b !=
'\"') ++b;
240 const CHAR* f = std::find(ESCAPE_CODES, ESCAPE_CODES + ESCAPE_CODE_COUNT, *b);
241 if (f != ESCAPE_CODES + ESCAPE_CODE_COUNT) (d +=
'\\') += ESCAPE_CHARS[f - ESCAPE_CODES];
242 else if (uintChar(*b) == uintChar(*b)) (d += STR(
"\\x")) += intToString<S>(uintChar(*b), 16, 2);
243 else (d += STR(
"\\u")) += intToString<S>(uintChar(*b), 16, 4);
251 template<
class S> STLValue<S>::operator bool()
const {
252 if (S(*
this) == STR(
"false"))
return false;
253 else if (S(*
this) == STR(
"true"))
return true;
254 else throw Exception<S>(S(STR(
"Invalid boolean: ")) +=
escape(S(*
this)));
257 template<
class S> STLValue<S>::operator long()
const {
258 typename S::const_iterator p = S::begin();
259 long y = stringToLong<S>(p, S::end());
260 if (p == S::begin() || p < S::end()) throw Exception<S>(S(STR(
"Invalid integer: ")) +=
escape(S(*
this)));
264 template<
class S> STLValue<S>::operator double()
const {
266 if (!
stringToDouble(*
this, d))
throw Exception<S>(S(STR(
"Invalid number: ")) +=
escape(S(*
this)));
273 return (lnum == rnum ? (lnum ? lv < rv : (
const S&)(*
this) < (
const S&)(r)) : lnum);
279 return (lnum == rnum && (lnum ? lv == rv : (
const S&)(*
this) == (
const S&)(r)));
283 typename S::const_iterator b = S::begin(), p = S::end();
284 if (p > b)
switch (*(p - 1)) {
285 case '$':
if (--p == b)
break;
286 case '^':
while (p > b && *(p - 1) ==
'^') --p;
if (p == b)
break;
287 case ':':
if (p - 1 > b && *(p - 1) ==
':' && *b ==
':' && std::find(b + 1, p,
':') == p - 1) p = b;
break;
289 return (p == b) ? S(*
this) + S(i) : (S(*this) + STR(
'.')) += S(i);
295 , previous(previous), closure(this), label(previous == 0 ? String(STR(
"::")) : root.generateLabel()) { }
297 TMPL T_TYPE(
Value) Script<CFG>::Frame::rvalue(const XValue& v,
bool fallback) {
298 return !v.first ? v.second :
get(v.second, fallback);
301 TMPL
const T_TYPE(
Value)& Script<CFG>::Frame::lvalue(
const XValue& v) {
302 if (!v.first)
throw Xception(STR(
"Invalid lvalue"));
306 TMPL T_TYPE(Frame*) Script<CFG>::Frame::resolveFrame(
StringIt& p, const
StringIt& e)
const {
308 Frame* f =
const_cast<Frame*
>(
this);
309 if (p < e && *p ==
':') {
310 StringIt n = std::find(p + 1, e,
':');
314 while (f->label != s)
319 for (; p < e && *p ==
'^'; ++p)
320 if ((f = f->previous) == 0)
throw Xception(STR(
"Frame does not exist"));
321 if (p >= e || *p !=
'$') f = f->closure;
326 switch (identifier[0]) {
327 default:
return std::pair<Frame*, String>(closure, identifier);
328 case '$':
return std::pair<Frame*, String>(
const_cast<Frame*
>(
this), identifier);
329 case ':':
case '^': {
330 StringIt b =
const_cast<const String&
>(identifier).begin(), e =
const_cast<const String&
>(identifier).end();
331 Frame* frame = resolveFrame(b, e);
332 return std::pair<Frame*, String>(frame,
String(b, e));
337 TMPL T_TYPE(
Value) Script<CFG>::Frame::get(const
String& identifier,
bool fallback)
const {
339 std::pair<Frame*, String> fs = resolveFrame(identifier);
340 if (fs.first->vars.lookup(fs.second, temp))
return temp;
341 else if (fallback && isSymbolChar(identifier[0]) && root.vars.lookup(fs.second, temp))
return temp;
345 TMPL T_TYPE(
Value) Script<CFG>::Frame::getOptional(const
String& identifier, const
Value& defaultValue)
const {
346 std::pair<Frame*, String> fs = resolveFrame(identifier);
348 return fs.first->vars.lookup(fs.second, temp) ? temp : defaultValue;
352 std::pair<Frame*, String> fs = resolveFrame(identifier);
353 if (!fs.first->vars.assign(fs.second, v))
throw Xception(
String(STR(
"Cannot modify: ")) +=
escape(identifier));
357 TMPL T_TYPE(
Value) Script<CFG>::Frame::reference(const
String& identifier)
const {
358 std::pair<Frame*, String> fs = resolveFrame(identifier);
359 return fs.first->label + fs.second;
362 TMPL T_TYPE(
Value) Script<CFG>::Frame::execute(const
String& body) {
363 StringIt b = body.begin(), e = body.end();
364 switch (b < e ? *b : 0) {
365 case '{':
return evaluate(body);
366 case '>': closure = resolveFrame(++b, e);
367 return evaluate(
String(b, e));
368 case '<':
if (--e - ++b > 0) {
369 Frame* nativeFrame = (*b ==
':' ? resolveFrame(b, e) : &root);
370 Native* native = nativeFrame->vars.lookupNative(
String(b, e));
371 if (native != 0)
return native->pikaCall(*
this);
382 case ' ':
case '\t':
case '\r':
case '\n': ++p;
break;
383 case '/':
if (p + 1 < e && p[1] ==
'/') {
384 static const Char END_CHARS[] = {
'\r',
'\n' };
385 p = find_first_of(p += 2, e, END_CHARS, END_CHARS + 2);
387 }
else if (p + 1 < e && p[1] ==
'*') {
388 static const Char END_CHARS[] = {
'*',
'/' };
389 p = search(p += 2, e, END_CHARS, END_CHARS + 2);
390 if (p >= e)
throw Xception(STR(
"Missing '*/'"));
402 while (*token != 0 && t < e && *t == *token) { ++t; ++token; }
403 if (*token == 0 && (t >= e || !isSymbolChar(*t))) {
404 if ((p = t) < e && maybeWhite(*p)) white(p, e);
409 TMPL
template<
class F>
bool Script<CFG>::Frame::binaryOp(
StringIt& p,
const StringIt& e, XValue& v,
bool dry
413 if (thres >= prec)
return false;
415 expr(p += hop, e, r,
false, dry, prec);
416 if (!dry) v = XValue(
false, op(rvalue(v), rvalue(r)));
420 TMPL
template<
class F>
bool Script<CFG>::Frame::assignableOp(
StringIt& p,
const StringIt& e, XValue& v,
bool dry
424 if (p + hop >= e || p[hop] !=
'=')
return binaryOp(p, e, v, dry, thres, hop, prec, op);
425 if (thres >
ASSIGN)
return false;
427 expr(p += hop + 1, e, r,
false, dry,
ASSIGN);
428 if (!dry) v = XValue(
false, set(lvalue(v), op(rvalue(v,
false), rvalue(r))));
432 TMPL
template<
class F>
bool Script<CFG>::Frame::addSubOp(
StringIt& p,
const StringIt& e, XValue& v,
bool dry
435 if (p + 1 >= e || p[1] != *p)
return assignableOp(p, e, v, dry, thres, 1,
ADD_SUB, f);
436 else if (thres >=
POSTFIX)
return false;
438 Value r = rvalue(v,
false);
439 set(lvalue(v), f(
long(r), 1));
440 v = XValue(
false, r);
446 TMPL
template<
class E,
class I,
class S>
bool Script<CFG>::Frame::lgtOp(
StringIt& p,
const StringIt& e, XValue& v
447 ,
bool dry,
Precedence thres,
const E& excl,
const I& incl, S shift) {
450 if (p + 1 < e && p[1] == *p)
return assignableOp(p, e, v, dry, thres, 2,
SHIFT, shift);
451 else if (p + 1 < e && p[1] ==
'=')
return binaryOp(p, e, v, dry, thres, 2,
COMPARE, incl);
452 else return binaryOp(p, e, v, dry, thres, 1,
COMPARE, excl);
455 TMPL
bool Script<CFG>::Frame::pre(
StringIt& p,
const StringIt& e, XValue& v,
bool dry) {
458 switch (p < e ? *p : 0) {
459 case 0:
return false;
460 case '!': expr(++p, e, v,
false, dry,
PREFIX);
if (!dry) v = XValue(
false, !rvalue(v));
return true;
461 case '~': expr(++p, e, v,
false, dry,
PREFIX);
if (!dry) v = XValue(
false, ~ulong(rvalue(v)));
return true;
462 case '(': termExpr(++p, e, v,
false, dry,
BRACKETS,
')');
return true;
463 case ':':
if (p + 1 < e && p[1] ==
':') p += 2;
break;
464 case '^':
while (++p < e && *p ==
'^');
break;
465 case '@': expr(++p, e, v,
false, dry,
PREFIX);
if (!dry) v = XValue(
false, reference(lvalue(v)));
return true;
466 case '[': termExpr(++p, e, v,
false, dry,
BRACKETS,
']');
if (!dry) v = XValue(
true, rvalue(v));
return true;
467 case '<': p = std::find(p, e,
'>');
if (p < e) ++p;
if (!dry) v = XValue(
false,
String(b, p));
return true;
468 case '\'':
case '"': {
Value s(unescape<String>(p, e));
if (!dry) v = XValue(
false, s); }
return true;
469 case 'e':
if (token(p, e, STR(
"lse")))
throw Xception(STR(
"Unexpected 'else' (preceded by ';'?)"));
break;
470 case 't':
if (token(p, e, STR(
"rue"))) {
if (!dry) v = XValue(
false,
true);
return true; }
break;
471 case 'v':
if (token(p, e, STR(
"oid"))) {
if (!dry) v = XValue(
false,
Value());
return true; }
break;
473 case '>':
if (++p < e && maybeWhite(*p)) white(p, e);
476 if (!dry) v = XValue(
false, (
String(STR(
">")) += closure->label) +=
String(b, p));
479 case '{':
do { expr(++p, e, v,
true, dry,
STATEMENT); }
while (p < e && *p ==
';');
480 if (p >= e)
throw Xception(STR(
"Missing '}'"));
481 if (*p !=
'}')
throw Xception(STR(
"Syntax error (missing ';')?"));
486 if (token(p, e, STR(
"infinity"))) p = b + 1;
487 else if (++p >= e)
return false;
489 expr(++p, e, v,
false, dry,
PREFIX);
490 if (!dry) v = XValue(
false, set(lvalue(v),
long(rvalue(v,
false)) + (*b ==
'-' ? -1 : 1)));
492 }
else if (*p < '0' || *p >
'9') {
493 expr(p, e, v,
false, dry,
PREFIX);
494 if (!dry) v = XValue(
false, *b ==
'-' ? -
double(rvalue(v)) :
double(rvalue(v)));
498 case '0':
if (p + 1 < e && p[1] ==
'x') {
499 ulong l = hexToLong<String>(p += 2, e);
500 if (p == b + 2)
throw Xception(STR(
"Invalid hexadecimal number"));
501 if (!dry) v = XValue(
false, *b ==
'-' ? -
long(l) : l);
505 case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9': {
506 double d = stringToDouble<String>(p, e);
507 if (!dry) v = XValue(
false, *b ==
'-' ? -d : d);
511 case 'f':
if (token(p, e, STR(
"alse"))) {
if (!dry) v = XValue(
false,
false);
return true; }
512 else if (token(p, e, STR(
"or"))) {
513 if (p >= e || *p !=
'(')
throw Xception(STR(
"Expected '('"));
515 termExpr(++p, e, xv,
true, dry,
ARGUMENT,
';');
517 termExpr(p, e, xv,
true, dry,
ARGUMENT,
';');
519 termExpr(p, e, xv,
true,
true,
ARGUMENT,
')');
521 bool cb = !dry && rvalue(xv);
523 expr(p = bp, e, v,
true, !cb,
BODY);
527 expr(p = ip, e, xv,
true,
false,
ARGUMENT);
528 expr(p = cp, e, xv,
true,
false,
ARGUMENT);
535 }
else if (token(p, e, STR(
"unction"))) {
536 if (p >= e || *p !=
'{')
throw Xception(STR(
"Expected '{'"));
539 if (!dry) v = XValue(
false,
String(b, p));
544 case 'i':
if (p + 1 < e && token(p, e, STR(
"f"))) {
545 if (p >= e || *p !=
'(')
throw Xception(STR(
"Expected '('"));
547 termExpr(++p, e, c,
false, dry,
ARGUMENT,
')');
548 bool b = dry || rvalue(c);
549 expr(p, e, v,
false, dry || !b,
BODY);
550 if (p < e && *p ==
'e' && token(p, e, STR(
"lse"))) expr(p, e, v,
false, dry || b,
BODY);
555 while (p < e && isSymbolChar(*p)) ++p;
556 if (b != p && !dry) v = XValue(
true,
String(b, p));
562 switch (p < e ? *p : 0) {
563 case 0:
return false;
564 case ' ':
case '\t':
case '\r':
case '\n':
565 if (thres <
DEFINITION) {
Char c = *p;
while (++p < e && *p == c);
return true; }
break;
566 case '/':
if (thres <
DEFINITION && p + 1 < e && (p[1] ==
'/' || p[1] ==
'*')) { white(p, e);
return true; }
567 return assignableOp(p, e, v, dry, thres, 1,
MUL_DIV, std::divides<double>());
568 case '+':
return addSubOp(p, e, v, dry, thres, std::plus<double>());
569 case '-':
return addSubOp(p, e, v, dry, thres, std::minus<double>());
570 case '#':
return assignableOp(p, e, v, dry, thres, 1,
CONCAT, std::plus<String>());
571 case '*':
return assignableOp(p, e, v, dry, thres, 1,
MUL_DIV, std::multiplies<double>());
572 case '\\':
return assignableOp(p, e, v, dry, thres, 1,
MUL_DIV, std::divides<long>());
573 case '%':
return assignableOp(p, e, v, dry, thres, 1,
MUL_DIV, std::ptr_fun<double, double>(fmod));
574 case '^':
return assignableOp(p, e, v, dry, thres, 1,
BIT_XOR, bitXor);
575 case '<':
return lgtOp(p, e, v, dry, thres, std::less<Value>(), std::less_equal<Value>(), shiftLeft);
576 case '>':
return lgtOp(p, e, v, dry, thres, std::greater<Value>(), std::greater_equal<Value>(), shiftRight);
578 case '!':
if (e - p > 2 && p[2] ==
'=' && p[1] ==
'=')
579 return binaryOp(p, e, v, dry, thres, 3,
EQUALITY, std::not_equal_to<String>());
580 else if (p + 1 < e && p[1] ==
'=')
581 return binaryOp(p, e, v, dry, thres, 2,
EQUALITY, std::not_equal_to<Value>());
584 case '=':
if (e - p > 2 && p[2] ==
'=' && p[1] ==
'=')
585 return binaryOp(p, e, v, dry, thres, 3,
EQUALITY, std::equal_to<String>());
586 else if (p + 1 < e && p[1] ==
'=')
587 return binaryOp(p, e, v, dry, thres, 2,
EQUALITY, std::equal_to<Value>());
588 else if (thres <=
ASSIGN) {
590 expr(++p, e, r,
false, dry,
ASSIGN);
591 if (!dry) v = XValue(
false, set(lvalue(v), rvalue(r)));
596 case '&':
if (p + 1 < e && p[1] !=
'&')
return assignableOp(p, e, v, dry, thres, 1,
BIT_AND, bitAnd);
598 bool l = !dry && rvalue(v);
600 if (!dry) v = XValue(
false, l && rvalue(v));
605 case '|':
if (p + 1 < e && p[1] !=
'|')
return assignableOp(p, e, v, dry, thres, 1,
BIT_OR, bitOr);
607 bool l = dry || rvalue(v);
609 if (!dry) v = XValue(
false, l || rvalue(v));
615 if (++p < e && maybeWhite(*p)) white(p, e);
617 while (p < e && isSymbolChar(*p)) ++p;
618 if (!dry) v = XValue(
true, lvalue(v)[
String(b, p)]);
622 case '[':
if (thres <
POSTFIX) {
624 termExpr(++p, e, element,
false, dry,
BRACKETS,
']');
625 if (!dry) v = XValue(
true, lvalue(v)[rvalue(element)]);
630 case '{':
if (thres <
POSTFIX) {
632 bool gotIndex = expr(++p, e, index,
true, dry,
BRACKETS);
633 if (p >= e || (*p !=
':' && *p !=
'}'))
throw Xception(STR(
"Expected '}' or ':'"));
636 bool gotCount = termExpr(p, e, count,
true, dry,
BRACKETS,
'}');
639 long i = !gotIndex ? 0L : long(rvalue(index));
640 long n = gotCount ? long(rvalue(count)) + mini(i, 0L) :
String::npos;
641 v = XValue(
false, i <=
long(s.size()) && (!gotCount || n >= 0L)
644 }
else if (gotIndex && !dry) {
646 long i = long(rvalue(index));
647 v = XValue(
false, i >= 0L && i <=
long(s.size()) ?
Value(s.substr(i, 1)) :
Value());
648 }
else if (!dry)
throw Xception(STR(
"Syntax error"));
653 case '(':
if (thres <
POSTFIX) {
654 typename CFG::Locals locals;
655 Frame calleeFrame(locals, root,
this);
658 if (++p < e && maybeWhite(*p)) white(p, e);
659 if (p < e && *p ==
')' && n == 0)
break;
661 if (expr(p, e, arg,
true, dry,
ARGUMENT) && !dry)
662 locals.assign(
String(STR(
"$")) += intToString<String>(n), rvalue(arg));
664 }
while (p < e && *p ==
',');
665 if (p >= e || *p !=
')')
throw Xception(STR(
"Expected ',' or ')'"));
668 locals.assign(STR(
"$n"), n);
669 if (v.first) locals.assign(STR(
"$callee"), v.second);
670 v = XValue(
false, calleeFrame.execute(rvalue(v)));
679 TMPL
void Script<CFG>::Frame::tick(
const StringIt& p,
const XValue& v,
Precedence thres,
bool exit) {
680 root.trace(*
this, *source, p - source->begin(), v.first, v.second, thres, exit);
685 if (p < e && maybeWhite(*p)) white(p, e);
686 if (!dry && root.doTrace(thres)) tick(p, v, thres,
false);
687 if (pre(p, e, v, dry)) {
688 while (post(p, e, v, dry, thres));
689 if (!dry && root.doTrace(thres)) tick(p, v, thres,
true);
691 }
else if (!emptyOk)
throw Xception(STR(
"Syntax error"));
695 TMPL
bool Script<CFG>::Frame::termExpr(
StringIt& p,
const StringIt& e, XValue& v,
bool emptyOk,
bool dry
698 bool nonEmpty = expr(p, e, v, emptyOk, dry, thres);
699 if (p >= e || *p != term)
throw Xception((
String(STR(
"Missing '")) +=
String(&term, 1)) +=
'\'');
704 TMPL T_TYPE(
Value) Script<CFG>::Frame::evaluate(const
String source) {
706 const String* oldSource = this->source;
707 this->source = &source;
709 StringIt p = source.begin(), e = source.end();
716 if (*p !=
';')
throw Xception(STR(
"Syntax error"));
720 v = XValue(
false, rvalue(v));
724 }
catch (
const std::exception& x) {
726 const char* s = x.what();
727 String err =
String(std::basic_string<Char>(s, s + strlen(s)));
728 tick(p, XValue(
false, err),
TRACE_ERROR, previous == 0);
733 tick(p, XValue(
false, STR(
"Unknown exception")),
TRACE_ERROR, previous == 0);
742 this->source = oldSource;
745 this->source = oldSource;
750 assert(begin <= end);
753 if (!literal) expr(p, end, dummy,
true,
true,
STATEMENT);
754 else switch (p < e ? *p : 0) {
755 case 'f':
if (!token(p, e, STR(
"alse")) && token(p, e, STR(
"unction"))) pre(p = begin, e, dummy,
true);
break;
756 case 't': token(p, e, STR(
"rue"));
break;
757 case 'v': token(p, e, STR(
"oid"));
break;
758 case '+':
case '-':
if (token(p, e, STR(
"infinity")) || p + 1 >= e || p[1] <
'0' || p[1] >
'9')
break;
759 case '<':
case '>':
case '0':
case '\'':
case '"':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
760 case '7':
case '8':
case '9': pre(p, e, dummy,
true);
break;
765 TMPL T_TYPE(
Value) Script<CFG>::Frame::call(const
String& callee, const
Value& body,
long argc, const
Value* argv) {
767 typename CFG::Locals locals;
768 Frame calleeFrame(locals, root,
this);
769 locals.assign(STR(
"$n"), argc);
770 for (
long i = 0; i < argc; ++i) locals.assign(
String(STR(
"$")) += intToString<String>(i), argv[i]);
771 if (!callee.empty()) locals.assign(STR(
"$callee"), callee);
772 return calleeFrame.execute(body.empty() ?
get(callee,
true) : body);
776 std::pair<Frame*, String> p = resolveFrame(identifier);
777 if (!p.first->vars.assignNative(p.second, native))
780 p.first->set(p.second, (
String(STR(
"<")) += (p.first == &root ? p.second : p.first->label + p.second)) +=
'>');
785 TMPL Script<CFG>::Root::Root(Variables& vars) : Frame(vars, *this, 0), traceLevel(
NO_TRACE), isInsideTracer(false)
786 , autoLabelStart(autoLabel + 29) {
787 std::fill_n(autoLabel, 32,
':');
790 TMPL T_TYPE(
String) Script<CFG>::Root::generateLabel() {
791 Char* b = autoLabelStart, * p = autoLabel + 30;
794 case ':': *p =
'1'; *--b =
':'; autoLabelStart = b;
break;
795 case '9': *p =
'A';
break;
796 case 'Z': *p =
'a';
break;
797 default: (*p)++;
break;
799 for (++p; *p !=
':'; ++p) *p =
'0';
800 return String(const_cast<const Char*>(b), autoLabel + 31 - b);
804 this->traceLevel = traceLevel;
805 this->tracerFunction = tracerFunction;
810 if (!tracerFunction.isVoid() && !isInsideTracer) {
812 isInsideTracer =
true;
813 Value argv[6] = { source,
static_cast<ulong
>(offset), lvalue, value,
int(level), exit };
814 frame.call(
String(), tracerFunction, 6, argv);
815 isInsideTracer =
false;
817 isInsideTracer =
false;
826 TMPL
bool Script<CFG>::STLVariables::lookup(
const String& symbol,
Value& result) {
827 typename VariableMap::const_iterator it = vars.find(symbol);
828 if (it == vars.end())
return false;
833 TMPL
void Script<CFG>::STLVariables::list(
const String& key,
typename Variables::VarList& list) {
834 for (
typename VariableMap::const_iterator it = vars.lower_bound(key)
835 ; it != vars.end() && it->first.substr(0, key.size()) == key; ++it) list.push_back(*it);
838 TMPL T_TYPE(Native*) Script<CFG>::STLVariables::lookupNative(const
String& identifier) {
839 typename NativeMap::iterator it = natives.find(identifier);
840 return (it == natives.end() ? 0 : it->second);
843 TMPL
bool Script<CFG>::STLVariables::assignNative(
const String& identifier, Native* native) {
844 typename NativeMap::iterator it = natives.insert(
typename NativeMap::value_type(identifier, (Native*)(0))).first;
845 if (it->second != native)
delete it->second;
850 TMPL Script<CFG>::STLVariables::~STLVariables() {
851 for (
typename NativeMap::iterator it = natives.begin(); it != natives.end(); ++it)
delete it->second;
855 const String fn = f.get(STR(
"$callee"));
856 StringIt it = std::find(fn.rbegin(), fn.rend(),
'.').base();
857 if (it <= fn.begin())
throw Xception(STR(
"Non-method call"));
858 return std::pair<Value, String>(f.getPrevious().reference(
String(fn.begin(), it - 1)),
String(it, fn.end()));
863 TMPL T_TYPE(
Value) Script<CFG>::lib::elevate(Frame& f) {
return f.execute(f.get(
getThisAndMethod(f).first,
true)); }
864 TMPL ulong Script<CFG>::lib::length(
const String& s) {
return static_cast<ulong
>(s.size()); }
865 TMPL T_TYPE(
String) Script<CFG>::lib::lower(
String s) { transform(s.begin(), s.end(), s.begin(), ::tolower);
return s; }
866 TMPL
void Script<CFG>::lib::print(
const String& s) { xcout<Char>() << std::basic_string<Char>(s) << std::endl; }
867 TMPL
double Script<CFG>::lib::random(
double m) {
return m * rand() / double(RAND_MAX); }
868 TMPL T_TYPE(
String) Script<CFG>::lib::reverse(
String s) { std::reverse(s.begin(), s.end());
return s; }
869 TMPL
void Script<CFG>::lib::thrower(
const String& s) {
throw Xception(s); }
870 TMPL T_TYPE(
Value) Script<CFG>::lib::time(const Frame&) {
return double(::time(0)); }
871 TMPL T_TYPE(
String) Script<CFG>::lib::upper(
String s) { transform(s.begin(), s.end(), s.begin(), ::toupper);
return s; }
873 TMPL T_TYPE(
String) Script<CFG>::lib::character(
double d) {
874 if (uintChar(
Char(d)) != d)
throw Xception(
String(STR(
"Illegal character code: ")) += doubleToString<String>(d));
878 TMPL uint Script<CFG>::lib::ordinal(
const String& s) {
880 return uintChar(s[0]);
883 TMPL
bool Script<CFG>::lib::deleter(
const Frame& f) {
884 Value x = f.get(STR(
"$0"));
885 std::pair<Frame*, String> fs = f.getPrevious().resolveFrame(x);
886 return fs.first->getVariables().erase(fs.second);
889 TMPL T_TYPE(
Value) Script<CFG>::lib::evaluate(const Frame& f) {
890 return f.resolveFrame(f.getOptional(STR(
"$1"))).first->evaluate(f.get(STR(
"$0")));
893 TMPL
bool Script<CFG>::lib::exists(
const Frame& f) {
894 Value x = f.get(STR(
"$0"));
896 std::pair<Frame*, String> fs = f.getPrevious().resolveFrame(x);
897 return fs.first->getVariables().lookup(fs.second, result);
900 TMPL ulong Script<CFG>::lib::find(
const String& a,
const String& b) {
901 return static_cast<ulong
>(find_first_of(a.begin(), a.end(), b.begin(), b.end()) - a.begin());
904 TMPL
void Script<CFG>::lib::foreach(Frame& f) {
905 Value arg1 = f.get(STR(
"$1"));
906 std::pair<Frame*, String> fs = f.getPrevious().resolveFrame(f.get(STR(
"$0"))[
Value()]);
907 typename Variables::VarList list;
908 fs.first->getVariables().list(fs.second, list);
909 for (
typename Variables::VarList::const_iterator it = list.begin(); it != list.end(); ++it) {
910 Value argv[3] = { fs.first->reference(it->first), it->first.substr(fs.second.size()), it->second };
911 f.call(
String(), arg1, 3, argv);
915 TMPL T_TYPE(
String) Script<CFG>::lib::input(const
String& prompt) {
916 xcout<Char>() << std::basic_string<Char>(prompt);
917 std::basic_string<Char> s;
918 std::basic_istream<Char>& instream = xcin<Char>();
919 if (instream.eof())
throw Xception(STR(
"Unexpected end of input file"));
920 if (!instream.good())
throw Xception(STR(
"Input file error"));
921 getline(instream, s);
925 TMPL T_TYPE(
Value) Script<CFG>::lib::invoke(Frame& f) {
926 Value source = f.get(STR(
"$2")), arg4 = f.getOptional(STR(
"$4"));
927 long offset = long(f.getOptional(STR(
"$3"), 0));
928 long n = arg4.isVoid() ? long(f.get(source[
String(STR(
"n"))])) - offset :
long(arg4);
929 if (n < 0)
throw Xception(STR(
"Too few array elements"));
930 std::vector<Value> a(n);
931 for (
long i = 0; i < long(a.size()); ++i) a[i] = f.get(source[i + offset]);
932 return f.call(f.getOptional(STR(
"$0")), f.getOptional(STR(
"$1")), long(a.size()), a.empty() ? 0 : &a[0]);
935 TMPL T_TYPE(
String) Script<CFG>::lib::load(const
String& file) {
936 std::basic_ifstream<Char> instream(
toStdString(file).c_str());
937 if (!instream.good())
throw Xception(
String(STR(
"Cannot open file for reading: ")) +=
escape(file));
939 while (!instream.eof()) {
942 instream.read(buffer, 4096);
943 chars +=
String(buffer, static_cast<typename String::size_type>(instream.gcount()));
948 TMPL ulong Script<CFG>::lib::mismatch(
const String& a,
const String& b) {
949 if (a.size() > b.size())
return static_cast<ulong>(std::mismatch(b.begin(), b.end(), a.begin()).first - b.begin());
950 else return static_cast<ulong
>(std::mismatch(a.begin(), a.end(), b.begin()).first - a.begin());
953 TMPL ulong Script<CFG>::lib::parse(Frame& f) {
954 const String source = f.get(STR(
"$0"));
955 return static_cast<ulong
>(f.parse(source.begin(), source.end(), f.get(STR(
"$1"))) - source.begin());
958 TMPL T_TYPE(
String) Script<CFG>::lib::radix(const Frame& f) {
959 int radix = f.get(STR(
"$1"));
960 if (radix < 2 || radix > 16)
throw Xception(
String(STR(
"Radix out of range: ")) += intToString<String>(radix));
961 int minLength = f.getOptional(STR(
"$2"), 1);
962 if (minLength < 0 || minLength >
int(
sizeof (
int) * 8))
963 throw Xception(
String(STR(
"Minimum length out of range: ")) += intToString<String>(minLength));
964 return intToString<String, ulong>(f.get(STR(
"$0")), f.get(STR(
"$1")), minLength);
967 TMPL
void Script<CFG>::lib::save(
const String& file,
const String& chars) {
968 std::basic_ofstream<Char> outstream(
toStdString(file).c_str());
969 if (!outstream.good())
throw Xception(
String(STR(
"Cannot open file for writing: ")) +=
escape(file));
970 outstream.write(chars.data(), chars.size());
974 TMPL ulong Script<CFG>::lib::search(
const String& a,
const String& b) {
975 return static_cast<ulong
>(std::search(a.begin(), a.end(), b.begin(), b.end()) - a.begin());
978 TMPL ulong Script<CFG>::lib::span(
const String& a,
const String& b) {
979 typename String::const_iterator it;
980 for (it = a.begin(); it != a.end() && std::find(b.begin(), b.end(), *it) != b.end(); ++it);
981 return static_cast<ulong
>(it - a.begin());
984 TMPL T_TYPE(
String) Script<CFG>::lib::precision(const Frame& f) {
985 return doubleToString<String>(f.get(STR(
"$0")), mini(maxi(
int(f.get(STR(
"$1"))), 1), 16));
988 TMPL
int Script<CFG>::lib::system(
const String& command) {
989 int xc = (command.empty() ? -1 : ::system(
toStdString(command).c_str()));
994 TMPL
void Script<CFG>::lib::trace(
const Frame& f) {
995 f.getRoot().setTracer(
Precedence(
int(f.getOptional(STR(
"$1"),
int(
TRACE_CALL)))), f.getOptional(STR(
"$0")));
998 TMPL T_TYPE(
Value) Script<CFG>::lib::tryer(Frame& f) {
999 try { f.call(
String(), f.get(STR(
"$0")), 0); }
catch (
const Xception& x) {
return x.getError(); }
1004 f.set(STR(
"VERSION"), PIKA_SCRIPT_VERSION);
1005 f.set(STR(
"run"), STR(
">::evaluate((>{ $s = load($0); if ($s{:2} == '#!') $s{find($s, \"\\n\"):} })($0), @$)"));
1006 f.registerNative(STR(
"abs"), (
double (*)(
double))(fabs));
1007 f.registerNative(STR(
"acos"), (
double (*)(
double))(acos));
1008 f.registerNative(STR(
"asin"), (
double (*)(
double))(asin));
1009 f.registerNative(STR(
"atan"), (
double (*)(
double))(atan));
1010 f.registerNative(STR(
"atan2"), (
double (*)(
double,
double))(atan2));
1011 f.registerNative(STR(
"ceil"), (
double (*)(
double))(ceil));
1012 f.registerNative(STR(
"char"), lib::character);
1013 f.registerNative(STR(
"cos"), (
double (*)(
double))(cos));
1014 f.registerNative(STR(
"cosh"), (
double (*)(
double))(cosh));
1015 f.registerNative(STR(
"delete"), lib::deleter);
1017 f.registerNative(STR(
"exists"), lib::exists);
1019 f.registerNative(STR(
"evaluate"), lib::evaluate);
1020 f.registerNative(STR(
"exp"), (
double (*)(
double))(exp));
1021 f.registerNative(STR(
"find"), lib::find);
1022 f.registerNative(STR(
"floor"), (
double (*)(
double))(floor));
1023 f.registerNative(STR(
"foreach"), lib::foreach);
1024 f.set(STR(
"include"), STR(
">::if (!exists(@::included[$0])) { invoke('run',, @$); ::included[$0] = true }"));
1025 if (includeIO) f.registerNative(STR(
"input"), lib::input);
1026 f.registerNative(STR(
"invoke"), lib::invoke);
1027 f.registerNative(STR(
"length"), lib::length);
1028 f.registerNative(STR(
"log"), (
double (*)(
double))(log));
1029 f.registerNative(STR(
"log10"), (
double (*)(
double))(log10));
1030 if (includeIO) f.registerNative(STR(
"load"), lib::load);
1031 f.registerNative(STR(
"lower"), lib::lower);
1032 f.registerNative(STR(
"mismatch"), lib::mismatch);
1033 f.registerNative(STR(
"ordinal"), lib::ordinal);
1034 f.registerNative(STR(
"pow"), (
double (*)(
double,
double))(pow));
1035 f.registerNative(STR(
"parse"), lib::parse);
1036 f.registerNative(STR(
"precision"), lib::precision);
1037 if (includeIO) f.registerNative(STR(
"print"), lib::print);
1038 f.registerNative(STR(
"radix"), lib::radix);
1039 f.registerNative(STR(
"random"), lib::random);
1040 f.registerNative(STR(
"reverse"), lib::reverse);
1041 f.registerNative(STR(
"sin"), (
double (*)(
double))(sin));
1042 f.registerNative(STR(
"sinh"), (
double (*)(
double))(sinh));
1043 if (includeIO) f.registerNative(STR(
"save"), lib::save);
1044 f.registerNative(STR(
"search"), lib::search);
1045 f.registerNative(STR(
"span"), lib::span);
1046 f.registerNative(STR(
"sqrt"), (
double (*)(
double))(sqrt));
1047 if (includeIO) f.registerNative(STR(
"system"), lib::system);
1048 f.registerNative(STR(
"tan"), (
double (*)(
double))(tan));
1049 f.registerNative(STR(
"tanh"), (
double (*)(
double))(tanh));
1050 f.registerNative(STR(
"time"), lib::time);
1051 f.registerNative(STR(
"throw"), lib::thrower);
1052 f.registerNative(STR(
"trace"), lib::trace);
1053 f.registerNative(STR(
"try"), lib::tryer);
1054 f.registerNative(STR(
"upper"), lib::upper);
1057 TMPL Script<CFG>::Native::~Native() { }
x=y x*=y x/=y x\=y x%=y x+=y x-=y x<<=y x>>=y x#=y x&=y x^=y x|=y
static void addLibraryNatives(Frame &frame, bool includeIO=true)
Registers the standard library native functions to frame. If includeIO is false, 'load', 'save', 'input', 'print' and 'system' will not be registered. Please, refer to the PikaScript standard library reference guide for more info on individual native functions.
String::value_type Char
The character type for all strings (defined by the string class). E.g. char.
virtual ~Variables()
Destructor.
S unescape(typename S::const_iterator &p, const typename S::const_iterator &e)
Converts a string that is either enclosed in single (' ') or double (" ") quotes. ...
String::const_iterator StringIt
The const_iterator of the string is used so frequently it deserves its own typedef.
bool operator<(const STLValue &r) const
Less than comparison operator.
Config::Value Value
The class used for all values and variables (defined by the configuration meta-class). E.g. STLValue.
std::pair< Frame *, String > resolveFrame(const String &identifier) const
Resolves the frame for identifier and returns it together with identifier stripped of any prefixed "f...
S escape(const S &s)
Depending on the contents of the source string s it is encoded either in single (' ') or double (" ")...
Precedence
Precedence levels are used both internally for the parser and externally for the tracing mechanism...
used only for tracing with tick()
S intToString(T i, int radix=10, int minLength=1)
Converts the integer i to a string with a radix and minimum length of your choice.
void registerNative(const String &identifier, Native *native)
Registers the native function (or object) native with identifier in the appropriate variable space (d...
double stringToDouble(typename S::const_iterator &p, const typename S::const_iterator &e)
Converts a string in scientific e notation (e.g. -12.34e-3) to a double floating point value...
ulong hexToLong(typename S::const_iterator &p, const typename S::const_iterator &e)
Converts a string in hexadecimal form to an ulong integer.
Value::String String
The class used for strings (defined by the string class). E.g. std::string.
const STLValue operator[](const STLValue &i) const
The subscript operator returns the concatenation of the value with the dot (.) separator (if necessar...
String::size_type SizeType
The length type for all strings (defined by the string class). E.g. size_t.
static std::pair< Value, String > getThisAndMethod(Frame &frame)
getThisAndMethod splits the $callee variable of frame into object ("this") and method.
bool operator==(const STLValue &r) const
Equality operator.
used only for tracing with tick()
Exception< String > Xception
The exception type.
const Value & set(const String &identifier, const Value &v)
Sets a variable value.
used only for tracing with tick()
long stringToLong(typename S::const_iterator &p, const typename S::const_iterator &e)
Converts a string in decimal form to a signed long integer.
virtual void trace(Frame &frame, const String &source, SizeType offset, bool lvalue, const Value &value, Precedence level, bool exit)
Overload this member function if you want to customize the tracing mechanism in PikaScript.
virtual void setTracer(Precedence traceLevel, const Value &tracerFunction)
Called by the standard library function "trace" to assign a PikaScript tracer function and a trace le...
S doubleToString(double d, int precision=14)
Converts the double d to a string (in scientific e notation, e.g. -12.34e-3).
static Value elevate(Frame &frame)
Used to "aggregate" different method calls into a single function call.
Frame(Variables &vars, Root &root, Frame *previous)
Constructs the Frame and associates it with the variable space vars.
used only for tracing with tick()
std::string toStdString(const S &s)
Converts the string s to a standard C++ string.