Compare commits
3 commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
c33b2eee86 | ||
![]() |
88ca215573 | ||
![]() |
ba2bf4b404 |
39
CMakeLists.txt
Normal file
39
CMakeLists.txt
Normal file
|
@ -0,0 +1,39 @@
|
|||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
project("OMGL")
|
||||
|
||||
# set(CMAKE_CXX_FLAGS "-O3")
|
||||
# set(CMAKE_CXX_FLAGS "-O0 --coverage -ftest-coverage -fprofile-arcs")
|
||||
|
||||
find_package(GTest REQUIRED)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
include_directories(
|
||||
"include"
|
||||
${GTEST_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
set(SOURCE_FILES
|
||||
src/preprocessor.cpp
|
||||
src/lexer.cpp
|
||||
src/syntax_tree.cpp
|
||||
)
|
||||
|
||||
set(TEST_FILES
|
||||
)
|
||||
|
||||
add_executable(OMGL src/main.cpp ${SOURCE_FILES})
|
||||
|
||||
add_executable(Test tests/MainTest.cpp ${TEST_FILES} ${SOURCE_FILES})
|
||||
|
||||
target_link_libraries(Test ${GTEST_LIBRARIES} Threads::Threads)
|
||||
|
||||
add_custom_target(copy-files ALL
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
${CMAKE_SOURCE_DIR}/tests/programs/
|
||||
${CMAKE_BINARY_DIR}/programs
|
||||
)
|
||||
|
||||
add_dependencies(Test copy-files)
|
36
README.md
36
README.md
|
@ -1,3 +1,39 @@
|
|||
# Oh my god programming language
|
||||
|
||||
Питонисты плакали когда увидели это!
|
||||
|
||||
Реализован язык с типами int string. Я могу создавать переменные в любой степени вложенности, они корректно убираются со стека. Также корректно работает приоритет операций. Пример кода на моем языке:
|
||||
```c++
|
||||
string s = "abacaba ";
|
||||
for (int i = 0; i < 5; i += 1) {
|
||||
string t = "";
|
||||
for (int j = 0; j < 3; j += 1) {
|
||||
t += "t";
|
||||
}
|
||||
s += t;
|
||||
print(s);
|
||||
}
|
||||
|
||||
int x = (1 + 2) * 3;
|
||||
print(x, x + 1, x + 2);
|
||||
print(7777);
|
||||
|
||||
while (x != 0) {
|
||||
x -= 1;
|
||||
}
|
||||
|
||||
print(x);
|
||||
```
|
||||
|
||||
Результат работы:
|
||||
```text
|
||||
abacaba ttt
|
||||
abacaba tttttt
|
||||
abacaba ttttttttt
|
||||
abacaba tttttttttttt
|
||||
abacaba ttttttttttttttt
|
||||
9 10 11
|
||||
7777
|
||||
0
|
||||
END
|
||||
```
|
48
include/lexer.hpp
Normal file
48
include/lexer.hpp
Normal file
|
@ -0,0 +1,48 @@
|
|||
#pragma once
|
||||
#include <string>
|
||||
#include <list>
|
||||
|
||||
class Lexer {
|
||||
public:
|
||||
struct LexerToken {
|
||||
enum class Type {
|
||||
None, Word, Semicolon, CurlyOpenBracket, CurlyCloseBracket,
|
||||
RoundOpenBracket, RoundCloseBracket, Period, Commo, Plus, Minus, Star,
|
||||
Slash, Equal, ExclamationMark, PlusEqual, MinusEqual, StarEqual,
|
||||
SlashEqual, EqualEqual, ExclamationMarkEqual, PlusPlus, MinusMinus,
|
||||
LAngle, RAngle, LAngleEqual, RAngleEqual, LArrow, RArrow, FOR, IF, ELSE,
|
||||
WHILE, StringLiteral
|
||||
};
|
||||
LexerToken() = default;
|
||||
LexerToken(const LexerToken&) = default;
|
||||
LexerToken(LexerToken&&) = default;
|
||||
LexerToken(const Type& type, const std::string& info) : type(type), info(info) {}
|
||||
LexerToken(const Type& type, std::string&& info) : type(type), info(info) {}
|
||||
LexerToken(Type&& type, const std::string& info) : type(type), info(info) {}
|
||||
LexerToken(Type&& type, std::string&& info) : type(type), info(info) {}
|
||||
|
||||
LexerToken& operator=(LexerToken&&) = default;
|
||||
LexerToken& operator=(const LexerToken&) = default;
|
||||
|
||||
Type type;
|
||||
std::string info;
|
||||
};
|
||||
using LexerTokenList = std::list<LexerToken>;
|
||||
|
||||
void ParseText(std::string&&);
|
||||
void ParseText(const std::string&);
|
||||
|
||||
LexerTokenList GetTokens() const;
|
||||
private:
|
||||
void PushToken();
|
||||
LexerTokenList tokens;
|
||||
|
||||
LexerToken::Type current_state = LexerToken::Type::None;
|
||||
std::string current_info;
|
||||
};
|
||||
|
||||
using LexerTokenList = Lexer::LexerTokenList;
|
||||
|
||||
namespace std {
|
||||
std::string to_string(Lexer::LexerToken::Type);
|
||||
}
|
5
include/preprocessor.hpp
Normal file
5
include/preprocessor.hpp
Normal file
|
@ -0,0 +1,5 @@
|
|||
#pragma once
|
||||
#include <string>
|
||||
|
||||
std::string Preprocessor(std::string&& s);
|
||||
std::string Preprocessor(const std::string& s);
|
232
include/syntax_tree.hpp
Normal file
232
include/syntax_tree.hpp
Normal file
|
@ -0,0 +1,232 @@
|
|||
#pragma once
|
||||
#include <variant>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include "lexer.hpp"
|
||||
|
||||
struct Node;
|
||||
struct Variable;
|
||||
struct VariableInStack;
|
||||
struct Expression;
|
||||
|
||||
void SetPrintStringStream();
|
||||
std::stringstream& GetPrintStringStream();
|
||||
|
||||
struct Node {
|
||||
Node* parent = nullptr;
|
||||
Node* next = nullptr;
|
||||
Node* previous = nullptr;
|
||||
|
||||
void Insert(Node*);
|
||||
void InsertBefore(Node*);
|
||||
virtual void Run(std::vector<std::shared_ptr<VariableInStack>>& stack) = 0;
|
||||
|
||||
virtual ~Node() {}
|
||||
};
|
||||
|
||||
enum class Operation {
|
||||
None,
|
||||
Plus,
|
||||
Minus,
|
||||
Star,
|
||||
Slash,
|
||||
Equal,
|
||||
ExclamationMark,
|
||||
PlusEqual,
|
||||
MinusEqual,
|
||||
StarEqual,
|
||||
SlashEqual,
|
||||
EqualEqual,
|
||||
ExclamationMarkEqual,
|
||||
PlusPlus,
|
||||
MinusMinus,
|
||||
LAngle,
|
||||
RAngle,
|
||||
LAngleEqual,
|
||||
RAngleEqual,
|
||||
Value
|
||||
};
|
||||
|
||||
enum class PriorityType {
|
||||
L, R
|
||||
};
|
||||
|
||||
struct TypeVariable {
|
||||
enum ID {
|
||||
none = 0, type_int = 1, type_string = 2
|
||||
};
|
||||
|
||||
TypeVariable() {}
|
||||
|
||||
TypeVariable(ID id) : id(id) {}
|
||||
|
||||
ID id = ID::none;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
struct DeallocateStack : public Node {
|
||||
DeallocateStack(size_t count) : count(count) {}
|
||||
|
||||
void Run(std::vector<std::shared_ptr<VariableInStack>>& stack) override;
|
||||
|
||||
size_t count;
|
||||
};
|
||||
|
||||
struct HiddenDeallocateStack : public Node {
|
||||
HiddenDeallocateStack(size_t count) : count(count) {}
|
||||
|
||||
void Run(std::vector<std::shared_ptr<VariableInStack>>& stack) override;
|
||||
|
||||
size_t count;
|
||||
};
|
||||
|
||||
struct VariableInStack {
|
||||
VariableInStack(TypeVariable type_variable) : type_variable(type_variable) {}
|
||||
|
||||
void CallOperator(std::shared_ptr<VariableInStack>& another, std::shared_ptr<VariableInStack>& result, Operation op);
|
||||
|
||||
void Clear();
|
||||
|
||||
void Allocate();
|
||||
|
||||
TypeVariable type_variable;
|
||||
void* memory = nullptr;
|
||||
private:
|
||||
|
||||
void CallEqualOperator(const std::shared_ptr<VariableInStack>& another,
|
||||
std::shared_ptr<VariableInStack>& result,
|
||||
const Operation& op);
|
||||
void CallComparisonOperator(const std::shared_ptr<VariableInStack>& another,
|
||||
std::shared_ptr<VariableInStack>& result,
|
||||
const Operation& op) const;
|
||||
void CallArithmeticOperator(const std::shared_ptr<VariableInStack>& another,
|
||||
std::shared_ptr<VariableInStack>& result,
|
||||
const Operation& op) const;
|
||||
void CallEqualOperatorInt(const std::shared_ptr<VariableInStack>& another,
|
||||
const std::shared_ptr<VariableInStack>& result,
|
||||
const Operation& op) const;
|
||||
void CallEqualOperatorString(const std::shared_ptr<VariableInStack>& another,
|
||||
const std::shared_ptr<VariableInStack>& result,
|
||||
const Operation& op) const;
|
||||
};
|
||||
|
||||
struct Variable : public Node {
|
||||
Variable(std::string type, std::string name) : type(std::move(type)), name(std::move(name)) {}
|
||||
|
||||
void Run(std::vector<std::shared_ptr<VariableInStack>>& stack) override;
|
||||
std::string type;
|
||||
std::string name;
|
||||
Expression* default_value = nullptr;
|
||||
// TYPE
|
||||
// STORAGE
|
||||
};
|
||||
|
||||
struct Container : public Node {
|
||||
void AddChildren(Node*);
|
||||
|
||||
void Run(std::vector<std::shared_ptr<VariableInStack>>& stack) override;
|
||||
|
||||
Node* children_begin = nullptr;
|
||||
Node* children_end = nullptr;
|
||||
};
|
||||
|
||||
struct CodeBlock : public Container {};
|
||||
|
||||
struct CreateVariables : Node {
|
||||
CreateVariables(size_t count) : count(count) {}
|
||||
|
||||
void Run(std::vector<std::shared_ptr<VariableInStack>>& stack) override {}
|
||||
|
||||
size_t count;
|
||||
};
|
||||
|
||||
struct CallFunction : public Node {
|
||||
CallFunction(std::string name_function) : name_function(std::move(name_function)) {}
|
||||
|
||||
void Run(std::vector<std::shared_ptr<VariableInStack>>& stack) override;
|
||||
std::string name_function;
|
||||
std::vector<Expression*> parameters;
|
||||
};
|
||||
|
||||
using stack_pointer = size_t;
|
||||
|
||||
struct Expression : public Node {
|
||||
using Types = std::variant<std::string, Expression*, stack_pointer>;
|
||||
|
||||
Expression() {}
|
||||
|
||||
Expression(Types type1, Types type2, Operation op);
|
||||
|
||||
void AddStackPointer(stack_pointer d);
|
||||
|
||||
void Run(std::vector<std::shared_ptr<VariableInStack>>& stack) override;
|
||||
|
||||
static size_t GetPriority(Operation);
|
||||
static PriorityType GetPriorityType(Operation);
|
||||
static Operation Convert(Lexer::LexerToken::Type);
|
||||
Types type1, type2;
|
||||
Operation op = Operation::None;
|
||||
stack_pointer position_result = 0;
|
||||
size_t count;
|
||||
};
|
||||
|
||||
struct BlockFor : public Node {
|
||||
void Run(std::vector<std::shared_ptr<VariableInStack>>& stack) override;
|
||||
|
||||
Variable* var = nullptr;
|
||||
Expression* check = nullptr;
|
||||
Expression* tick = nullptr;
|
||||
CodeBlock* code = nullptr;
|
||||
};
|
||||
|
||||
struct BlockIf : public Node {
|
||||
void Run(std::vector<std::shared_ptr<VariableInStack>>& stack) override;
|
||||
|
||||
Expression* check = nullptr;
|
||||
CodeBlock* code = nullptr;
|
||||
};
|
||||
|
||||
struct BlockWhile : public Node {
|
||||
void Run(std::vector<std::shared_ptr<VariableInStack>>& stack) override;
|
||||
|
||||
Expression* check = nullptr;
|
||||
CodeBlock* code = nullptr;
|
||||
};
|
||||
|
||||
class SyntaxTree {
|
||||
public:
|
||||
SyntaxTree() {}
|
||||
|
||||
void PushLexerTokenList(const LexerTokenList&);
|
||||
void Compile();
|
||||
void Run();
|
||||
private:
|
||||
bool IsTypeName(Node*, const std::string&);
|
||||
bool IsVariableName(Node*, const std::string&);
|
||||
bool IsFunctionName(Node*, const std::string&);
|
||||
CodeBlock* ParseCurlyBrackets(Container*, const LexerTokenList&, LexerTokenList::const_iterator&);
|
||||
void PushCurlyBrackets(Container*, const LexerTokenList&, LexerTokenList::const_iterator&);
|
||||
void PushRoundBrackets(Container*, const LexerTokenList&, LexerTokenList::const_iterator&);
|
||||
void PushFunction(Container*, const LexerTokenList&, LexerTokenList::const_iterator&);
|
||||
void PushLine(Container*, const LexerTokenList&, LexerTokenList::const_iterator&);
|
||||
Variable* ParseNewVariable(const LexerTokenList&, LexerTokenList::const_iterator&);
|
||||
void PushNewVariable(Container*, const LexerTokenList&, LexerTokenList::const_iterator&);
|
||||
void PushExpression(Container*, const LexerTokenList&, LexerTokenList::const_iterator&);
|
||||
void PushParametersFunction(CallFunction*, const LexerTokenList&, LexerTokenList::const_iterator&);
|
||||
void PushCallFunction(Container*, const LexerTokenList&, LexerTokenList::const_iterator&);
|
||||
void PushBlockFor(Container* container, const LexerTokenList& list, LexerTokenList::const_iterator& it);
|
||||
void PushSignatureBlockFor(BlockFor* node_for, const LexerTokenList& list, LexerTokenList::const_iterator& it);
|
||||
void PushBlockIf(Container* container, const LexerTokenList& list, LexerTokenList::const_iterator& it);
|
||||
void PushSignatureBlockIf(BlockIf* node_if, const LexerTokenList& list, LexerTokenList::const_iterator& it);
|
||||
void PushBlockWhile(Container* container, const LexerTokenList& list, LexerTokenList::const_iterator& it);
|
||||
void PushSignatureBlockWhile(BlockWhile* node_if, const LexerTokenList& list, LexerTokenList::const_iterator& it);
|
||||
Expression* ParseExpression(LexerTokenList::const_iterator, LexerTokenList::const_iterator);
|
||||
Expression* ParseExpression(const LexerTokenList&, LexerTokenList::const_iterator&);
|
||||
void PushDeallocateStack(Node*, size_t);
|
||||
stack_pointer GetCountStackOffsetForVariable(Node* node, std::string name);
|
||||
void LinkVariables(Node*);
|
||||
void LinkVariablesInExpression(Expression*, Node*);
|
||||
CodeBlock* tree_ = nullptr;
|
||||
};
|
||||
|
253
src/lexer.cpp
Normal file
253
src/lexer.cpp
Normal file
|
@ -0,0 +1,253 @@
|
|||
#include <unordered_set>
|
||||
#include <stdexcept>
|
||||
#include "lexer.hpp"
|
||||
|
||||
bool IsNumber(char x) {
|
||||
return x >= '0' && x <= '9';
|
||||
}
|
||||
|
||||
std::unordered_set<char> special_symbols = {';', '{', '}', '(', ')', '.', ','};
|
||||
|
||||
bool IsSpecialSymbol(char x) {
|
||||
return special_symbols.find(x) != special_symbols.end();
|
||||
}
|
||||
|
||||
std::unordered_set<char> break_symbols = {'+', '-', '*', '/', '=', '!', '<', '>'};
|
||||
|
||||
bool IsBreakSymbol(char x) {
|
||||
return break_symbols.find(x) != special_symbols.end();
|
||||
}
|
||||
|
||||
void Lexer::ParseText(std::string&& text) {
|
||||
using Type = LexerToken::Type;
|
||||
for (char x: text) {
|
||||
if (x == '\"') {
|
||||
if (current_state != Type::StringLiteral) {
|
||||
current_info += x;
|
||||
PushToken();
|
||||
current_state = Type::StringLiteral;
|
||||
} else {
|
||||
current_info += x;
|
||||
PushToken();
|
||||
}
|
||||
} else if (current_state == Type::StringLiteral) {
|
||||
current_info += x;
|
||||
} else if (std::isspace(x)) {
|
||||
PushToken();
|
||||
} else if (IsBreakSymbol(x)) {
|
||||
if (x == '=') {
|
||||
if (current_state == Type::Plus) {
|
||||
current_state = Type::PlusEqual;
|
||||
PushToken();
|
||||
} else if (current_state == Type::Minus) {
|
||||
current_state = Type::MinusEqual;
|
||||
PushToken();
|
||||
} else if (current_state == Type::Star) {
|
||||
current_state = Type::StarEqual;
|
||||
PushToken();
|
||||
} else if (current_state == Type::Slash) {
|
||||
current_state = Type::SlashEqual;
|
||||
PushToken();
|
||||
} else if (current_state == Type::Equal) {
|
||||
current_state = Type::EqualEqual;
|
||||
PushToken();
|
||||
} else if (current_state == Type::ExclamationMark) {
|
||||
current_state = Type::ExclamationMarkEqual;
|
||||
PushToken();
|
||||
} else if (current_state == Type::LAngle) {
|
||||
current_state = Type::LAngleEqual;
|
||||
PushToken();
|
||||
} else if (current_state == Type::RAngle) {
|
||||
current_state = Type::RAngleEqual;
|
||||
PushToken();
|
||||
} else {
|
||||
PushToken();
|
||||
current_state = Type::Equal;
|
||||
}
|
||||
} else if (x == '+') {
|
||||
if (current_state == Type::Plus) {
|
||||
current_state = Type::PlusPlus;
|
||||
PushToken();
|
||||
} else {
|
||||
PushToken();
|
||||
current_state = Type::Plus;
|
||||
}
|
||||
} else if (x == '-') {
|
||||
if (current_state == Type::Minus) {
|
||||
current_state = Type::MinusMinus;
|
||||
PushToken();
|
||||
} else if (current_state == Type::LAngle) {
|
||||
current_state = Type::LArrow;
|
||||
PushToken();
|
||||
} else {
|
||||
PushToken();
|
||||
current_state = Type::Minus;
|
||||
}
|
||||
} else if (x == '>') {
|
||||
if (current_state == Type::Minus) {
|
||||
current_state = Type::RArrow;
|
||||
PushToken();
|
||||
} else {
|
||||
PushToken();
|
||||
current_state = Type::RAngle;
|
||||
}
|
||||
} else {
|
||||
PushToken();
|
||||
if (x == '+') {
|
||||
current_state = Type::Plus;
|
||||
} else if (x == '-') {
|
||||
current_state = Type::Minus;
|
||||
} else if (x == '*') {
|
||||
current_state = Type::Star;
|
||||
} else if (x == '/') {
|
||||
current_state = Type::Slash;
|
||||
} else if (x == '=') {
|
||||
current_state = Type::Equal;
|
||||
} else if (x == '!') {
|
||||
current_state = Type::ExclamationMark;
|
||||
} else if (x == '<') {
|
||||
current_state = Type::LAngle;
|
||||
} else if (x == '>') {
|
||||
current_state = Type::RAngle;
|
||||
} else {
|
||||
throw std::logic_error("incorrect operator");
|
||||
}
|
||||
}
|
||||
} else if (current_state == Type::None || IsSpecialSymbol(x)) {
|
||||
PushToken();
|
||||
if (x == ';') {
|
||||
current_state = Type::Semicolon;
|
||||
PushToken();
|
||||
} else if (x == '{') {
|
||||
current_state = Type::CurlyOpenBracket;
|
||||
PushToken();
|
||||
} else if (x == '}') {
|
||||
current_state = Type::CurlyCloseBracket;
|
||||
PushToken();
|
||||
} else if (x == '(') {
|
||||
current_state = Type::RoundOpenBracket;
|
||||
PushToken();
|
||||
} else if (x == ')') {
|
||||
current_state = Type::RoundCloseBracket;
|
||||
PushToken();
|
||||
} else if (x == '.') {
|
||||
current_state = Type::Period;
|
||||
PushToken();
|
||||
} else if (x == ',') {
|
||||
current_state = Type::Commo;
|
||||
PushToken();
|
||||
} else {
|
||||
current_state = Type::Word;
|
||||
current_info += x;
|
||||
}
|
||||
} else if (current_state == Type::Word) {
|
||||
current_info += x;
|
||||
}
|
||||
}
|
||||
|
||||
PushToken();
|
||||
}
|
||||
|
||||
void Lexer::ParseText(const std::string& text) {
|
||||
std::string copy(text);
|
||||
ParseText(std::move(copy));
|
||||
}
|
||||
|
||||
void Lexer::PushToken() {
|
||||
using Type = LexerToken::Type;
|
||||
if (current_state != Type::None) {
|
||||
if (current_state == Type::Word && current_info == "for") {
|
||||
current_state = Type::FOR;
|
||||
current_info.clear();
|
||||
} else if (current_state == Type::Word && current_info == "while") {
|
||||
current_state = Type::WHILE;
|
||||
current_info.clear();
|
||||
} else if (current_state == Type::Word && current_info == "if") {
|
||||
current_state = Type::IF;
|
||||
current_info.clear();
|
||||
} else if (current_state == Type::Word && current_info == "else") {
|
||||
current_state = Type::ELSE;
|
||||
current_info.clear();
|
||||
}
|
||||
tokens.emplace_back(current_state, std::move(current_info));
|
||||
current_state = Type::None;
|
||||
current_info.clear();
|
||||
}
|
||||
}
|
||||
|
||||
LexerTokenList Lexer::GetTokens() const {
|
||||
return tokens;
|
||||
}
|
||||
|
||||
namespace std {
|
||||
std::string to_string(Lexer::LexerToken::Type x) {
|
||||
using Type = Lexer::LexerToken::Type;
|
||||
if (x == Type::None) {
|
||||
return "None";
|
||||
} else if (x == Type::Word) {
|
||||
return "Word";
|
||||
} else if (x == Type::Semicolon) {
|
||||
return "Semicolon";
|
||||
} else if (x == Type::CurlyOpenBracket) {
|
||||
return "CurlyOpenBrackets";
|
||||
} else if (x == Type::CurlyCloseBracket) {
|
||||
return "CurlyCloseBrackets";
|
||||
} else if (x == Type::RoundOpenBracket) {
|
||||
return "RoundOpenBrackets";
|
||||
} else if (x == Type::RoundCloseBracket) {
|
||||
return "RoundCloseBrackets";
|
||||
} else if (x == Type::Commo) {
|
||||
return "Commo";
|
||||
} else if (x == Type::Plus) {
|
||||
return "Plus";
|
||||
} else if (x == Type::Minus) {
|
||||
return "Minus";
|
||||
} else if (x == Type::Star) {
|
||||
return "Star";
|
||||
} else if (x == Type::Slash) {
|
||||
return "Slash";
|
||||
} else if (x == Type::Equal) {
|
||||
return "Equal";
|
||||
} else if (x == Type::ExclamationMark) {
|
||||
return "ExclamationMark";
|
||||
} else if (x == Type::PlusEqual) {
|
||||
return "PlusEqual";
|
||||
} else if (x == Type::MinusEqual) {
|
||||
return "MinusEqual";
|
||||
} else if (x == Type::StarEqual) {
|
||||
return "StarEqual";
|
||||
} else if (x == Type::SlashEqual) {
|
||||
return "SlashEqual";
|
||||
} else if (x == Type::EqualEqual) {
|
||||
return "EqualEqual";
|
||||
} else if (x == Type::ExclamationMarkEqual) {
|
||||
return "ExclamationMarkEqual";
|
||||
} else if (x == Type::PlusPlus) {
|
||||
return "PlusPlus";
|
||||
} else if (x == Type::MinusMinus) {
|
||||
return "MinusMinus";
|
||||
} else if (x == Type::LAngle) {
|
||||
return "LAngle";
|
||||
} else if (x == Type::RAngle) {
|
||||
return "RAngle";
|
||||
} else if (x == Type::LAngleEqual) {
|
||||
return "LAngleEqual";
|
||||
} else if (x == Type::RAngleEqual) {
|
||||
return "RAngleEqual";
|
||||
} else if (x == Type::LArrow) {
|
||||
return "LArrow";
|
||||
} else if (x == Type::RArrow) {
|
||||
return "RArrow";
|
||||
} else if (x == Type::FOR) {
|
||||
return "for";
|
||||
} else if (x == Type::WHILE) {
|
||||
return "while";
|
||||
} else if (x == Type::IF) {
|
||||
return "if";
|
||||
} else if (x == Type::ELSE) {
|
||||
return "else";
|
||||
} else {
|
||||
return "ERROR";
|
||||
}
|
||||
}
|
||||
}
|
45
src/main.cpp
Normal file
45
src/main.cpp
Normal file
|
@ -0,0 +1,45 @@
|
|||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include "preprocessor.hpp"
|
||||
#include "lexer.hpp"
|
||||
#include "syntax_tree.hpp"
|
||||
|
||||
std::string program;
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
if (argc != 2) {
|
||||
std::cout << "Invalid file" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
std::ifstream fin(argv[1]);
|
||||
{
|
||||
std::string str;
|
||||
while (getline(fin, str)) {
|
||||
str.push_back('\n');
|
||||
program += str;
|
||||
}
|
||||
}
|
||||
|
||||
// std::cout << program << std::endl;
|
||||
|
||||
program = Preprocessor(std::move(program));
|
||||
|
||||
// std::cout << program << std::endl;
|
||||
|
||||
Lexer lexer;
|
||||
lexer.ParseText(program);
|
||||
auto tokens = lexer.GetTokens();
|
||||
/*
|
||||
for (auto i: tokens) {
|
||||
std::cout << std::to_string(i.type) << " " << i.info << "; ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
*/
|
||||
SyntaxTree tree;
|
||||
tree.PushLexerTokenList(tokens);
|
||||
tree.Compile();
|
||||
tree.Run();
|
||||
std::cout << "END";
|
||||
}
|
||||
|
73
src/preprocessor.cpp
Normal file
73
src/preprocessor.cpp
Normal file
|
@ -0,0 +1,73 @@
|
|||
#include "preprocessor.hpp"
|
||||
#include <vector>
|
||||
|
||||
std::string DeleteComments(std::string&& text) {
|
||||
std::vector<std::pair<int, int>> text_without_comments;
|
||||
size_t n = text.size();
|
||||
int begin_segment = 0;
|
||||
|
||||
text_without_comments.emplace_back(begin_segment, n);
|
||||
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
if (text[i] == '/' && i + 1 < n && text[i + 1] == '/') {
|
||||
text_without_comments.pop_back();
|
||||
text_without_comments.emplace_back(begin_segment, i);
|
||||
while (i + 1 < n && text[i + 1] != '\n') {
|
||||
++i;
|
||||
}
|
||||
begin_segment = i + 1;
|
||||
text_without_comments.emplace_back(begin_segment, n);
|
||||
}
|
||||
}
|
||||
|
||||
int current_place = 0;
|
||||
for (auto i: text_without_comments) {
|
||||
std::copy(text.begin() + i.first, text.begin() + i.second, text.begin() +
|
||||
current_place);
|
||||
current_place += i.second - i.first;
|
||||
}
|
||||
text.resize(current_place);
|
||||
return std::move(text);
|
||||
}
|
||||
|
||||
std::string DeleteMultiLineComments(std::string&& text) {
|
||||
std::vector<std::pair<int, int>> text_without_comments;
|
||||
size_t n = text.size();
|
||||
int begin_segment = 0;
|
||||
|
||||
text_without_comments.emplace_back(begin_segment, n);
|
||||
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
if (text[i] == '/' && i + 1 < n && text[i + 1] == '*') {
|
||||
text_without_comments.pop_back();
|
||||
text_without_comments.emplace_back(begin_segment, i);
|
||||
i++;
|
||||
while (i < n && (text[i - 1] != '*' || text[i] != '/')) {
|
||||
++i;
|
||||
}
|
||||
begin_segment = i + 1;
|
||||
text_without_comments.emplace_back(begin_segment, n);
|
||||
}
|
||||
}
|
||||
|
||||
int current_place = 0;
|
||||
for (auto i: text_without_comments) {
|
||||
std::copy(text.begin() + i.first, text.begin() + i.second, text.begin() +
|
||||
current_place);
|
||||
current_place += i.second - i.first;
|
||||
}
|
||||
text.resize(current_place);
|
||||
return std::move(text);
|
||||
}
|
||||
|
||||
std::string Preprocessor(std::string&& text) {
|
||||
text = DeleteComments(std::move(text));
|
||||
text = DeleteMultiLineComments(std::move(text));
|
||||
return std::move(text);
|
||||
}
|
||||
|
||||
std::string Preprocessor(const std::string& text) {
|
||||
std::string copy(text);
|
||||
copy = Preprocessor(std::move(copy));
|
||||
return copy;
|
||||
}
|
949
src/syntax_tree.cpp
Normal file
949
src/syntax_tree.cpp
Normal file
|
@ -0,0 +1,949 @@
|
|||
#include <unordered_set>
|
||||
#include <stdexcept>
|
||||
#include <limits>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
#include "syntax_tree.hpp"
|
||||
|
||||
using Type = Lexer::LexerToken::Type;
|
||||
|
||||
std::optional<std::stringstream> output_print;
|
||||
|
||||
void SetPrintStringStream() {
|
||||
output_print = std::stringstream();
|
||||
}
|
||||
|
||||
std::stringstream& GetPrintStringStream() {
|
||||
if (output_print) {
|
||||
return *output_print;
|
||||
}
|
||||
throw std::logic_error("not string stream");
|
||||
}
|
||||
|
||||
std::ostream& GetPrintOstream() {
|
||||
if (output_print) {
|
||||
return *output_print;
|
||||
}
|
||||
return std::cout;
|
||||
}
|
||||
|
||||
bool IsOperation(Type type) {
|
||||
switch (type) {
|
||||
case Type::Equal:
|
||||
case Type::Plus:
|
||||
case Type::Minus:
|
||||
case Type::Star:
|
||||
case Type::Slash:
|
||||
case Type::ExclamationMark:
|
||||
case Type::PlusEqual:
|
||||
case Type::MinusEqual:
|
||||
case Type::StarEqual:
|
||||
case Type::SlashEqual:
|
||||
case Type::EqualEqual:
|
||||
case Type::ExclamationMarkEqual:
|
||||
// case Type::PlusPlus:
|
||||
// case Type::MinusMinus:
|
||||
case Type::LAngle:
|
||||
case Type::RAngle:
|
||||
case Type::LAngleEqual:
|
||||
case Type::RAngleEqual:return true;
|
||||
default:return false;
|
||||
}
|
||||
}
|
||||
|
||||
void Node::Insert(Node* node) {
|
||||
node->previous = this;
|
||||
node->parent = parent;
|
||||
if (next) {
|
||||
next->previous = node;
|
||||
node->next = next;
|
||||
} else {
|
||||
if (parent) {
|
||||
dynamic_cast<Container*>(parent)->children_end = node;
|
||||
}
|
||||
}
|
||||
next = node;
|
||||
}
|
||||
|
||||
void Node::InsertBefore(Node* node) {
|
||||
node->next = this;
|
||||
node->parent = parent;
|
||||
if (previous) {
|
||||
previous->next = node;
|
||||
node->previous = previous;
|
||||
} else {
|
||||
if (parent) {
|
||||
dynamic_cast<Container*>(parent)->children_begin = node;
|
||||
}
|
||||
}
|
||||
previous = node;
|
||||
}
|
||||
|
||||
void Container::AddChildren(Node* node) {
|
||||
if (children_end) {
|
||||
children_end->next = node;
|
||||
node->previous = children_end;
|
||||
children_end = node;
|
||||
node->parent = this;
|
||||
} else {
|
||||
children_begin = children_end = node;
|
||||
node->parent = this;
|
||||
}
|
||||
}
|
||||
|
||||
void Container::Run(std::vector<std::shared_ptr<VariableInStack>>& stack) {
|
||||
Node* current = children_begin;
|
||||
while (current) {
|
||||
current->Run(stack);
|
||||
current = current->next;
|
||||
}
|
||||
}
|
||||
|
||||
size_t Expression::GetPriority(Operation op) {
|
||||
switch (op) {
|
||||
case Operation::Equal:
|
||||
case Operation::StarEqual:
|
||||
case Operation::SlashEqual:
|
||||
case Operation::PlusEqual:
|
||||
case Operation::MinusEqual:
|
||||
return 0;
|
||||
case Operation::EqualEqual:
|
||||
case Operation::ExclamationMarkEqual:
|
||||
return 6;
|
||||
case Operation::LAngle:
|
||||
case Operation::LAngleEqual:
|
||||
case Operation::RAngle:
|
||||
case Operation::RAngleEqual:
|
||||
return 7;
|
||||
case Operation::Plus:
|
||||
case Operation::Minus:
|
||||
return 9;
|
||||
case Operation::Star:
|
||||
case Operation::Slash:
|
||||
return 10;
|
||||
}
|
||||
throw std::logic_error("unsupported operator");
|
||||
}
|
||||
|
||||
Operation Expression::Convert(Lexer::LexerToken::Type type) {
|
||||
switch (type) {
|
||||
case Type::Equal:return Operation::Equal;
|
||||
case Type::LAngle:return Operation::LAngle;
|
||||
case Type::Plus:return Operation::Plus;
|
||||
case Type::Minus:return Operation::Minus;
|
||||
case Type::Star:return Operation::Star;
|
||||
case Type::Slash:return Operation::Slash;
|
||||
case Type::ExclamationMark:return Operation::ExclamationMark;
|
||||
case Type::PlusEqual:return Operation::PlusEqual;
|
||||
case Type::MinusEqual:return Operation::MinusEqual;
|
||||
case Type::StarEqual:return Operation::StarEqual;
|
||||
case Type::SlashEqual:return Operation::SlashEqual;
|
||||
case Type::EqualEqual:return Operation::EqualEqual;
|
||||
case Type::ExclamationMarkEqual:return Operation::ExclamationMarkEqual;
|
||||
// case Type::PlusPlus:
|
||||
// case Type::MinusMinus:
|
||||
case Type::RAngle:return Operation::RAngle;
|
||||
case Type::LAngleEqual:return Operation::LAngleEqual;
|
||||
case Type::RAngleEqual:return Operation::RAngleEqual;
|
||||
}
|
||||
std::cerr << std::to_string(type) << std::endl;
|
||||
throw std::logic_error("unsupported operator");
|
||||
}
|
||||
|
||||
PriorityType Expression::GetPriorityType(Operation op) {
|
||||
switch (op) {
|
||||
case Operation::Equal:
|
||||
case Operation::StarEqual:
|
||||
case Operation::SlashEqual:
|
||||
case Operation::PlusEqual:
|
||||
case Operation::MinusEqual:
|
||||
return PriorityType::R;
|
||||
case Operation::EqualEqual:
|
||||
case Operation::ExclamationMarkEqual:
|
||||
case Operation::LAngle:
|
||||
case Operation::LAngleEqual:
|
||||
case Operation::RAngle:
|
||||
case Operation::RAngleEqual:
|
||||
case Operation::Plus:
|
||||
case Operation::Minus:
|
||||
case Operation::Star:
|
||||
case Operation::Slash:
|
||||
return PriorityType::L;
|
||||
}
|
||||
|
||||
throw std::logic_error("unsupported operator");
|
||||
}
|
||||
|
||||
void Expression::Run(std::vector<std::shared_ptr<VariableInStack>>& stack) {
|
||||
if (op == Operation::Value) {
|
||||
if (std::holds_alternative<size_t>(type1)) {
|
||||
stack.push_back(*(stack.end() - std::get<size_t>(type1)));
|
||||
} else {
|
||||
stack.push_back(std::make_shared<VariableInStack>(TypeVariable()));
|
||||
if (std::get<std::string>(type1)[0] == '\"') {
|
||||
stack.back()->type_variable = TypeVariable(TypeVariable::ID::type_string);
|
||||
stack.back()->Allocate();
|
||||
(*(std::string*) stack.back()->memory) =
|
||||
std::get<std::string>(type1).substr(1, std::get<std::string>(type1).size() - 2);
|
||||
} else {
|
||||
stack.back()->type_variable = TypeVariable(TypeVariable::ID::type_int);
|
||||
stack.back()->Allocate();
|
||||
(*(int*) stack.back()->memory) = std::atoi(std::get<std::string>(type1).c_str());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
std::get<Expression*>(type1)->Run(stack);
|
||||
std::shared_ptr<VariableInStack> x = std::move(stack.back());
|
||||
stack.pop_back();
|
||||
std::get<Expression*>(type2)->Run(stack);
|
||||
std::shared_ptr<VariableInStack> y = std::move(stack.back());
|
||||
stack.pop_back();
|
||||
stack.push_back(std::make_shared<VariableInStack>(TypeVariable()));
|
||||
x->CallOperator(y, stack.back(), op);
|
||||
}
|
||||
}
|
||||
|
||||
void Expression::AddStackPointer(stack_pointer d) {
|
||||
position_result += d;
|
||||
}
|
||||
|
||||
Expression::Expression(Expression::Types type1, Expression::Types type2, Operation op) : type1(std::move(type1)), type2(std::move(type2)), op(op) {
|
||||
if (op == Operation::Value) {
|
||||
count = 1;
|
||||
} else {
|
||||
count = 1;
|
||||
auto e_type1 = std::get<Expression*>(type1);
|
||||
auto e_type2 = std::get<Expression*>(type2);
|
||||
count += e_type1->count;
|
||||
count += e_type2->count;
|
||||
|
||||
position_result = count;
|
||||
e_type2->AddStackPointer(e_type1->count);
|
||||
}
|
||||
}
|
||||
|
||||
bool SyntaxTree::IsTypeName(Node* node, const std::string& str) {
|
||||
return str == "int" || str == "string";
|
||||
}
|
||||
|
||||
bool SyntaxTree::IsVariableName(Node* node, const std::string& name) {
|
||||
while (node) {
|
||||
if (auto var = dynamic_cast<Variable*>(node)) {
|
||||
if (var->name == name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (node->previous) {
|
||||
node = node->previous;
|
||||
} else {
|
||||
node = node->parent;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SyntaxTree::IsFunctionName(Node*, const std::string& str) {
|
||||
return str == "print";
|
||||
}
|
||||
|
||||
void SyntaxTree::PushLexerTokenList(const LexerTokenList& list) {
|
||||
if (!tree_) {
|
||||
tree_ = new CodeBlock();
|
||||
}
|
||||
for (auto it = list.begin(); it != list.end(); ++it) {
|
||||
PushLine(tree_, list, it);
|
||||
}
|
||||
}
|
||||
|
||||
CodeBlock* SyntaxTree::ParseCurlyBrackets(Container* container,
|
||||
const LexerTokenList& list,
|
||||
LexerTokenList::const_iterator& it) {
|
||||
auto* current = new CodeBlock();
|
||||
current->previous = container->children_end;
|
||||
current->parent = container;
|
||||
|
||||
for (; it->type != Type::CurlyCloseBracket; ++it) {
|
||||
PushLine(current, list, it);
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
void SyntaxTree::PushCurlyBrackets(Container* container, const LexerTokenList& list,
|
||||
LexerTokenList::const_iterator& it) {
|
||||
container->AddChildren(ParseCurlyBrackets(container, list, it));
|
||||
}
|
||||
|
||||
void SyntaxTree::PushLine(Container* container, const LexerTokenList& list,
|
||||
LexerTokenList::const_iterator& it) {
|
||||
if (it->type == Type::CurlyOpenBracket) {
|
||||
PushCurlyBrackets(container, list, ++it);
|
||||
} else if (it->type == Type::RoundOpenBracket) {
|
||||
PushExpression(container, list, it);
|
||||
} else if (it->type == Type::IF) {
|
||||
PushBlockIf(container, list, it);
|
||||
} else if (it->type == Type::FOR) {
|
||||
PushBlockFor(container, list, it);
|
||||
} else if (it->type == Type::WHILE) {
|
||||
PushBlockWhile(container, list, it);
|
||||
} else if (it->type == Type::Word) {
|
||||
auto place = container->children_end;
|
||||
if (!place)
|
||||
place = container;
|
||||
if (it->info == "def") {
|
||||
// PushFunction(container, list, ++it);
|
||||
} else if (IsTypeName(place, it->info)) {
|
||||
PushNewVariable(container, list, it);
|
||||
} else if (IsVariableName(place, it->info)) {
|
||||
PushExpression(container, list, it);
|
||||
} else if (IsFunctionName(place, it->info)) {
|
||||
PushCallFunction(container, list, it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Variable* SyntaxTree::ParseNewVariable(const LexerTokenList& list, LexerTokenList::const_iterator& it) {
|
||||
std::string type = it->info;
|
||||
++it;
|
||||
if (it->type == Type::Word) {
|
||||
auto var = new Variable(std::move(type), it->info);
|
||||
++it;
|
||||
if (it->type == Type::Semicolon)
|
||||
return var;
|
||||
if (it->type == Type::Equal) {
|
||||
--it;
|
||||
var->default_value = ParseExpression(list, it);
|
||||
if (it->type == Type::Semicolon) {
|
||||
return var;
|
||||
} else {
|
||||
throw std::logic_error("Expected ;");
|
||||
}
|
||||
} else {
|
||||
throw std::logic_error("Expected =");
|
||||
}
|
||||
} else {
|
||||
throw std::logic_error("Expected variable name");
|
||||
}
|
||||
}
|
||||
|
||||
void SyntaxTree::PushNewVariable(Container* container, const LexerTokenList& list, LexerTokenList::const_iterator& it) {
|
||||
container->AddChildren(ParseNewVariable(list, it));
|
||||
}
|
||||
|
||||
void SyntaxTree::PushExpression(Container* container, const LexerTokenList& list, LexerTokenList::const_iterator& it) {
|
||||
container->AddChildren(ParseExpression(list, it));
|
||||
}
|
||||
|
||||
void SyntaxTree::PushParametersFunction(CallFunction* call_function,
|
||||
const LexerTokenList& list,
|
||||
LexerTokenList::const_iterator& it) {
|
||||
if (it->type != Type::RoundOpenBracket) {
|
||||
throw std::logic_error("expected ( after function name");
|
||||
}
|
||||
while (it->type != Type::RoundCloseBracket) {
|
||||
auto expr = ParseExpression(list, ++it);
|
||||
call_function->parameters.push_back(new Expression(new Expression((size_t) 0, nullptr, Operation::Value),
|
||||
expr,
|
||||
Operation::Equal));
|
||||
auto& t = call_function->parameters.back();
|
||||
t->parent = call_function;
|
||||
std::get<Expression*>(t->type1)->parent = t;
|
||||
std::get<Expression*>(t->type2)->parent = t;
|
||||
}
|
||||
for (size_t i = 0; i < call_function->parameters.size(); ++i) {
|
||||
std::get<Expression*>(call_function->parameters[i]->type1)->type1 = call_function->parameters.size() - i;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
|
||||
void SyntaxTree::PushCallFunction(Container* container,
|
||||
const LexerTokenList& list,
|
||||
LexerTokenList::const_iterator& it) {
|
||||
auto function = new CallFunction(it->info);
|
||||
PushParametersFunction(function, list, ++it);
|
||||
container->AddChildren(new CreateVariables(function->parameters.size()));
|
||||
container->AddChildren(function);
|
||||
container->AddChildren(new HiddenDeallocateStack(function->parameters.size()));
|
||||
}
|
||||
|
||||
void SyntaxTree::PushBlockFor(Container* container,
|
||||
const LexerTokenList& list,
|
||||
std::list<Lexer::LexerToken>::const_iterator& it) {
|
||||
auto c = new Container;
|
||||
auto f = new BlockFor();
|
||||
container->AddChildren(c);
|
||||
PushSignatureBlockFor(f, list, ++it);
|
||||
c->AddChildren(f->var);
|
||||
c->AddChildren(f);
|
||||
if (it->type != Type::CurlyOpenBracket) {
|
||||
throw std::logic_error("expected { after for (...)");
|
||||
}
|
||||
f->code = ParseCurlyBrackets(c, list, ++it);
|
||||
f->code->parent = f;
|
||||
f->code->previous = nullptr;
|
||||
}
|
||||
|
||||
void SyntaxTree::PushSignatureBlockFor(BlockFor* node_for,
|
||||
const LexerTokenList& list,
|
||||
std::list<Lexer::LexerToken>::const_iterator& it) {
|
||||
if (it->type != Type::RoundOpenBracket)
|
||||
throw std::logic_error("expected ( after for");
|
||||
++it;
|
||||
if (it->type == Type::Semicolon) {
|
||||
++it;
|
||||
} else {
|
||||
if (it->type != Type::Word || !IsTypeName(node_for, it->info)) {
|
||||
throw std::logic_error("expected type after for (");
|
||||
}
|
||||
node_for->var = ParseNewVariable(list, it);
|
||||
node_for->var->parent = node_for;
|
||||
|
||||
if (it->type != Type::Semicolon) {
|
||||
throw std::logic_error("expected ; after for ( type name");
|
||||
}
|
||||
++it;
|
||||
}
|
||||
if (it->type == Type::Semicolon) {
|
||||
++it;
|
||||
} else {
|
||||
node_for->check = ParseExpression(list, it);
|
||||
node_for->check->parent = node_for;
|
||||
if (it->type != Type::Semicolon) {
|
||||
throw std::logic_error("expected ; after for ( type name; expr");
|
||||
}
|
||||
++it;
|
||||
}
|
||||
if (it->type == Type::RoundCloseBracket) {
|
||||
} else {
|
||||
node_for->tick = ParseExpression(list, it);
|
||||
node_for->tick->parent = node_for;
|
||||
if (it->type != Type::RoundCloseBracket) {
|
||||
throw std::logic_error("expected ) after for ( type name; expr; expr");
|
||||
}
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
void SyntaxTree::PushBlockIf(Container* container,
|
||||
const LexerTokenList& list,
|
||||
std::list<Lexer::LexerToken>::const_iterator& it) {
|
||||
auto f = new BlockIf();
|
||||
container->AddChildren(f);
|
||||
PushSignatureBlockIf(f, list, ++it);
|
||||
f->check->parent = f;
|
||||
if (it->type != Type::CurlyOpenBracket) {
|
||||
throw std::logic_error("expected { after if (...)");
|
||||
}
|
||||
f->code = ParseCurlyBrackets(container, list, ++it);
|
||||
f->code->parent = f;
|
||||
}
|
||||
|
||||
void SyntaxTree::PushSignatureBlockIf(BlockIf* node_if,
|
||||
const LexerTokenList& list,
|
||||
std::list<Lexer::LexerToken>::const_iterator& it) {
|
||||
if (it->type != Type::RoundOpenBracket)
|
||||
throw std::logic_error("expected ( after if");
|
||||
++it;
|
||||
node_if->check = ParseExpression(list, it);
|
||||
node_if->check->parent = node_if;
|
||||
if (it->type != Type::RoundCloseBracket) {
|
||||
throw std::logic_error("expected ) after if(...");
|
||||
}
|
||||
++it;
|
||||
}
|
||||
|
||||
void SyntaxTree::PushBlockWhile(Container* container,
|
||||
const LexerTokenList& list,
|
||||
std::list<Lexer::LexerToken>::const_iterator& it) {
|
||||
auto f = new BlockWhile();
|
||||
container->AddChildren(f);
|
||||
PushSignatureBlockWhile(f, list, ++it);
|
||||
if (it->type != Type::CurlyOpenBracket) {
|
||||
throw std::logic_error("expected { after while (...)");
|
||||
}
|
||||
f->code = ParseCurlyBrackets(container, list, ++it);
|
||||
}
|
||||
|
||||
void SyntaxTree::PushSignatureBlockWhile(BlockWhile* node_if,
|
||||
const LexerTokenList& list,
|
||||
std::list<Lexer::LexerToken>::const_iterator& it) {
|
||||
if (it->type != Type::RoundOpenBracket)
|
||||
throw std::logic_error("expected ( after while");
|
||||
++it;
|
||||
node_if->check = ParseExpression(list, it);
|
||||
node_if->check->parent = node_if;
|
||||
if (it->type != Type::RoundCloseBracket) {
|
||||
throw std::logic_error("expected ) after while(...");
|
||||
}
|
||||
++it;
|
||||
}
|
||||
|
||||
Expression* SyntaxTree::ParseExpression(LexerTokenList::const_iterator l, LexerTokenList::const_iterator r) {
|
||||
if (std::next(l) == r) {
|
||||
return new Expression(l->info, nullptr, Operation::Value);
|
||||
}
|
||||
{
|
||||
auto it = l;
|
||||
if (it->type == Type::RoundOpenBracket) {
|
||||
int balance = 1;
|
||||
do {
|
||||
++it;
|
||||
if (it->type == Type::RoundOpenBracket) {
|
||||
++balance;
|
||||
} else if (it->type == Type::RoundCloseBracket) {
|
||||
--balance;
|
||||
}
|
||||
} while (balance != 0);
|
||||
}
|
||||
++it;
|
||||
if (it == r) {
|
||||
return ParseExpression(std::next(l), std::prev(r));
|
||||
}
|
||||
}
|
||||
auto mid = l;
|
||||
size_t current_priority = std::numeric_limits<size_t>::max();
|
||||
for (auto it = l; std::next(it) != r; ++it) {
|
||||
if (it->type == Type::RoundOpenBracket) {
|
||||
int balance = 1;
|
||||
do {
|
||||
++it;
|
||||
if (it->type == Type::RoundOpenBracket) {
|
||||
++balance;
|
||||
} else if (it->type == Type::RoundCloseBracket) {
|
||||
--balance;
|
||||
}
|
||||
} while (balance != 0);
|
||||
}
|
||||
++it;
|
||||
|
||||
size_t tmp = Expression::GetPriority(Expression::Convert(it->type));
|
||||
if (tmp < current_priority) {
|
||||
mid = it;
|
||||
current_priority = tmp;
|
||||
} else if (tmp == current_priority) {
|
||||
if (Expression::GetPriorityType(Expression::Convert(it->type)) == PriorityType::R) {
|
||||
mid = it;
|
||||
}
|
||||
}
|
||||
}
|
||||
return new Expression(ParseExpression(l, mid), ParseExpression(std::next(mid), r), Expression::Convert(mid->type));
|
||||
}
|
||||
|
||||
Expression* SyntaxTree::ParseExpression(const LexerTokenList& list,
|
||||
std::list<Lexer::LexerToken>::const_iterator& it) {
|
||||
auto l = it;
|
||||
int balance = 0;
|
||||
while (it->type == Type::Word || IsOperation(it->type) || it->type == Type::StringLiteral
|
||||
|| it->type == Type::RoundOpenBracket || it->type == Type::RoundCloseBracket) {
|
||||
if (it->type == Type::RoundOpenBracket) {
|
||||
++balance;
|
||||
} else if (it->type == Type::RoundCloseBracket) {
|
||||
if (balance == 0) {
|
||||
break;
|
||||
} else {
|
||||
--balance;
|
||||
}
|
||||
}
|
||||
++it;
|
||||
}
|
||||
return ParseExpression(l, it);
|
||||
}
|
||||
|
||||
void SyntaxTree::PushDeallocateStack(Node* node, size_t count_variables = 0) {
|
||||
if (auto code_block = dynamic_cast<Container*>(node)) {
|
||||
if (code_block->children_begin) {
|
||||
PushDeallocateStack(code_block->children_begin);
|
||||
}
|
||||
}
|
||||
if (auto if_ = dynamic_cast<BlockIf*>(node)) {
|
||||
PushDeallocateStack(if_->code);
|
||||
}
|
||||
if (auto for_ = dynamic_cast<BlockFor*>(node)) {
|
||||
PushDeallocateStack(for_->code);
|
||||
}
|
||||
if (dynamic_cast<Variable*>(node)) {
|
||||
++count_variables;
|
||||
}
|
||||
if (auto expr = dynamic_cast<Expression*>(node)) {
|
||||
node->Insert(new DeallocateStack(1));
|
||||
}
|
||||
if (node->next) {
|
||||
PushDeallocateStack(node->next, count_variables);
|
||||
} else {
|
||||
if (count_variables) {
|
||||
node->Insert(new DeallocateStack(count_variables));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SyntaxTree::Compile() {
|
||||
PushDeallocateStack(tree_);
|
||||
LinkVariables(tree_);
|
||||
}
|
||||
|
||||
void SyntaxTree::Run() {
|
||||
std::vector<std::shared_ptr<VariableInStack>> stack;
|
||||
tree_->Run(stack);
|
||||
}
|
||||
|
||||
void SyntaxTree::LinkVariables(Node* node) {
|
||||
while (node) {
|
||||
if (auto expr = dynamic_cast<Expression*>(node)) {
|
||||
LinkVariablesInExpression(expr, node);
|
||||
}
|
||||
if (auto var = dynamic_cast<Variable*>(node)) {
|
||||
if (var->default_value) {
|
||||
LinkVariablesInExpression(var->default_value, node);
|
||||
}
|
||||
}
|
||||
if (auto if_ = dynamic_cast<BlockIf*>(node)) {
|
||||
LinkVariablesInExpression(if_->check, node);
|
||||
LinkVariables(if_->code->children_begin);
|
||||
}
|
||||
if (auto while_ = dynamic_cast<BlockWhile*>(node)) {
|
||||
LinkVariablesInExpression(while_->check, node);
|
||||
LinkVariables(while_->code->children_begin);
|
||||
}
|
||||
if (auto for_ = dynamic_cast<BlockFor*>(node)) {
|
||||
LinkVariablesInExpression(for_->check, node);
|
||||
LinkVariablesInExpression(for_->tick, node);
|
||||
LinkVariables(for_->code->children_begin);
|
||||
}
|
||||
if (auto call_function = dynamic_cast<CallFunction*>(node)) {
|
||||
for (auto i: call_function->parameters) {
|
||||
LinkVariablesInExpression(i, node);
|
||||
}
|
||||
}
|
||||
if (auto container = dynamic_cast<Container*>(node)) {
|
||||
LinkVariables(container->children_begin);
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
}
|
||||
|
||||
stack_pointer SyntaxTree::GetCountStackOffsetForVariable(Node* node, std::string name) {
|
||||
stack_pointer result = 0;
|
||||
while (node) {
|
||||
if (auto var = dynamic_cast<Variable*>(node)) {
|
||||
result += 1;
|
||||
if (var->name == name) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
if (auto vars = dynamic_cast<CreateVariables*>(node)) {
|
||||
result += vars->count;
|
||||
}
|
||||
if (auto vars = dynamic_cast<HiddenDeallocateStack*>(node)) {
|
||||
result -= vars->count;
|
||||
}
|
||||
if (node->previous) {
|
||||
node = node->previous;
|
||||
} else {
|
||||
node = node->parent;
|
||||
}
|
||||
}
|
||||
return std::numeric_limits<stack_pointer>::max();
|
||||
}
|
||||
|
||||
void SyntaxTree::LinkVariablesInExpression(Expression* expression, Node* node) {
|
||||
if (expression->op == Operation::Value) {
|
||||
if (std::holds_alternative<std::string>(expression->type1)) {
|
||||
auto& var = std::get<std::string>(expression->type1);
|
||||
stack_pointer offset = GetCountStackOffsetForVariable(node, var);
|
||||
if (offset != std::numeric_limits<stack_pointer>::max()) {
|
||||
expression->type1 = offset;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LinkVariablesInExpression(std::get<Expression*>(expression->type1), node);
|
||||
LinkVariablesInExpression(std::get<Expression*>(expression->type2), node);
|
||||
}
|
||||
}
|
||||
|
||||
void CallFunction::Run(std::vector<std::shared_ptr<VariableInStack>>& stack) {
|
||||
for (size_t i = 0; i < parameters.size(); ++i) {
|
||||
stack.push_back(std::make_shared<VariableInStack>(TypeVariable()));
|
||||
// stack.back().Allocate();
|
||||
}
|
||||
for (auto i: parameters) {
|
||||
i->Run(stack);
|
||||
stack.pop_back();
|
||||
}
|
||||
if (name_function == "print") {
|
||||
for (size_t i = 0; i < parameters.size(); ++i) {
|
||||
std::shared_ptr<VariableInStack>& t = *(stack.end() - parameters.size() + i);
|
||||
if (t->type_variable.id == TypeVariable::ID::type_int) {
|
||||
GetPrintOstream() << *((int*) t->memory) << (i + 1 != parameters.size() ? " " : "");
|
||||
} else if (t->type_variable.id == TypeVariable::ID::type_string) {
|
||||
GetPrintOstream() << *((std::string*) t->memory) << (i + 1 != parameters.size() ? " " : "");
|
||||
}
|
||||
}
|
||||
GetPrintOstream() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void Variable::Run(std::vector<std::shared_ptr<VariableInStack>>& stack) {
|
||||
if (type == "int") {
|
||||
stack.push_back(std::make_shared<VariableInStack>(TypeVariable(TypeVariable::ID::type_int)));
|
||||
} else if (type == "string") {
|
||||
stack.push_back(std::make_shared<VariableInStack>(TypeVariable(TypeVariable::ID::type_string)));
|
||||
}
|
||||
stack.back()->Allocate();
|
||||
if (default_value) {
|
||||
default_value->Run(stack);
|
||||
stack.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
void VariableInStack::CallOperator(std::shared_ptr<VariableInStack>& another,
|
||||
std::shared_ptr<VariableInStack>& result,
|
||||
Operation op) {
|
||||
if (op == Operation::Equal || op == Operation::PlusEqual || op == Operation::MinusEqual
|
||||
|| op == Operation::StarEqual || op == Operation::SlashEqual) {
|
||||
CallEqualOperator(another, result, op);
|
||||
return;
|
||||
}
|
||||
if (op == Operation::EqualEqual || op == Operation::ExclamationMarkEqual || op == Operation::LAngle
|
||||
|| op == Operation::RAngle || op == Operation::LAngleEqual || op == Operation::RAngleEqual) {
|
||||
CallComparisonOperator(another, result, op);
|
||||
return;
|
||||
}
|
||||
if (op == Operation::Plus || op == Operation::Minus || op == Operation::Star || op == Operation::Slash) {
|
||||
CallArithmeticOperator(another, result, op);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void VariableInStack::CallArithmeticOperator(const std::shared_ptr<VariableInStack>& another,
|
||||
std::shared_ptr<VariableInStack>& result,
|
||||
const Operation& op) const {
|
||||
result->Clear();
|
||||
result->type_variable.id = another->type_variable.id;
|
||||
result->Allocate();
|
||||
if (another->type_variable.id == TypeVariable::type_int && type_variable.id == TypeVariable::type_int) {
|
||||
int& x = *static_cast<int*>(memory);
|
||||
int& y = *static_cast<int*>(another->memory);
|
||||
int& res = *static_cast<int*>(result->memory);
|
||||
if (op == Operation::Plus) {
|
||||
res = (x + y);
|
||||
} else if (op == Operation::Minus) {
|
||||
res = (x - y);
|
||||
} else if (op == Operation::Star) {
|
||||
res = (x * y);
|
||||
} else if (op == Operation::Slash) {
|
||||
res = (x / y);
|
||||
}
|
||||
} else if (another->type_variable.id == TypeVariable::type_string
|
||||
&& type_variable.id == TypeVariable::type_string) {
|
||||
std::string& x = *static_cast<std::string*>(memory);
|
||||
std::string& y = *static_cast<std::string*>(another->memory);
|
||||
std::string& res = *static_cast<std::string*>(result->memory);
|
||||
if (op == Operation::Plus) {
|
||||
res = (x + y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VariableInStack::CallComparisonOperator(const std::shared_ptr<VariableInStack>& another,
|
||||
std::shared_ptr<VariableInStack>& result,
|
||||
const Operation& op) const {
|
||||
result->Clear();
|
||||
result->type_variable.id = TypeVariable::type_int;
|
||||
result->Allocate();
|
||||
int& res = *static_cast<int*>(result->memory);
|
||||
|
||||
if (another->type_variable.id == TypeVariable::type_int && type_variable.id == TypeVariable::type_int) {
|
||||
auto& x = *static_cast<int*>(memory);
|
||||
auto& y = *static_cast<int*>(another->memory);
|
||||
if (op == Operation::EqualEqual) {
|
||||
res = (x == y);
|
||||
} else if (op == Operation::ExclamationMarkEqual) {
|
||||
res = (x != y);
|
||||
} else if (op == Operation::LAngle) {
|
||||
res = (x < y);
|
||||
} else if (op == Operation::RAngle) {
|
||||
res = (x > y);
|
||||
} else if (op == Operation::LAngleEqual) {
|
||||
res = (x <= y);
|
||||
} else if (op == Operation::RAngleEqual) {
|
||||
res = (x >= y);
|
||||
}
|
||||
} else if (another->type_variable.id == TypeVariable::type_string
|
||||
&& type_variable.id == TypeVariable::type_string) {
|
||||
auto& x = *static_cast<std::string*>(memory);
|
||||
auto& y = *static_cast<std::string*>(another->memory);
|
||||
if (op == Operation::EqualEqual) {
|
||||
res = (x == y);
|
||||
} else if (op == Operation::ExclamationMarkEqual) {
|
||||
res = (x != y);
|
||||
} else if (op == Operation::LAngle) {
|
||||
res = (x < y);
|
||||
} else if (op == Operation::RAngle) {
|
||||
res = (x > y);
|
||||
} else if (op == Operation::LAngleEqual) {
|
||||
res = (x <= y);
|
||||
} else if (op == Operation::RAngleEqual) {
|
||||
res = (x >= y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VariableInStack::CallEqualOperator(const std::shared_ptr<VariableInStack>& another,
|
||||
std::shared_ptr<VariableInStack>& result,
|
||||
const Operation& op) {
|
||||
if (type_variable.id != TypeVariable::none && type_variable.id != another->type_variable.id) {
|
||||
throw std::logic_error("error variable convert");
|
||||
}
|
||||
if (result->type_variable.id != TypeVariable::none && result->type_variable.id != another->type_variable.id) {
|
||||
throw std::logic_error("error variable convert");
|
||||
}
|
||||
result->Clear();
|
||||
result->type_variable.id = another->type_variable.id;
|
||||
result->Allocate();
|
||||
if (op == Operation::Equal) {
|
||||
Clear();
|
||||
type_variable.id = another->type_variable.id;
|
||||
Allocate();
|
||||
}
|
||||
if (another->type_variable.id == TypeVariable::type_int) {
|
||||
CallEqualOperatorInt(another, result, op);
|
||||
} else if (another->type_variable.id == TypeVariable::type_string) {
|
||||
CallEqualOperatorString(another, result, op);
|
||||
}
|
||||
}
|
||||
|
||||
void VariableInStack::CallEqualOperatorString(const std::shared_ptr<VariableInStack>& another,
|
||||
const std::shared_ptr<VariableInStack>& result,
|
||||
const Operation& op) const {
|
||||
std::string& x = *static_cast<std::string*>(memory);
|
||||
std::string& y = *static_cast<std::string*>(another->memory);
|
||||
std::string& res = *static_cast<std::string*>(result->memory);
|
||||
if (op == Operation::Equal) {
|
||||
if (this == another.get()) {
|
||||
res = x;
|
||||
} else {
|
||||
res = x = y;
|
||||
}
|
||||
} else if (op == Operation::PlusEqual) {
|
||||
if (this == another.get()) {
|
||||
res += x;
|
||||
} else {
|
||||
res = x += y;
|
||||
}
|
||||
} else {
|
||||
throw std::logic_error("invalid operation string");
|
||||
}
|
||||
}
|
||||
|
||||
void VariableInStack::CallEqualOperatorInt(const std::shared_ptr<VariableInStack>& another,
|
||||
const std::shared_ptr<VariableInStack>& result,
|
||||
const Operation& op) const {
|
||||
int& x = *static_cast<int*>(memory);
|
||||
int& y = *static_cast<int*>(another->memory);
|
||||
int& res = *static_cast<int*>(result->memory);
|
||||
if (op == Operation::Equal) {
|
||||
if (this == another.get()) {
|
||||
res = x;
|
||||
} else {
|
||||
res = x = y;
|
||||
}
|
||||
} else if (op == Operation::PlusEqual) {
|
||||
if (this == another.get()) {
|
||||
res += x;
|
||||
} else {
|
||||
res = x += y;
|
||||
}
|
||||
} else if (op == Operation::MinusEqual) {
|
||||
if (this == another.get()) {
|
||||
res -= x;
|
||||
} else {
|
||||
res = x -= y;
|
||||
}
|
||||
} else if (op == Operation::StarEqual) {
|
||||
if (this == another.get()) {
|
||||
res *= x;
|
||||
} else {
|
||||
res = x *= y;
|
||||
}
|
||||
} else {
|
||||
if (this == another.get()) {
|
||||
res /= x;
|
||||
} else {
|
||||
res = x /= y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VariableInStack::Clear() {
|
||||
if (memory) {
|
||||
if (type_variable.id == TypeVariable::ID::type_int) {
|
||||
delete static_cast<int*>(memory);
|
||||
} else if (type_variable.id == TypeVariable::ID::type_string) {
|
||||
delete static_cast<std::string*>(memory);
|
||||
}
|
||||
}
|
||||
memory = nullptr;
|
||||
}
|
||||
|
||||
void VariableInStack::Allocate() {
|
||||
if (type_variable.id == TypeVariable::ID::type_int) {
|
||||
memory = new int();
|
||||
} else if (type_variable.id == TypeVariable::ID::type_string) {
|
||||
memory = new std::string();
|
||||
}
|
||||
}
|
||||
|
||||
void BlockWhile::Run(std::vector<std::shared_ptr<VariableInStack>>& stack) {
|
||||
if (check) {
|
||||
while (true) {
|
||||
check->Run(stack);
|
||||
auto res = *((int*) stack.back()->memory);
|
||||
stack.pop_back();
|
||||
if (res) {
|
||||
code->Run(stack);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BlockIf::Run(std::vector<std::shared_ptr<VariableInStack>>& stack) {
|
||||
if (check) {
|
||||
check->Run(stack);
|
||||
auto res = *((int*) stack.back()->memory);
|
||||
stack.pop_back();
|
||||
if (res) {
|
||||
code->Run(stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BlockFor::Run(std::vector<std::shared_ptr<VariableInStack>>& stack) {
|
||||
while (true) {
|
||||
check->Run(stack);
|
||||
auto res = *((int*) stack.back()->memory);
|
||||
stack.pop_back();
|
||||
if (!res) {
|
||||
break;
|
||||
}
|
||||
code->Run(stack);
|
||||
size_t sz = stack.size();
|
||||
tick->Run(stack);
|
||||
while (stack.size() != sz) {
|
||||
stack.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HiddenDeallocateStack::Run(std::vector<std::shared_ptr<VariableInStack>>& stack) {
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
stack.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
void DeallocateStack::Run(std::vector<std::shared_ptr<VariableInStack>>& stack) {
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
stack.pop_back();
|
||||
}
|
||||
}
|
49
tests/MainTest.cpp
Normal file
49
tests/MainTest.cpp
Normal file
|
@ -0,0 +1,49 @@
|
|||
#include <gtest/gtest.h>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include "preprocessor.hpp"
|
||||
#include "lexer.hpp"
|
||||
#include "syntax_tree.hpp"
|
||||
|
||||
std::string LoadFile(const std::string& file) {
|
||||
std::string program;
|
||||
std::ifstream fin(file);
|
||||
{
|
||||
std::string str;
|
||||
while (getline(fin, str)) {
|
||||
str.push_back('\n');
|
||||
program += str;
|
||||
}
|
||||
}
|
||||
return program;
|
||||
}
|
||||
|
||||
void RunTest(const std::string& program_file, const std::string& result_file) {
|
||||
std::string program = LoadFile(program_file);
|
||||
program = Preprocessor(std::move(program));
|
||||
Lexer lexer;
|
||||
lexer.ParseText(program);
|
||||
auto tokens = lexer.GetTokens();
|
||||
SyntaxTree tree;
|
||||
tree.PushLexerTokenList(tokens);
|
||||
tree.Compile();
|
||||
|
||||
SetPrintStringStream();
|
||||
tree.Run();
|
||||
std::string result = LoadFile(result_file);
|
||||
ASSERT_EQ(result, GetPrintStringStream().str());
|
||||
}
|
||||
|
||||
TEST(test_int, full_test) {
|
||||
RunTest("programs/int/1.omgpl", "programs/int/1.res");
|
||||
}
|
||||
|
||||
TEST(test_string, full_test) {
|
||||
RunTest("programs/string/1.omgpl", "programs/string/1.res");
|
||||
}
|
||||
|
||||
int main() {
|
||||
testing::InitGoogleTest();
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
13
tests/programs/int/1.omgpl
Normal file
13
tests/programs/int/1.omgpl
Normal file
|
@ -0,0 +1,13 @@
|
|||
int x = 0;
|
||||
for (int i = 0; i < 10; i += 1) {
|
||||
x += i;
|
||||
}
|
||||
print(x);
|
||||
for (int i = 0; i < 10; i += 1) {
|
||||
x -= i;
|
||||
}
|
||||
print(x);
|
||||
for (int i = 0; i < 4; i += 1) {
|
||||
x = (x + 1) * i;
|
||||
}
|
||||
print(x);
|
3
tests/programs/int/1.res
Normal file
3
tests/programs/int/1.res
Normal file
|
@ -0,0 +1,3 @@
|
|||
45
|
||||
0
|
||||
15
|
11
tests/programs/string/1.omgpl
Normal file
11
tests/programs/string/1.omgpl
Normal file
|
@ -0,0 +1,11 @@
|
|||
string s = "";
|
||||
|
||||
for (int i = 0; i < 10; i += 1) {
|
||||
if ((i / 2) * 2 == i) {
|
||||
s += "a";
|
||||
}
|
||||
if ((i / 2) * 2 != i) {
|
||||
s += "b";
|
||||
}
|
||||
}
|
||||
print(s);
|
1
tests/programs/string/1.res
Normal file
1
tests/programs/string/1.res
Normal file
|
@ -0,0 +1 @@
|
|||
ababababab
|
Loading…
Reference in a new issue