// AUTOGENERATED COPYRIGHT HEADER START // Copyright (C) 2017-2025 Michael Fabian 'Xaymar' Dirks // AUTOGENERATED COPYRIGHT HEADER END #include "ast.hpp" #include "util.hpp" blitz::ast::variable::~variable() { /* Variable Parsing * * Declaration: * - 8bit Signed Integer Variable * Variable:Byte * Variable:Int8 * - 8bit Unsigned Integer Variable * Variable:UByte * Variable:UInt8 * - 16bit Signed Integer Variable * Variable:Short * Variable:Int16 * - 16bit Unsigned Integer Variable * Variable:UShort * Variable:UInt16 * - 32bit Signed Integer Variable * Variable * Variable% * Variable:Int * Variable:Int32 * - 32bit Unsigned Integer Variable * Variable:UInt * Variable:UInt32 * - 64bit Signed Integer Variable * Variable%% * Variable:Long * Variable:Int64 * - 64bit Unsigned Integer Variable * Variable:ULong * Variable:UInt64 * - 32bit Real Variable * Variable# * Variable:Float * Variable:Float32 * Variable:Real * Variable:Real32 * - 64bit Real Variable * Variable## * Variable:Double * Variable:Float64 * Variable:Real64 * - UTF-8 String Variable * Variable$ * Variable:String * - Struct Variable * Variable.StructName * Variable:StructName * * Access: * - Struct Access: * Variable\Key * - Array Access: * Variable[IntegerIndex] * - Dynamic Array Access: * Variable(IntegerIndex) * - Direct Access: * Variable */ } bool blitz::ast::variable::can_parse(std::shared_ptr lexer) { return lexer->current().type == blitz::token::variant::TEXT; } std::shared_ptr blitz::ast::variable::try_parse(std::shared_ptr lexer) { auto file = lexer->file(); auto name_tk = lexer->current(); if (name_tk.type != blitz::token::variant::TEXT) { throw blitz::error(file, name_tk.location, name_tk.location, blitz::format("Unexpected %s, expected text.", name_tk.to_string().c_str())); } auto node = std::make_shared(); node->tokens.push_back(name_tk); node->type = blitz::types::type::UNKNOWN; node->name = name_tk.text; // Check if this has a type definition auto symbol_tk = lexer->peek(); if (symbol_tk.type != blitz::token::variant::SYMBOL) { return node; } else if (symbol_tk.location.second != (name_tk.location.second + name_tk.text.length())) { // We only care about these if they're immediately attached to us. return node; } if (symbol_tk.text == ":") { // :Type node->tokens.push_back(lexer->next()); // Advance to next token. auto type_tk = lexer->next(); if (type_tk != blitz::token::variant::TEXT) { throw blitz::error(file, name_tk.location, type_tk.location, blitz::format("Unexpected %s, expected text.", type_tk.to_string().c_str())); } if (type_tk.location.second != (symbol_tk.location.second + 1)) { throw blitz::error(file, name_tk.location, type_tk.location, blitz::format("Unexpected white space, expected text.")); } auto type = blitz::types::from_string(type_tk.text); if (type == blitz::types::type::UNKNOWN) { throw blitz::error(file, name_tk.location, type_tk.location, blitz::format("Unexpected %s, expected built-in type name.", type_tk.text.c_str())); } node->tokens.push_back(type_tk); node->type = type; } else if (symbol_tk.text == ".") { // .Struct node->tokens.push_back(lexer->next()); // Advance to next token. auto type_tk = lexer->next(); if (type_tk != blitz::token::variant::TEXT) { throw blitz::error(file, name_tk.location, type_tk.location, blitz::format("Unexpected %s, expected text.", type_tk.to_string().c_str())); } if (type_tk.location.second != (symbol_tk.location.second + 1)) { throw blitz::error(file, name_tk.location, type_tk.location, blitz::format("Unexpected white space, expected text.")); } node->tokens.push_back(type_tk); node->type = blitz::types::type::STRUCT; node->struct_name = type_tk.text; } else if (symbol_tk.text == "%") { // Int32 node->tokens.push_back(lexer->next()); // Advance to next token. node->type = blitz::types::type::INT32; } else if (symbol_tk.text == "#") { // Float node->tokens.push_back(lexer->next()); // Advance to next token. node->type = blitz::types::type::FLOAT32; } else if (symbol_tk.text == "$") { // String node->tokens.push_back(lexer->next()); // Advance to next token. node->type = blitz::types::type::STRING; } return node; } blitz::ast::value::~value() {} bool blitz::ast::value::can_parse(std::shared_ptr lexer) { auto tk = lexer->current(); switch (tk.type) { case blitz::token::variant::STRING: case blitz::token::variant::REAL: case blitz::token::variant::INTEGER: return true; case blitz::token::variant::TEXT: { // We can only parse True, False, Null std::string text = tk.text; std::transform(text.cbegin(), text.cend(), text.begin(), blitz::utility::utf8_safe_tolower); if (text == "false") { return true; } else if (text == "true") { return true; } else if (text == "null") { return true; } break; } default: break; } return false; } std::shared_ptr blitz::ast::value::try_parse(std::shared_ptr lexer) { auto file = lexer->file(); auto tk = lexer->current(); auto node = std::make_shared(); node->type = variant::UNKNOWN; if (tk.type == blitz::token::variant::STRING) { node->type = variant::STRING; node->text = tk.text; return node; } else if (tk.type == blitz::token::variant::REAL) { node->type = variant::REAL; node->number.f = strtod(tk.text.c_str(), nullptr); if (errno == ERANGE) { throw blitz::error(file, tk.location, tk.location, blitz::format("Real '%s' is not representable on this system.", tk.text.c_str())); } } else if (tk.type == blitz::token::variant::INTEGER) { // Figure out which base this integer is in (and where it starts). int base = 10; const char* text = tk.text.c_str(); if ((tk.text.length() > 1) && (text[0] == '0')) { if (text[1] == 'x') { // Base 16 base = 16; text = text += 2; } else if (text[1] == 'b') { // Base 2 base = 2; text = text += 2; } else if (isdigit(text[1])) { base = 8; text = text += 1; } } if (tk.text[tk.text.length() - 1] == 'u') { // User wants this to be unsigned. node->type = variant::UNSIGNED_INTEGER; node->number.ui = strtoull(text, nullptr, base); if (errno == ERANGE) { throw blitz::error(file, tk.location, tk.location, blitz::format("Integer '%s' is not representable on this system.", tk.text.c_str())); } } else { // Try and figure out if it is unsigned. node->number.i = strtoll(text, nullptr, base); if (errno == ERANGE) { node->type = variant::UNSIGNED_INTEGER; node->number.ui = strtoull(text, nullptr, base); if (errno == ERANGE) { throw blitz::error(file, tk.location, tk.location, blitz::format("Integer '%s' is not representable on this system.", tk.text.c_str())); } } else { node->type = variant::INTEGER; } } } else if (tk.type == blitz::token::variant::TEXT) { std::string text = tk.text; std::transform(text.cbegin(), text.cend(), text.begin(), blitz::utility::utf8_safe_tolower); if (text == "false") { node->type = variant::BOOL; node->number.b = false; } else if (text == "true") { node->type = variant::BOOL; node->number.b = true; } else if (text == "null") { node->type = variant::NULL; node->number.ui = 0; } } return node; } blitz::ast::declare::~declare() { /* Variable Declaration * * Examples: * - Local myVar1, myVar2%, myVar3 = "Help", myVar4$ = "Me" * - Global myVar2g# = 3.147 * * * */ } bool blitz::ast::declare::can_parse(std::shared_ptr lexer) { auto tk = lexer->current(); if (tk != blitz::token::variant::TEXT) { return false; } std::string text = tk.text; std::transform(text.cbegin(), text.cend(), text.begin(), blitz::utility::utf8_safe_tolower); if (text == "local") { return true; } else if (text == "global") { return true; } return false; } std::shared_ptr blitz::ast::declare::try_parse(std::shared_ptr lexer) { bool is_global; auto file = lexer->file(); auto tk = lexer->current(); if (tk != blitz::token::variant::TEXT) { throw blitz::error(file, tk.location, tk.location, blitz::format("Unexpected %s, expected text.", tk.to_string().c_str())); } // Check if this is Local or Global. std::string text = tk.text; std::transform(text.cbegin(), text.cend(), text.begin(), blitz::utility::utf8_safe_tolower); if (text == "local") { is_global = false; } else if (text == "global") { is_global = true; } else { throw blitz::error(file, tk.location, tk.location, blitz::format("Unexpected %s, expected Local or Global.", tk.to_string().c_str())); } auto node = std::make_shared(); node->global = is_global; // Local myVar // Local myVar, myVar2 // Local myVar = Expression // Local myVar = Expression, myVar2 // Local myVar = Expression : // Local myVar : Local MyVar // Local myVar:Int // Local myVar.StructType for (tk = lexer->peek(); tk != blitz::token::variant::ENDOFFILE; tk = lexer->peek()) { // Declarations require a valid variable name if (tk.type != blitz::token::variant::TEXT) { throw blitz::error(file, tk.location, tk.location, blitz::format("Expected variable name, got %s.", tk.to_string().c_str())); } else if (!blitz::ast::variable::can_parse(lexer)) { throw blitz::error(file, tk.location, tk.location, blitz::format("Expected variable name, got %s.", tk.to_string().c_str())); } // Advance the lexer and parse the variable declaration. tk = lexer->next(); auto variable_nd = blitz::ast::variable::try_parse(lexer); node->nodes.push_back(variable_nd); // Peek at what's coming up and decide on behavior. tk = lexer->peek(); if ((tk.type == blitz::token::variant::NEWLINE) || ((tk.type == blitz::token::variant::SYMBOL) && (tk.text == ":")) || (tk.type == blitz::token::variant::ENDOFFILE)) { // Nothing useful, break out here. break; } else if ((tk.type == blitz::token::variant::SYMBOL) && (tk.text == ",")) { // Next variable is being declared. lexer->next(); continue; } else if ((tk.type == blitz::token::variant::SYMBOL) && (tk.text == "=")) { // Assignment, not implemented yet. Skip until next valid symbol. lexer->next(); do { if ((tk.type == blitz::token::variant::SYMBOL) && (tk.text == ",")) { // Next variable is being declared. break; } else if ((tk.type == blitz::token::variant::NEWLINE) || ((tk.type == blitz::token::variant::SYMBOL) && (tk.text == ":"))) { return node; } else if (tk.type == blitz::token::variant::ENDOFFILE) { return node; } tk = lexer->next(); } while (true); } } return node; }