bool Parser::parseExpressionList(std::vector<Expression>& list, int min, int max) { bool valid = true; list.clear(); list.reserve(max >= 0 ? max : 32); const Token& start = peekToken(); Expression exp = parseExpression(); list.push_back(exp); if (exp.isLoaded() == false) { printError(start,L"Parameter failure"); getTokenizer()->skipLookahead(); valid = false; } while (peekToken().type == TokenType::Comma) { eatToken(); exp = parseExpression(); list.push_back(exp); if (exp.isLoaded() == false) { printError(start,L"Parameter failure"); getTokenizer()->skipLookahead(); valid = false; } } if (list.size() < (size_t) min) { printError(start,L"Not enough parameters (min %d)",min); return false; } if (max != -1 && (size_t) max < list.size()) { printError(start,L"Too many parameters (max %d)",max); return false; } return valid; }
CAssemblerCommand* Parser::parseMacroCall() { const Token& start = peekToken(); if (start.type != TokenType::Identifier) return nullptr; auto it = macros.find(start.getStringValue()); if (it == macros.end()) return nullptr; ParserMacro& macro = it->second; eatToken(); // create a token stream for the macro content, // registering replacements for parameter values TokenStreamTokenizer macroTokenizer; std::set<std::wstring> identifierParameters; for (size_t i = 0; i < macro.parameters.size(); i++) { if (i != 0) { if (nextToken().type != TokenType::Comma) return nullptr; } if (i == macro.parameters.size()) { size_t count = macro.parameters.size(); while (peekToken().type == TokenType::Comma) { eatToken(); parseExpression(); } printError(start,L"Not enough macro arguments (%d vs %d)",count,macro.parameters.size()); return nullptr; } TokenizerPosition startPos = getTokenizer()->getPosition(); Expression exp = parseExpression(); if (exp.isLoaded() == false) return nullptr; TokenizerPosition endPos = getTokenizer()->getPosition(); std::vector<Token> tokens = getTokenizer()->getTokens(startPos,endPos); // remember any single identifier parameters for the label replacement if (tokens.size() == 1 && tokens[0].type == TokenType::Identifier) identifierParameters.insert(tokens[0].getStringValue()); // give them as a replacement to new tokenizer macroTokenizer.registerReplacement(macro.parameters[i],tokens); } if (peekToken().type == TokenType::Comma) { size_t count = macro.parameters.size(); while (peekToken().type == TokenType::Comma) { eatToken(); parseExpression(); count++; } printError(start,L"Too many macro arguments (%d vs %d)",count,macro.parameters.size()); return nullptr; } // a macro is fully parsed once when it's loaded // to gather all labels. it's not necessary to // instantiate other macros at that time if (initializingMacro) return new DummyCommand(); // the first time a macro is instantiated, it needs to be analyzed // for labels if (macro.counter == 0) { initializingMacro = true; // parse the short lived next command macroTokenizer.init(macro.content); CAssemblerCommand* command = parse(¯oTokenizer,true); delete command; macro.labels = macroLabels; macroLabels.clear(); initializingMacro = false; } // register labels and replacements for (const std::wstring& label: macro.labels) { // check if the label is using the name of a parameter // in that case, don't register a unique replacement if (identifierParameters.find(label) != identifierParameters.end()) continue; // otherwise make sure the name is unique std::wstring fullName; if (Global.symbolTable.isLocalSymbol(label)) fullName = formatString(L"@@%s_%s_%08X",macro.name,label.substr(2),macro.counter); else if (Global.symbolTable.isStaticSymbol(label)) fullName = formatString(L"@%s_%s_%08X",macro.name,label.substr(1),macro.counter); else fullName = formatString(L"%s_%s_%08X",macro.name,label,macro.counter); macroTokenizer.registerReplacement(label,fullName); } macroTokenizer.init(macro.content); macro.counter++; return parse(¯oTokenizer,true); }
bool Parser::checkMacroDefinition() { const Token& first = peekToken(); if (first.type != TokenType::Identifier) return false; if (!first.stringValueStartsWith(L'.') || first.getStringValue() != L".macro") return false; eatToken(); // nested macro definitions are not allowed if (initializingMacro) { printError(first,L"Nested macro definitions not allowed"); while (!atEnd()) { const Token& token = nextToken(); if (token.type == TokenType::Identifier && token.getStringValue() == L".endmacro") break; } return true; } std::vector<Expression> parameters; if (parseExpressionList(parameters,1,-1) == false) return false; // load name std::wstring macroName; if (parameters[0].evaluateIdentifier(macroName) == false) return false; // duplicate check the macro ParserMacro ¯o = macros[macroName]; if (macro.name.length() != 0) { printError(first,L"Macro \"%s\" already defined",macro.name); return false; } // and register it macro.name = macroName; macro.counter = 0; // load parameters for (size_t i = 1; i < parameters.size(); i++) { std::wstring name; if (parameters[i].evaluateIdentifier(name) == false) return false; macro.parameters.push_back(name); } // load macro content TokenizerPosition start = getTokenizer()->getPosition(); bool valid = false; while (atEnd() == false) { const Token& tok = nextToken(); if (tok.type == TokenType::Identifier && tok.getStringValue() == L".endmacro") { valid = true; break; } } // no .endmacro, not valid if (valid == false) return true; // get content TokenizerPosition end = getTokenizer()->getPosition().previous(); macro.content = getTokenizer()->getTokens(start,end); return true; }
Expression Parser::parseExpression() { return ::parseExpression(*getTokenizer()); }
bool IWORKParser::parse() { const shared_ptr<xmlTextReader> sharedReader(xmlReaderForIO(readFromStream, closeStream, m_input.get(), "", nullptr, 0), xmlFreeTextReader); if (!sharedReader) return false; xmlTextReaderPtr reader = sharedReader.get(); assert(reader); const IWORKTokenizer &tokenizer = getTokenizer(); stack<IWORKXMLContextPtr_t> contextStack; int ret = xmlTextReaderRead(reader); contextStack.push(createDocumentContext()); bool keynoteDocTypeChecked=false; char const *defaultNS=nullptr; while ((1 == ret)) { switch (xmlTextReaderNodeType(reader)) { case XML_READER_TYPE_ELEMENT: { if (!keynoteDocTypeChecked) { // check for keynote 1 file with doctype node and not a namespace in first node keynoteDocTypeChecked=true; if (xmlTextReaderNodeType(reader)==XML_READER_TYPE_ELEMENT && xmlTextReaderConstNamespaceUri(reader)==nullptr) defaultNS="http://developer.apple.com/schemas/APXL"; } const int id = tokenizer.getQualifiedId(char_cast(xmlTextReaderConstLocalName(reader)), defaultNS ? defaultNS : char_cast(xmlTextReaderConstNamespaceUri(reader))); IWORKXMLContextPtr_t newContext = contextStack.top()->element(id); if (!newContext) newContext = createDiscardContext(); const bool isEmpty = xmlTextReaderIsEmptyElement(reader); newContext->startOfElement(); if (xmlTextReaderHasAttributes(reader)) { ret = xmlTextReaderMoveToFirstAttribute(reader); while (1 == ret) { processAttribute(reader, newContext, tokenizer); ret = xmlTextReaderMoveToNextAttribute(reader); } } if (isEmpty) newContext->endOfElement(); else contextStack.push(newContext); break; } case XML_READER_TYPE_END_ELEMENT: { contextStack.top()->endOfElement(); contextStack.pop(); break; } case XML_READER_TYPE_CDATA : { const xmlChar *text = xmlTextReaderConstValue(reader); if (text) contextStack.top()->CDATA(char_cast(text)); break; } case XML_READER_TYPE_TEXT : { xmlChar *const text = xmlTextReaderReadString(reader); contextStack.top()->text(char_cast(text)); xmlFree(text); break; } default: break; } ret = xmlTextReaderRead(reader); } while (!contextStack.empty()) // finish parsing in case of broken XML { contextStack.top()->endOfElement(); contextStack.pop(); } xmlTextReaderClose(reader); return true; }
bool IWORKParser::parse() { const shared_ptr<xmlTextReader> sharedReader(xmlReaderForIO(readFromStream, closeStream, m_input.get(), "", 0, 0), xmlFreeTextReader); if (!sharedReader) return false; xmlTextReaderPtr reader = sharedReader.get(); assert(reader); const IWORKTokenizer &tokenizer = getTokenizer(); stack<IWORKXMLContextPtr_t> contextStack; int ret = xmlTextReaderRead(reader); contextStack.push(createDocumentContext()); while ((1 == ret)) { switch (xmlTextReaderNodeType(reader)) { case XML_READER_TYPE_ELEMENT: { const int id = tokenizer.getQualifiedId(char_cast(xmlTextReaderConstLocalName(reader)), char_cast(xmlTextReaderConstNamespaceUri(reader))); IWORKXMLContextPtr_t newContext = contextStack.top()->element(id); if (!newContext) newContext = createDiscardContext(); const bool isEmpty = xmlTextReaderIsEmptyElement(reader); newContext->startOfElement(); if (xmlTextReaderHasAttributes(reader)) { ret = xmlTextReaderMoveToFirstAttribute(reader); while (1 == ret) { processAttribute(reader, newContext, tokenizer); ret = xmlTextReaderMoveToNextAttribute(reader); } } if (isEmpty) newContext->endOfElement(); else contextStack.push(newContext); break; } case XML_READER_TYPE_END_ELEMENT: { contextStack.top()->endOfElement(); contextStack.pop(); break; } case XML_READER_TYPE_TEXT : { xmlChar *const text = xmlTextReaderReadString(reader); contextStack.top()->text(char_cast(text)); xmlFree(text); break; } default: break; } ret = xmlTextReaderRead(reader); } while (!contextStack.empty()) // finish parsing in case of broken XML { contextStack.top()->endOfElement(); contextStack.pop(); } xmlTextReaderClose(reader); return true; }