metaforce/hecl/lib/Frontend/Parser.cpp

194 lines
5.2 KiB
C++

#include "HECL/HECL.hpp"
#include "HECL/Frontend.hpp"
/* Syntatical token parsing system */
namespace HECL
{
namespace Frontend
{
void Parser::skipWhitespace(std::string::const_iterator& it)
{
while (it != m_source->cend())
{
while (it != m_source->cend() && isspace(*it))
++it;
/* Skip comment line */
if (it != m_source->cend() && *it == '#')
{
while (it != m_source->cend() && *it != '\n')
++it;
if (it != m_source->cend() && *it == '\n')
++it;
continue;
}
break;
}
}
void Parser::reset(const std::string& source)
{
m_source = &source;
m_sourceIt = m_source->cbegin();
m_parenStack.clear();
m_reset = true;
}
Parser::Token Parser::consumeToken()
{
if (!m_source)
return Parser::Token(TokenType::None, SourceLocation());
/* If parser has just been reset, emit begin token */
if (m_reset)
{
m_reset = false;
return Parser::Token(TokenType::SourceBegin, getLocation());
}
/* Skip whitespace */
skipWhitespace(m_sourceIt);
/* Check for source end */
if (m_sourceIt == m_source->cend())
return Parser::Token(TokenType::SourceEnd, getLocation());
/* Check for numeric literal */
{
char* strEnd;
float val = strtof(&*m_sourceIt, &strEnd);
if (&*m_sourceIt != strEnd)
{
Parser::Token tok(TokenType::NumLiteral, getLocation());
tok.m_tokenFloat = val;
m_sourceIt += (strEnd - &*m_sourceIt);
return tok;
}
}
/* Check for swizzle op */
if (*m_sourceIt == '.')
{
int count = 0;
std::string::const_iterator tmp = m_sourceIt + 1;
if (tmp != m_source->cend())
{
for (int i=0 ; i<4 ; ++i)
{
std::string::const_iterator tmp2 = tmp + i;
if (tmp2 == m_source->cend())
break;
char ch = tolower(*tmp2);
if (ch >= 'w' && ch <= 'z')
++count;
else if (ch == 'r' || ch == 'g' || ch == 'b' || ch == 'a')
++count;
else
break;
}
}
if (count)
{
Parser::Token tok(TokenType::VectorSwizzle, getLocation());
for (int i=0 ; i<count ; ++i)
{
std::string::const_iterator tmp2 = tmp + i;
tok.m_tokenString += tolower(*tmp2);
}
m_sourceIt = tmp + count;
return tok;
}
}
/* Check for arithmetic op */
if (*m_sourceIt == '+' || *m_sourceIt == '-' || *m_sourceIt == '*' || *m_sourceIt == '/')
{
Parser::Token tok(TokenType::ArithmeticOp, getLocation());
tok.m_tokenInt = *m_sourceIt;
++m_sourceIt;
return tok;
}
/* Check for parenthesis end (group or function call) */
if (*m_sourceIt == ')')
{
if (m_parenStack.empty())
{
m_diag.reportParserErr(getLocation(), "unexpected ')' while parsing");
return Parser::Token(TokenType::None, SourceLocation());
}
Parser::Token tok(m_parenStack.back(), getLocation());
++m_sourceIt;
m_parenStack.pop_back();
return tok;
}
/* Check for group start */
if (*m_sourceIt == '(')
{
m_parenStack.push_back(TokenType::EvalGroupEnd);
Parser::Token tok(TokenType::EvalGroupStart, getLocation());
++m_sourceIt;
return tok;
}
/* Check for function start */
if (isalpha(*m_sourceIt) || *m_sourceIt == '_')
{
std::string::const_iterator tmp = m_sourceIt + 1;
while (tmp != m_source->cend() && (isalnum(*tmp) || *tmp == '_') && *tmp != '(')
++tmp;
std::string::const_iterator nameEnd = tmp;
skipWhitespace(tmp);
if (*tmp == '(')
{
Parser::Token tok(TokenType::FunctionStart, getLocation());
tok.m_tokenString.assign(m_sourceIt, nameEnd);
m_sourceIt = tmp + 1;
m_parenStack.push_back(TokenType::FunctionEnd);
return tok;
}
}
/* Check for function arg delimitation */
if (*m_sourceIt == ',')
{
if (m_parenStack.empty() || m_parenStack.back() != TokenType::FunctionEnd)
{
m_diag.reportParserErr(getLocation(), "unexpected ',' while parsing");
return Parser::Token(TokenType::None, SourceLocation());
}
Parser::Token tok(TokenType::FunctionArgDelim, getLocation());
++m_sourceIt;
return tok;
}
/* Error condition if reached */
m_diag.reportParserErr(getLocation(), "unexpected token while parsing");
return Parser::Token(TokenType::None, SourceLocation());
}
SourceLocation Parser::getLocation() const
{
if (!m_source)
return SourceLocation();
std::string::const_iterator it = m_source->cbegin();
int line = 0;
int col = 0;
for (; it != m_sourceIt ; ++it)
{
++col;
if (*it == '\n')
{
++line;
col = 0;
}
}
return {line+1, col+1};
}
}
}