mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-11-04 08:56:43 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			301 lines
		
	
	
	
		
			8.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			301 lines
		
	
	
	
		
			8.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#include "lexer.hpp"
 | 
						|
 | 
						|
#include <string_view>
 | 
						|
#include <string>
 | 
						|
#include <variant>
 | 
						|
#include <optional>
 | 
						|
#include <cstdint>
 | 
						|
#include <array>
 | 
						|
#include <cmath>
 | 
						|
#include <exception>
 | 
						|
 | 
						|
#include <components/misc/stringops.hpp>
 | 
						|
 | 
						|
#include <components/debug/debuglog.hpp>
 | 
						|
 | 
						|
#include "types.hpp"
 | 
						|
 | 
						|
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<std::string_view> 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--;
 | 
						|
                    auto sv = std::string_view{start, static_cast<std::string_view::size_type>(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<std::string_view::size_type>(mHead - start)};
 | 
						|
 | 
						|
            if (value == "shared") return Shared{};
 | 
						|
            if (value == "technique") return Technique{};
 | 
						|
            if (value == "main_pass") return Main_Pass{};
 | 
						|
            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<std::string_view::size_type>(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<float>(buffer)};
 | 
						|
            }
 | 
						|
 | 
						|
            return Integer{static_cast<int>(buffer)};
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 |