/* * Copyright 2015 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // Pure parsing. Calls methods on a Builder (template argument) to actually // construct the AST // // XXX All parsing methods assume they take ownership of the input string. This // lets them reuse parts of it. You will segfault if the input string cannot // be reused and written to. #ifndef wasm_parser_h #define wasm_parser_h #include #include #include #include #include #include "istring.h" #include "support/safe_integer.h" namespace cashew { // common strings extern IString TOPLEVEL; extern IString DEFUN; extern IString BLOCK; extern IString VAR; extern IString CONST; extern IString CONDITIONAL; extern IString BINARY; extern IString RETURN; extern IString IF; extern IString ELSE; extern IString WHILE; extern IString DO; extern IString FOR; extern IString SEQ; extern IString SUB; extern IString CALL; extern IString LABEL; extern IString BREAK; extern IString CONTINUE; extern IString SWITCH; extern IString STRING; extern IString TRY; extern IString INF; extern IString NaN; extern IString LLVM_CTTZ_I32; extern IString UDIVMODDI4; extern IString UNARY_PREFIX; extern IString UNARY_POSTFIX; extern IString MATH_FROUND; extern IString MATH_CLZ32; extern IString INT64; extern IString INT64_CONST; extern IString SIMD_FLOAT32X4; extern IString SIMD_FLOAT64X2; extern IString SIMD_INT8X16; extern IString SIMD_INT16X8; extern IString SIMD_INT32X4; extern IString PLUS; extern IString MINUS; extern IString OR; extern IString AND; extern IString XOR; extern IString L_NOT; extern IString B_NOT; extern IString LT; extern IString GE; extern IString LE; extern IString GT; extern IString EQ; extern IString NE; extern IString DIV; extern IString MOD; extern IString MUL; extern IString RSHIFT; extern IString LSHIFT; extern IString TRSHIFT; extern IString HEAP8; extern IString HEAP16; extern IString HEAP32; extern IString HEAPF32; extern IString HEAPU8; extern IString HEAPU16; extern IString HEAPU32; extern IString HEAPF64; extern IString F0; extern IString EMPTY; extern IString FUNCTION; extern IString OPEN_PAREN; extern IString OPEN_BRACE; extern IString OPEN_CURLY; extern IString CLOSE_CURLY; extern IString COMMA; extern IString QUESTION; extern IString COLON; extern IString CASE; extern IString DEFAULT; extern IString DOT; extern IString PERIOD; extern IString NEW; extern IString ARRAY; extern IString OBJECT; extern IString THROW; extern IString SET; extern IString ATOMICS; extern IString COMPARE_EXCHANGE; extern IString LOAD; extern IString STORE; extern IStringSet keywords; extern const char *OPERATOR_INITS, *SEPARATORS; extern int MAX_OPERATOR_SIZE, LOWEST_PREC; struct OperatorClass { enum Type { Binary = 0, Prefix = 1, Postfix = 2, Tertiary = 3 }; IStringSet ops; bool rtl; Type type; OperatorClass(const char* o, bool r, Type t) : ops(o), rtl(r), type(t) {} static int getPrecedence(Type type, IString op); static bool getRtl(int prec); }; extern std::vector operatorClasses; extern bool isIdentInit(char x); extern bool isIdentPart(char x); // parser template class Parser { static bool isSpace(char x) { return x == 32 || x == 9 || x == 10 || x == 13; } /* space, tab, linefeed/newline, or return */ static void skipSpace(char*& curr) { while (*curr) { if (isSpace(*curr)) { curr++; continue; } if (curr[0] == '/' && curr[1] == '/') { curr += 2; while (*curr && *curr != '\n') { curr++; } if (*curr) { curr++; } continue; } if (curr[0] == '/' && curr[1] == '*') { curr += 2; while (*curr && (curr[0] != '*' || curr[1] != '/')) { curr++; } curr += 2; continue; } return; } } static bool isDigit(char x) { return x >= '0' && x <= '9'; } static bool hasChar(const char* list, char x) { while (*list) { if (*list++ == x) { return true; } } return false; } // An atomic fragment of something. Stops at a natural boundary. enum FragType { KEYWORD = 0, OPERATOR = 1, IDENT = 2, STRING = 3, // without quotes INT = 4, DOUBLE = 5, SEPARATOR = 6 }; struct Frag { // MSVC does not allow unrestricted unions: // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2544.pdf #ifndef _MSC_VER union { #endif IString str; double num; #ifndef _MSC_VER }; #endif int size; FragType type; bool isNumber() const { return type == INT || type == DOUBLE; } explicit Frag(char* src) { char* start = src; if (isIdentInit(*src)) { // read an identifier or a keyword src++; while (isIdentPart(*src)) { src++; } if (*src == 0) { str.set(start); } else { char temp = *src; *src = 0; str.set(start, false); *src = temp; } type = keywords.has(str) ? KEYWORD : IDENT; } else if (isDigit(*src) || (src[0] == '.' && isDigit(src[1]))) { if (src[0] == '0' && (src[1] == 'x' || src[1] == 'X')) { // Explicitly parse hex numbers of form "0x...", because strtod // supports hex number strings only in C++11, and Visual Studio 2013 // does not yet support that functionality. src += 2; num = 0; while (1) { if (*src >= '0' && *src <= '9') { num *= 16; num += *src - '0'; } else if (*src >= 'a' && *src <= 'f') { num *= 16; num += *src - 'a' + 10; } else if (*src >= 'A' && *src <= 'F') { num *= 16; num += *src - 'A' + 10; } else { break; } src++; } } else { num = strtod(start, &src); } // asm.js must have a '.' for double values. however, we also tolerate // uglify's tendency to emit without a '.' (and fix it later with a +). // for valid asm.js input, the '.' should be enough, and for uglify // in the emscripten optimizer pipeline, we use simple_ast where // INT/DOUBLE is quite the same at this point anyhow type = (std::find(start, src, '.') == src && (wasm::isSInteger32(num) || wasm::isUInteger32(num))) ? INT : DOUBLE; assert(src > start); } else if (hasChar(OPERATOR_INITS, *src)) { switch (*src) { case '!': str = src[1] == '=' ? NE : L_NOT; break; case '%': str = MOD; break; case '&': str = AND; break; case '*': str = MUL; break; case '+': str = PLUS; break; case ',': str = COMMA; break; case '-': str = MINUS; break; case '.': str = PERIOD; break; case '/': str = DIV; break; case ':': str = COLON; break; case '<': str = src[1] == '<' ? LSHIFT : (src[1] == '=' ? LE : LT); break; case '=': str = src[1] == '=' ? EQ : SET; break; case '>': str = src[1] == '>' ? (src[2] == '>' ? TRSHIFT : RSHIFT) : (src[1] == '=' ? GE : GT); break; case '?': str = QUESTION; break; case '^': str = XOR; break; case '|': str = OR; break; case '~': str = B_NOT; break; default: abort(); } size = strlen(str.str); #ifndef NDEBUG char temp = start[size]; start[size] = 0; assert(strcmp(str.str, start) == 0); start[size] = temp; #endif type = OPERATOR; return; } else if (hasChar(SEPARATORS, *src)) { type = SEPARATOR; char temp = src[1]; src[1] = 0; str.set(src, false); src[1] = temp; src++; } else if (*src == '"' || *src == '\'') { char* end = strchr(src + 1, *src); *end = 0; str.set(src + 1); src = end + 1; type = STRING; } else { dump("frag parsing", src); abort(); } size = src - start; } }; struct ExpressionElement { bool isNode; // MSVC does not allow unrestricted unions: // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2544.pdf #ifndef _MSC_VER union { #endif NodeRef node; IString op; #ifndef _MSC_VER }; #endif ExpressionElement(NodeRef n) : isNode(true), node(n) {} ExpressionElement(IString o) : isNode(false), op(o) {} NodeRef getNode() { assert(isNode); return node; } IString getOp() { assert(!isNode); return op; } }; // This is a list of the current stack of node-operator-node-operator-etc. // this works by each parseExpression call appending to the vector; then // recursing out, and the toplevel sorts it all typedef std::vector ExpressionParts; std::vector expressionPartsStack; // Parses an element in a list of such elements, e.g. list of statements in a // block, or list of parameters in a call NodeRef parseElement(char*& src, const char* seps = ";") { // dump("parseElement", src); skipSpace(src); Frag frag(src); src += frag.size; switch (frag.type) { case KEYWORD: { return parseAfterKeyword(frag, src, seps); } case IDENT: { return parseAfterIdent(frag, src, seps); } case STRING: case INT: case DOUBLE: { return parseExpression(parseFrag(frag), src, seps); } case SEPARATOR: { if (frag.str == OPEN_PAREN) { return parseExpression(parseAfterParen(src), src, seps); } if (frag.str == OPEN_BRACE) { return parseExpression(parseAfterBrace(src), src, seps); } if (frag.str == OPEN_CURLY) { return parseExpression(parseAfterCurly(src), src, seps); } abort(); } case OPERATOR: { return parseExpression(frag.str, src, seps); } default: /* dump("parseElement", src); printf("bad frag type: %d\n",frag.type); */ abort(); } return nullptr; } NodeRef parseFrag(Frag& frag) { switch (frag.type) { case IDENT: return Builder::makeName(frag.str); case STRING: return Builder::makeString(frag.str); case INT: return Builder::makeInt(uint32_t(frag.num)); case DOUBLE: return Builder::makeDouble(frag.num); default: abort(); } return nullptr; } NodeRef parseAfterKeyword(Frag& frag, char*& src, const char* seps) { skipSpace(src); if (frag.str == FUNCTION) { return parseFunction(src, seps); } else if (frag.str == VAR) { return parseVar(src, seps, false); } else if (frag.str == CONST) { return parseVar(src, seps, true); } else if (frag.str == RETURN) { return parseReturn(src, seps); } else if (frag.str == IF) { return parseIf(src, seps); } else if (frag.str == DO) { return parseDo(src, seps); } else if (frag.str == WHILE) { return parseWhile(src, seps); } else if (frag.str == BREAK) { return parseBreak(src, seps); } else if (frag.str == CONTINUE) { return parseContinue(src, seps); } else if (frag.str == SWITCH) { return parseSwitch(src, seps); } else if (frag.str == NEW) { return parseNew(src, seps); } else if (frag.str == FOR) { return parseFor(src, seps); } dump(frag.str.str, src); abort(); return nullptr; } NodeRef parseFunction(char*& src, const char* seps) { Frag name(src); if (name.type == IDENT) { src += name.size; } else { assert(name.type == SEPARATOR && name.str[0] == '('); name.str = IString(); } NodeRef ret = Builder::makeFunction(name.str); skipSpace(src); assert(*src == '('); src++; while (1) { skipSpace(src); if (*src == ')') { break; } Frag arg(src); assert(arg.type == IDENT); src += arg.size; Builder::appendArgumentToFunction(ret, arg.str); skipSpace(src); if (*src == ')') { break; } if (*src == ',') { src++; continue; } abort(); } src++; Builder::setBlockContent(ret, parseBracketedBlock(src)); // TODO: parse expression? return ret; } NodeRef parseVar(char*& src, const char* seps, bool is_const) { NodeRef ret = Builder::makeVar(is_const); while (1) { skipSpace(src); if (*src == ';') { break; } Frag name(src); assert(name.type == IDENT); NodeRef value; src += name.size; skipSpace(src); if (*src == '=') { src++; skipSpace(src); value = parseElement(src, ";,"); } Builder::appendToVar(ret, name.str, value); skipSpace(src); if (*src == ';') { break; } if (*src == ',') { src++; continue; } abort(); } src++; return ret; } NodeRef parseReturn(char*& src, const char* seps) { skipSpace(src); NodeRef value = !hasChar(seps, *src) ? parseElement(src, seps) : nullptr; skipSpace(src); assert(hasChar(seps, *src)); if (*src == ';') { src++; } return Builder::makeReturn(value); } NodeRef parseIf(char*& src, const char* seps) { NodeRef condition = parseParenned(src); NodeRef ifTrue = parseMaybeBracketed(src, seps); skipSpace(src); NodeRef ifFalse; if (!hasChar(seps, *src)) { Frag next(src); if (next.type == KEYWORD && next.str == ELSE) { src += next.size; ifFalse = parseMaybeBracketed(src, seps); } } return Builder::makeIf(condition, ifTrue, ifFalse); } NodeRef parseDo(char*& src, const char* seps) { NodeRef body = parseMaybeBracketed(src, seps); skipSpace(src); Frag next(src); assert(next.type == KEYWORD && next.str == WHILE); src += next.size; NodeRef condition = parseParenned(src); return Builder::makeDo(body, condition); } NodeRef parseWhile(char*& src, const char* seps) { NodeRef condition = parseParenned(src); NodeRef body = parseMaybeBracketed(src, seps); return Builder::makeWhile(condition, body); } NodeRef parseFor(char*& src, const char* seps) { skipSpace(src); assert(*src == '('); src++; NodeRef init = parseElement(src, ";"); skipSpace(src); assert(*src == ';'); src++; NodeRef condition = parseElement(src, ";"); skipSpace(src); assert(*src == ';'); src++; NodeRef inc = parseElement(src, ")"); skipSpace(src); assert(*src == ')'); src++; NodeRef body = parseMaybeBracketed(src, seps); return Builder::makeFor(init, condition, inc, body); } NodeRef parseBreak(char*& src, const char* seps) { skipSpace(src); Frag next(src); if (next.type == IDENT) { src += next.size; } return Builder::makeBreak(next.type == IDENT ? next.str : IString()); } NodeRef parseContinue(char*& src, const char* seps) { skipSpace(src); Frag next(src); if (next.type == IDENT) { src += next.size; } return Builder::makeContinue(next.type == IDENT ? next.str : IString()); } NodeRef parseSwitch(char*& src, const char* seps) { NodeRef ret = Builder::makeSwitch(parseParenned(src)); skipSpace(src); assert(*src == '{'); src++; while (1) { // find all cases and possibly a default skipSpace(src); if (*src == '}') { break; } Frag next(src); if (next.type == KEYWORD) { if (next.str == CASE) { src += next.size; skipSpace(src); NodeRef arg; Frag value(src); if (value.isNumber()) { arg = parseFrag(value); src += value.size; } else if (value.type == OPERATOR) { // negative number assert(value.str == MINUS); src += value.size; skipSpace(src); Frag value2(src); assert(value2.isNumber()); arg = Builder::makePrefix(MINUS, parseFrag(value2)); src += value2.size; } else { // identifier and function call assert(value.type == IDENT); src += value.size; skipSpace(src); arg = parseCall(parseFrag(value), src); } Builder::appendCaseToSwitch(ret, arg); skipSpace(src); assert(*src == ':'); src++; continue; } else if (next.str == DEFAULT) { src += next.size; Builder::appendDefaultToSwitch(ret); skipSpace(src); assert(*src == ':'); src++; continue; } // otherwise, may be some keyword that happens to start a block (e.g. // case 1: _return_ 5) } // not case X: or default: or }, so must be some code skipSpace(src); bool explicitBlock = *src == '{'; NodeRef subBlock = explicitBlock ? parseBracketedBlock(src) : parseBlock(src, ";}", CASE, DEFAULT); Builder::appendCodeToSwitch(ret, subBlock, explicitBlock); } skipSpace(src); assert(*src == '}'); src++; return ret; } NodeRef parseNew(char*& src, const char* seps) { return Builder::makeNew(parseElement(src, seps)); } NodeRef parseAfterIdent(Frag& frag, char*& src, const char* seps) { skipSpace(src); if (*src == '(') { return parseExpression(parseCall(parseFrag(frag), src), src, seps); } if (*src == '[') { return parseExpression(parseIndexing(parseFrag(frag), src), src, seps); } if (*src == ':' && expressionPartsStack.back().size() == 0) { src++; skipSpace(src); NodeRef inner; if (*src == '{') { // context lets us know this is not an object, but a block inner = parseBracketedBlock(src); } else { inner = parseElement(src, seps); } return Builder::makeLabel(frag.str, inner); } if (*src == '.') { return parseExpression(parseDotting(parseFrag(frag), src), src, seps); } return parseExpression(parseFrag(frag), src, seps); } NodeRef parseCall(NodeRef target, char*& src) { expressionPartsStack.resize(expressionPartsStack.size() + 1); assert(*src == '('); src++; NodeRef ret = Builder::makeCall(target); while (1) { skipSpace(src); if (*src == ')') { break; } Builder::appendToCall(ret, parseElement(src, ",)")); skipSpace(src); if (*src == ')') { break; } if (*src == ',') { src++; continue; } abort(); } src++; assert(expressionPartsStack.back().size() == 0); expressionPartsStack.pop_back(); return ret; } NodeRef parseIndexing(NodeRef target, char*& src) { expressionPartsStack.resize(expressionPartsStack.size() + 1); assert(*src == '['); src++; NodeRef ret = Builder::makeIndexing(target, parseElement(src, "]")); skipSpace(src); assert(*src == ']'); src++; assert(expressionPartsStack.back().size() == 0); expressionPartsStack.pop_back(); return ret; } NodeRef parseDotting(NodeRef target, char*& src) { assert(*src == '.'); src++; Frag key(src); assert(key.type == IDENT); src += key.size; return Builder::makeDot(target, key.str); } NodeRef parseAfterParen(char*& src) { expressionPartsStack.resize(expressionPartsStack.size() + 1); skipSpace(src); NodeRef ret = parseElement(src, ")"); skipSpace(src); assert(*src == ')'); src++; assert(expressionPartsStack.back().size() == 0); expressionPartsStack.pop_back(); return ret; } NodeRef parseAfterBrace(char*& src) { expressionPartsStack.resize(expressionPartsStack.size() + 1); NodeRef ret = Builder::makeArray(); while (1) { skipSpace(src); assert(*src); if (*src == ']') { break; } NodeRef element = parseElement(src, ",]"); Builder::appendToArray(ret, element); skipSpace(src); if (*src == ']') { break; } if (*src == ',') { src++; continue; } abort(); } src++; return ret; } NodeRef parseAfterCurly(char*& src) { expressionPartsStack.resize(expressionPartsStack.size() + 1); NodeRef ret = Builder::makeObject(); while (1) { skipSpace(src); assert(*src); if (*src == '}') { break; } Frag key(src); assert(key.type == IDENT || key.type == STRING); src += key.size; skipSpace(src); assert(*src == ':'); src++; NodeRef value = parseElement(src, ",}"); Builder::appendToObject(ret, key.str, value); skipSpace(src); if (*src == '}') { break; } if (*src == ',') { src++; continue; } abort(); } src++; return ret; } void dumpParts(ExpressionParts& parts, int i) { printf("expressionparts: %d (at %d)\n", parts.size(), i); printf("| "); for (int i = 0; i < parts.size(); i++) { if (parts[i].isNode) { parts[i].getNode()->stringify(std::cout); printf(" "); } else { printf(" _%s_ ", parts[i].getOp().str); } } printf("|\n"); } NodeRef makeBinary(NodeRef left, IString op, NodeRef right) { if (op == PERIOD) { return Builder::makeDot(left, right); } else { return Builder::makeBinary(left, op, right); } } NodeRef parseExpression(ExpressionElement initial, char*& src, const char* seps) { // dump("parseExpression", src); ExpressionParts& parts = expressionPartsStack.back(); skipSpace(src); if (*src == 0 || hasChar(seps, *src)) { if (parts.size() > 0) { parts.push_back(initial); // cherry on top of the cake } return initial.getNode(); } bool top = parts.size() == 0; if (initial.isNode) { Frag next(src); if (next.type == OPERATOR) { parts.push_back(initial); src += next.size; parts.push_back(next.str); } else { if (*src == '(') { initial = parseCall(initial.getNode(), src); } else if (*src == '[') { initial = parseIndexing(initial.getNode(), src); } else { dump("bad parseExpression state", src); abort(); } return parseExpression(initial, src, seps); } } else { parts.push_back(initial); } NodeRef last = parseElement(src, seps); if (!top) { return last; } { // |parts| may have been invalidated by that call ExpressionParts& parts = expressionPartsStack.back(); // we are the toplevel. sort it all out // collapse right to left, highest priority first // dumpParts(parts, 0); for (auto& ops : operatorClasses) { if (ops.rtl) { // right to left for (int i = parts.size() - 1; i >= 0; i--) { if (parts[i].isNode) { continue; } IString op = parts[i].getOp(); if (!ops.ops.has(op)) { continue; } if (ops.type == OperatorClass::Binary && i > 0 && i < (int)parts.size() - 1) { parts[i] = makeBinary(parts[i - 1].getNode(), op, parts[i + 1].getNode()); parts.erase(parts.begin() + i + 1); parts.erase(parts.begin() + i - 1); } else if (ops.type == OperatorClass::Prefix && i < (int)parts.size() - 1) { if (i > 0 && parts[i - 1].isNode) { // cannot apply prefix operator if it would join two nodes continue; } parts[i] = Builder::makePrefix(op, parts[i + 1].getNode()); parts.erase(parts.begin() + i + 1); } else if (ops.type == OperatorClass::Tertiary) { // we must be at X ? Y : Z // ^ // dumpParts(parts, i); if (op != COLON) { continue; } assert(i < (int)parts.size() - 1 && i >= 3); if (parts[i - 2].getOp() != QUESTION) { continue; // e.g. x ? y ? 1 : 0 : 2 } parts[i - 3] = Builder::makeConditional(parts[i - 3].getNode(), parts[i - 1].getNode(), parts[i + 1].getNode()); parts.erase(parts.begin() + i - 2, parts.begin() + i + 2); // basically a reset, due to things like x ? y ? 1 : 0 : 2 i = parts.size(); } // TODO: postfix } } else { // left to right for (int i = 0; i < (int)parts.size(); i++) { if (parts[i].isNode) { continue; } IString op = parts[i].getOp(); if (!ops.ops.has(op)) { continue; } if (ops.type == OperatorClass::Binary && i > 0 && i < (int)parts.size() - 1) { parts[i] = makeBinary(parts[i - 1].getNode(), op, parts[i + 1].getNode()); parts.erase(parts.begin() + i + 1); parts.erase(parts.begin() + i - 1); i--; } else if (ops.type == OperatorClass::Prefix && i < (int)parts.size() - 1) { if (i > 0 && parts[i - 1].isNode) { // cannot apply prefix operator if it would join two nodes continue; } parts[i] = Builder::makePrefix(op, parts[i + 1].getNode()); parts.erase(parts.begin() + i + 1); // allow a previous prefix operator to cascade i = std::max(i - 2, 0); } // TODO: tertiary, postfix } } } assert(parts.size() == 1); NodeRef ret = parts[0].getNode(); parts.clear(); return ret; } } // Parses a block of code (e.g. a bunch of statements inside {,}, or the top // level of o file) NodeRef parseBlock(char*& src, const char* seps = ";", IString keywordSep1 = IString(), IString keywordSep2 = IString()) { NodeRef block = Builder::makeBlock(); // dump("parseBlock", src); while (1) { skipSpace(src); if (*src == 0) { break; } if (*src == ';') { src++; // skip a statement in this block continue; } if (hasChar(seps, *src)) { break; } if (!!keywordSep1) { Frag next(src); if (next.type == KEYWORD && next.str == keywordSep1) { break; } } if (!!keywordSep2) { Frag next(src); if (next.type == KEYWORD && next.str == keywordSep2) { break; } } NodeRef element = parseElementOrStatement(src, seps); Builder::appendToBlock(block, element); } return block; } NodeRef parseBracketedBlock(char*& src) { skipSpace(src); assert(*src == '{'); src++; // the two are not symmetrical, ; is just internally separating, } is the // final one - parseBlock knows all this NodeRef block = parseBlock(src, ";}"); assert(*src == '}'); src++; return block; } NodeRef parseElementOrStatement(char*& src, const char* seps) { skipSpace(src); if (*src == ';') { src++; // we don't need the brackets here, but oh well return Builder::makeBlock(); } if (*src == '{') { // detect a trivial {} in a statement context char* before = src; src++; skipSpace(src); if (*src == '}') { src++; // we don't need the brackets here, but oh well return Builder::makeBlock(); } src = before; } NodeRef ret = parseElement(src, seps); skipSpace(src); if (*src == ';') { ret = Builder::makeStatement(ret); src++; } return ret; } NodeRef parseMaybeBracketed(char*& src, const char* seps) { skipSpace(src); return *src == '{' ? parseBracketedBlock(src) : parseElementOrStatement(src, seps); } NodeRef parseParenned(char*& src) { skipSpace(src); assert(*src == '('); src++; NodeRef ret = parseElement(src, ")"); skipSpace(src); assert(*src == ')'); src++; return ret; } // Debugging char* allSource = nullptr; int allSize = 0; static void dump(const char* where, char* curr) { /* printf("%s:\n=============\n", where); for (int i = 0; i < allSize; i++) printf("%c", allSource[i] ? allSource[i] : '?'); printf("\n"); for (int i = 0; i < (curr - allSource); i++) printf(" "); printf("^\n=============\n"); */ fprintf(stderr, "%s:\n==========\n", where); int newlinesLeft = 2; int charsLeft = 200; while (*curr) { if (*curr == '\n') { newlinesLeft--; if (newlinesLeft == 0) { break; } } charsLeft--; if (charsLeft == 0) { break; } fprintf(stderr, "%c", *curr++); } fprintf(stderr, "\n\n"); } public: Parser() { expressionPartsStack.resize(1); } // Highest-level parsing, as of a JavaScript script file. NodeRef parseToplevel(char* src) { allSource = src; allSize = strlen(src); NodeRef toplevel = Builder::makeToplevel(); Builder::setBlockContent(toplevel, parseBlock(src)); return toplevel; } }; } // namespace cashew #endif // wasm_parser_h