mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-10-31 16:26:38 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			315 lines
		
	
	
	
		
			8.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			315 lines
		
	
	
	
		
			8.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include "lexer.hpp"
 | |
| 
 | |
| #include <cctype>
 | |
| #include <cstdlib>
 | |
| #include <optional>
 | |
| #include <string>
 | |
| #include <string_view>
 | |
| 
 | |
| #include <components/misc/strings/format.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--;
 | |
|                     mLine--;
 | |
|                     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 == "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) };
 | |
|         }
 | |
|     }
 | |
| }
 |