diff --git a/.gitignore b/.gitignore index 76f7285..b3a00de 100644 --- a/.gitignore +++ b/.gitignore @@ -50,7 +50,9 @@ cmake-windows-* *.out *.app nasal +nasal-format nasal.exe +nasal-format.exe # Visual Studio specific *.sln diff --git a/CMakeLists.txt b/CMakeLists.txt index 1cb6a3f..a04118e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,6 +41,7 @@ set(NASAL_OBJECT_SOURCE_FILE ${CMAKE_SOURCE_DIR}/src/util/fs.cpp ${CMAKE_SOURCE_DIR}/src/util/util.cpp ${CMAKE_SOURCE_DIR}/src/ast_dumper.cpp + ${CMAKE_SOURCE_DIR}/src/ast_format.cpp ${CMAKE_SOURCE_DIR}/src/ast_visitor.cpp ${CMAKE_SOURCE_DIR}/src/nasal_ast.cpp ${CMAKE_SOURCE_DIR}/src/nasal_codegen.cpp @@ -62,12 +63,18 @@ target_include_directories(nasal-object PRIVATE ${CMAKE_SOURCE_DIR}/src) add_executable(nasal ${CMAKE_SOURCE_DIR}/src/main.cpp) target_link_libraries(nasal nasal-object) +# build nasal-format +add_executable(nasal-format ${CMAKE_SOURCE_DIR}/src/format.cpp) +target_link_libraries(nasal-format nasal-object) + # link ldl and lpthread if(NOT CMAKE_HOST_SYSTEM_NAME MATCHES "Windows") target_link_libraries(nasal dl) target_link_libraries(nasal pthread) + target_link_libraries(nasal-format pthread) endif() target_include_directories(nasal PRIVATE ${CMAKE_SOURCE_DIR}/src) +target_include_directories(nasal-format PRIVATE ${CMAKE_SOURCE_DIR}/src) # copy nasal from build dir to the outside dir if(NOT CMAKE_HOST_SYSTEM_NAME MATCHES "Windows") @@ -77,6 +84,12 @@ if(NOT CMAKE_HOST_SYSTEM_NAME MATCHES "Windows") ${CMAKE_SOURCE_DIR}/build/nasal ${CMAKE_SOURCE_DIR}/nasal ) + add_custom_command( + TARGET nasal-format POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_SOURCE_DIR}/build/nasal-format + ${CMAKE_SOURCE_DIR}/nasal-format + ) endif() # build module diff --git a/src/ast_dumper.h b/src/ast_dumper.h index 1586d0b..e8c5565 100644 --- a/src/ast_dumper.h +++ b/src/ast_dumper.h @@ -38,7 +38,7 @@ private: if (indent.size() && indent.back()=="│ ") { indent.back() = "├──"; } - for(const auto& i : indent) { + for (const auto& i : indent) { std::cout << i; } } diff --git a/src/ast_format.cpp b/src/ast_format.cpp new file mode 100644 index 0000000..f50b822 --- /dev/null +++ b/src/ast_format.cpp @@ -0,0 +1,412 @@ +#include "ast_format.h" +#include "util/util.h" + +#include + +namespace nasal { + +bool ast_format::visit_use_stmt(use_stmt* node) { + dump_formating_node_info(node, "use statement"); + out << "use "; + for(auto i : node->get_path()) { + i->accept(this); + if (i != node->get_path().back()) { + out << "."; + } + } + return true; +} + +bool ast_format::visit_null_expr(null_expr* node) { + dump_formating_node_info(node, "null expression"); + out << "null"; + return true; +} + +bool ast_format::visit_nil_expr(nil_expr* node) { + dump_formating_node_info(node, "nil expression"); + out << "nil"; + return true; +} + +bool ast_format::visit_number_literal(number_literal* node) { + dump_formating_node_info(node, "number expression"); + out << node->get_number(); + return true; +} + +bool ast_format::visit_string_literal(string_literal* node) { + dump_formating_node_info(node, "string expression"); + out << "\"" << util::rawstr(node->get_content()) << "\""; + return true; +} + +bool ast_format::visit_identifier(identifier* node) { + dump_formating_node_info(node, "identifier"); + out << node->get_name(); + return true; +} + +bool ast_format::visit_bool_literal(bool_literal* node) { + dump_formating_node_info(node, "bool expression"); + out << (node->get_flag()? "true" : "false"); + return true; +} + +bool ast_format::visit_vector_expr(vector_expr* node) { + dump_formating_node_info(node, "vector expression"); + out << "["; + for(auto i : node->get_elements()) { + i->accept(this); + if (i != node->get_elements().back()) { + out << ", "; + } + } + out << "]"; + return true; +} + +bool ast_format::visit_hash_expr(hash_expr* node) { + dump_formating_node_info(node, "hash expression"); + out << "{"; + for(auto i : node->get_members()) { + i->accept(this); + if (i != node->get_members().back()) { + out << ", "; + } + } + out << "}"; + return true; +} + +bool ast_format::visit_hash_pair(hash_pair* node) { + dump_formating_node_info(node, "hash pair"); + out << node->get_name(); + if (node->get_value()) { + out << " : "; + node->get_value()->accept(this); + } + return true; +} + +bool ast_format::visit_function(function* node) { + dump_formating_node_info(node, "function"); + out << "func("; + for(auto i : node->get_parameter_list()) { + i->accept(this); + if (i != node->get_parameter_list().back()) { + out << ", "; + } + } + out << ") "; + node->get_code_block()->accept(this); + return true; +} + +bool ast_format::visit_code_block(code_block* node) { + dump_formating_node_info(node, "code block"); + out << "{\n"; + push_indent(); + for(auto i : node->get_expressions()) { + dump_indent(); + i->accept(this); + if (need_dump_semi(i)) { + out << ";\n"; + } else { + out << "\n"; + } + } + pop_indent(); + dump_indent(); + out << "}"; + return true; +} + +bool ast_format::visit_parameter(parameter* node) { + dump_formating_node_info(node, "parameter"); + out << node->get_parameter_name(); + switch (node->get_parameter_type()) { + case parameter::kind::normal_parameter: break; + case parameter::kind::dynamic_parameter: out << "..."; break; + case parameter::kind::default_parameter: out << " = "; break; + } + if (node->get_default_value()) { + node->get_default_value()->accept(this); + } + return true; +} + +bool ast_format::visit_ternary_operator(ternary_operator* node) { + dump_formating_node_info(node, "ternary operator"); + node->get_condition()->accept(this); + out << " ? "; + node->get_left()->accept(this); + out << " : "; + node->get_right()->accept(this); + return true; +} + +bool ast_format::visit_binary_operator(binary_operator* node) { + dump_formating_node_info(node, "binary operator"); + out << "("; + node->get_left()->accept(this); + switch(node->get_operator_type()) { + case binary_operator::kind::add: out << " + "; break; + case binary_operator::kind::sub: out << " - "; break; + case binary_operator::kind::mult: out << " * "; break; + case binary_operator::kind::div: out << " / "; break; + case binary_operator::kind::concat: out << " ~ "; break; + case binary_operator::kind::bitwise_and: out << " & "; break; + case binary_operator::kind::bitwise_or: out << " | "; break; + case binary_operator::kind::bitwise_xor: out << " ^ "; break; + case binary_operator::kind::cmpeq: out << " == "; break; + case binary_operator::kind::cmpneq: out << " != "; break; + case binary_operator::kind::grt: out << " > "; break; + case binary_operator::kind::geq: out << " >= "; break; + case binary_operator::kind::less: out << " < "; break; + case binary_operator::kind::leq: out << " <= "; break; + case binary_operator::kind::condition_and: out << " and "; break; + case binary_operator::kind::condition_or: out << " or "; break; + case binary_operator::kind::null_chain: out << " ?? "; break; + } + node->get_right()->accept(this); + out << ")"; + return true; +} + +bool ast_format::visit_unary_operator(unary_operator* node) { + dump_formating_node_info(node, "unary operator"); + switch(node->get_operator_type()) { + case unary_operator::kind::negative: out << "-"; break; + case unary_operator::kind::logical_not: out << "!"; break; + case unary_operator::kind::bitwise_not: out << "~"; break; + } + node->get_value()->accept(this); + return true; +} + +bool ast_format::visit_call_expr(call_expr* node) { + dump_formating_node_info(node, "call expression"); + node->get_first()->accept(this); + for(auto i : node->get_calls()) { + i->accept(this); + } + return true; +} + +bool ast_format::visit_call_hash(call_hash* node) { + dump_formating_node_info(node, "call hash"); + out << "." << node->get_field(); + return true; +} + +bool ast_format::visit_null_access(null_access* node) { + dump_formating_node_info(node, "null access operator(?.)"); + out << "?." << node->get_field(); + return true; +} + +bool ast_format::visit_call_vector(call_vector* node) { + dump_formating_node_info(node, "call vector"); + out << "["; + for(auto i : node->get_slices()) { + i->accept(this); + if (i != node->get_slices().back()) { + out << ", "; + } + } + out << "]"; + return true; +} + +bool ast_format::visit_call_function(call_function* node) { + dump_formating_node_info(node, "call function"); + out << "("; + for(auto i : node->get_argument()) { + i->accept(this); + if (i != node->get_argument().back()) { + out << ", "; + } + } + out << ")"; + return true; +} + +bool ast_format::visit_slice_vector(slice_vector* node) { + dump_formating_node_info(node, "slice vector"); + node->get_begin()->accept(this); + if (node->get_end()) { + out << " : "; + node->get_end()->accept(this); + } + return true; +} + +bool ast_format::visit_definition_expr(definition_expr* node) { + dump_formating_node_info(node, "definition"); + out << "var "; + if (node->get_variable_name()) { + node->get_variable_name()->accept(this); + } else { + node->get_variables()->accept(this); + } + out << " = "; + if (node->get_tuple()) { + node->get_tuple()->accept(this); + } else { + node->get_value()->accept(this); + } + return true; +} + +bool ast_format::visit_assignment_expr(assignment_expr* node) { + dump_formating_node_info(node, "assignment"); + node->get_left()->accept(this); + switch(node->get_assignment_type()) { + case assignment_expr::kind::add_equal: out << " += "; break; + case assignment_expr::kind::sub_equal: out << " -= "; break; + case assignment_expr::kind::mult_equal: out << " *= "; break; + case assignment_expr::kind::div_equal: out << " /= "; break; + case assignment_expr::kind::concat_equal: out << " ~= "; break; + case assignment_expr::kind::equal: out << " = "; break; + case assignment_expr::kind::bitwise_and_equal: out << " &= "; break; + case assignment_expr::kind::bitwise_or_equal: out << " |= "; break; + case assignment_expr::kind::bitwise_xor_equal: out << " ^= "; break; + } + node->get_right()->accept(this); + return true; +} + +bool ast_format::visit_multi_identifier(multi_identifier* node) { + dump_formating_node_info(node, "multi identifier"); + out << "("; + for(auto i : node->get_variables()) { + i->accept(this); + if (i != node->get_variables().back()) { + out << ", "; + } + } + out << ")"; + return true; +} + +bool ast_format::visit_tuple_expr(tuple_expr* node) { + dump_formating_node_info(node, "tuple expression"); + out << "("; + for(auto i : node->get_elements()) { + i->accept(this); + if (i != node->get_elements().back()) { + out << ", "; + } + } + out << ")"; + return true; +} + +bool ast_format::visit_multi_assign(multi_assign* node) { + dump_formating_node_info(node, "multi assign"); + node->get_tuple()->accept(this); + out << " = "; + node->get_value()->accept(this); + return true; +} + +bool ast_format::visit_while_expr(while_expr* node) { + dump_formating_node_info(node, "while statement"); + out << "while ("; + node->get_condition()->accept(this); + out << ") "; + node->get_code_block()->accept(this); + return true; +} + +bool ast_format::visit_for_expr(for_expr* node) { + dump_formating_node_info(node, "for statement"); + out << "for ("; + node->get_initial()->accept(this); + out << "; "; + node->get_condition()->accept(this); + out << "; "; + node->get_step()->accept(this); + out << ") "; + node->get_code_block()->accept(this); + return true; +} + +bool ast_format::visit_iter_expr(iter_expr* node) { + dump_formating_node_info(node, "iteration expression"); + if (node->is_definition()) { + out << "var "; + } + if (node->get_name()) { + node->get_name()->accept(this); + } else { + node->get_call()->accept(this); + } + return true; +} + +bool ast_format::visit_forei_expr(forei_expr* node) { + dump_formating_node_info(node, "forindex/foreach statement"); + if (node->get_loop_type()==forei_expr::kind::foreach) { + out << "foreach "; + } else { + out << "forindex "; + } + out << "("; + node->get_iterator()->accept(this); + out << "; "; + node->get_value()->accept(this); + out << ") "; + node->get_code_block()->accept(this); + return true; +} + +bool ast_format::visit_condition_expr(condition_expr* node) { + dump_formating_node_info(node, "condition statement"); + out << "if "; + node->get_if_statement()->accept(this); + for (auto i : node->get_elsif_stataments()) { + out << " elsif "; + i->accept(this); + } + if (node->get_else_statement()) { + out << " else "; + node->get_else_statement()->accept(this); + } + return true; +} + +bool ast_format::visit_if_expr(if_expr* node) { + dump_formating_node_info(node, "if statement"); + if (node->get_condition()) { + out << "("; + node->get_condition()->accept(this); + out << ") "; + } + node->get_code_block()->accept(this); + return true; +} + +bool ast_format::visit_continue_expr(continue_expr* node) { + dump_formating_node_info(node, "continue statement"); + out << "continue"; + return true; +} + +bool ast_format::visit_break_expr(break_expr* node) { + dump_formating_node_info(node, "break statement"); + out << "break"; + return true; +} + +bool ast_format::visit_return_expr(return_expr* node) { + dump_formating_node_info(node, "return statement"); + out << "return "; + if (node->get_value()) { + node->get_value()->accept(this); + } + return true; +} + +} diff --git a/src/ast_format.h b/src/ast_format.h new file mode 100644 index 0000000..8f3b23c --- /dev/null +++ b/src/ast_format.h @@ -0,0 +1,144 @@ +#pragma once + +#include "ast_visitor.h" + +#include +#include +#include +#include +#include +#include + +namespace nasal { + +class ast_format: public ast_visitor { +private: + std::ofstream out; + std::vector indent; + +private: + void push_indent() { + indent.push_back(" "); + } + void pop_indent() { + if (indent.size()) { + indent.pop_back(); + } + } + void dump_indent() { + for (const auto& i : indent) { + out << i; + } + } + void dump_formating_node_info(expr* n, const char* name) { + std::cout << " formating " << name << " @ 0x"; + std::cout << std::hex << n << std::dec << "\n"; + } + bool need_dump_semi(expr* n) { + switch (n->get_type()) { + case expr_type::ast_use: + case expr_type::ast_null: + case expr_type::ast_nil: + case expr_type::ast_num: + case expr_type::ast_str: + case expr_type::ast_bool: + case expr_type::ast_vec: + case expr_type::ast_hash: + case expr_type::ast_call: return true; + case expr_type::ast_def: { + auto dn = reinterpret_cast(n); + if (dn->get_value() && + dn->get_value()->get_type() == expr_type::ast_func) { + return false; + } + return true; + } + case expr_type::ast_assign: { + auto dn = reinterpret_cast(n); + if (dn->get_right() && + dn->get_right()->get_type() == expr_type::ast_func) { + return false; + } + return true; + } + case expr_type::ast_ret: { + auto dn = reinterpret_cast(n); + if (dn->get_value() && + dn->get_value()->get_type() == expr_type::ast_func) { + return false; + } + return true; + } + default: break; + } + + return false; + } + +public: + bool visit_use_stmt(use_stmt*) override; + bool visit_null_expr(null_expr*) override; + bool visit_nil_expr(nil_expr*) override; + bool visit_number_literal(number_literal*) override; + bool visit_string_literal(string_literal*) override; + bool visit_identifier(identifier*) override; + bool visit_bool_literal(bool_literal*) override; + bool visit_vector_expr(vector_expr*) override; + bool visit_hash_expr(hash_expr*) override; + bool visit_hash_pair(hash_pair*) override; + bool visit_function(function*) override; + bool visit_code_block(code_block*) override; + bool visit_parameter(parameter*) override; + bool visit_ternary_operator(ternary_operator*) override; + bool visit_binary_operator(binary_operator*) override; + bool visit_unary_operator(unary_operator*) override; + bool visit_call_expr(call_expr*) override; + bool visit_call_hash(call_hash*) override; + bool visit_null_access(null_access*) override; + bool visit_call_vector(call_vector*) override; + bool visit_call_function(call_function*) override; + bool visit_slice_vector(slice_vector*) override; + bool visit_definition_expr(definition_expr*) override; + bool visit_assignment_expr(assignment_expr*) override; + bool visit_multi_identifier(multi_identifier*) override; + bool visit_tuple_expr(tuple_expr*) override; + bool visit_multi_assign(multi_assign*) override; + bool visit_while_expr(while_expr*) override; + bool visit_for_expr(for_expr*) override; + bool visit_iter_expr(iter_expr*) override; + bool visit_forei_expr(forei_expr*) override; + bool visit_condition_expr(condition_expr*) override; + bool visit_if_expr(if_expr*) override; + bool visit_continue_expr(continue_expr*) override; + bool visit_break_expr(break_expr*) override; + bool visit_return_expr(return_expr*) override; + +public: + ast_format(const std::string output_file): out(output_file) { + if (out.fail()) { + throw std::runtime_error("can't open file: " + output_file); + } + } + + void do_format(code_block* root) { + std::cout << "nasal-format is not stable right now, "; + std::cout << "take care of source code!\n"; + dump_formating_node_info(root, "program root"); + bool is_use_statement = true; + for (auto i : root->get_expressions()) { + if (is_use_statement && i->get_type() != expr_type::ast_use) { + is_use_statement = false; + out << "\n"; + } + + i->accept(this); + if (need_dump_semi(i)) { + out << ";\n"; + } else { + out << "\n"; + } + } + } +}; + +} diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index 8605fa1..9961467 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -1,6 +1,12 @@ +#include "nasal.h" #include "cli/cli.h" +#include "util/util.h" +#include "nasal_parse.h" #include +#include +#include +#include namespace nasal::cli { @@ -59,4 +65,75 @@ std::ostream& help(std::ostream& out) { return out; } +std::ostream& nasal_format_help(std::ostream& out) { + out + << "\n" + << " ,--#-,\n" + << "<3 / \\____\\ <3\n" + << " |_|__A_|\n" + << "\nnasal-format