You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
openmw/components/compiler/controlparser.cpp

293 lines
8.8 KiB
C++

#include "controlparser.hpp"
#include <algorithm>
#include <iterator>
#include <stdexcept>
#include "errorhandler.hpp"
#include "generator.hpp"
#include "scanner.hpp"
#include "skipparser.hpp"
namespace Compiler
{
bool ControlParser::parseIfBody(int keyword, const TokenLoc& loc, Scanner& scanner)
{
if (keyword == Scanner::K_endif || keyword == Scanner::K_elseif || keyword == Scanner::K_else)
{
std::pair<Codes, Codes> entry;
if (mState != IfElseBodyState)
mExprParser.append(entry.first);
std::copy(mCodeBlock.begin(), mCodeBlock.end(), std::back_inserter(entry.second));
mIfCode.push_back(entry);
mCodeBlock.clear();
if (keyword == Scanner::K_endif)
{
// store code for if-cascade
Codes codes;
for (auto iter(mIfCode.rbegin()); iter != mIfCode.rend(); ++iter)
{
Codes block;
if (iter != mIfCode.rbegin())
Generator::jump(iter->second, static_cast<int>(codes.size() + 1));
if (!iter->first.empty())
{
// if or elseif
std::copy(iter->first.begin(), iter->first.end(), std::back_inserter(block));
Generator::jumpOnZero(block, static_cast<int>(iter->second.size() + 1));
}
std::copy(iter->second.begin(), iter->second.end(), std::back_inserter(block));
std::swap(codes, block);
std::copy(block.begin(), block.end(), std::back_inserter(codes));
}
std::copy(codes.begin(), codes.end(), std::back_inserter(mCode));
mIfCode.clear();
mState = IfEndifState;
}
else if (keyword == Scanner::K_elseif)
{
mExprParser.reset();
scanner.scan(mExprParser);
mState = IfElseifEndState;
}
else if (keyword == Scanner::K_else)
{
mState = IfElseJunkState; /// \todo should be IfElseEndState; add an option for that
}
return true;
}
else if (keyword == Scanner::K_if || keyword == Scanner::K_while)
{
// nested
ControlParser parser(getErrorHandler(), getContext(), mLocals, mLiterals);
if (parser.parseKeyword(keyword, loc, scanner))
scanner.scan(parser);
parser.appendCode(mCodeBlock);
return true;
}
else
{
mLineParser.reset();
if (mLineParser.parseKeyword(keyword, loc, scanner))
scanner.scan(mLineParser);
return true;
}
}
bool ControlParser::parseWhileBody(int keyword, const TokenLoc& loc, Scanner& scanner)
{
if (keyword == Scanner::K_endwhile)
{
Codes loop;
Codes expr;
mExprParser.append(expr);
Generator::jump(loop, -static_cast<int>(mCodeBlock.size() + expr.size()));
std::copy(expr.begin(), expr.end(), std::back_inserter(mCode));
Codes skip;
Generator::jumpOnZero(skip, static_cast<int>(mCodeBlock.size() + loop.size() + 1));
std::copy(skip.begin(), skip.end(), std::back_inserter(mCode));
std::copy(mCodeBlock.begin(), mCodeBlock.end(), std::back_inserter(mCode));
Codes loop2;
Generator::jump(loop2, -static_cast<int>(mCodeBlock.size() + expr.size() + skip.size()));
if (loop.size() != loop2.size())
throw std::logic_error("Internal compiler error: failed to generate a while loop");
std::copy(loop2.begin(), loop2.end(), std::back_inserter(mCode));
mState = WhileEndwhileState;
return true;
}
else if (keyword == Scanner::K_if || keyword == Scanner::K_while)
{
// nested
ControlParser parser(getErrorHandler(), getContext(), mLocals, mLiterals);
if (parser.parseKeyword(keyword, loc, scanner))
scanner.scan(parser);
parser.appendCode(mCodeBlock);
return true;
}
else
{
mLineParser.reset();
if (mLineParser.parseKeyword(keyword, loc, scanner))
scanner.scan(mLineParser);
return true;
}
}
ControlParser::ControlParser(ErrorHandler& errorHandler, const Context& context, Locals& locals, Literals& literals)
: Parser(errorHandler, context)
, mLocals(locals)
, mLiterals(literals)
, mLineParser(errorHandler, context, locals, literals, mCodeBlock)
, mExprParser(errorHandler, context, locals, literals)
, mState(StartState)
{
}
void ControlParser::appendCode(std::vector<Interpreter::Type_Code>& code) const
{
std::copy(mCode.begin(), mCode.end(), std::back_inserter(code));
}
bool ControlParser::parseName(const std::string& name, const TokenLoc& loc, Scanner& scanner)
{
if (mState == IfBodyState || mState == IfElseifBodyState || mState == IfElseBodyState
|| mState == WhileBodyState)
{
scanner.putbackName(name, loc);
mLineParser.reset();
scanner.scan(mLineParser);
return true;
}
else if (mState == IfElseJunkState)
{
getErrorHandler().warning("Extra text after else", loc);
SkipParser skip(getErrorHandler(), getContext());
scanner.scan(skip);
mState = IfElseBodyState;
return true;
}
return Parser::parseName(name, loc, scanner);
}
bool ControlParser::parseKeyword(int keyword, const TokenLoc& loc, Scanner& scanner)
{
if (mState == StartState)
{
if (keyword == Scanner::K_if || keyword == Scanner::K_elseif)
{
if (keyword == Scanner::K_elseif)
getErrorHandler().warning("elseif without matching if", loc);
mExprParser.reset();
scanner.scan(mExprParser);
mState = IfEndState;
return true;
}
else if (keyword == Scanner::K_while)
{
mExprParser.reset();
scanner.scan(mExprParser);
mState = WhileEndState;
return true;
}
}
else if (mState == IfBodyState || mState == IfElseifBodyState || mState == IfElseBodyState)
{
if (parseIfBody(keyword, loc, scanner))
return true;
}
else if (mState == WhileBodyState)
{
if (parseWhileBody(keyword, loc, scanner))
return true;
}
else if (mState == IfElseJunkState)
{
getErrorHandler().warning("Extra text after else", loc);
SkipParser skip(getErrorHandler(), getContext());
scanner.scan(skip);
mState = IfElseBodyState;
return true;
}
return Parser::parseKeyword(keyword, loc, scanner);
}
bool ControlParser::parseSpecial(int code, const TokenLoc& loc, Scanner& scanner)
{
if (code == Scanner::S_newline)
{
switch (mState)
{
case IfEndState:
mState = IfBodyState;
return true;
case IfElseifEndState:
mState = IfElseifBodyState;
return true;
case IfElseEndState:
mState = IfElseBodyState;
return true;
case IfElseJunkState:
mState = IfElseBodyState;
return true;
case WhileEndState:
mState = WhileBodyState;
return true;
case IfBodyState:
case IfElseifBodyState:
case IfElseBodyState:
case WhileBodyState:
return true; // empty line
case IfEndifState:
case WhileEndwhileState:
return false;
default:;
}
}
else if (mState == IfElseJunkState)
{
getErrorHandler().warning("Extra text after else", loc);
SkipParser skip(getErrorHandler(), getContext());
scanner.scan(skip);
mState = IfElseBodyState;
return true;
}
return Parser::parseSpecial(code, loc, scanner);
}
void ControlParser::reset()
{
mCode.clear();
mCodeBlock.clear();
mIfCode.clear();
mState = StartState;
Parser::reset();
}
}