mirror of https://github.com/OpenMW/openmw.git
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.
293 lines
8.8 KiB
C++
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();
|
|
}
|
|
}
|