diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index c7fc759..922f9ef 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -17,10 +17,8 @@ jobs: git push -f origin next_macOS - name: Build run: | - make -j4 - cd module - make all -j4 - cd .. + mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Release + make -j6 - name: Test run: make test - name: Package @@ -33,7 +31,7 @@ jobs: prerelease: true draft: false files: | - nasal-Darwin.tar + nasal-macOS-aarch64.tar linux-x86_64-build: runs-on: ubuntu-latest @@ -46,10 +44,8 @@ jobs: git push -f origin next_linux_x86_64 - name: Build run: | - make -j4 - cd module - make all -j4 - cd .. + mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Release + make -j6 - name: Test run: make test - name: Package @@ -62,4 +58,4 @@ jobs: prerelease: true draft: false files: | - nasal-Linux.tar + nasal-linux-x86_64.tar diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 329941f..3ffb188 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,10 +14,8 @@ jobs: - uses: actions/checkout@v4 - name: Build run: | - make -j4 - cd module - make all -j4 - cd .. + mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Release + make -j6 - name: Test run: make test @@ -27,9 +25,7 @@ jobs: - uses: actions/checkout@v4 - name: Build run: | - make -j4 - cd module - make all -j4 - cd .. + mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Release + make -j6 - name: Test run: make test diff --git a/.gitignore b/.gitignore index 7ccf091..b3a00de 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,8 @@ build/ out/ dist/ -cmake-build-*/ +cmake-build-* +cmake-windows-* # IDE and editor files .vscode/ @@ -49,7 +50,9 @@ cmake-build-*/ *.out *.app nasal +nasal-format nasal.exe +nasal-format.exe # Visual Studio specific *.sln diff --git a/CMakeLists.txt b/CMakeLists.txt index 69c03f8..a04118e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,9 +9,13 @@ set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED True) set(CMAKE_CXX_FLAGS_RELEASE_INIT "-Wshadow -Wall") -add_compile_options(-fPIC) +# MSVC does not need -fPIC +if (NOT MSVC) + add_compile_options(-fPIC) +endif() + # MSVC needs this command option to really enable utf-8 output -if(MSVC) +if (MSVC) add_compile_options(/utf-8) endif() @@ -19,9 +23,6 @@ if (APPLE) add_compile_options(-mmacosx-version-min=10.15) endif() -# generate release executables -set(CMAKE_BUILD_TYPE "Release") - # build nasal used object set(NASAL_OBJECT_SOURCE_FILE ${CMAKE_SOURCE_DIR}/src/cli/cli.cpp @@ -40,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 @@ -61,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") @@ -76,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 @@ -103,16 +117,21 @@ target_link_libraries(mat module-used-object) add_library(nasock SHARED ${CMAKE_SOURCE_DIR}/module/nasocket.cpp) target_include_directories(nasock PRIVATE ${CMAKE_SOURCE_DIR}/src) +if (WIN32) + target_link_libraries(nasock ws2_32) +endif() target_link_libraries(nasock module-used-object) -# Add web library -add_library(nasal-web SHARED - src/nasal_web.cpp - ${NASAL_OBJECT_SOURCE_FILE} -) -target_include_directories(nasal-web PRIVATE ${CMAKE_SOURCE_DIR}/src) -set_target_properties(nasal-web PROPERTIES - C_VISIBILITY_PRESET hidden - CXX_VISIBILITY_PRESET hidden - VISIBILITY_INLINES_HIDDEN ON -) +# Add web library, not for MSVC now +if (NOT MSVC) + add_library(nasal-web SHARED + src/nasal_web.cpp + ${NASAL_OBJECT_SOURCE_FILE} + ) + target_include_directories(nasal-web PRIVATE ${CMAKE_SOURCE_DIR}/src) + set_target_properties(nasal-web PROPERTIES + C_VISIBILITY_PRESET hidden + CXX_VISIBILITY_PRESET hidden + VISIBILITY_INLINES_HIDDEN ON + ) +endif() \ No newline at end of file diff --git a/README.md b/README.md index 119dbf4..30e0694 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,7 @@ Make sure thread model is `posix thread model`, otherwise no thread library exis ### __Windows (Visual Studio)__ -There is a [__CMakelists.txt__](./CMakeLists.txt) to create project. +There is a [__CMakelists.txt__](./CMakeLists.txt) to create project. Recently we find how to build it with command line tools: [__Build Nasal-Interpreter on Windows__](./doc/windows-build.md). ### __Linux / macOS / Unix__ diff --git a/doc/README_zh.md b/doc/README_zh.md index b750837..ccefecb 100644 --- a/doc/README_zh.md +++ b/doc/README_zh.md @@ -81,6 +81,7 @@ Windows 平台的预览版解释器现在还没配置相关流水线, ### __Windows 平台 (Vistual Studio)__ 项目提供了 [__CMakeLists.txt__](../CMakeLists.txt) 用于在`Visual Studio`中创建项目。 +最近我们总结了命令行编译的方法 [__如何在 Windows 平台编译 Nasal-Interpreter__](./windows-build.md)。 ### __Linux / macOS / Unix 平台__ diff --git a/doc/windows-build.md b/doc/windows-build.md new file mode 100644 index 0000000..53fdbb7 --- /dev/null +++ b/doc/windows-build.md @@ -0,0 +1,27 @@ +# Build Nasal-Interpreter on Windows + +## MSVC / Visual Studio + +Need CMake and Visual Studio 2022. Remember to add MSBuild.exe to Path. + +Valid on powershell: + +```sh +mkdir cmake-windows-msvc +cd cmake-windows-msvc +cmake .. -DCMAKE_BUILD_TYPE=Release -G "Visual Studio 17 2022" +MSBuild.exe nasal.sln /p:Configuration=Release /p:Platform=x64 +``` + +## MingW-W64 + +Need CMake and MingW-W64. Remember to add MingW-W64 bin to Path. + +Valid on powershell: + +```sh +mkdir cmake-windows-mingw +cd cmake-windows-mingw +cmake .. -DCMAKE_BUILD_TYPE=Release -G "MinGW Makefiles" -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ +mingw32-make.exe -j6 +``` diff --git a/makefile b/makefile index 4482512..fb2f959 100644 --- a/makefile +++ b/makefile @@ -333,10 +333,10 @@ test: @ ./nasal -t -d test/regex_test.nas @ ./nasal -t -d test/replace_test.nas @ ./nasal -e test/scalar.nas hello world - @ ./nasal test/subprocess_test.nas - @ ./nasal -e test/trait.nas + @ ./nasal -t test/subprocess_test.nas + @ ./nasal -t test/trait.nas @ ./nasal -t -d test/turingmachine.nas - @ ./nasal -d test/wavecollapse.nas - @ ./nasal -d test/wavecity.nas - @ ./nasal test/word_collector.nas test/md5compare.nas + @ ./nasal -t -d test/wavecollapse.nas + @ ./nasal -t -d test/wavecity.nas + @ ./nasal -t test/word_collector.nas test/md5compare.nas @ ./nasal -t -d test/ycombinator.nas diff --git a/module/makefile b/module/makefile index 981ab8c..6445e8a 100644 --- a/module/makefile +++ b/module/makefile @@ -12,9 +12,9 @@ ifndef OS OS = $(shell uname) endif ifeq ($(OS), Darwin) - CXXFLAGS = -std=$(STD) -c -O3 -fPIC -mmacosx-version-min=10.15 + CXXFLAGS = -std=$(STD) -c -O3 -fPIC -mmacosx-version-min=10.15 -I ../src else - CXXFLAGS = -std=$(STD) -c -O3 -fPIC + CXXFLAGS = -std=$(STD) -c -O3 -fPIC -I ../src endif all: $(dynamic_libs_so) @@ -29,7 +29,7 @@ libfib.so: fib.cpp $(used_header) $(used_object) @ rm fib.o libfib.dll: fib.cpp $(used_header) $(used_object) @ echo [Compiling] libfib.dll - @ $(CXX) -std=$(STD) -c -O3 fib.cpp -fPIC -o fib.o + @ $(CXX) -std=$(STD) -c -O3 fib.cpp -fPIC -o fib.o -I ../src @ $(CXX) -shared -o libfib.dll fib.o $(used_object) -static @ del fib.o @@ -40,7 +40,7 @@ libkey.so: keyboard.cpp $(used_header) $(used_object) @ rm keyboard.o libkey.dll: keyboard.cpp $(used_header) $(used_object) @ echo [Compiling] libkey.dll - @ $(CXX) -std=$(STD) -c -O3 keyboard.cpp -fPIC -o keyboard.o -static + @ $(CXX) -std=$(STD) -c -O3 keyboard.cpp -fPIC -o keyboard.o -static -I ../src @ $(CXX) -shared -o libkey.dll keyboard.o $(used_object) -static @ del keyboard.o @@ -51,7 +51,7 @@ libnasock.so: nasocket.cpp $(used_header) $(used_object) @ rm nasocket.o libnasock.dll: nasocket.cpp $(used_header) $(used_object) @ echo [Compiling] libnasock.dll - @ $(CXX) -std=$(STD) -c -O3 nasocket.cpp -fPIC -o nasocket.o -lwsock32 -static + @ $(CXX) -std=$(STD) -c -O3 nasocket.cpp -fPIC -I ../src -o nasocket.o -lwsock32 -static @ $(CXX) -shared -o libnasock.dll nasocket.o $(used_object) -lwsock32 -static @ del nasocket.o @@ -62,7 +62,7 @@ libmat.so: matrix.cpp $(used_header) $(used_object) @ rm matrix.o libmat.dll: matrix.cpp $(used_header) $(used_object) @ echo [Compiling] libmat.dll - @ $(CXX) -std=$(STD) -c -O3 matrix.cpp -fPIC -o matrix.o -static + @ $(CXX) -std=$(STD) -c -O3 matrix.cpp -fPIC -I ../src -o matrix.o -static @ $(CXX) -shared -o libmat.dll matrix.o $(used_object) -static @ del matrix.o 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..a1f310e --- /dev/null +++ b/src/ast_format.cpp @@ -0,0 +1,427 @@ +#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"); + 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_raw_text(); + 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"); + bool contain_not_identifier = false; + for (auto c : node->get_name()) { + if (!(std::isdigit(c) || std::isalpha(c) || c == '_')) { + contain_not_identifier = true; + } + } + if (contain_not_identifier) { + out << "\"" << node->get_name() << "\""; + } else { + 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 << ") "; + if (node->get_code_block()->get_expressions().empty()) { + out << "{}"; + return true; + } + 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"); + out << "("; + node->get_condition()->accept(this); + out << " ? "; + node->get_left()->accept(this); + out << " : "; + node->get_right()->accept(this); + out << ")"; + 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..e350188 --- /dev/null +++ b/src/ast_format.h @@ -0,0 +1,148 @@ +#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: + case expr_type::ast_multi_assign: + case expr_type::ast_unary: + case expr_type::ast_binary: + case expr_type::ast_ternary: 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