Первый
This commit is contained in:
+14
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
print("=== Welcome to FoxLang ===");
|
||||||
|
|
||||||
|
print("Math test:");
|
||||||
|
print(20 + 30 * 2);
|
||||||
|
|
||||||
|
print("Type something:");
|
||||||
|
print(input());
|
||||||
|
|
||||||
|
{
|
||||||
|
print("I am inside a block!");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
#include <iostream>
|
||||||
|
#include <cmath>
|
||||||
|
#include <random>
|
||||||
|
|
||||||
|
// Хелпер для красивого вывода чисел
|
||||||
|
static std::string formatNumber(double val) {
|
||||||
|
std::string s = std::to_string(val);
|
||||||
|
// Удаляем лишние нули и точку, если число целое
|
||||||
|
s.erase(s.find_last_not_of('0') + 1, std::string::npos);
|
||||||
|
if (s.back() == '.') s.pop_back();
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Node {
|
||||||
|
virtual ~Node() = default;
|
||||||
|
virtual std::string eval() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NumberNode : Node {
|
||||||
|
std::string val;
|
||||||
|
NumberNode(std::string v) : val(v) {}
|
||||||
|
std::string eval() override { return val; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BinOpNode : Node {
|
||||||
|
char op;
|
||||||
|
std::unique_ptr<Node> left, right;
|
||||||
|
BinOpNode(char o, std::unique_ptr<Node> l, std::unique_ptr<Node> r)
|
||||||
|
: op(o), left(std::move(l)), right(std::move(r)) {}
|
||||||
|
|
||||||
|
std::string eval() override {
|
||||||
|
double l = std::stod(left->eval());
|
||||||
|
double r = std::stod(right->eval());
|
||||||
|
double res = 0;
|
||||||
|
if (op == '+') res = l + r;
|
||||||
|
else if (op == '-') res = l - r;
|
||||||
|
else if (op == '*') res = l * r;
|
||||||
|
else if (op == '/') res = (r != 0) ? l / r : 0;
|
||||||
|
return formatNumber(res);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RoundNode : Node {
|
||||||
|
std::unique_ptr<Node> expr;
|
||||||
|
RoundNode(std::unique_ptr<Node> e) : expr(std::move(e)) {}
|
||||||
|
std::string eval() override {
|
||||||
|
double val = std::stod(expr->eval());
|
||||||
|
return formatNumber(std::round(val));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RandomNode : Node {
|
||||||
|
std::string eval() override {
|
||||||
|
return std::to_string(rand() % 100); // Случайное от 0 до 99
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PrintNode : Node {
|
||||||
|
std::unique_ptr<Node> expr;
|
||||||
|
PrintNode(std::unique_ptr<Node> e) : expr(std::move(e)) {}
|
||||||
|
std::string eval() override {
|
||||||
|
std::cout << expr->eval() << std::endl;
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct InputNode : Node {
|
||||||
|
std::string eval() override {
|
||||||
|
std::string buffer;
|
||||||
|
std::getline(std::cin, buffer);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
#include "Lexer.h"
|
||||||
|
#include <cctype>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
Lexer::Lexer(std::string src) : source(src) {}
|
||||||
|
|
||||||
|
std::vector<Token> Lexer::tokenize() {
|
||||||
|
std::vector<Token> tokens;
|
||||||
|
while (pos < source.length()) {
|
||||||
|
char current = source[pos];
|
||||||
|
|
||||||
|
if (isspace(current)) {
|
||||||
|
if (current == '\n') line++;
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
else if (isdigit(current)) {
|
||||||
|
std::string num;
|
||||||
|
while (pos < source.length() && isdigit(source[pos])) num += source[pos++];
|
||||||
|
// Точка для дробных чисел (например, 3.14)
|
||||||
|
if (pos < source.length() && source[pos] == '.') {
|
||||||
|
num += source[pos++];
|
||||||
|
while (pos < source.length() && isdigit(source[pos])) num += source[pos++];
|
||||||
|
}
|
||||||
|
tokens.push_back({TokenType::NUMBER, num, line});
|
||||||
|
}
|
||||||
|
else if (current == '"') {
|
||||||
|
pos++; std::string str;
|
||||||
|
while (pos < source.length() && source[pos] != '"') str += source[pos++];
|
||||||
|
pos++;
|
||||||
|
tokens.push_back({TokenType::STRING, str, line});
|
||||||
|
}
|
||||||
|
else if (isalpha(current)) {
|
||||||
|
std::string id;
|
||||||
|
while (pos < source.length() && isalpha(source[pos])) id += source[pos++];
|
||||||
|
|
||||||
|
if (id == "print") tokens.push_back({TokenType::PRINT, id, line});
|
||||||
|
else if (id == "input") tokens.push_back({TokenType::INPUT, id, line});
|
||||||
|
else if (id == "round") tokens.push_back({TokenType::ROUND, id, line});
|
||||||
|
else if (id == "random") tokens.push_back({TokenType::RANDOM, id, line});
|
||||||
|
else if (id == "fox") tokens.push_back({TokenType::FOX, id, line});
|
||||||
|
else tokens.push_back({TokenType::IDENTIFIER, id, line}); // Неизвестное слово
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
switch (current) {
|
||||||
|
case '+': tokens.push_back({TokenType::PLUS, "+", line}); break;
|
||||||
|
case '-': tokens.push_back({TokenType::MINUS, "-", line}); break;
|
||||||
|
case '*': tokens.push_back({TokenType::STAR, "*", line}); break;
|
||||||
|
case '/': tokens.push_back({TokenType::SLASH, "/", line}); break;
|
||||||
|
case '(': tokens.push_back({TokenType::LPAREN, "(", line}); break;
|
||||||
|
case ')': tokens.push_back({TokenType::RPAREN, ")", line}); break;
|
||||||
|
case '{': tokens.push_back({TokenType::LBRACE, "{", line}); break;
|
||||||
|
case '}': tokens.push_back({TokenType::RBRACE, "}", line}); break;
|
||||||
|
case ';': tokens.push_back({TokenType::SEMICOLON, ";", line}); break;
|
||||||
|
default:
|
||||||
|
std::cerr << "Lexer Error: Unknown char '" << current << "' at line " << line << std::endl;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tokens.push_back({TokenType::END, "", line});
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include "Token.h"
|
||||||
|
|
||||||
|
class Lexer {
|
||||||
|
std::string source;
|
||||||
|
size_t pos = 0;
|
||||||
|
int line = 1;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Lexer(std::string src);
|
||||||
|
std::vector<Token> tokenize();
|
||||||
|
};
|
||||||
+111
@@ -0,0 +1,111 @@
|
|||||||
|
#include "Parser.h"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
Parser::Parser(std::vector<Token> t) : tokens(t) {}
|
||||||
|
|
||||||
|
Token Parser::consume(TokenType type) {
|
||||||
|
if (tokens[pos].type == type) return tokens[pos++];
|
||||||
|
std::cerr << "Syntax Error: Expected token type " << (int)type
|
||||||
|
<< " but found '" << tokens[pos].value
|
||||||
|
<< "' on line " << tokens[pos].line << std::endl;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Выражения (возвращают значение)
|
||||||
|
std::unique_ptr<Node> Parser::primary() {
|
||||||
|
if (tokens[pos].type == TokenType::NUMBER) return std::make_unique<NumberNode>(consume(TokenType::NUMBER).value);
|
||||||
|
if (tokens[pos].type == TokenType::STRING) return std::make_unique<NumberNode>(consume(TokenType::STRING).value);
|
||||||
|
|
||||||
|
// input()
|
||||||
|
if (tokens[pos].type == TokenType::INPUT) {
|
||||||
|
consume(TokenType::INPUT); consume(TokenType::LPAREN); consume(TokenType::RPAREN);
|
||||||
|
return std::make_unique<InputNode>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// round( expr )
|
||||||
|
if (tokens[pos].type == TokenType::ROUND) {
|
||||||
|
consume(TokenType::ROUND);
|
||||||
|
consume(TokenType::LPAREN);
|
||||||
|
auto node = expression();
|
||||||
|
consume(TokenType::RPAREN);
|
||||||
|
return std::make_unique<RoundNode>(std::move(node));
|
||||||
|
}
|
||||||
|
|
||||||
|
// random()
|
||||||
|
if (tokens[pos].type == TokenType::RANDOM) {
|
||||||
|
consume(TokenType::RANDOM); consume(TokenType::LPAREN); consume(TokenType::RPAREN);
|
||||||
|
return std::make_unique<RandomNode>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tokens[pos].type == TokenType::LPAREN) {
|
||||||
|
consume(TokenType::LPAREN);
|
||||||
|
auto node = expression();
|
||||||
|
consume(TokenType::RPAREN);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cerr << "Error: Unexpected token '" << tokens[pos].value << "' in expression at line " << tokens[pos].line << std::endl;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Node> Parser::multiplication() {
|
||||||
|
auto node = primary();
|
||||||
|
while (tokens[pos].type == TokenType::STAR || tokens[pos].type == TokenType::SLASH) {
|
||||||
|
char op = tokens[pos].value[0]; pos++;
|
||||||
|
node = std::make_unique<BinOpNode>(op, std::move(node), primary());
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Node> Parser::expression() {
|
||||||
|
auto node = multiplication();
|
||||||
|
while (tokens[pos].type == TokenType::PLUS || tokens[pos].type == TokenType::MINUS) {
|
||||||
|
char op = tokens[pos].value[0]; pos++;
|
||||||
|
node = std::make_unique<BinOpNode>(op, std::move(node), multiplication());
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Блоки кода
|
||||||
|
void Parser::block() {
|
||||||
|
consume(TokenType::LBRACE);
|
||||||
|
while (tokens[pos].type != TokenType::RBRACE && tokens[pos].type != TokenType::END) {
|
||||||
|
statement();
|
||||||
|
}
|
||||||
|
consume(TokenType::RBRACE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Инструкции (заканчиваются ;)
|
||||||
|
void Parser::statement() {
|
||||||
|
if (tokens[pos].type == TokenType::PRINT) {
|
||||||
|
consume(TokenType::PRINT);
|
||||||
|
consume(TokenType::LPAREN);
|
||||||
|
auto expr = expression();
|
||||||
|
consume(TokenType::RPAREN);
|
||||||
|
consume(TokenType::SEMICOLON);
|
||||||
|
auto pNode = std::make_unique<PrintNode>(std::move(expr));
|
||||||
|
pNode->eval();
|
||||||
|
}
|
||||||
|
else if (tokens[pos].type == TokenType::FOX) {
|
||||||
|
consume(TokenType::FOX);
|
||||||
|
consume(TokenType::LPAREN); // fox (
|
||||||
|
consume(TokenType::RPAREN); // )
|
||||||
|
consume(TokenType::SEMICOLON); // ;
|
||||||
|
std::cout << " (\\_/) \n (o.o) \n (> <) OFOX!" << std::endl;
|
||||||
|
}
|
||||||
|
else if (tokens[pos].type == TokenType::LBRACE) {
|
||||||
|
block();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// ОБРАБОТКА ОШИБОК: Если встретили слово, которое не является командой
|
||||||
|
std::cerr << "Runtime Error: Unknown command or function '" << tokens[pos].value
|
||||||
|
<< "' at line " << tokens[pos].line << std::endl;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Parser::run() {
|
||||||
|
while (tokens[pos].type != TokenType::END) {
|
||||||
|
statement();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "Token.h"
|
||||||
|
#include "AST.h"
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
class Parser {
|
||||||
|
std::vector<Token> tokens;
|
||||||
|
size_t pos = 0;
|
||||||
|
|
||||||
|
Token consume(TokenType type);
|
||||||
|
std::unique_ptr<Node> primary();
|
||||||
|
std::unique_ptr<Node> multiplication();
|
||||||
|
std::unique_ptr<Node> expression();
|
||||||
|
void statement(); // Обработка отдельной команды с точкой с запятой
|
||||||
|
void block(); // Обработка блока { ... }
|
||||||
|
|
||||||
|
public:
|
||||||
|
Parser(std::vector<Token> t);
|
||||||
|
void run();
|
||||||
|
};
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
enum class TokenType {
|
||||||
|
NUMBER, STRING,
|
||||||
|
PLUS, MINUS, STAR, SLASH,
|
||||||
|
LPAREN, RPAREN,
|
||||||
|
LBRACE, RBRACE, SEMICOLON,
|
||||||
|
PRINT, INPUT,
|
||||||
|
ROUND, FOX, RANDOM, // Новые команды
|
||||||
|
IDENTIFIER, // Для неизвестных слов
|
||||||
|
END, ERROR
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Token {
|
||||||
|
TokenType type;
|
||||||
|
std::string value;
|
||||||
|
int line;
|
||||||
|
};
|
||||||
Executable
BIN
Binary file not shown.
@@ -0,0 +1,29 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
#include "Lexer.h"
|
||||||
|
#include "Parser.h"
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
if (argc < 2) {
|
||||||
|
std::cout << "Usage: foxlang <script.fox>" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ifstream file(argv[1]);
|
||||||
|
if (!file.is_open()) {
|
||||||
|
std::cerr << "Error: File not found!" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::stringstream buffer;
|
||||||
|
buffer << file.rdbuf();
|
||||||
|
|
||||||
|
Lexer lexer(buffer.str());
|
||||||
|
auto tokens = lexer.tokenize();
|
||||||
|
|
||||||
|
Parser parser(tokens);
|
||||||
|
parser.run();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user