#include "lexer.hpp" #include #include #include #include #include #include #include namespace fx { namespace Lexer { Lexer::Lexer(std::string_view buffer) : mHead(buffer.data()) , mTail(mHead + buffer.length()) , mAbsolutePos(0) , mColumn(0) , mLine(0) , mBuffer(buffer) , mLastToken(Eof{}) { } Token Lexer::next() { if (mLookahead) { auto token = *mLookahead; drop(); return token; } mLastToken = scanToken(); return mLastToken; } Token Lexer::peek() { if (!mLookahead) mLookahead = scanToken(); return *mLookahead; } void Lexer::drop() { mLookahead = std::nullopt; } std::optional Lexer::jump() { bool multi = false; bool single = false; auto start = mHead; std::size_t level = 1; mLastJumpBlock.line = mLine; if (head() == '}') { mLastJumpBlock.content = {}; return mLastJumpBlock.content; } for (; mHead != mTail; advance()) { if (head() == '\n') { mLine++; mColumn = 0; if (single) { single = false; continue; } } else if (multi && head() == '*' && peekChar('/')) { multi = false; advance(); continue; } else if (multi || single) { continue; } else if (head() == '/' && peekChar('/')) { single = true; advance(); continue; } else if (head() == '/' && peekChar('*')) { multi = true; advance(); continue; } if (head() == '{') level++; else if (head() == '}') level--; if (level == 0) { mHead--; mLine--; auto sv = std::string_view{ start, static_cast(mHead + 1 - start) }; mLastJumpBlock.content = sv; return sv; } } mLastJumpBlock = {}; return std::nullopt; } Lexer::Block Lexer::getLastJumpBlock() const { return mLastJumpBlock; } [[noreturn]] void Lexer::error(const std::string& msg) { throw LexerException(Misc::StringUtils::format("Line %zu Col %zu. %s", mLine + 1, mColumn, msg)); } void Lexer::advance() { mAbsolutePos++; mHead++; mColumn++; } char Lexer::head() { return *mHead; } bool Lexer::peekChar(char c) { if (mHead == mTail) return false; return *(mHead + 1) == c; } Token Lexer::scanToken() { while (true) { if (mHead == mTail) return { Eof{} }; if (head() == '\n') { mLine++; mColumn = 0; } if (!std::isspace(head())) break; advance(); } if (head() == '\"') return scanStringLiteral(); if (std::isalpha(head())) return scanLiteral(); if (std::isdigit(head()) || head() == '.' || head() == '-') return scanNumber(); switch (head()) { case '=': advance(); return { Equal{} }; case '{': advance(); return { Open_bracket{} }; case '}': advance(); return { Close_bracket{} }; case '(': advance(); return { Open_Parenthesis{} }; case ')': advance(); return { Close_Parenthesis{} }; case '\"': advance(); return { Quote{} }; case ':': advance(); return { Colon{} }; case ';': advance(); return { SemiColon{} }; case '|': advance(); return { VBar{} }; case ',': advance(); return { Comma{} }; default: error(Misc::StringUtils::format("unexpected token <%c>", head())); } } Token Lexer::scanLiteral() { auto start = mHead; advance(); while (mHead != mTail && (std::isalnum(head()) || head() == '_')) advance(); std::string_view value{ start, static_cast(mHead - start) }; if (value == "shared") return Shared{}; if (value == "technique") return Technique{}; if (value == "render_target") return Render_Target{}; if (value == "vertex") return Vertex{}; if (value == "fragment") return Fragment{}; if (value == "compute") return Compute{}; if (value == "sampler_1d") return Sampler_1D{}; if (value == "sampler_2d") return Sampler_2D{}; if (value == "sampler_3d") return Sampler_3D{}; if (value == "uniform_bool") return Uniform_Bool{}; if (value == "uniform_float") return Uniform_Float{}; if (value == "uniform_int") return Uniform_Int{}; if (value == "uniform_vec2") return Uniform_Vec2{}; if (value == "uniform_vec3") return Uniform_Vec3{}; if (value == "uniform_vec4") return Uniform_Vec4{}; if (value == "true") return True{}; if (value == "false") return False{}; if (value == "vec2") return Vec2{}; if (value == "vec3") return Vec3{}; if (value == "vec4") return Vec4{}; return Literal{ value }; } Token Lexer::scanStringLiteral() { advance(); // consume quote auto start = mHead; bool terminated = false; for (; mHead != mTail; advance()) { if (head() == '\"') { terminated = true; advance(); break; } } if (!terminated) error("unterminated string"); return String{ { start, static_cast(mHead - start - 1) } }; } Token Lexer::scanNumber() { double buffer; char* endPtr; buffer = std::strtod(mHead, &endPtr); if (endPtr == nullptr) error("critical error while parsing number"); const char* tmp = mHead; mHead = endPtr; for (; tmp != endPtr; ++tmp) { if ((*tmp == '.')) return Float{ static_cast(buffer) }; } return Integer{ static_cast(buffer) }; } } }