├── .gitignore ├── README.md ├── test.neo ├── CMakeLists.txt ├── grammer.md └── src ├── main.cpp ├── tokenization.hpp ├── generation.hpp └── parser.hpp /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .vscode -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Neo compiler -------------------------------------------------------------------------------- /test.neo: -------------------------------------------------------------------------------- 1 | let x=7; 2 | let y=8; 3 | exit(x); -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20) 2 | 3 | project(Neo) 4 | 5 | set(CMAKE_CXX_STANDARD 20) 6 | 7 | add_executable(neo src/main.cpp) -------------------------------------------------------------------------------- /grammer.md: -------------------------------------------------------------------------------- 1 | $$ 2 | \begin{align} 3 | [\text{prog}] &\to [\text{stmt}]^* \\ 4 | [\text{stmt}] &\to 5 | \begin{cases} 6 | \text{exit}([\text{expr}]); \\ 7 | \text{let}\space\text{ident} = [\text{expr}]; 8 | \end{cases} \\ 9 | [\text{expr}] &\to 10 | \begin{cases} 11 | \text{int\_lit} \\ 12 | \text{ident} 13 | \end{cases} 14 | \end{align} 15 | $$ -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | using namespace std; 9 | 10 | int main(int argc, char* argv[]){ 11 | if(argc!=2){ 12 | cerr<<"Incorrect usage.Input file is required"<"< tokens=tokenizer.tokenize(); 26 | 27 | Parser parser(tokens); 28 | optional prog=parser.parse_prog(); 29 | 30 | if(!prog.has_value()){ 31 | cerr<<"Invalid Syntax"< 3 | #include 4 | using namespace std; 5 | 6 | enum class TokenType{ 7 | _exit, 8 | int_lit, 9 | semi, 10 | open_paren, 11 | closed_paren, 12 | identifier, 13 | _let, 14 | eq 15 | }; 16 | 17 | struct Token 18 | { 19 | TokenType type; 20 | optional value {}; 21 | }; 22 | 23 | class Tokenizer{ 24 | public: 25 | inline explicit Tokenizer(const string &src):m_src(move(src)){} 26 | 27 | inline vector tokenize(){ 28 | vector tokens {}; 29 | string buff; 30 | 31 | while(peek().has_value()){ 32 | if(isalpha(peek().value())){ 33 | buff.push_back(consume()); 34 | while(peek().has_value() && isalnum(peek().value())){ 35 | buff.push_back(consume()); 36 | } 37 | if(buff=="exit"){ 38 | tokens.push_back(Token{TokenType::_exit}); 39 | buff.clear(); 40 | } 41 | else if(buff=="let"){ 42 | tokens.push_back(Token{TokenType::_let}); 43 | buff.clear(); 44 | } 45 | else{ 46 | tokens.push_back(Token{TokenType::identifier,buff}); 47 | buff.clear(); 48 | } 49 | } 50 | else if(isdigit(peek().value())){ 51 | buff.push_back(consume()); 52 | while(peek().has_value() && isdigit(peek().value())){ 53 | buff.push_back(consume()); 54 | } 55 | tokens.push_back(Token{TokenType::int_lit,buff}); 56 | buff.clear(); 57 | } 58 | else if(peek().value()=='('){ 59 | tokens.push_back(Token{TokenType::open_paren}); 60 | consume(); 61 | } 62 | else if(peek().value()==')'){ 63 | tokens.push_back(Token{TokenType::closed_paren}); 64 | consume(); 65 | } 66 | else if(peek().value()==';'){ 67 | tokens.push_back(Token{TokenType::semi}); 68 | consume(); 69 | } 70 | else if(peek().value()=='='){ 71 | tokens.push_back(Token{TokenType::eq}); 72 | consume(); 73 | } 74 | else if(isspace(peek().value())){ 75 | consume(); 76 | } 77 | else{ 78 | cerr<<"Unknown token "< peek(int offset=0) const{ 90 | if(m_pos+offset>=m_src.length()) 91 | return {}; 92 | return m_src.at(m_pos+offset); 93 | } 94 | 95 | inline char consume(){ 96 | return m_src.at(m_pos++); 97 | } 98 | }; -------------------------------------------------------------------------------- /src/generation.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "parser.hpp" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace std; 10 | 11 | 12 | class Generator{ 13 | public: 14 | inline explicit Generator(NodeProgram root):m_prog(move(root)){} 15 | 16 | 17 | void gen_expr(const NodeExpr& expr){ 18 | struct ExprVistor{ 19 | Generator* gen; 20 | void operator()(const NodeExprIntLit expr_int_lit){ 21 | gen->m_output<<" mov rax, "<push("rax"); 23 | 24 | } 25 | void operator()(const NodeExprIdent expr_indent){ 26 | if(gen->m_vars.find(expr_indent.ident.value.value())==gen->m_vars.end()){ 27 | cerr<<"Undeclared identifier: "<m_vars.at(expr_indent.ident.value.value()); 31 | stringstream offset; 32 | offset<<"QWORD [rsp + "<<(gen->m_stack_size-var.stack_location-1)*8<<"]\n"; 33 | gen->push(offset.str()); 34 | } 35 | }; 36 | 37 | ExprVistor visitor{.gen=this}; 38 | visit(visitor,expr.var); 39 | } 40 | 41 | void gen_stmt(const NodeStmt& statement){ 42 | struct StmtVisitor{ 43 | Generator* gen; 44 | void operator()(const NodeStmtExit stmt_exit) const{ 45 | gen->gen_expr(stmt_exit.expr); 46 | gen->m_output<<" mov rax, 60"<pop("rdi"); 48 | gen->m_output<<" syscall"<m_vars.find(stmt_let.ident.value.value())!=gen->m_vars.end()){ 52 | cerr<<"Identifier already in use "<m_vars.insert({stmt_let.ident.value.value(),Var{.stack_location=gen->m_stack_size}}); 56 | gen->gen_expr(stmt_let.expr); 57 | } 58 | }; 59 | StmtVisitor vistor{.gen=this}; 60 | visit(vistor,statement.var); 61 | } 62 | 63 | [[nodiscard]] inline string gen_prog(){ 64 | m_output<<"global _start\n_start:\n"; 65 | 66 | for(const NodeStmt &stmt:m_prog.statements){ 67 | gen_stmt(stmt); 68 | } 69 | 70 | m_output<<" mov rax, 60"< m_vars {}; 95 | }; -------------------------------------------------------------------------------- /src/parser.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "tokenization.hpp" 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | namespace node { 10 | struct NodeExprIntLit { 11 | Token int_lit; 12 | }; 13 | struct NodeExprIdent { 14 | Token ident; 15 | }; 16 | struct NodeExpr { 17 | variant var; 18 | }; 19 | struct NodeStmtExit { 20 | NodeExpr expr; 21 | }; 22 | struct NodeStmtLet { 23 | Token ident; 24 | NodeExpr expr; 25 | }; 26 | struct NodeStmt { 27 | variant var; 28 | }; 29 | struct NodeProgram { 30 | vector statements; 31 | }; 32 | } 33 | 34 | 35 | using namespace std; 36 | using namespace node; 37 | 38 | class Parser{ 39 | public: 40 | inline explicit Parser(vector tokens):m_tokens(move(tokens)){} 41 | 42 | optional parse_prog(){ 43 | NodeProgram prog; 44 | 45 | while(peek().has_value()){ 46 | if(auto statement=parse_stmt()){ 47 | prog.statements.push_back(statement.value()); 48 | } 49 | else{ 50 | cerr<<"Invalid statement"< parse_expr(){ 58 | if(peek().has_value() && peek().value().type==TokenType::int_lit){ 59 | return NodeExpr{.var=NodeExprIntLit{.int_lit=consume()}}; 60 | } 61 | else if(peek().has_value() && peek().value().type==TokenType::identifier){ 62 | return NodeExpr{.var=NodeExprIdent{.ident=consume()}}; 63 | } 64 | return {}; 65 | } 66 | 67 | optional parse_stmt(){ 68 | if(peek().has_value() && peek().value().type==TokenType::_exit){ 69 | consume(); 70 | if(peek().has_value() && peek().value().type==TokenType::open_paren){ 71 | consume(); 72 | if(auto expr = parse_expr()){ 73 | if(peek().has_value() && peek().value().type==TokenType::closed_paren){ 74 | consume(); 75 | if(peek().has_value() && peek().value().type==TokenType::semi){ 76 | consume(); 77 | return NodeStmt{.var=NodeStmtExit{.expr=expr.value()}}; 78 | } 79 | else{ 80 | cerr<<"Expected ; after expression"< m_tokens; 150 | size_t m_pos=0; 151 | 152 | [[nodiscard]] inline optional peek(int offset=0) const{ 153 | if(m_pos+offset>=m_tokens.size()) 154 | return {}; 155 | return m_tokens.at(m_pos+offset); 156 | } 157 | 158 | inline Token consume(){ 159 | return m_tokens.at(m_pos++); 160 | } 161 | }; --------------------------------------------------------------------------------