Even more work

This commit is contained in:
Michael Fabian 'Xaymar' Dirks
2025-02-12 00:03:19 +01:00
parent b61005bcaa
commit 15b8ed7690
9 changed files with 250 additions and 55 deletions
+148 -20
View File
@@ -2,9 +2,7 @@
// Copyright (C) 2017-2025 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
// AUTOGENERATED COPYRIGHT HEADER END
#include "ast.hpp"
#include <algorithm>
#include <cctype>
#include <cstdlib>
#include "util.hpp"
blitz::ast::variable::~variable()
{
@@ -91,6 +89,9 @@ std::shared_ptr<blitz::ast::node> blitz::ast::variable::try_parse(std::shared_pt
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
@@ -99,6 +100,9 @@ std::shared_ptr<blitz::ast::node> blitz::ast::variable::try_parse(std::shared_pt
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) {
@@ -114,6 +118,9 @@ std::shared_ptr<blitz::ast::node> blitz::ast::variable::try_parse(std::shared_pt
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;
@@ -145,20 +152,15 @@ bool blitz::ast::value::can_parse(std::shared_ptr<blitz::lexer> lexer)
case blitz::token::variant::REAL:
case blitz::token::variant::INTEGER:
return true;
case blitz::token::variant::STRING: {
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(), [](char from) {
if (from & 0b10000000) { // Exclude Unicode
return from;
}
return (char)std::tolower(from);
});
if (tk.text == "false") {
std::transform(text.cbegin(), text.cend(), text.begin(), blitz::utility::utf8_safe_tolower);
if (text == "false") {
return true;
} else if (tk.text == "true") {
} else if (text == "true") {
return true;
} else if (tk.text == "null") {
} else if (text == "null") {
return true;
}
break;
@@ -170,8 +172,8 @@ bool blitz::ast::value::can_parse(std::shared_ptr<blitz::lexer> lexer)
std::shared_ptr<blitz::ast::node> blitz::ast::value::try_parse(std::shared_ptr<blitz::lexer> lexer)
{
auto tk = lexer->current();
auto utk = lexer->peek();
auto file = lexer->file();
auto tk = lexer->current();
auto node = std::make_shared<blitz::ast::value>();
node->type = variant::UNKNOWN;
@@ -180,6 +182,12 @@ std::shared_ptr<blitz::ast::node> blitz::ast::value::try_parse(std::shared_ptr<b
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;
@@ -191,18 +199,18 @@ std::shared_ptr<blitz::ast::node> blitz::ast::value::try_parse(std::shared_ptr<b
} else if (text[1] == 'b') { // Base 2
base = 2;
text = text += 2;
} else if (text[1] == '0') {
} else if (isdigit(text[1])) {
base = 8;
text = text += 1;
}
}
if (utk.type == blitz::token::variant::TEXT && utk.text == "u") {
// User specific this is unsigned, so treat it as such.
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("Value '%s' is not representable on this system.", tk.text.c_str()));
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.
@@ -211,12 +219,132 @@ std::shared_ptr<blitz::ast::node> blitz::ast::value::try_parse(std::shared_ptr<b
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("Value '%s' is not representable on this system.", tk.text.c_str()));
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<blitz::lexer> 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::node> blitz::ast::declare::try_parse(std::shared_ptr<blitz::lexer> 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<blitz::ast::declare>();
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;
}
+13 -1
View File
@@ -29,6 +29,8 @@
// - Function, Return, End Function: Defines a function, and allows returning values. Yes, I know, End itself terminates the program, this is a special case. Thanks younger Sibly.
// - And, Or, Not: Logical operator, self-explanatory really.
#undef NULL
namespace blitz {
namespace ast {
struct node {
@@ -72,7 +74,17 @@ namespace blitz {
static std::shared_ptr<blitz::ast::node> try_parse(std::shared_ptr<blitz::lexer> lexer);
};
struct expression : public node {};
struct declare : public node {
// Local, Global
bool global;
std::list<std::shared_ptr<blitz::ast::node>> nodes;
virtual ~declare();
static bool can_parse(std::shared_ptr<blitz::lexer> lexer);
static std::shared_ptr<blitz::ast::node> try_parse(std::shared_ptr<blitz::lexer> lexer);
};
struct expression : public node {};
} // namespace ast
} // namespace blitz