void jsvStringIteratorAppendString(JsvStringIterator *it, JsVar *str, size_t startIdx) { JsvStringIterator sit; jsvStringIteratorNew(&sit, str, startIdx); while (jsvStringIteratorHasChar(&sit)) { jsvStringIteratorAppend(it, jsvStringIteratorGetChar(&sit)); jsvStringIteratorNext(&sit); } jsvStringIteratorFree(&sit); }
static void jslLexRegex() { lex->tokenValue = jsvNewFromEmptyString(); if (!lex->tokenValue) { lex->tk = LEX_EOF; return; } JsvStringIterator it; jsvStringIteratorNew(&it, lex->tokenValue, 0); jsvStringIteratorAppend(&it, '/'); // strings... jslGetNextCh(); while (lex->currCh && lex->currCh!='/') { if (lex->currCh == '\\') { jsvStringIteratorAppend(&it, lex->currCh); jslGetNextCh(); } else if (lex->currCh=='\n') { /* Was a newline - this is now allowed * unless we're a template string */ break; } jsvStringIteratorAppend(&it, lex->currCh); jslGetNextCh(); } lex->tk = LEX_REGEX; if (lex->currCh!='/') { lex->tk++; // +1 gets you to 'unfinished X' } else { jsvStringIteratorAppend(&it, '/'); jslGetNextCh(); // regex modifiers while (lex->currCh=='g' || lex->currCh=='i' || lex->currCh=='m' || lex->currCh=='y' || lex->currCh=='u') { jslTokenAppendChar(lex->currCh); jsvStringIteratorAppend(&it, lex->currCh); jslGetNextCh(); } } jsvStringIteratorFree(&it); }
/*JSON{ "type" : "method", "class" : "File", "name" : "read", "generate" : "jswrap_file_read", "description" : [ "Read data in a file in byte size chunks"], "params" : [ ["length", "int32", "is an integer specifying the number of bytes to read."] ], "return" : [ "JsVar", "A string containing the characters that were read" ] }*/ JsVar *jswrap_file_read(JsVar* parent, int length) { JsVar *buffer = 0; JsvStringIterator it; FRESULT res = 0; size_t bytesRead = 0; if (jsfsInit()) { JsFile file; if (fileGetFromVar(&file, parent)) { if(file.data.mode == FM_READ || file.data.mode == FM_READ_WRITE) { char buf[32]; size_t actual = 0; while (bytesRead < (size_t)length) { size_t requested = (size_t)length - bytesRead; if (requested > sizeof( buf )) requested = sizeof( buf ); actual = 0; #ifndef LINUX res = f_read(&file.data.handle, buf, requested, &actual); if(res) break; #else actual = fread(buf, 1, requested, file.data.handle); #endif if (actual>0) { if (!buffer) { buffer = jsvNewFromEmptyString(); if (!buffer) return 0; // out of memory jsvStringIteratorNew(&it, buffer, 0); } size_t i; for (i=0;i<actual;i++) jsvStringIteratorAppend(&it, buf[i]); } bytesRead += actual; if(actual != requested) break; } fileSetVar(&file); } } } if (res) jsfsReportError("Unable to read file", res); if (buffer) jsvStringIteratorFree(&it); // automatically close this file if we're at the end of it if (bytesRead!=(size_t)length) jswrap_file_close(parent); return buffer; }
/*JSON{ "type" : "method", "class" : "String", "name" : "toUpperCase", "generate_full" : "jswrap_string_toUpperLowerCase(parent, true)", "params" : [ ], "return" : ["JsVar","The uppercase version of this string"] }*/ JsVar *jswrap_string_toUpperLowerCase(JsVar *parent, bool upper) { JsVar *res = jsvNewFromEmptyString(); if (!res) return 0; // out of memory JsVar *parentStr = jsvAsString(parent); JsvStringIterator itsrc, itdst; jsvStringIteratorNew(&itsrc, parentStr, 0); jsvStringIteratorNew(&itdst, res, 0); while (jsvStringIteratorHasChar(&itsrc)) { char ch = jsvStringIteratorGetChar(&itsrc); ch = upper ? jsvStringCharToUpper(ch) : jsvStringCharToLower(ch); jsvStringIteratorAppend(&itdst, ch); jsvStringIteratorNext(&itsrc); } jsvStringIteratorFree(&itsrc); jsvStringIteratorFree(&itdst); jsvUnLock(parentStr); return res; }
/*JSON{ "type" : "method", "class" : "String", "name" : "toUpperCase", "generate_full" : "jswrap_string_toUpperLowerCase(parent, true)", "params" : [ ], "return" : ["JsVar","The uppercase version of this string"] }*/ JsVar *jswrap_string_toUpperLowerCase(JsVar *parent, bool upper) { JsVar *res = jsvNewFromEmptyString(); if (!res) return 0; // out of memory JsvStringIterator itsrc, itdst; jsvStringIteratorNew(&itsrc, parent, 0); jsvStringIteratorNew(&itdst, res, 0); while (jsvStringIteratorHasChar(&itsrc)) { char ch = jsvStringIteratorGetChar(&itsrc); if (upper) { if (ch >= 97 && ch <= 122) ch = (char)(ch - 32); } else { if (ch >= 65 && ch <= 90) ch = (char)(ch + 32); // A-Z } jsvStringIteratorAppend(&itdst, ch); jsvStringIteratorNext(&itsrc); } jsvStringIteratorFree(&itsrc); jsvStringIteratorFree(&itdst); return res; }
/*JSON{ "type" : "method", "class" : "File", "name" : "read", "generate" : "jswrap_file_read", "params" : [ ["length","int32","is an integer specifying the number of bytes to read."] ], "return" : ["JsVar","A string containing the characters that were read"] } Read data in a file in byte size chunks */ JsVar *jswrap_file_read(JsVar* parent, int length) { if (length<0) length=0; JsVar *buffer = 0; JsvStringIterator it; FRESULT res = 0; size_t bytesRead = 0; if (jsfsInit()) { JsFile file; if (fileGetFromVar(&file, parent)) { if(file.data->mode == FM_READ || file.data->mode == FM_READ_WRITE) { size_t actual = 0; #ifndef LINUX // if we're able to load this into a flat string, do it! size_t len = f_size(&file.data->handle)-f_tell(&file.data->handle); if ( len == 0 ) { // file all read return 0; // if called from a pipe signal end callback } if (len > (size_t)length) len = (size_t)length; buffer = jsvNewFlatStringOfLength(len); if (buffer) { res = f_read(&file.data->handle, jsvGetFlatStringPointer(buffer), len, &actual); if (res) jsfsReportError("Unable to read file", res); return buffer; } #endif char buf[32]; while (bytesRead < (size_t)length) { size_t requested = (size_t)length - bytesRead; if (requested > sizeof( buf )) requested = sizeof( buf ); actual = 0; #ifndef LINUX res = f_read(&file.data->handle, buf, requested, &actual); if(res) break; #else actual = fread(buf, 1, requested, file.data->handle); #endif if (actual>0) { if (!buffer) { buffer = jsvNewFromEmptyString(); if (!buffer) return 0; // out of memory jsvStringIteratorNew(&it, buffer, 0); } size_t i; for (i=0;i<actual;i++) jsvStringIteratorAppend(&it, buf[i]); } bytesRead += actual; if(actual != requested) break; } } } } if (res) jsfsReportError("Unable to read file", res); if (buffer) jsvStringIteratorFree(&it); return buffer; }
void jslGetNextToken() { jslGetNextToken_start: // Skip whitespace while (isWhitespace(lex->currCh)) jslGetNextCh(); // Search for comments if (lex->currCh=='/') { // newline comments if (jslNextCh()=='/') { while (lex->currCh && lex->currCh!='\n') jslGetNextCh(); jslGetNextCh(); goto jslGetNextToken_start; } // block comments if (jslNextCh()=='*') { while (lex->currCh && !(lex->currCh=='*' && jslNextCh()=='/')) jslGetNextCh(); if (!lex->currCh) { lex->tk = LEX_UNFINISHED_COMMENT; return; /* an unfinished multi-line comment. When in interactive console, detect this and make sure we accept new lines */ } jslGetNextCh(); jslGetNextCh(); goto jslGetNextToken_start; } } lex->tk = LEX_EOF; lex->tokenl = 0; // clear token string if (lex->tokenValue) { jsvUnLock(lex->tokenValue); lex->tokenValue = 0; } // record beginning of this token lex->tokenLastStart = jsvStringIteratorGetIndex(&lex->tokenStart.it) - 1; /* we don't lock here, because we know that the string itself will be locked * because of lex->sourceVar */ lex->tokenStart.it = lex->it; lex->tokenStart.currCh = lex->currCh; // tokens if (((unsigned char)lex->currCh) < jslJumpTableStart || ((unsigned char)lex->currCh) > jslJumpTableEnd) { // if unhandled by the jump table, just pass it through as a single character jslSingleChar(); } else { switch(jslJumpTable[((unsigned char)lex->currCh) - jslJumpTableStart]) { case JSLJT_ID: { while (isAlpha(lex->currCh) || isNumeric(lex->currCh) || lex->currCh=='$') { jslTokenAppendChar(lex->currCh); jslGetNextCh(); } lex->tk = LEX_ID; // We do fancy stuff here to reduce number of compares (hopefully GCC creates a jump table) switch (lex->token[0]) { case 'b': if (jslIsToken("break", 1)) lex->tk = LEX_R_BREAK; break; case 'c': if (jslIsToken("case", 1)) lex->tk = LEX_R_CASE; else if (jslIsToken("catch", 1)) lex->tk = LEX_R_CATCH; else if (jslIsToken("continue", 1)) lex->tk = LEX_R_CONTINUE; break; case 'd': if (jslIsToken("default", 1)) lex->tk = LEX_R_DEFAULT; else if (jslIsToken("delete", 1)) lex->tk = LEX_R_DELETE; else if (jslIsToken("do", 1)) lex->tk = LEX_R_DO; else if (jslIsToken("debugger", 1)) lex->tk = LEX_R_DEBUGGER; break; case 'e': if (jslIsToken("else", 1)) lex->tk = LEX_R_ELSE; break; case 'f': if (jslIsToken("false", 1)) lex->tk = LEX_R_FALSE; else if (jslIsToken("finally", 1)) lex->tk = LEX_R_FINALLY; else if (jslIsToken("for", 1)) lex->tk = LEX_R_FOR; else if (jslIsToken("function", 1)) lex->tk = LEX_R_FUNCTION; break; case 'i': if (jslIsToken("if", 1)) lex->tk = LEX_R_IF; else if (jslIsToken("in", 1)) lex->tk = LEX_R_IN; else if (jslIsToken("instanceof", 1)) lex->tk = LEX_R_INSTANCEOF; break; case 'n': if (jslIsToken("new", 1)) lex->tk = LEX_R_NEW; else if (jslIsToken("null", 1)) lex->tk = LEX_R_NULL; break; case 'r': if (jslIsToken("return", 1)) lex->tk = LEX_R_RETURN; break; case 's': if (jslIsToken("switch", 1)) lex->tk = LEX_R_SWITCH; break; case 't': if (jslIsToken("this", 1)) lex->tk = LEX_R_THIS; else if (jslIsToken("throw", 1)) lex->tk = LEX_R_THROW; else if (jslIsToken("true", 1)) lex->tk = LEX_R_TRUE; else if (jslIsToken("try", 1)) lex->tk = LEX_R_TRY; else if (jslIsToken("typeof", 1)) lex->tk = LEX_R_TYPEOF; break; case 'u': if (jslIsToken("undefined", 1)) lex->tk = LEX_R_UNDEFINED; break; case 'w': if (jslIsToken("while", 1)) lex->tk = LEX_R_WHILE; break; case 'v': if (jslIsToken("var", 1)) lex->tk = LEX_R_VAR; else if (jslIsToken("void", 1)) lex->tk = LEX_R_VOID; break; default: break; } break; case JSLJT_NUMBER: { // TODO: check numbers aren't the wrong format bool canBeFloating = true; if (lex->currCh=='.') { jslGetNextCh(); if (isNumeric(lex->currCh)) { // it is a float lex->tk = LEX_FLOAT; jslTokenAppendChar('.'); } else { // it wasn't a number after all lex->tk = '.'; break; } } else { if (lex->currCh=='0') { jslTokenAppendChar(lex->currCh); jslGetNextCh(); if ((lex->currCh=='x' || lex->currCh=='X') || (lex->currCh=='b' || lex->currCh=='B') || (lex->currCh=='o' || lex->currCh=='O')) { canBeFloating = false; jslTokenAppendChar(lex->currCh); jslGetNextCh(); } } lex->tk = LEX_INT; while (isNumeric(lex->currCh) || (!canBeFloating && isHexadecimal(lex->currCh))) { jslTokenAppendChar(lex->currCh); jslGetNextCh(); } if (canBeFloating && lex->currCh=='.') { lex->tk = LEX_FLOAT; jslTokenAppendChar('.'); jslGetNextCh(); } } // parse fractional part if (lex->tk == LEX_FLOAT) { while (isNumeric(lex->currCh)) { jslTokenAppendChar(lex->currCh); jslGetNextCh(); } } // do fancy e-style floating point if (canBeFloating && (lex->currCh=='e'||lex->currCh=='E')) { lex->tk = LEX_FLOAT; jslTokenAppendChar(lex->currCh); jslGetNextCh(); if (lex->currCh=='-' || lex->currCh=='+') { jslTokenAppendChar(lex->currCh); jslGetNextCh(); } while (isNumeric(lex->currCh)) { jslTokenAppendChar(lex->currCh); jslGetNextCh(); } } } break; case JSLJT_STRING: { char delim = lex->currCh; lex->tokenValue = jsvNewFromEmptyString(); if (!lex->tokenValue) { lex->tk = LEX_EOF; return; } JsvStringIterator it; jsvStringIteratorNew(&it, lex->tokenValue, 0); // strings... jslGetNextCh(); while (lex->currCh && lex->currCh!=delim) { if (lex->currCh == '\\') { jslGetNextCh(); char ch = lex->currCh; switch (lex->currCh) { case 'n' : ch = 0x0A; jslGetNextCh(); break; case 'b' : ch = 0x08; jslGetNextCh(); break; case 'f' : ch = 0x0C; jslGetNextCh(); break; case 'r' : ch = 0x0D; jslGetNextCh(); break; case 't' : ch = 0x09; jslGetNextCh(); break; case 'v' : ch = 0x0B; jslGetNextCh(); break; case 'u' : case 'x' : { // hex digits char buf[5] = "0x??"; if (lex->currCh == 'u') { // We don't support unicode, so we just take the bottom 8 bits // of the unicode character jslGetNextCh(); jslGetNextCh(); } jslGetNextCh(); buf[2] = lex->currCh; jslGetNextCh(); buf[3] = lex->currCh; jslGetNextCh(); ch = (char)stringToInt(buf); } break; default: if (lex->currCh>='0' && lex->currCh<='7') { // octal digits char buf[5] = "0"; buf[1] = lex->currCh; int n=2; jslGetNextCh(); if (lex->currCh>='0' && lex->currCh<='7') { buf[n++] = lex->currCh; jslGetNextCh(); if (lex->currCh>='0' && lex->currCh<='7') { buf[n++] = lex->currCh; jslGetNextCh(); } } buf[n]=0; ch = (char)stringToInt(buf); } else { // for anything else, just push the character through jslGetNextCh(); } break; } jslTokenAppendChar(ch); jsvStringIteratorAppend(&it, ch); } else { jslTokenAppendChar(lex->currCh); jsvStringIteratorAppend(&it, lex->currCh); jslGetNextCh(); } } jsvStringIteratorFree(&it); lex->tk = lex->currCh==delim ? LEX_STR : LEX_UNFINISHED_STR; jslGetNextCh(); } break; case JSLJT_EXCLAMATION: jslSingleChar(); if (lex->currCh=='=') { // != lex->tk = LEX_NEQUAL; jslGetNextCh(); if (lex->currCh=='=') { // !== lex->tk = LEX_NTYPEEQUAL; jslGetNextCh(); } } break; case JSLJT_PLUS: jslSingleChar(); if (lex->currCh=='=') { lex->tk = LEX_PLUSEQUAL; jslGetNextCh(); } else if (lex->currCh=='+') { lex->tk = LEX_PLUSPLUS; jslGetNextCh(); } break; case JSLJT_MINUS: jslSingleChar(); if (lex->currCh=='=') { lex->tk = LEX_MINUSEQUAL; jslGetNextCh(); } else if (lex->currCh=='-') { lex->tk = LEX_MINUSMINUS; jslGetNextCh(); } break; case JSLJT_AND: jslSingleChar(); if (lex->currCh=='=') { lex->tk = LEX_ANDEQUAL; jslGetNextCh(); } else if (lex->currCh=='&') { lex->tk = LEX_ANDAND; jslGetNextCh(); } break; case JSLJT_OR: jslSingleChar(); if (lex->currCh=='=') { lex->tk = LEX_OREQUAL; jslGetNextCh(); } else if (lex->currCh=='|') { lex->tk = LEX_OROR; jslGetNextCh(); } break; case JSLJT_TOPHAT: jslSingleChar(); if (lex->currCh=='=') { lex->tk = LEX_XOREQUAL; jslGetNextCh(); } break; case JSLJT_STAR: jslSingleChar(); if (lex->currCh=='=') { lex->tk = LEX_MULEQUAL; jslGetNextCh(); } break; case JSLJT_FORWARDSLASH: jslSingleChar(); if (lex->currCh=='=') { lex->tk = LEX_DIVEQUAL; jslGetNextCh(); } break; case JSLJT_PERCENT: jslSingleChar(); if (lex->currCh=='=') { lex->tk = LEX_MODEQUAL; jslGetNextCh(); } break; case JSLJT_EQUAL: jslSingleChar(); if (lex->currCh=='=') { // == lex->tk = LEX_EQUAL; jslGetNextCh(); if (lex->currCh=='=') { // === lex->tk = LEX_TYPEEQUAL; jslGetNextCh(); } } break; case JSLJT_LESSTHAN: jslSingleChar(); if (lex->currCh=='=') { // <= lex->tk = LEX_LEQUAL; jslGetNextCh(); } else if (lex->currCh=='<') { // << lex->tk = LEX_LSHIFT; jslGetNextCh(); if (lex->currCh=='=') { // <<= lex->tk = LEX_LSHIFTEQUAL; jslGetNextCh(); } } break; case JSLJT_GREATERTHAN: jslSingleChar(); if (lex->currCh=='=') { // >= lex->tk = LEX_GEQUAL; jslGetNextCh(); } else if (lex->currCh=='>') { // >> lex->tk = LEX_RSHIFT; jslGetNextCh(); if (lex->currCh=='=') { // >>= lex->tk = LEX_RSHIFTEQUAL; jslGetNextCh(); } else if (lex->currCh=='>') { // >>> jslGetNextCh(); if (lex->currCh=='=') { // >>>= lex->tk = LEX_RSHIFTUNSIGNEDEQUAL; jslGetNextCh(); } else { lex->tk = LEX_RSHIFTUNSIGNED; } } } break; case JSLJT_SINGLECHAR: jslSingleChar(); break; default: assert(0);break; } } } }
/*JSON{ "type" : "method", "class" : "String", "name" : "replace", "generate" : "jswrap_string_replace", "params" : [ ["subStr","JsVar","The string to search for"], ["newSubStr","JsVar","The string to replace it with"] ], "return" : ["JsVar","This string with `subStr` replaced"] } Search and replace ONE occurrance of `subStr` with `newSubStr` and return the result. This doesn't alter the original string. Regular expressions not supported. */ JsVar *jswrap_string_replace(JsVar *parent, JsVar *subStr, JsVar *newSubStr) { JsVar *str = jsvAsString(parent); #ifndef SAVE_ON_FLASH // Use RegExp if one is passed in if (jsvIsInstanceOf(subStr, "RegExp")) { JsVar *replace; if (jsvIsFunction(newSubStr) || jsvIsString(newSubStr)) replace = jsvLockAgain(newSubStr); else replace = jsvAsString(newSubStr); jsvObjectSetChildAndUnLock(subStr, "lastIndex", jsvNewFromInteger(0)); bool global = jswrap_regexp_hasFlag(subStr,'g'); JsVar *match; match = jswrap_regexp_exec(subStr, str); while (match && !jsvIsNull(match) && !jspIsInterrupted()) { // get info about match JsVar *matchStr = jsvGetArrayItem(match,0); JsVarInt idx = jsvGetIntegerAndUnLock(jsvObjectGetChild(match,"index",0)); JsVarInt len = (JsVarInt)jsvGetStringLength(matchStr); // do the replacement JsVar *newStr = jsvNewFromStringVar(str, 0, (size_t)idx); JsvStringIterator dst; jsvStringIteratorNew(&dst, newStr, 0); jsvStringIteratorGotoEnd(&dst); if (jsvIsFunction(replace)) { unsigned int argCount = 0; JsVar *args[13]; args[argCount++] = jsvLockAgain(matchStr); JsVar *v; while ((v = jsvGetArrayItem(match, (JsVarInt)argCount))) args[argCount++] = v; args[argCount++] = jsvObjectGetChild(match,"index",0); args[argCount++] = jsvObjectGetChild(match,"input",0); JsVar *result = jsvAsStringAndUnLock(jspeFunctionCall(replace, 0, 0, false, (JsVarInt)argCount, args)); jsvUnLockMany(argCount, args); jsvStringIteratorAppendString(&dst, result, 0); jsvUnLock(result); } else { JsvStringIterator src; jsvStringIteratorNew(&src, replace, 0); while (jsvStringIteratorHasChar(&src)) { char ch = jsvStringIteratorGetChar(&src); if (ch=='$') { jsvStringIteratorNext(&src); ch = jsvStringIteratorGetChar(&src); JsVar *group = 0; if (ch>'0' && ch<='9') group = jsvGetArrayItem(match, ch-'0'); if (group) { jsvStringIteratorAppendString(&dst, group, 0); jsvUnLock(group); } else { jsvStringIteratorAppend(&dst, '$'); jsvStringIteratorAppend(&dst, ch); } } else { jsvStringIteratorAppend(&dst, ch); } jsvStringIteratorNext(&src); } jsvStringIteratorFree(&src); } JsVarInt lastIndex = 1+(JsVarInt)jsvStringIteratorGetIndex(&dst); jsvStringIteratorAppendString(&dst, str, (size_t)(idx+len)); jsvStringIteratorFree(&dst); jsvUnLock2(str,matchStr); str = newStr; // search again if global jsvUnLock(match); match = 0; if (global) { jsvObjectSetChildAndUnLock(subStr, "lastIndex", jsvNewFromInteger(lastIndex)); match = jswrap_regexp_exec(subStr, str); } } jsvUnLock(match); jsvUnLock(replace); // reset lastIndex if global if (global) jsvObjectSetChildAndUnLock(subStr, "lastIndex", jsvNewFromInteger(0)); return str; } #endif newSubStr = jsvAsString(newSubStr); subStr = jsvAsString(subStr); int idx = jswrap_string_indexOf(parent, subStr, 0, false); if (idx>=0) { JsVar *newStr = jsvNewFromStringVar(str, 0, (size_t)idx); jsvAppendStringVar(newStr, newSubStr, 0, JSVAPPENDSTRINGVAR_MAXLENGTH); jsvAppendStringVar(newStr, str, (size_t)idx+jsvGetStringLength(subStr), JSVAPPENDSTRINGVAR_MAXLENGTH); jsvUnLock(str); str = newStr; } jsvUnLock2(subStr, newSubStr); return str; }
static void jslLexString() { char delim = lex->currCh; lex->tokenValue = jsvNewFromEmptyString(); if (!lex->tokenValue) { lex->tk = LEX_EOF; return; } JsvStringIterator it; jsvStringIteratorNew(&it, lex->tokenValue, 0); // strings... jslGetNextCh(); while (lex->currCh && lex->currCh!=delim) { if (lex->currCh == '\\') { jslGetNextCh(); char ch = lex->currCh; switch (lex->currCh) { case 'n' : ch = 0x0A; jslGetNextCh(); break; case 'b' : ch = 0x08; jslGetNextCh(); break; case 'f' : ch = 0x0C; jslGetNextCh(); break; case 'r' : ch = 0x0D; jslGetNextCh(); break; case 't' : ch = 0x09; jslGetNextCh(); break; case 'v' : ch = 0x0B; jslGetNextCh(); break; case 'u' : case 'x' : { // hex digits char buf[5] = "0x??"; if (lex->currCh == 'u') { // We don't support unicode, so we just take the bottom 8 bits // of the unicode character jslGetNextCh(); jslGetNextCh(); } jslGetNextCh(); buf[2] = lex->currCh; jslGetNextCh(); buf[3] = lex->currCh; jslGetNextCh(); ch = (char)stringToInt(buf); } break; default: if (lex->currCh>='0' && lex->currCh<='7') { // octal digits char buf[5] = "0"; buf[1] = lex->currCh; int n=2; jslGetNextCh(); if (lex->currCh>='0' && lex->currCh<='7') { buf[n++] = lex->currCh; jslGetNextCh(); if (lex->currCh>='0' && lex->currCh<='7') { buf[n++] = lex->currCh; jslGetNextCh(); } } buf[n]=0; ch = (char)stringToInt(buf); } else { // for anything else, just push the character through jslGetNextCh(); } break; } jslTokenAppendChar(ch); jsvStringIteratorAppend(&it, ch); } else if (lex->currCh=='\n' && delim!='`') { /* Was a newline - this is now allowed * unless we're a template string */ break; } else { jslTokenAppendChar(lex->currCh); jsvStringIteratorAppend(&it, lex->currCh); jslGetNextCh(); } } jsvStringIteratorFree(&it); if (delim=='`') lex->tk = LEX_TEMPLATE_LITERAL; else lex->tk = LEX_STR; // unfinished strings if (lex->currCh!=delim) lex->tk++; // +1 gets you to 'unfinished X' jslGetNextCh(); }