mirror of https://github.com/AxioDL/metaforce.git
194 lines
5.2 KiB
C++
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};
|
|
}
|
|
|
|
}
|
|
}
|