├── .gitignore ├── .gitmodules ├── .travis.yml ├── CMakeLists.txt ├── Dockerfile ├── LICENSE ├── README.md ├── cmake ├── CodeCoverage.cmake ├── FindReadline.cmake ├── SettiUtils.cmake ├── googletest.cmake └── sanitize-helpers.cmake ├── docs ├── docs │ ├── index.md │ └── lang-basics.md └── mkdocs.yml ├── lib ├── argparse.sh └── textcolor.sh ├── samples ├── .shpprc └── language │ ├── 1-hello-world.sh │ ├── 10-type.sh │ ├── 11-shell.sh │ ├── 12-cmd.sh │ ├── 13-sub-shell.sh │ ├── 14-io.sh │ ├── 15-functions.sh │ ├── 2-variables.sh │ ├── 3-arithmetic.sh │ ├── 4-string.sh │ ├── 5-array.sh │ ├── 6-map.sh │ ├── 7-if-else.sh │ ├── 8-switch.sh │ └── 9-loop.sh ├── scripts └── lint.sh ├── shell ├── CMakeLists.txt ├── main.cc ├── runner.cc └── runner.h ├── src ├── CMakeLists.txt ├── ast │ ├── ast-class-inl.h │ ├── ast-factory-inl.h │ ├── ast-printer.h │ ├── ast.cc │ └── ast.h ├── cmd-entry.cc ├── cmd-entry.h ├── cmd-exec.cc ├── cmd-exec.h ├── env-shell.cc ├── env-shell.h ├── interpreter │ ├── assign-executor.cc │ ├── assign-executor.h │ ├── cmd-executor.cc │ ├── cmd-executor.h │ ├── executor.h │ ├── expr-executor.cc │ ├── expr-executor.h │ ├── interpreter.cc │ ├── interpreter.h │ ├── scope-executor.cc │ ├── scope-executor.h │ ├── stmt-executor.cc │ ├── stmt-executor.h │ ├── symbol-table.cc │ └── symbol-table.h ├── modules │ ├── env.cc │ ├── env.h │ ├── std-cmds.cc │ ├── std-cmds.h │ ├── std-funcs.cc │ ├── std-funcs.h │ ├── sys.cc │ └── sys.h ├── msg.h ├── objects │ ├── abstract-obj.h │ ├── array-object.cc │ ├── array-object.h │ ├── cmd-object.cc │ ├── cmd-object.h │ ├── decl-class-object.cc │ ├── decl-class-object.h │ ├── exceptions-object.cc │ ├── exceptions-object.h │ ├── file-object.cc │ ├── file-object.h │ ├── func-object.cc │ ├── func-object.h │ ├── glob-object.cc │ ├── glob-object.h │ ├── map-object.cc │ ├── map-object.h │ ├── obj-type.cc │ ├── obj-type.h │ ├── object-factory.cc │ ├── object-factory.h │ ├── path.cc │ ├── path.h │ ├── regex.cc │ ├── regex.h │ ├── simple-object.cc │ ├── simple-object.h │ ├── slice-object.cc │ ├── slice-object.h │ ├── str-object.cc │ ├── str-object.h │ ├── tuple-object.cc │ └── tuple-object.h ├── parser │ ├── extract_expr.cc │ ├── extract_expr.h │ ├── lexer.cc │ ├── lexer.h │ ├── parser-oop.cc │ ├── parser.cc │ ├── parser.h │ ├── parser_literal_string.cc │ ├── parser_literal_string.h │ ├── parser_result.h │ ├── token.def │ └── token.h ├── run_time_error.h └── utils │ ├── check.h │ ├── dir.h │ ├── glob.cc │ ├── glob.h │ └── scope-exit.h ├── test ├── CMakeLists.txt ├── interpreter │ ├── cmds │ │ ├── binop_cmd.sh │ │ ├── cmd_decl.sh │ │ ├── cmd_decl_args.sh │ │ ├── io_cmd.sh │ │ ├── redirect.sh │ │ ├── simple_cmd.sh │ │ ├── status.sh │ │ ├── string.sh │ │ └── subshell.sh │ ├── func │ │ ├── ellipsis.sh │ │ ├── err_func_num_param.sh │ │ ├── err_func_num_param2.sh │ │ ├── err_func_num_param3.sh │ │ ├── err_func_num_param4.sh │ │ ├── err_func_param.sh │ │ ├── err_func_variadic.sh │ │ ├── fib.sh │ │ ├── func_args.sh │ │ ├── func_default.sh │ │ ├── func_default_param.sh │ │ ├── func_empty.sh │ │ ├── func_lambda.sh │ │ ├── func_lambda2.sh │ │ ├── func_params.sh │ │ ├── func_scope.sh │ │ ├── func_variadic.sh │ │ └── lambda_inside_class.sh │ ├── import │ │ ├── import1.sh │ │ ├── import12.sh │ │ ├── import_module1.sh │ │ ├── import_module2.sh │ │ ├── import_module3.sh │ │ ├── main1.sh │ │ └── module.sh │ ├── lang_basic │ │ ├── argv.sh │ │ ├── array.sh │ │ ├── assign.sh │ │ ├── file.sh │ │ ├── filter.sh │ │ ├── glob.sh │ │ ├── global.sh │ │ ├── if_else.sh │ │ ├── if_else2.sh │ │ ├── if_else_empty.sh │ │ ├── if_else_expr.sh │ │ ├── import-test.sh │ │ ├── import.sh │ │ ├── import_test.sh │ │ ├── lambda.sh │ │ ├── let.sh │ │ ├── let2.sh │ │ ├── list_comp.sh │ │ ├── quicksort.sh │ │ ├── range_it.sh │ │ ├── string.sh │ │ ├── tuple.sh │ │ ├── tuple2.sh │ │ ├── varenv.sh │ │ ├── while.sh │ │ ├── while_break.sh │ │ ├── while_break2.sh │ │ └── while_continue.sh │ ├── objects │ │ └── path.sh │ ├── oop │ │ ├── abastract-method.sh │ │ ├── attr.sh │ │ ├── class.sh │ │ ├── decorator.sh │ │ ├── err_try_catch5.sh │ │ ├── exceptions.sh │ │ ├── facade.sh │ │ ├── final.sh │ │ ├── ifaces.sh │ │ ├── inheritance.sh │ │ ├── inner_class.sh │ │ ├── instanceof.sh │ │ ├── is.sh │ │ ├── methods.sh │ │ ├── op_overload.sh │ │ ├── singleton.sh │ │ ├── static-method.sh │ │ ├── try_catch.sh │ │ ├── try_catch2.sh │ │ ├── try_catch3.sh │ │ ├── try_catch4.sh │ │ └── var_decl.sh │ └── run.sh ├── lib │ ├── argparse_test.sh │ └── textcolor_test.sh └── unit │ ├── CMakeLists.txt │ ├── cmd.cc │ ├── extract_expr.cc │ ├── lexer_test.cc │ ├── parser_test.cc │ └── token_test.cc └── tools ├── CMakeLists.txt ├── interpreter ├── CMakeLists.txt └── interpreter.cc ├── print_ast ├── CMakeLists.txt └── print_ast.cc ├── print_parser_string ├── CMakeLists.txt └── print_parser_string.cc └── print_tokens ├── CMakeLists.txt └── print_tokens.cc /.gitignore: -------------------------------------------------------------------------------- 1 | CMakeCache.txt 2 | CMakeFiles 3 | CMakeScripts 4 | Makefile 5 | cmake_install.cmake 6 | install_manifest.txt 7 | CTestTestfile.cmake 8 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "glob-cpp"] 2 | path = glob-cpp 3 | url = https://github.com/alexst07/glob-cpp 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | notifications: 2 | email: false 3 | 4 | # Enable C++ support 5 | language: cpp 6 | 7 | # Compiler selection 8 | addons: 9 | apt: 10 | sources: 11 | - boost-latest 12 | - ubuntu-toolchain-r-test 13 | packages: 14 | - gdb 15 | - gcc-6 16 | - g++-6 17 | - cmake 18 | - build-essential 19 | - libreadline6 20 | - libreadline6-dev 21 | - libboost-all-dev 22 | 23 | before_install: 24 | - export CXX="g++-6" CC="gcc-6" 25 | 26 | # Build steps 27 | script: 28 | - mkdir build 29 | - cd build 30 | - cmake .. && make 31 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Alex Silva Torres 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | cmake_minimum_required(VERSION 2.8) 16 | project(shpp C CXX) 17 | 18 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/") 19 | 20 | include(cmake/SettiUtils.cmake) 21 | include(cmake/sanitize-helpers.cmake) 22 | include(CheckCXXCompilerFlag) 23 | 24 | CHECK_CXX_COMPILER_FLAG("-std=c++14" COMPILER_SUPPORTS_CXX14) 25 | 26 | if(COMPILER_SUPPORTS_CXX14) 27 | set(CMAKE_CXX_STANDARD 14) 28 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 29 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14") 30 | else() 31 | message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++14 support. Please use a different C++ compiler.") 32 | endif() 33 | 34 | set(CMAKE_CXX_FLAGS "-Wall -Wextra") 35 | set(CMAKE_CXX_FLAGS_DEBUG "-g") 36 | set(CMAKE_CXX_FLAGS_RELEASE "-O3") 37 | 38 | SET(CMAKE_SKIP_BUILD_RPATH FALSE) 39 | 40 | SET(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) 41 | 42 | SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib") 43 | 44 | SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) 45 | 46 | LIST(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/lib" isSystemDir) 47 | 48 | IF("${isSystemDir}" STREQUAL "-1") 49 | SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib") 50 | ENDIF("${isSystemDir}" STREQUAL "-1") 51 | 52 | SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) 53 | 54 | SHPP_OPTION(BUILD_UNIT_TESTS "Build unit tests" OFF) 55 | SHPP_OPTION(COV_TESTS "Coverage tests" OFF) 56 | SHPP_OPTION(STATIC_BUILD "Build with static libraries" OFF) 57 | 58 | if(COV_TESTS) 59 | # put on debug 60 | SET(CMAKE_CXX_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage") 61 | SET(CMAKE_C_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage") 62 | set(CMAKE_BUILD_TYPE "Debug") 63 | endif() 64 | 65 | if(STATIC_BUILD) 66 | SET(Boost_NO_SYSTEM_PATHS ON) 67 | SET(Boost_USE_STATIC_LIBS ON) 68 | SET(Boost_USE_MULTITHREADED ON) 69 | SET(Boost_USE_STATIC_RUNTIME ON) 70 | endif() 71 | 72 | # 73 | # Boost support 74 | # 75 | find_package(Boost COMPONENTS system filesystem locale REQUIRED) 76 | include_directories(${Boost_INCLUDE_DIR}) 77 | find_package(Threads REQUIRED) 78 | 79 | find_library(READLINE_LIBRARY NAMES readline REQUIRED) 80 | 81 | # if(STATIC_BUILD) 82 | # find_library(READLINE_LIBRARY NAMES libreadline.a readline REQUIRED) 83 | # else() 84 | # find_library(READLINE_LIBRARY NAMES readline REQUIRED) 85 | # endif() 86 | if(BUILD_UNIT_TESTS) 87 | enable_testing() 88 | include(googletest) 89 | include_directories(${googletest_INCLUDE_DIRS} 90 | ${googlemock_INCLUDE_DIRS}) 91 | endif() 92 | 93 | include_directories(${CMAKE_SOURCE_DIR}/src/) 94 | include_directories(${CMAKE_SOURCE_DIR}/glob-cpp/include) 95 | 96 | add_subdirectory(src) 97 | add_subdirectory(test) 98 | add_subdirectory(tools) 99 | add_subdirectory(shell) 100 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:16.04 2 | 3 | # Install. 4 | RUN \ 5 | sed -i 's/# \(.*multiverse$\)/\1/g' /etc/apt/sources.list && \ 6 | apt-get update && \ 7 | apt-get -y upgrade && \ 8 | apt-get install -y build-essential && \ 9 | apt-get install -y software-properties-common && \ 10 | apt-get install -y libboost-all-dev libreadline6 libreadline6-dev git cmake && \ 11 | git clone https://github.com/alexst07/shpp.git &&\ 12 | rm -rf /var/lib/apt/lists/* &&\ 13 | cd shpp && mkdir build && cd build &&\ 14 | cmake .. &&\ 15 | make install 16 | 17 | # Set environment variables. 18 | ENV HOME /root 19 | 20 | # Define working directory. 21 | WORKDIR /root 22 | 23 | # Define default command. 24 | CMD ["bash"] 25 | -------------------------------------------------------------------------------- /cmake/FindReadline.cmake: -------------------------------------------------------------------------------- 1 | # from http://websvn.kde.org/trunk/KDE/kdeedu/cmake/modules/FindReadline.cmake 2 | # http://websvn.kde.org/trunk/KDE/kdeedu/cmake/modules/COPYING-CMAKE-SCRIPTS 3 | # --> BSD licensed 4 | # 5 | # GNU Readline library finder 6 | if(READLINE_INCLUDE_DIR AND READLINE_LIBRARY) 7 | set(READLINE_FOUND TRUE) 8 | else(READLINE_INCLUDE_DIR AND READLINE_LIBRARY) 9 | FIND_PATH(READLINE_INCLUDE_DIR readline/readline.h 10 | /usr/include/readline 11 | ) 12 | 13 | # 2008-04-22 The next clause used to read like this: 14 | # 15 | # FIND_LIBRARY(READLINE_LIBRARY NAMES readline) 16 | # FIND_LIBRARY(NCURSES_LIBRARY NAMES ncurses ) 17 | # include(FindPackageHandleStandardArgs) 18 | # FIND_PACKAGE_HANDLE_STANDARD_ARGS(Readline DEFAULT_MSG NCURSES_LIBRARY READLINE_INCLUDE_DIR READLINE_LIBRARY ) 19 | # 20 | # I was advised to modify it such that it will find an ncurses library if 21 | # required, but not if one was explicitly given, that is, it allows the 22 | # default to be overridden. PH 23 | FIND_LIBRARY(READLINE_LIBRARY NAMES readline) 24 | include(FindPackageHandleStandardArgs) 25 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(Readline DEFAULT_MSG READLINE_INCLUDE_DIR READLINE_LIBRARY) 26 | 27 | MARK_AS_ADVANCED(READLINE_INCLUDE_DIR READLINE_LIBRARY) 28 | endif(READLINE_INCLUDE_DIR AND READLINE_LIBRARY) 29 | -------------------------------------------------------------------------------- /cmake/SettiUtils.cmake: -------------------------------------------------------------------------------- 1 | include(CMakeParseArguments) 2 | 3 | # Provides an option that the user can optionally select. 4 | # Can accept condition to control when option is available for user. 5 | # Usage: 6 | # option( "help string describing the option" [IF ]) 7 | macro(SHPP_OPTION variable description value) 8 | set(__value ${value}) 9 | set(__condition "") 10 | set(__varname "__value") 11 | foreach(arg ${ARGN}) 12 | if(arg STREQUAL "IF" OR arg STREQUAL "if") 13 | set(__varname "__condition") 14 | else() 15 | list(APPEND ${__varname} ${arg}) 16 | endif() 17 | endforeach() 18 | unset(__varname) 19 | if(__condition STREQUAL "") 20 | set(__condition 2 GREATER 1) 21 | endif() 22 | 23 | if(${__condition}) 24 | if(__value MATCHES ";") 25 | if(${__value}) 26 | option(${variable} "${description}" ON) 27 | else() 28 | option(${variable} "${description}" OFF) 29 | endif() 30 | elseif(DEFINED ${__value}) 31 | if(${__value}) 32 | option(${variable} "${description}" ON) 33 | else() 34 | option(${variable} "${description}" OFF) 35 | endif() 36 | else() 37 | option(${variable} "${description}" ${__value}) 38 | endif() 39 | else() 40 | unset(${variable} CACHE) 41 | endif() 42 | unset(__condition) 43 | unset(__value) 44 | endmacro() 45 | -------------------------------------------------------------------------------- /cmake/googletest.cmake: -------------------------------------------------------------------------------- 1 | # Copyright 2017 The TensorFlow Authors. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # ============================================================================== 15 | include (ExternalProject) 16 | 17 | set(googletest_INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR}/googletest/src/googletest/googletest/include) 18 | set(googlemock_INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR}/googletest/src/googletest/googlemock/include) 19 | set(googletest_VERSION 1.8.0) 20 | set(googletest_URL https://github.com/google/googletest/archive/release-${googletest_VERSION}.zip) 21 | set(googletest_BUILD ${CMAKE_CURRENT_BINARY_DIR}/googletest/) 22 | 23 | if(WIN32) 24 | if(${CMAKE_GENERATOR} MATCHES "Visual Studio.*") 25 | set(googletest_MAIN_STATIC_LIBRARIES 26 | ${CMAKE_CURRENT_BINARY_DIR}/googletest/src/googletest/googletest/$(Configuration)/gtest_main.lib) 27 | set(googletest_STATIC_LIBRARIES 28 | ${CMAKE_CURRENT_BINARY_DIR}/googletest/src/googletest/googletest/$(Configuration)/gtest.lib) 29 | else() 30 | set(googletest_MAIN_STATIC_LIBRARIES 31 | ${CMAKE_CURRENT_BINARY_DIR}/googletest/src/googletest/googletest/gtest_main.lib) 32 | set(googletest_STATIC_LIBRARIES 33 | ${CMAKE_CURRENT_BINARY_DIR}/googletest/src/googletest/googletest/gtest.lib) 34 | endif() 35 | else() 36 | set(googletest_MAIN_STATIC_LIBRARIES 37 | ${CMAKE_CURRENT_BINARY_DIR}/googletest/src/googletest/googlemock/gtest/libgtest_main.a) 38 | set(googletest_STATIC_LIBRARIES 39 | ${CMAKE_CURRENT_BINARY_DIR}/googletest/src/googletest/googlemock/gtest/libgtest.a 40 | ${CMAKE_CURRENT_BINARY_DIR}/googletest/src/googletest/googlemock/libgmock.a) 41 | endif() 42 | 43 | ExternalProject_Add(googletest 44 | PREFIX googletest 45 | URL ${googletest_URL} 46 | DOWNLOAD_DIR "${DOWNLOAD_LOCATION}" 47 | BUILD_IN_SOURCE 1 48 | BUILD_BYPRODUCTS ${googletest_STATIC_LIBRARIES} ${googletest_MAIN_STATIC_LIBRARIES} 49 | INSTALL_COMMAND "" 50 | CMAKE_ARGS 51 | -DCMAKE_C_COMPILER:STRING=${CMAKE_C_COMPILER} 52 | -DCMAKE_CXX_COMPILER:STRING=${CMAKE_CXX_COMPILER} 53 | -DCMAKE_C_FLAGS:STRING=${CMAKE_C_FLAGS} 54 | -DCMAKE_CXX_FLAGS:STRING=${CMAKE_CXX_FLAGS} 55 | CMAKE_CACHE_ARGS 56 | -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} 57 | -DBUILD_GMOCK:BOOL=ON 58 | -DBUILD_GTEST:BOOL=ON 59 | -Dgtest_force_shared_crt:BOOL=ON 60 | ) 61 | -------------------------------------------------------------------------------- /cmake/sanitize-helpers.cmake: -------------------------------------------------------------------------------- 1 | set(ASAN_LIB_NAMES 2 | asan 3 | libasan.so.0 4 | libasan.so.1 5 | libasan.so.2 6 | libasan.so.3 7 | ) 8 | set(UBSAN_LIB_NAMES 9 | ubsan 10 | libubsan.so.0 11 | ) 12 | set(TSAN_LIB_NAMES 13 | tsan 14 | libtsan.so.0 15 | ) 16 | 17 | option(SANITIZE_ADDRESS "Enable Address Sanitizer" OFF) 18 | option(SANITIZE_UNDEFINED "Enable Undefined Behavior Sanitizer" OFF) 19 | option(SANITIZE_THREAD "Enable Thread Sanitizer" OFF) 20 | 21 | if ( SANITIZE_ADDRESS ) 22 | if (SANITIZE_ADDRESS AND SANITIZE_THREAD) 23 | message(FATAL_ERROR "AddressSanitizer is not compatible with " 24 | "ThreadSanitizer or MemorySanitizer.") 25 | endif () 26 | 27 | find_library(__libasan NAMES ${ASAN_LIB_NAMES} PATHS /usr/lib /usr/lib/x86_64-linux-gnu) 28 | if (NOT __libasan) 29 | message(STATUS "Enabling Address Sanitizer - Failed") 30 | message(STATUS "Missing libasan.so") 31 | unset(SANITIZE_ADDRESS CACHE) 32 | else () 33 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address") 34 | execute_process (COMMAND bash -c "ls /usr/bin/llvm-symbolizer-*" OUTPUT_VARIABLE llvm_sym) 35 | set(ASAN_SYMBOLIZER_PATH "${llvm_sym}") 36 | set(ASAN_OPTIONS "symbolize=1") 37 | message(STATUS "Enabling Address Sanitizer - Success") 38 | message("") 39 | message("### For a better understanding of Asan output, execute:") 40 | message("") 41 | message("### export ASAN_SYMBOLIZER_PATH=${ASAN_SYMBOLIZER_PATH}") 42 | message("### export ASAN_OPTIONS=${ASAN_OPTIONS}") 43 | message("") 44 | endif () 45 | endif() 46 | 47 | if ( SANITIZE_UNDEFINED ) 48 | find_library(__libubsan NAMES ${UBSAN_LIB_NAMES} PATHS /usr/lib /usr/lib/x86_64-linux-gnu) 49 | if (NOT __libubsan) 50 | message(STATUS "Enabling Undefined Behavior Sanitizer - Failed") 51 | message(STATUS "Missing libubsan.so") 52 | unset(SANITIZE_UNDEFINED CACHE) 53 | else () 54 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined") 55 | message(STATUS "Enabling Undefined Behavior Sanitizer - Success") 56 | endif () 57 | endif() 58 | 59 | if ( SANITIZE_THREAD ) 60 | find_library(__libtsan NAMES ${TSAN_LIB_NAMES} PATHS /usr/lib /usr/lib/x86_64-linux-gnu) 61 | if (NOT __libtsan) 62 | message(STATUS "Enabling Thread Sanitizer - Failed") 63 | message(STATUS "Missing libtsan.so") 64 | unset(SANITIZE_THREAD CACHE) 65 | else () 66 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread") 67 | message(STATUS "Enabling Thread Sanitizer - Success") 68 | endif () 69 | endif() 70 | 71 | -------------------------------------------------------------------------------- /docs/docs/index.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | Shell++ is a programming language that aims bring features from modern languages, 4 | as facility to manipulate data structures, object oriented programming, 5 | functional programming and others, to shell script. 6 | 7 | # Building 8 | 9 | ## Requirements: 10 | * A compiler that supports C++ 14 (gcc or clang) 11 | * Boost 12 | * Readline 13 | * CMake 14 | * Linux 15 | 16 | ## Compiling 17 | In the root of the project: 18 | ``` 19 | $ mkdir build 20 | $ cd build 21 | $ cmake .. 22 | $ make 23 | ``` 24 | ## Installing 25 | ``` 26 | $ sudo make install 27 | ``` 28 | # Running 29 | ## Hello world 30 | ``` 31 | $ shell++ 32 | > echo hello world 33 | ``` 34 | ## Running a file 35 | ``` 36 | $ shell++ file.shell++ 37 | ``` 38 | -------------------------------------------------------------------------------- /docs/mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: Shell++ 2 | 3 | pages: 4 | - Home: index.md 5 | - Language: lang-basics.md 6 | -------------------------------------------------------------------------------- /lib/argparse.sh: -------------------------------------------------------------------------------- 1 | class InvalidArgumentException(Exception) { 2 | func __init__(msg) { 3 | Exception.__init__(this, msg) 4 | } 5 | } 6 | 7 | class Argument { 8 | func __init__(args, 9 | dest, 10 | nargs, 11 | type_arg, 12 | choices, 13 | required, 14 | default_value, 15 | help) { 16 | this.args = args 17 | this.dest = dest 18 | this.nargs = nargs 19 | this.type_arg = type_arg 20 | this.choices = choices 21 | this.required = required 22 | this.default_value = default_value 23 | this.help = help 24 | } 25 | 26 | func match(arg) { 27 | if this.args is array { 28 | for a in this.args { 29 | if arg.find(a) == 0 { 30 | return true 31 | } 32 | } 33 | } else { 34 | if this.args is string { 35 | if arg.find(this.args) == 0 { 36 | return true 37 | } 38 | } 39 | } 40 | 41 | return false 42 | } 43 | 44 | func args() { 45 | return this.args 46 | } 47 | 48 | func dest() { 49 | return this.dest 50 | } 51 | 52 | func nargs() { 53 | return this.nargs 54 | } 55 | 56 | func type_arg() { 57 | return this.type_arg 58 | } 59 | 60 | func choices() { 61 | return this.choices 62 | } 63 | 64 | func default_value() { 65 | return this.default_value 66 | } 67 | 68 | func help() { 69 | return this.help 70 | } 71 | } 72 | 73 | class ArgumentParser { 74 | func __init__() { 75 | this.arguments_list = [] 76 | this.vars = {} 77 | this.others = [] 78 | } 79 | 80 | func add_argument(args, 81 | dest, 82 | nargs = 1, 83 | type_arg = string, 84 | choices = [], 85 | required = false, 86 | default_value = null, 87 | help = "") { 88 | argument = Argument(args = args, 89 | dest = dest, 90 | nargs = nargs, 91 | type_arg = type_arg, 92 | choices = choices, 93 | required = required, 94 | default_value = default_value, 95 | help = help) 96 | 97 | this.arguments_list.append(argument) 98 | } 99 | 100 | func parser_str_arg(arg) { 101 | pos_eq = arg.find("=") 102 | 103 | if (!pos_eq) { 104 | throw InvalidArgumentException("'=' not found on argument: ", arg) 105 | } 106 | 107 | arg_value = arg.split("=") 108 | return arg_value[1] 109 | } 110 | 111 | func process_arg(line_arg) { 112 | for argument in this.arguments_list { 113 | if argument.match(line_args) { 114 | switch type(argument.type_arg()) { 115 | case bool { 116 | this.vars[argument.dest()] = true 117 | } 118 | 119 | case string { 120 | arg_value = parser_str_arg(line_arg) 121 | if not arg_value in argument.choices() { 122 | throw InvalidArgumentException("'", arg_value, "'", "is not ", 123 | "on choices of argument: ", line_arg) 124 | } 125 | 126 | this.vars[argument.dest()] = arg_value 127 | } 128 | 129 | default { 130 | throw InvalidArgumentException("not valid argument type") 131 | } 132 | } 133 | } else { 134 | this.others.append(line_args) 135 | } 136 | } 137 | } 138 | 139 | func fill_vars() { 140 | for argument in arguments_list { 141 | this.vars += {argument.dest(): null} 142 | } 143 | } 144 | 145 | func process(line_args) { 146 | for line_arg in line_args { 147 | process_arg(line_arg) 148 | } 149 | 150 | for argument in this.arguments_list { 151 | # check if some required argument was not filled 152 | if argument.required() { 153 | throw InvalidArgumentException(argument.dest(), " is required") 154 | } else { 155 | this.vars[argument.dest()] = argument.default_value() 156 | } 157 | } 158 | } 159 | 160 | func vars() { 161 | return this.vars 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /lib/textcolor.sh: -------------------------------------------------------------------------------- 1 | __all__ = [ 2 | "error", 3 | "warning", 4 | "info", 5 | "success" 6 | ] 7 | 8 | colors_map = { 9 | "black": "\e[30m", 10 | "red": "\e[31m", 11 | "green": "\e[32m", 12 | "yellow": "\e[33m", 13 | "blue": "\e[34m", 14 | "magenta": "\e[35m", 15 | "cyan": "\e[36m", 16 | "lgray": "\e[37m", 17 | "dgray": "\e[90m", 18 | "lred": "\e[91m", 19 | "lgreen": "\e[92m", 20 | "lyellow": "\e[93m", 21 | "lblue": "\e[94m", 22 | "lmagenta": "\e[95m", 23 | "lcyan": "\e[96m", 24 | "white": "\e[97m" 25 | } 26 | 27 | back_colors_map = { 28 | "back_black": "\e[30m", 29 | "back_red": "\e[30m", 30 | "back_green": "\e[30m", 31 | "back_yellow": "\e[30m", 32 | "back_blue": "\e[30m", 33 | "back_magenta": "\e[30m", 34 | "back_cyan": "\e[30m", 35 | "back_lgray": "\e[30m", 36 | "back_lgray": "\e[30m", 37 | "back_lred": "\e[30m", 38 | "back_lgreen": "\e[30m", 39 | "back_lyellow ": "\e[30m", 40 | "back_lblue": "\e[30m", 41 | "back_lmagenta": "\e[30m", 42 | "back_lcyan": "\e[30m" 43 | } 44 | 45 | styles_map = { 46 | "bold": ("\e[1m", "\e[21m"), 47 | "dim": ("\e[2m", "\e[22m"), 48 | "underlined": ("\e[4m", "\e[24m"), 49 | "blink": ("\e[5m", "\e[25m"), 50 | "reverse": ("\e[7m", "\e[27m"), 51 | "hidden": ("\e[8m", "\e[28m") 52 | } 53 | 54 | func error(args...) { 55 | print_err("\e[1m\e[31m[ERROR] \e[0m\e[31m", ...args, "\e[0m") 56 | } 57 | 58 | func warning(args...) { 59 | print("\e[1m\e[35m[WARNING] \e[0m\e[35m", ...args, "\e[0m") 60 | } 61 | 62 | func info(args...) { 63 | print("\e[1m\e[94m[INFO] \e[0m\e[94m", ...args, "\e[0m") 64 | } 65 | 66 | func success(args...) { 67 | print("\e[1m\e[92m[SUCCESS] \e[0m\e[92m", ...args, "\e[0m") 68 | } 69 | 70 | func join_args(args...) { 71 | str = [...args].join("") 72 | return str 73 | } 74 | 75 | func insert_var(var_name, obj) { 76 | str_code = var_name + "=" + "obj" 77 | eval(str_code) 78 | } 79 | 80 | for style_name, style in colors_map { 81 | fn = func(args...) { 82 | return style + join_args(...args) + "\e[39m" 83 | } 84 | 85 | insert_var(style_name, fn) 86 | __all__.append(style_name) 87 | } 88 | 89 | for style_name, style in back_colors_map { 90 | fn = func(args...) { 91 | return style + join_args(...args) + "\e[49m" 92 | } 93 | 94 | insert_var(style_name, fn) 95 | __all__.append(style_name) 96 | } 97 | 98 | for style_name, style in styles_map { 99 | fn = func(args...) { 100 | return style[0] + join_args(...args) + style[0] 101 | } 102 | 103 | insert_var(style_name, fn) 104 | __all__.append(style_name) 105 | } 106 | -------------------------------------------------------------------------------- /samples/.shpprc: -------------------------------------------------------------------------------- 1 | func PS1() { 2 | CL1 = "\033[1;32m" 3 | CL2 = "\033[1;34m" 4 | NC = "\033[0m" 5 | 6 | p = $(git branch) 7 | branch = "" 8 | 9 | if p.err() == "" { 10 | for line in p { 11 | if line[0] == "*" { 12 | branch = line[2:] 13 | } 14 | } 15 | } 16 | 17 | path_ = path.pwd().replace($HOME, "~/").split("/") 18 | 19 | search = string(path.pwd()).find($HOME) 20 | search = not search == false 21 | 22 | if search { 23 | # compare with 3 because when split vector the first element is empty 24 | # becuase the first bar / 25 | if len(path_) > 3 { 26 | path_ = "~/…/" + path_[-2:].join("/") 27 | } else { 28 | path_ = path_[-3:].join("/") 29 | } 30 | } else { 31 | if len(path_) > 3 { 32 | path_ = "/…/" + path_[-2:].join("/") 33 | } else { 34 | path_ = path_[-2:].join("/") 35 | } 36 | } 37 | 38 | if branch != "" { 39 | return CL1 + path_ + CL2 + "(" + branch + ")" + CL1 +"$ " + NC 40 | } 41 | 42 | return CL1 + path_ + "$ " + NC 43 | } 44 | 45 | alias ls = ls --color=auto 46 | alias grep = grep --color=auto 47 | 48 | func on_path(dir, f, args...) { 49 | p = $(pwd) 50 | cd ${dir} 51 | f(...args) 52 | cd ${p} 53 | } 54 | 55 | -------------------------------------------------------------------------------- /samples/language/1-hello-world.sh: -------------------------------------------------------------------------------- 1 | # Hello World using external echo command 2 | echo Hello World 3 | 4 | # Hello World using builtin print function 5 | print("Hello World") 6 | -------------------------------------------------------------------------------- /samples/language/10-type.sh: -------------------------------------------------------------------------------- 1 | # 2 | # get type of a variable 3 | # 4 | 5 | s = "test" 6 | print("type(s) -> ", type(s)) 7 | 8 | i = 4 9 | print("type(i) -> ", type(i)) 10 | 11 | f = 5.9 12 | print("type(f) -> ", type(f)) 13 | 14 | a = [1, "str", 1.6] 15 | print("type(a) -> ", type(a)) 16 | 17 | m = {"key": 7} 18 | print("type(m) -> ", type(m)) 19 | 20 | n = null 21 | print("type(n) -> ", type(n)) 22 | 23 | b = true 24 | print("type(b) -> ", type(b)) 25 | 26 | t = type(5) 27 | print("type(t) -> ", type(t)) 28 | 29 | # 30 | # convert from one type to another 31 | # 32 | 33 | # convert string to integer 34 | s = "4" 35 | i = int(i) 36 | print("i + 3 -> ", i + 3) 37 | 38 | # convert integer to string 39 | i = 7 40 | s = string(i) 41 | print("my string: " + s) 42 | 43 | # convert from string to real 44 | s = "4.3" 45 | f = real(f) 46 | print("f + 3 -> ", f + 3) 47 | 48 | # convert from real to string 49 | f = 7.32 50 | s = string(f) 51 | print("my string: " + s) 52 | -------------------------------------------------------------------------------- /samples/language/11-shell.sh: -------------------------------------------------------------------------------- 1 | # 2 | # Basic commands 3 | # 4 | 5 | echo Hello World 6 | ls -l 7 | 8 | # redirect commands output and input 9 | 10 | # create file test.txt and insert echo output 11 | echo this is my line > test.txt 12 | 13 | # append echo output on test.txt 14 | echo this is my other line >> test.txt 15 | 16 | # redirect test.txt to input of command cat 17 | cat < test.txt 18 | 19 | # remove test.txt 20 | rm test.txt 21 | 22 | # 23 | # executing expression inside shell commands 24 | # 25 | 26 | # to execute an expression as commands arguments, you have to use ${} 27 | # print a simple expression with echo command 28 | my_var = "this is a test" 29 | echo ${my_var} 30 | echo ${my_var.to_upper()} 31 | 32 | # an expression can be passed on redirect too 33 | my_file = "other.txt" 34 | echo my line > ${my_file} 35 | echo my other line >> ${my_file} 36 | echo ${my_var} >> ${my_file} 37 | cat < ${my_file} 38 | rm ${my_file} 39 | 40 | # commands pipeline 41 | echo first line > grep.txt 42 | echo second line >> grep.txt 43 | echo last part >> grep.txt 44 | cat < grep.txt | grep part 45 | cat < ${"grep.txt"} | grep ${"line"} 46 | cat < ${"grep.txt"} | grep ${"part"} > line.txt 47 | cat < line.txt 48 | rm line.txt 49 | 50 | # redirect from variable 51 | my_var = "test redirect\n" 52 | cat << ${my_var} 53 | 54 | # command as an expression 55 | echo = $(echo my test) 56 | print(echo) 57 | 58 | # command expression is an object of type cmdobj 59 | print(type(echo)) 60 | 61 | # convert cmdobj to string 62 | str = string(echo) 63 | print(str) 64 | 65 | # iterate over a cmdobj with for ... in statement 66 | for line in $(cat < grep.txt) { 67 | print("line: ", line) 68 | } 69 | 70 | # convert cmdobj to array 71 | c = $(cat < grep.txt) 72 | arr = array(c) 73 | print(arr[1]) 74 | 75 | rm grep.txt 76 | -------------------------------------------------------------------------------- /samples/language/12-cmd.sh: -------------------------------------------------------------------------------- 1 | # to declare your own command you use the keyword cmd 2 | cmd test { 3 | print("test") 4 | } 5 | 6 | # so, you can use your command like any other 7 | test 8 | 9 | # the commands declared can handle arguments, like any other command 10 | # all arguments passed by the user is elements from args array 11 | # args is a variable the is created inside command symbol table 12 | cmd test_with_args { 13 | print("args type: ", type(args)) 14 | print("args len: ", len(args)) 15 | 16 | for arg in args { 17 | print("> ", arg) 18 | } 19 | } 20 | 21 | # here is the test of the command with arguments 22 | test_with_args --a arg1 --b arg2 23 | 24 | # you can passa an array as arguments to command too 25 | test_with_args $@{["arg1", "arg2"]} 26 | 27 | # commands can output on stdout or stderr 28 | # to print in stderr, just use print_err 29 | cmd test_out { 30 | print("stdout: ", args.join(",")) 31 | print_err("stderr: ", args.join(",")) 32 | } 33 | 34 | # print stderr on file test_err.txt and discard stdout output 35 | test_out 2> test_err.txt > /dev/null 36 | 37 | # print stout on file test_out.txt and discard stdout stderr 38 | test_out > test_out.txt 2> /dev/null 39 | 40 | # show file test_err.txt 41 | cat < test_err.txt 42 | 43 | # show file test_out.txt 44 | cat < test_out.txt 45 | 46 | # remove files 47 | rm test_err.txt 48 | rm test_out.txt 49 | 50 | # commands can be used with pipeline too 51 | cmd test_lines { 52 | echo first line 53 | echo second line 54 | echo third 55 | echo other 56 | } 57 | 58 | # on this case grep will receive the std output from command test_lines 59 | # grep will get the lines 'first line' and 'second line' 60 | test_lines | grep line 61 | 62 | # alias is a keyword used to give a nickname to a command with some arguments 63 | alias grep = grep --color=auto 64 | 65 | # now grep will show the lines with color 66 | test_lines | grep line 67 | 68 | cmd test_a { 69 | echo test_a executed 70 | exit 0 71 | } 72 | 73 | cmd test_b { 74 | echo test_b executed 75 | exit 0 76 | } 77 | 78 | cmd test_c { 79 | echo test_c executed 80 | exit 1 81 | } 82 | 83 | # as test_a return 0(no error) and test_b return 0(no error) 84 | # && execute test_a and test_b 85 | test_a && test_b 86 | 87 | # as test_a return 0(no error) and test_b return 0(no error) 88 | # && execute test_a only test_a, because test_b would be executed 89 | # only if test_a had exited with error (different from 0) 90 | test_a || test_b 91 | 92 | # as test_c return 1(error) and test_a return 0(no error) 93 | # && execute test_c, but it won't execute test_a 94 | test_c && test_a 95 | 96 | # as test_c return 1(error) and test_a return 0(no error) 97 | # || execute test_c, and as test_c exited with error, so test_a will be 98 | # executed 99 | test_c || test_a 100 | -------------------------------------------------------------------------------- /samples/language/13-sub-shell.sh: -------------------------------------------------------------------------------- 1 | # subshell works like a command in place, it means, it create another process 2 | # too, like the commands do. 3 | # every operation you do with command you can do with subshell, for example 4 | # pipeline subshell output to other command 5 | shell { 6 | print("line one") 7 | print("line two") 8 | print("other") 9 | } | grep line 10 | 11 | # you can output to file using subshell 12 | shell { 13 | print("line stdout") 14 | print_err("line stderr") 15 | } > test.txt 2> test_err.txt 16 | 17 | cat < test_err.txt 18 | cat < test.txt 19 | 20 | rm test_err.txt test.txt 21 | 22 | # variables changed inside subshell don't affect outside subshell 23 | my_var = 5 24 | shell { 25 | print("in subshell: my_var: ", my_var) 26 | my_var = 8 27 | print("in subshell: my_var: ", my_var) 28 | } 29 | 30 | print("out subshell: my_var: ", my_var) 31 | 32 | # like commands, you can do &&(and) and ||(or) operations with subshell 33 | 34 | # executes shell A and shell B 35 | shell { 36 | echo shell A 37 | exit 0 38 | } && shell { 39 | echo shell B 40 | exit 0 41 | } 42 | 43 | # executes only shell A 44 | shell { 45 | echo shell A 46 | exit 0 47 | } || shell { 48 | echo shell B 49 | exit 0 50 | } 51 | 52 | # executes only shell A 53 | shell { 54 | echo shell A 55 | exit 1 56 | } && shell { 57 | echo shell B 58 | exit 0 59 | } 60 | 61 | # executes shell A and shell B 62 | shell { 63 | echo shell A 64 | exit 1 65 | } || shell { 66 | echo shell B 67 | exit 0 68 | } 69 | -------------------------------------------------------------------------------- /samples/language/14-io.sh: -------------------------------------------------------------------------------- 1 | # read is a builtin function used to get input from stdin 2 | # generally stdin is a keyboard input, but not always 3 | 4 | # read a line from user input, and print the line 5 | print("enter with a line: ", end="") 6 | s = read() 7 | print("line: ", s) 8 | 9 | # using read you can do a command that read input from other command 10 | cmd test { 11 | while let r = read() { 12 | print("test> ", r) 13 | } 14 | } 15 | 16 | echo line from echo | test 17 | 18 | # or you can read a file with a command 19 | echo line one > test.txt 20 | echo line two >> test.txt 21 | test < test.txt 22 | 23 | # you can read from another declared command 24 | cmd test2 { 25 | print("line stdout") 26 | print_err("line stderr") 27 | } 28 | 29 | test2 2>&1 > /dev/null | test 30 | 31 | # read file from subshell 32 | shell { 33 | while let r = read() { 34 | print("shell> ", r) 35 | } 36 | } < test.txt 37 | 38 | rm test.txt 39 | -------------------------------------------------------------------------------- /samples/language/15-functions.sh: -------------------------------------------------------------------------------- 1 | # A function is a block of organized, reusable code that is used to perform 2 | # a single, related action. Functions provide better modularity for your 3 | # application and a high degree of code reusing. 4 | 5 | # Defining a simple function 6 | func print_str(str) { 7 | print(str) 8 | } 9 | 10 | # calling a function 11 | print_str("test function print_str") 12 | 13 | # Pass argument by value 14 | func not_change(i) { 15 | i = 50 16 | print("in func: i: ", i) 17 | } 18 | 19 | i = 20 20 | not_change(i) 21 | print("out func: i: ", i) 22 | 23 | # Pass argument by reference 24 | # arguments like array, tuple or instance of some class is passed always by 25 | # reference, so what you change inside function, change outside function too 26 | func change(a) { 27 | a.append([1, 5]) 28 | print("in func change(): a: ", a) 29 | } 30 | 31 | a = [1, 2, 3] 32 | print("before call change(): a: ", a) 33 | change(a) 34 | print("after call change(): a: ", a) 35 | 36 | # 37 | # Function Arguments 38 | # 39 | 40 | # You can call a function by using the following types of formal arguments 41 | # - Required arguments 42 | # - Keyword arguments 43 | # - Default arguments 44 | # - Variable-length arguments 45 | 46 | # Required arguments 47 | # 48 | # Required arguments are the arguments passed to a function in correct 49 | # positional order. Here, the number of arguments in the function call 50 | # should match exactly with the function definition. 51 | 52 | func test(str) { 53 | print(str) 54 | } 55 | 56 | test("test") 57 | 58 | # Keyword arguments 59 | # 60 | # Keyword arguments are related to the function calls. 61 | # When you use keyword arguments in a function call, 62 | # the caller identifies the arguments by the parameter name. 63 | 64 | func print_info(name, age) { 65 | print("Name: ", name) 66 | print("Age: ", age) 67 | } 68 | 69 | print_info(age=25, name="abcd") 70 | 71 | # Default arguments 72 | # 73 | # A default argument is an argument that assumes a default value 74 | # if a value is not provided in the function call for that argument. 75 | func print_info_default(name, age = 28) { 76 | print("Name: ", name) 77 | print("Age: ", age) 78 | } 79 | 80 | print_info_default(name="dcba", age=75) 81 | print_info_default(name="dcba") 82 | 83 | # Variable-length arguments 84 | # 85 | # You may need to process a function for more arguments than you 86 | # specified while defining the function. 87 | func print_args(arg0, my_args...) { 88 | print("arg0: ", arg0) 89 | print("len(my_args): ", len(my_args)) 90 | 91 | for arg in my_args { 92 | print("arg: ", arg) 93 | } 94 | } 95 | 96 | print_args("arg0", 5, "test", [4, 9]) 97 | -------------------------------------------------------------------------------- /samples/language/2-variables.sh: -------------------------------------------------------------------------------- 1 | # declaring a int variable and printing it 2 | x = 10 3 | print("the value of x is ", x) 4 | 5 | # changing the value of a variable 6 | x = 20 7 | print("now the value of x is ", x) 8 | 9 | # string variable 10 | name = "alex" 11 | print("my name is ", name) 12 | 13 | # float variable 14 | pi = 3.14 15 | print("the value of PI is ", pi) 16 | 17 | # bool 18 | var1 = true 19 | var2 = false 20 | print("var1 is ", var1, " and, var2 is ", var2) 21 | print("var1 is equal v2? ", var1 == var2) 22 | 23 | a, b, c, d = 4, 4.5, "string", true 24 | print(a, " ", b, " ", c, " ", d) 25 | 26 | # 27 | # let expression 28 | # 29 | 30 | # let keyword allow handle an assignment as an expression 31 | # and let return the same object that was assigned to the variables 32 | x = let y = (let w = 15) - 4 33 | print("x: ", x, " y: ", y, " w: ", w) 34 | 35 | # other example 36 | print(">> ", let q = "hello") 37 | print("q: ", q) 38 | 39 | # with tuple 40 | a, b = let x, y = 1, 5 41 | print("a: ", a, " b: ", b) 42 | print("x: ", x, " y: ", y) 43 | -------------------------------------------------------------------------------- /samples/language/3-arithmetic.sh: -------------------------------------------------------------------------------- 1 | # arithmetic with integers variables 2 | ia = 4 + 3 - 2 3 | print("the value of ia is ", ia) 4 | 5 | x1 = 74 6 | x2 = 48 7 | x3 = 3 8 | x4 = 6 9 | x5 = 8 10 | x = (x1 + (x5 - x2)*x3)/x4 11 | print("value of x is ", x) 12 | 13 | # shift right 14 | sr1 = 7 15 | sr2 = 2 16 | sr = sr1 << sr2 17 | print("value of sr is ", sr) 18 | 19 | # shift left 20 | sl1 = 28 21 | sl2 = 2 22 | sl = sr1 >> sr2 23 | print("value of sl is ", sl) 24 | 25 | # arithmetic with float variables 26 | f1 = 4.48 27 | f2 = 74.6 28 | f3 = 42.69 29 | f4 = -23,74 30 | f = (f1*f3 + f4)/f2 31 | print("value of f is ", f) 32 | 33 | print("value of f + x is ", f + x) 34 | print("value of f - x is ", f - x) 35 | print("value of f * x is ", f * x) 36 | print("value of x / f is ", x / f) 37 | -------------------------------------------------------------------------------- /samples/language/4-string.sh: -------------------------------------------------------------------------------- 1 | # string variable 2 | str_var1 = "my string" 3 | str_var2 = "nice" 4 | # concatenate string 5 | str_concat = str_var1 + " is " + str_var2 6 | 7 | # print string variable 8 | print("my string is: ", str_concat) 9 | 10 | # slicing string 11 | abc = "abcdefghijklmnopqrstuvwxyz" 12 | print(abc) 13 | print(abc[20:]) 14 | print(abc[0:3]) 15 | print(abc[-5:]) 16 | print(abc[20:-2]) 17 | print(abc[-4:-2]) 18 | 19 | # string position get 20 | print(abc[1]) 21 | print(abc.at(2)) 22 | print(abc[0] + " ... " + abc[-1:]) 23 | 24 | # string size 25 | print("abc has ", len(abc), " letters") 26 | 27 | # upper case and lower case 28 | name = "Grüssen" 29 | print("upper name: " + name.to_upper()) 30 | print("lower name: " + name.to_lower()) 31 | 32 | # trim opration 33 | trim = " \t shell script \t " 34 | trim_l = " \t shell script \t " 35 | trim_r = " \t shell script \t " 36 | print("_" + trim_l.trim_left() + "_") 37 | print("_" + trim_r.trim_right() + "_") 38 | print("_" + trim.trim() + "_") 39 | 40 | # find operation 41 | statement = "hello for everyone in the room" 42 | f_str = "for" 43 | pos = statement.find(f_str) 44 | print("found in position: ", pos) 45 | 46 | f_str = "not" 47 | pos = statement.find(f_str) 48 | print("found? ", pos) 49 | 50 | # replace operation 51 | statement.replace("hello", "hy") 52 | print("new statement: ", statement) 53 | 54 | str_ex = "cat and echo and grep and others" 55 | print(str_ex) 56 | str_ex.replace_first("and", ",") 57 | print(str_ex) 58 | str_ex.replace_last("and", ",") 59 | print(str_ex) 60 | 61 | # erase all operation 62 | str_ex2 = "cat and echo and grep and others" 63 | print(str_ex2.erase_all("and ")) 64 | 65 | # count operation 66 | str_ex3 = "cat and echo and grep and others" 67 | print("the sentece has ", str_ex3.count("and"), " \"and\" word") 68 | -------------------------------------------------------------------------------- /samples/language/5-array.sh: -------------------------------------------------------------------------------- 1 | cmds = ["echo", "cat", "ls", "rm", "cd"] 2 | 3 | # slice arrays 4 | print("cmds[1] -> ", cmds[1]) 5 | print("cmds[1:] -> ", cmds[1:]) 6 | print("cmds[1:3] -> ", cmds[1:3]) 7 | print("cmds[:3] -> ", cmds[:3]) 8 | print("cmds[0:-1] -> ", cmds[0:-1]) 9 | print("cmds[-2:] -> ", cmds[-2:]) 10 | 11 | # append 12 | cmds.append("chmod") 13 | print("cmds.append(\"chmod\") -> ") 14 | print("-> ", cmds) 15 | print() 16 | 17 | # extends 18 | cmds += ["grep"] + ["tr", "more"] 19 | print("cmds += [\"grep\"] + [\"tr\", \"more\"] -> ") 20 | print("-> ", cmds) 21 | print() 22 | 23 | cmds.extend(["tail"]) 24 | print("cmds.extend([\"tail\"]) -> ") 25 | print("-> ", cmds) 26 | print() 27 | 28 | # insert 29 | cmds.insert(1, "mv") 30 | print("cmds.insert(1, \"mv\")") 31 | print("-> ", cmds) 32 | print() 33 | 34 | # remove 35 | cmds.remove("cat") 36 | print("cmds.remove(\"cat\")") 37 | print("-> ", cmds) 38 | print() 39 | 40 | # pop 41 | cmds.pop(4) 42 | print("cmds.pop(4)") 43 | print("-> ", cmds) 44 | print() 45 | 46 | # index 47 | print("cmds.index(\"mv\")") 48 | print("-> ", cmds.index("mv")) 49 | print() 50 | 51 | # count 52 | cmds += ["ls"] 53 | print("cmds.count(\"ls\")") 54 | print("-> ", cmds.count("ls")) 55 | print() 56 | 57 | # sort 58 | print("cmds.sort()") 59 | print("-> ", cmds.sort()) 60 | print() 61 | 62 | # reverse 63 | print("cmds.reverse()") 64 | print("-> ", cmds.reverse()) 65 | print() 66 | 67 | # map 68 | print("cmds.map...") 69 | cmds.map(lambda x: x.to_upper() if len(x) > 2 else x) 70 | print("-> ", cmds) 71 | print() 72 | 73 | # filter 74 | print("cmds.filter...") 75 | cmds.filter(lambda x: true if len(x) == 2 else false) 76 | print("-> ", cmds) 77 | print() 78 | 79 | # join 80 | print("cmds.join...") 81 | s = cmds.join("-") 82 | print(s) 83 | print("-> ", cmds) 84 | print() 85 | 86 | # del operator 87 | del cmds[2] 88 | print("del cmds[2]") 89 | print("-> ", cmds) 90 | print() 91 | 92 | # clear 93 | print("cmds.clear()") 94 | cmds.clear() 95 | print("-> ", cmds) 96 | print() 97 | -------------------------------------------------------------------------------- /samples/language/6-map.sh: -------------------------------------------------------------------------------- 1 | files_type = {"jpg": "image file type", 2 | "cpp": "cpp code", 3 | "wav": "audio file type", 4 | "sh": "shell script file type"} 5 | 6 | # key access 7 | print("files_type[\"cpp\"]") 8 | print("-> ", files_type["cpp"], "\n") 9 | 10 | files_type["cpp"] = "c plus plus file type" 11 | print("files_type[\"cpp\"]") 12 | print("-> ", files_type["cpp"], "\n") 13 | 14 | # keys operation 15 | print("files_type.keys()") 16 | print("-> ", files_type.keys()) 17 | print() 18 | 19 | # values operation 20 | print("files_type.values()") 21 | print("-> ", files_type.values()) 22 | print() 23 | 24 | # exists operation 25 | print("files_type.exists(\"sh\")") 26 | print("-> ", files_type.exists("sh")) 27 | print() 28 | 29 | print("files_type.exists(\"shpp\")") 30 | print("-> ", files_type.exists("shpp")) 31 | print() 32 | 33 | # append operation 34 | files_type += {"avi": "video file type"} 35 | print("files_type.update...") 36 | print("-> ", files_type) 37 | 38 | # del operator 39 | del files_type["sh"] 40 | print("del files_type[\"sh\"]") 41 | print("-> ", files_type) 42 | -------------------------------------------------------------------------------- /samples/language/7-if-else.sh: -------------------------------------------------------------------------------- 1 | a = 5 2 | b = 8 3 | 4 | if a < b { 5 | print("a < b -> true") 6 | } else { 7 | print("a < b -> false") 8 | } 9 | 10 | t = "test" 11 | 12 | if t == "other" { 13 | print("t == other") 14 | } else if t == "tests" { 15 | print("t == tests") 16 | } else { 17 | print("none above") 18 | } 19 | 20 | x = true 21 | y = true 22 | 23 | if x && y { 24 | print("x && y -> ", true) 25 | } 26 | 27 | # testing compration operators 28 | print("7 == 7 -> ", 7 >= 7) 29 | print("7 != 17 -> ", 7 != 17) 30 | print("7 >= 7 -> ", 7 >= 7) 31 | print("7 <= 7 -> ", 7 <= 7) 32 | print("9 > 8 -> ", 9 > 8) 33 | print("7 < 8 -> ", 7 < 8) 34 | print("true && true -> ", true && true) 35 | print("true || false -> ", true || false) 36 | 37 | # ! and 'not' has different precedence 38 | print("!false && true -> ", !false && true) 39 | print("!(true && false) -> ", !(true && false)) 40 | print("! true && true -> ", ! true && true) 41 | print("not false && false -> ", not false && false) 42 | print("not not not !!!!false -> ", not not not !!!!false) 43 | 44 | # bitwise operators 45 | print("7 & 3 -> ", 7 & 3) 46 | print("7 | 3 -> ", 7 | 3) 47 | print("7 ^ 3 -> ", 7 ^ 3) 48 | print("~5 -> ", ~5) 49 | -------------------------------------------------------------------------------- /samples/language/8-switch.sh: -------------------------------------------------------------------------------- 1 | # switch statement works similar with others languages as C, C++, Java and 2 | # javascript, but its case blocks are delimited by {} insted of break. 3 | # 4 | # a case can have several expression separated by ',', it means the case 5 | # accept a list of expression, if some these expression matches, so the case 6 | # block a executed 7 | 8 | t = "cat" 9 | 10 | switch t { 11 | case "ls" { 12 | print("command ls") 13 | } 14 | 15 | case "cat", "mv" { 16 | print("command cat or mv") 17 | } 18 | 19 | default { 20 | print("none above") 21 | } 22 | } 23 | 24 | t = "other" 25 | 26 | switch t { 27 | case "ls" { 28 | print("command ls") 29 | } 30 | 31 | case "cat", "mv" { 32 | print("command cat or mv") 33 | } 34 | 35 | default { 36 | print("none above") 37 | } 38 | } 39 | 40 | t = "ls" 41 | 42 | switch t { 43 | case "ls" { 44 | print("command ls") 45 | } 46 | 47 | case "cat", "mv" { 48 | print("command cat or mv") 49 | } 50 | 51 | default { 52 | print("none above") 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /samples/language/9-loop.sh: -------------------------------------------------------------------------------- 1 | ### while statement ### 2 | # 3 | # A while statement executes its statements as long as a specified condition 4 | # evaluates to true. 5 | 6 | # The following while loop iterates as long as n is less than three: 7 | n = 0 8 | x = 0 9 | 10 | while n < 3 { 11 | n += 1 12 | x += n 13 | print("value of x: ", x) 14 | } 15 | 16 | ### break statement ### 17 | # 18 | # Use the break statement to terminate a loop, switch, or in conjunction with 19 | # a labeled statement. 20 | 21 | # The following example iterates through the elements in an array until it 22 | # finds the index of an element whose value is the_value: 23 | a = [1, 3, 8, 5, 2, 4] 24 | i = 0 25 | while i < len(a) { 26 | if a[i] == 5 { 27 | break 28 | } 29 | 30 | print("while: value o a[", i, "]: ", a[i]) 31 | i += 1 32 | } 33 | 34 | ### continue statement ### 35 | # 36 | # The continue statement can be used to restart a while or for in loop 37 | 38 | # The following example shows a while loop with a continue statement that 39 | # executes when the value of i is three. 40 | i = 0 41 | while i < 5 { 42 | i += 1break 43 | 44 | if i == 3 { 45 | continue 46 | } 47 | 48 | print("value of i: ", i) 49 | } 50 | 51 | ### for...in statement 52 | # 53 | # The for...in statement creates a loop iterating over iterable objects 54 | # (including array, map, tuple, and others iterable objects) 55 | 56 | arr = ["cat", "mv", "ls", "rm"] 57 | 58 | for a in arr { 59 | print(": ", a) 60 | } 61 | 62 | for a in arr { 63 | if a == "ls" { 64 | break 65 | } 66 | 67 | print(":: ", a) 68 | } 69 | 70 | for a in arr { 71 | if a == "ls" { 72 | continue 73 | } 74 | 75 | print("> ", a) 76 | } 77 | 78 | # 79 | # List comprehension 80 | # 81 | files = [file for file in $(ls) if file.size("k") > 10] 82 | print(files) 83 | -------------------------------------------------------------------------------- /scripts/lint.sh: -------------------------------------------------------------------------------- 1 | func exist_cmd(name) { 2 | c = $(whereis ${name}) 3 | 4 | arr = string(c).trim().split(":") 5 | 6 | if arr[1] == "" { 7 | return false 8 | } 9 | 10 | return true 11 | } 12 | 13 | lint_path = __path__ + "/../build/" 14 | 15 | if !path.exists(lint_path + "/" + "cpplint.py") { 16 | if exist_cmd("wget") { 17 | echo output path: ${lint_path} 18 | wget -P ${lint_path} https://raw.githubusercontent.com/google/styleguide/gh-pages/cpplint/cpplint.py 19 | } 20 | } 21 | 22 | lint_exec = lint_path + "cpplint.py" 23 | chmod +x ${lint_exec} 24 | 25 | for file in $(find ../src) { 26 | if file.ends_with(".cc") || file.ends_with(".h") { 27 | print("lint file: ", file) 28 | ${lint_exec} ${file} 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /shell/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src) 2 | CHECK_CXX_COMPILER_FLAG("-std=c++14" COMPILER_SUPPORTS_CXX11) 3 | 4 | find_package(Readline REQUIRED) 5 | 6 | include_directories(${READLINE_INCLUDE_DIR}) 7 | 8 | file(GLOB SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.cc) 9 | 10 | add_executable(shpp_exe ${SOURCES}) 11 | set_target_properties(shpp_exe PROPERTIES OUTPUT_NAME shpp) 12 | 13 | if(CMAKE_COMPILER_IS_GNUCXX AND COV_TESTS) 14 | message("gcov on") 15 | include(CodeCoverage) 16 | setup_target_for_coverage(shpp_coverage "./shell/shpp ../test/interpreter/run.shpp" coverage) 17 | endif() 18 | 19 | target_link_libraries(shpp_exe shpp ${READLINE_LIBRARY}) 20 | 21 | if(CMAKE_COMPILER_IS_GNUCXX AND COV_TESTS) 22 | target_link_libraries(shpp_exe gcov) 23 | endif() 24 | 25 | install(TARGETS shpp_exe RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) 26 | 27 | -------------------------------------------------------------------------------- /shell/main.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Alex Silva Torres 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "runner.h" 22 | 23 | void help() { 24 | std::cerr << "shpp [file]\n"; 25 | } 26 | 27 | std::vector Args(int argc, char **argv) { 28 | std::vector args; 29 | 30 | if (argc > 1) { 31 | for (int i = 1; i < argc; i++) { 32 | args.push_back(std::string(argv[i])); 33 | } 34 | } 35 | 36 | return args; 37 | } 38 | 39 | int main(int argc, char **argv) { 40 | shpp::Runner runner; 41 | 42 | if (argc == 1) { 43 | runner.ExecInterative(); 44 | } else { 45 | std::vector args = Args(argc, argv); 46 | runner.Exec(argv[1], std::move(args)); 47 | } 48 | 49 | return 0; 50 | } 51 | -------------------------------------------------------------------------------- /shell/runner.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Alex Silva Torres 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "runner.h" 16 | 17 | #include 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "env-shell.h" 29 | #include "objects/str-object.h" 30 | #include "utils/dir.h" 31 | 32 | namespace shpp { 33 | 34 | static sigjmp_buf ctrlc_buf; 35 | 36 | void HandleSignals(int signo) { 37 | if (signo == SIGINT) { 38 | std::cout << "\n"; 39 | siglongjmp(ctrlc_buf, 1); 40 | } 41 | } 42 | 43 | Runner::Runner(): interpreter_(true) { 44 | using namespace internal; 45 | 46 | internal::EnvShell::instance()->InitShell(); 47 | } 48 | 49 | void Runner::Exec(std::string name, std::vector&& args) { 50 | try { 51 | internal::ScriptStream file(name); 52 | 53 | if (!file.IsOpen()) { 54 | throw std::invalid_argument((boost::format("can't open file: %1%")%name) 55 | .str()); 56 | } 57 | 58 | interpreter_.Exec(file, std::move(args)); 59 | } catch (RunTimeError& e) { 60 | std::cout << "File: '" << e.file() << "'" 61 | << "\n line: " << e.pos().line 62 | << " >> " << e.line_error() << "\n" 63 | << "Error: " << e.what() << "\n\n"; 64 | 65 | for (auto& msg: e.messages()) { 66 | std::cout << "File: '" << msg.file() << "'" 67 | << "\n line: " << msg.line() 68 | << " >> " << msg.line_error() << "\n" 69 | << "Error: " << msg.msg() << "\n\n"; 70 | } 71 | } catch (std::invalid_argument& e) { 72 | std::cout << "Error: " << e.what() << "\n\n"; 73 | } 74 | } 75 | 76 | void Runner::ExecInterative() { 77 | namespace fs = boost::filesystem; 78 | using std::placeholders::_1; 79 | 80 | if (signal(SIGINT, HandleSignals) == SIG_ERR) { 81 | std::cerr << "failed to register interrupts with kernel\n"; 82 | exit(-1); 83 | } 84 | 85 | internal::EnvShell::instance()->interective_exec(true); 86 | 87 | fs::path path_rc = fs::path(internal::GetHome() + "/.shpprc"); 88 | 89 | if (fs::exists(path_rc)) { 90 | Exec(path_rc.string()); 91 | } 92 | 93 | while (true) { 94 | try { 95 | interpreter_.ExecInterative([&](internal::Executor* exec, bool concat) { 96 | char *input; 97 | std::string str_source; 98 | std::string prompt; 99 | 100 | if (concat) { 101 | prompt = "| "; 102 | 103 | internal::ObjectPtr obj_func = interpreter_.LookupSymbol("PS2"); 104 | 105 | if (obj_func) { 106 | std::vector params; 107 | internal::ObjectPtr obj = obj_func->Call(exec, std::move(params)); 108 | 109 | if (obj->type() == internal::Object::ObjectType::STRING) { 110 | prompt = static_cast(*obj).value(); 111 | } 112 | } 113 | } else { 114 | prompt = "> "; 115 | 116 | internal::ObjectPtr obj_func = interpreter_.LookupSymbol("PS1"); 117 | 118 | if (obj_func) { 119 | std::vector params; 120 | internal::ObjectPtr obj = obj_func->Call(exec, std::move(params)); 121 | 122 | if (obj->type() == internal::Object::ObjectType::STRING) { 123 | prompt = static_cast(*obj).value(); 124 | } 125 | } 126 | } 127 | 128 | while (sigsetjmp(ctrlc_buf, 1) != 0 ); 129 | 130 | input = readline(prompt.c_str()); 131 | 132 | if (input == nullptr) { 133 | exit(0); 134 | } 135 | 136 | str_source = input; 137 | add_history(input); 138 | free(input); 139 | return str_source; 140 | }); 141 | } catch (RunTimeError& e) { 142 | std::cout << "Error: " << e.pos().line << ": " << e.pos().col 143 | << ": " << e.what() << "\n\n"; 144 | 145 | for (auto& msg: e.messages()) { 146 | std::cout << "Error: " << msg.line() << ": " << msg.pos() 147 | << ": " << msg.msg() << "\n"; 148 | } 149 | } 150 | } 151 | } 152 | 153 | } 154 | -------------------------------------------------------------------------------- /shell/runner.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Alex Silva Torres 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef SHPP_MAIN_INTERPRETER_H 16 | #define SHPP_MAIN_INTERPRETER_H 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #include "interpreter/symbol-table.h" 23 | #include "ast/ast.h" 24 | #include "interpreter/interpreter.h" 25 | 26 | namespace shpp { 27 | 28 | class Runner { 29 | public: 30 | Runner(); 31 | 32 | ~Runner() = default; 33 | 34 | void Exec(std::string file_name, std::vector&& args = {}); 35 | void ExecInterative(); 36 | 37 | private: 38 | internal::Interpreter interpreter_; 39 | }; 40 | 41 | } 42 | 43 | #endif // SHPP_MAIN_INTERPRETER_H 44 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Alex Silva Torres 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | file(GLOB SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.cc) 16 | file(GLOB SOURCES_PARSER ${CMAKE_CURRENT_SOURCE_DIR}/parser/*.cc) 17 | file(GLOB SOURCES_INTER ${CMAKE_CURRENT_SOURCE_DIR}/interpreter/*.cc) 18 | file(GLOB SOURCES_OBJECTS ${CMAKE_CURRENT_SOURCE_DIR}/objects/*.cc) 19 | file(GLOB SOURCES_MODULES ${CMAKE_CURRENT_SOURCE_DIR}/modules/*.cc) 20 | file(GLOB SOURCES_UTILS ${CMAKE_CURRENT_SOURCE_DIR}/utils/*.cc) 21 | file(GLOB SOURCES_AST ${CMAKE_CURRENT_SOURCE_DIR}/ast/*.cc) 22 | 23 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}) 24 | 25 | if(STATIC_BUILD) 26 | add_library(shpp STATIC ${SOURCES} ${SOURCES_PARSER} ${SOURCES_INTER} 27 | ${SOURCES_OBJECTS} ${SOURCES_MODULES} ${SOURCES_UTILS} ${SOURCES_AST}) 28 | else() 29 | add_library(shpp SHARED ${SOURCES} ${SOURCES_PARSER} ${SOURCES_INTER} 30 | ${SOURCES_OBJECTS} ${SOURCES_MODULES} ${SOURCES_UTILS} ${SOURCES_AST}) 31 | endif() 32 | 33 | message(READLINE_LIBRARY: ${READLINE_LIBRARY}) 34 | message(BOOST: ${Boost_FILESYSTEM_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_LOCALE_LIBRARY}) 35 | 36 | target_link_libraries(shpp 37 | ${CMAKE_THREAD_LIBS_INIT} 38 | ${Boost_FILESYSTEM_LIBRARY} 39 | ${Boost_SYSTEM_LIBRARY} 40 | ${Boost_LOCALE_LIBRARY} 41 | ${READLINE_LIBRARY}) 42 | 43 | install(TARGETS shpp DESTINATION "${CMAKE_INSTALL_PREFIX}/lib") 44 | SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib") 45 | -------------------------------------------------------------------------------- /src/ast/ast.cc: -------------------------------------------------------------------------------- 1 | #include "ast.h" 2 | 3 | namespace shpp { 4 | namespace internal { 5 | 6 | static const char* ast_node_str[] = { 7 | #define DECLARE_TYPE_CLASS(type) #type, 8 | AST_NODE_LIST(DECLARE_TYPE_CLASS) 9 | #undef DECLARE_TYPE_CLASS 10 | ""}; 11 | 12 | // define function only to avoid warning 13 | const char* AstNodeStr(size_t i) { return ast_node_str[i]; } 14 | 15 | AnnotationDeclaration::AnnotationDeclaration( 16 | std::unique_ptr decorator_expr, 17 | std::unique_ptr decl, Position position) 18 | : Declaration(NodeType::kCmdDeclaration, position), 19 | decorator_expr_(std::move(decorator_expr)), 20 | decl_(std::move(decl)) { 21 | if (decl_->type() == NodeType::kFunctionDeclaration) { 22 | std::string name = 23 | reinterpret_cast(decl_.get())->name()->name(); 24 | original_name_ = name; 25 | reinterpret_cast(decl_.get()) 26 | ->SetName(std::string("@") + name); 27 | } else if (decl_->type() == NodeType::kClassDeclaration) { 28 | std::string name = 29 | reinterpret_cast(decl_.get())->name()->name(); 30 | original_name_ = name; 31 | reinterpret_cast(decl_.get()) 32 | ->SetName(std::string("@") + name); 33 | } 34 | } 35 | 36 | } // namespace internal 37 | } // namespace shpp 38 | -------------------------------------------------------------------------------- /src/cmd-entry.cc: -------------------------------------------------------------------------------- 1 | #include "cmd-entry.h" 2 | #include "objects/object-factory.h" 3 | #include "interpreter/scope-executor.h" 4 | #include "utils/scope-exit.h" 5 | 6 | namespace shpp { 7 | namespace internal { 8 | 9 | void CmdDeclEntry::Exec(Executor* parent, std::vector&& args) { 10 | // it is the table function 11 | SymbolTablePtr table = 12 | SymbolTable::Create(SymbolTable::TableType::FUNC_TABLE); 13 | 14 | // main symbol of function 15 | symbol_table_.Push(table, false); 16 | 17 | BlockExecutor executor(parent, symbol_table_, true); 18 | 19 | // scope exit case an excpetion thrown 20 | auto cleanup = MakeScopeExit([&]() { 21 | executor.ExecuteDeferStack(); 22 | symbol_table_.Pop(); 23 | }); 24 | IgnoreUnused(cleanup); 25 | 26 | std::vector vec_args; 27 | 28 | for (auto& arg: args) { 29 | ObjectFactory obj_factory(symbol_table_); 30 | ObjectPtr str_obj(obj_factory.NewString(arg)); 31 | vec_args.push_back(str_obj); 32 | } 33 | 34 | ObjectFactory obj_factory(symbol_table_); 35 | ObjectPtr array_obj(obj_factory.NewArray(std::move(vec_args))); 36 | 37 | // arguments as passed to command as an array called args 38 | symbol_table_.SetEntry("args", array_obj); 39 | 40 | executor.Exec(start_node_.get()); 41 | } 42 | 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/cmd-entry.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Alex Silva Torres 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef SHPP_CMD_ENTRY_H 16 | #define SHPP_CMD_ENTRY_H 17 | 18 | #include 19 | 20 | #include "interpreter/executor.h" 21 | #include "interpreter/symbol-table.h" 22 | 23 | namespace shpp { 24 | namespace internal { 25 | 26 | // command entry class 27 | class CmdEntry { 28 | public: 29 | enum class Type { 30 | kDecl, 31 | kDef, 32 | kIn, 33 | kAlias 34 | }; 35 | 36 | CmdEntry(Type type): type_(type) {} 37 | 38 | virtual ~CmdEntry() = default; 39 | 40 | virtual void Exec(Executor* parent, std::vector&& args) = 0; 41 | 42 | Type type() const noexcept { 43 | return type_; 44 | } 45 | 46 | private: 47 | Type type_; 48 | }; 49 | 50 | using CmdEntryPtr = std::shared_ptr; 51 | 52 | class CmdDeclEntry: public CmdEntry { 53 | public: 54 | CmdDeclEntry(std::shared_ptr start_node, 55 | const SymbolTableStack& symbol_table) 56 | : CmdEntry(Type::kDecl) 57 | , start_node_(start_node) 58 | , symbol_table_(symbol_table.MainTable()) {} 59 | 60 | virtual ~CmdDeclEntry() = default; 61 | 62 | void Exec(Executor* parent, std::vector&& args) override; 63 | 64 | private: 65 | std::shared_ptr start_node_; 66 | SymbolTableStack symbol_table_; 67 | }; 68 | 69 | class CmdInEntry: public CmdEntry { 70 | public: 71 | CmdInEntry(const SymbolTableStack& symbol_table) 72 | : CmdEntry(Type::kIn) 73 | , symbol_table_(symbol_table.MainTable()) {} 74 | 75 | virtual ~CmdInEntry() = default; 76 | 77 | virtual void Exec(Executor* parent, std::vector&& args) = 0; 78 | 79 | void SetStdFd(int outfile, int errfile, int infile) { 80 | outfile_ = outfile; 81 | errfile_ = errfile; 82 | infile_ = infile; 83 | } 84 | 85 | std::tuple GetStdFd() { 86 | return std::tuple(outfile_, errfile_, infile_); 87 | } 88 | 89 | int GetStatus() { 90 | return status_; 91 | } 92 | 93 | protected: 94 | void SetStatus(int status) { 95 | status_ = status; 96 | } 97 | 98 | private: 99 | int outfile_; 100 | int errfile_; 101 | int infile_; 102 | int status_; 103 | SymbolTableStack symbol_table_; 104 | }; 105 | 106 | template 107 | void CmdSet(const std::string& name, SymbolTableStack& symbol_table) { 108 | CmdEntryPtr cmd_ptr(new C(symbol_table)); 109 | symbol_table.SetCmd(name, cmd_ptr); 110 | } 111 | 112 | class CmdAliasEntry: public CmdEntry { 113 | public: 114 | CmdAliasEntry(AstNode* start_node, const SymbolTableStack& symbol_table); 115 | 116 | virtual ~CmdAliasEntry() = default; 117 | 118 | const std::vector& args() const noexcept; 119 | 120 | private: 121 | std::vector args_; 122 | }; 123 | 124 | } 125 | } 126 | 127 | #endif // SHPP_CMD_ENTRY_H 128 | -------------------------------------------------------------------------------- /src/env-shell.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Alex Silva Torres 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "env-shell.h" 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | namespace shpp { 25 | namespace internal { 26 | 27 | FileDescriptorMap::FileDescriptorMap() { 28 | map_["0"] = STDIN_FILENO; 29 | map_["1"] = STDOUT_FILENO; 30 | map_["2"] = STDERR_FILENO; 31 | } 32 | 33 | int& FileDescriptorMap::operator[](const std::string& name) { 34 | try { 35 | return map_.at(name); 36 | } catch (std::out_of_range& e) { 37 | throw RunTimeError(RunTimeError::ErrorCode::FD_NOT_FOUND, 38 | boost::format("file descriptor '%1%' not found") 39 | %name); 40 | } 41 | } 42 | 43 | int FileDescriptorMap::operator[](const std::string& name) const { 44 | try { 45 | return map_.at(name); 46 | } catch (std::out_of_range& e) { 47 | throw RunTimeError(RunTimeError::ErrorCode::FD_NOT_FOUND, 48 | boost::format("file descriptor '%1%' not found") 49 | %name); 50 | } 51 | } 52 | 53 | void ImportTable::AddModule(const std::string& name, ObjectPtr module) { 54 | module_table_.insert(std::pair(name, module)); 55 | } 56 | 57 | ObjectPtr ImportTable::GetModule(const std::string& name) { 58 | auto it = module_table_.find(name); 59 | 60 | if (it != module_table_.end()) { 61 | return it->second; 62 | } 63 | 64 | return ObjectPtr(nullptr); 65 | } 66 | 67 | EnvShell *EnvShell::instance_ = 0; 68 | 69 | void EnvShell::InitShell() { 70 | // see if we are running interactively 71 | shell_terminal_ = STDIN_FILENO; 72 | shell_is_interactive_ = isatty(shell_terminal_); 73 | 74 | // starts the shared memory region 75 | shmid_ = shmget(IPC_PRIVATE, sizeof(CmdSharedError), 0640|IPC_CREAT); 76 | 77 | // gets pid of shell main process 78 | shell_pid_ = getpid(); 79 | 80 | // initialize global boost locale 81 | boost::locale::generator gen; 82 | auto loc = gen(""); 83 | std::locale::global(loc); 84 | std::cout.imbue(loc); 85 | 86 | if (shell_is_interactive_) { 87 | // loop until we are in the foreground 88 | while (tcgetpgrp(shell_terminal_) != (shell_pgid_ = getpgrp())) { 89 | kill(- shell_pgid_, SIGTTIN); 90 | } 91 | 92 | // ignore interactive and job-control signals 93 | signal(SIGINT, SIG_IGN); 94 | signal(SIGQUIT, SIG_IGN); 95 | signal(SIGTSTP, SIG_IGN); 96 | signal(SIGTTIN, SIG_IGN); 97 | signal(SIGTTOU, SIG_IGN); 98 | signal(SIGCHLD, SIG_IGN); 99 | 100 | // put ourselves in our own process group 101 | shell_pgid_ = getpid (); 102 | if (setpgid(shell_pgid_, shell_pgid_) < 0) { 103 | perror("Couldn't put the shell in its own process group"); 104 | exit(1); 105 | } 106 | 107 | /* Grab control of the terminal. */ 108 | tcsetpgrp(shell_terminal_, shell_pgid_); 109 | 110 | /* Save default terminal attributes for shell. */ 111 | tcgetattr(shell_terminal_, &shell_tmodes_); 112 | } 113 | } 114 | 115 | EnvShell::~EnvShell() { 116 | shmctl(shmid_, IPC_RMID, 0); 117 | } 118 | 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/env-shell.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Alex Silva Torres 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef SHPP_ENV_SHELL_H 16 | #define SHPP_ENV_SHELL_H 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "objects/abstract-obj.h" 24 | #include "interpreter/symbol-table.h" 25 | #include "ast/ast.h" 26 | #include "interpreter/interpreter.h" 27 | 28 | namespace shpp { 29 | namespace internal { 30 | 31 | #define SHPP_CMD_SIZE_MAX 256 32 | 33 | struct CmdSharedError { 34 | int err_code; 35 | int except_code; 36 | bool error; 37 | char err_str[SHPP_CMD_SIZE_MAX]; 38 | }; 39 | 40 | class FileDescriptorMap { 41 | public: 42 | FileDescriptorMap(); 43 | 44 | int& operator[](const std::string& name); 45 | 46 | int operator[](const std::string& name) const; 47 | 48 | private: 49 | std::unordered_map map_; 50 | }; 51 | 52 | class ImportTable { 53 | public: 54 | ImportTable() = default; 55 | ~ImportTable() = default; 56 | 57 | void AddModule(const std::string& name, ObjectPtr module); 58 | 59 | // if the module doesn't exists return ObjectPtr(nullptr) 60 | ObjectPtr GetModule(const std::string& name); 61 | 62 | private: 63 | std::unordered_map module_table_; 64 | }; 65 | 66 | class EnvShell { 67 | public: 68 | static EnvShell *instance() { 69 | if (!instance_) { 70 | instance_ = new EnvShell; 71 | } 72 | 73 | return instance_; 74 | } 75 | 76 | void InitShell(); 77 | 78 | inline bool shell_is_interactive() const noexcept { 79 | return shell_is_interactive_; 80 | } 81 | 82 | inline struct termios* shell_tmodes() { 83 | return &shell_tmodes_; 84 | } 85 | 86 | inline int shell_terminal() { 87 | return shell_terminal_; 88 | } 89 | 90 | pid_t shell_pgid() { 91 | return shell_pgid_; 92 | } 93 | 94 | pid_t shell_pid() { 95 | return shell_pid_; 96 | } 97 | 98 | int shmid() const noexcept { 99 | return shmid_; 100 | } 101 | 102 | FileDescriptorMap& fd_map() { 103 | return fd_map_; 104 | } 105 | 106 | void interective_exec(bool v) { 107 | interective_exec_ = v; 108 | } 109 | 110 | bool interective_exec() { 111 | return interective_exec_; 112 | } 113 | 114 | ImportTable& GetImportTable() { 115 | return import_table_; 116 | } 117 | 118 | void SetArgv(std::vector&& argv) { 119 | argv_ = std::move(argv); 120 | } 121 | 122 | const std::vector& Argv() const { 123 | return argv_; 124 | } 125 | 126 | inline void last_background_pid(int pid) { 127 | last_background_pid_ = pid; 128 | } 129 | 130 | inline int last_background_pid() { 131 | return last_background_pid_; 132 | } 133 | 134 | inline void last_foreground_pid(int pid) { 135 | last_foreground_pid_ = pid; 136 | } 137 | 138 | inline int last_foreground_pid() { 139 | return last_foreground_pid_; 140 | } 141 | 142 | inline void last_foreground_exit_code(int code) { 143 | last_foreground_exit_code_ = code; 144 | } 145 | 146 | inline int last_foreground_exit_code() { 147 | return last_foreground_exit_code_; 148 | } 149 | 150 | ~EnvShell(); 151 | 152 | private: 153 | EnvShell() 154 | : interective_exec_(false) 155 | , last_background_pid_(-1) 156 | , last_foreground_pid_(-1) 157 | , last_foreground_exit_code_(-1) {} 158 | 159 | static EnvShell *instance_; 160 | 161 | pid_t shell_pid_; 162 | pid_t shell_pgid_; 163 | FileDescriptorMap fd_map_; 164 | struct termios shell_tmodes_; 165 | int shell_terminal_; 166 | int shell_is_interactive_; 167 | int shmid_; 168 | bool interective_exec_; 169 | ImportTable import_table_; 170 | std::vector argv_; 171 | int last_background_pid_; 172 | int last_foreground_pid_; 173 | int last_foreground_exit_code_; 174 | }; 175 | 176 | } 177 | } 178 | 179 | #endif // SHPP_ENV_SHELL_H 180 | -------------------------------------------------------------------------------- /src/interpreter/assign-executor.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Alex Silva Torres 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef SHPP_ASSIGN_EXECUTOR_H 16 | #define SHPP_ASSIGN_EXECUTOR_H 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "ast/ast.h" 24 | #include "objects/obj-type.h" 25 | #include "executor.h" 26 | #include "symbol-table.h" 27 | #include "objects/object-factory.h" 28 | 29 | namespace shpp { 30 | namespace internal { 31 | 32 | // Class to execute assignment operation 33 | // 34 | // on this class some methods return reference for shared_ptr 35 | // it is not common in C++, but on this case, the objective 36 | // is not increment the counter, but change the variable 37 | class AssignExecutor: public Executor { 38 | public: 39 | AssignExecutor(Executor* parent, SymbolTableStack& symbol_table_stack) 40 | : Executor(parent, symbol_table_stack) 41 | , obj_factory_(symbol_table_stack) 42 | , global_(false) {} 43 | 44 | AssignExecutor(bool global, Executor* parent, 45 | SymbolTableStack& symbol_table_stack) 46 | : Executor(parent, symbol_table_stack) 47 | , obj_factory_(symbol_table_stack) 48 | , global_(global) {} 49 | 50 | // Entry point to execute assign operations 51 | void Exec(AstNode* node); 52 | 53 | // Execute assign operation as exec and return the the right side as object 54 | ObjectPtr ExecWithReturn(AstNode* node); 55 | 56 | void Assign(std::vector& left_exp_vec, 57 | std::vector& values, 58 | TokenKind assign_kind = TokenKind::ASSIGN); 59 | 60 | void AssignIdentifier(AstNode* node, ObjectPtr value, TokenKind token, 61 | bool create = false); 62 | 63 | void AssignOperation(Expression* left_exp, ObjectPtr value, TokenKind token); 64 | 65 | void AssignAtrribute(AstNode* node, ObjectPtr value, TokenKind token); 66 | 67 | void AssignArray(AstNode* node, ObjectPtr value, TokenKind token); 68 | 69 | void AssignLeftArray(AstNode* node, ObjectPtr value, TokenKind token); 70 | 71 | void AssignLeftTuple(AstNode* node, ObjectPtr value, TokenKind token); 72 | 73 | void AssignmentAcceptorExpr(AstNode* node, ObjectPtr value, TokenKind token); 74 | 75 | void set_stop(StopFlag flag); 76 | 77 | void AssignToRef(ObjectPtr& ref, ObjectPtr value, TokenKind token); 78 | 79 | void AssignToArray(ObjectPtr arr, ObjectPtr index, ObjectPtr value, 80 | TokenKind token); 81 | 82 | private: 83 | ObjectFactory obj_factory_; 84 | bool global_; 85 | }; 86 | 87 | std::vector Unpack(ObjectPtr obj); 88 | 89 | } 90 | } 91 | 92 | #endif // SHPP_ASSIGN_EXECUTOR_H 93 | -------------------------------------------------------------------------------- /src/interpreter/cmd-executor.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Alex Silva Torres 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef SHPP_CMD_EXECUTOR_H 16 | #define SHPP_CMD_EXECUTOR_H 17 | 18 | #include 19 | 20 | #include "executor.h" 21 | #include "cmd-exec.h" 22 | 23 | namespace shpp { 24 | namespace internal { 25 | 26 | typedef std::tuple CmdExprData; 27 | 28 | class CmdExecutor: public Executor { 29 | public: 30 | CmdExecutor(Executor* parent, SymbolTableStack& symbol_table_stack) 31 | : Executor(parent, symbol_table_stack) {} 32 | 33 | CmdExprData ExecGetResult(CmdFull *node); 34 | 35 | CmdExprData ExecCmdGetResult(Cmd *node); 36 | 37 | CmdExprData ExecCmdBinOp(CmdAndOr* cmd); 38 | 39 | int ExecCmdBinOp(CmdAndOr* cmd, bool wait); 40 | 41 | int Exec(CmdFull *node); 42 | 43 | int ExecCmd(Cmd *node, bool wait); 44 | 45 | int ExecSimpleCmd(SimpleCmd *node, bool wait); 46 | 47 | CmdExprData ExecSimpleCmdWithResult(SimpleCmd *node); 48 | 49 | std::string CmdOutput() const; 50 | 51 | private: 52 | std::string cmd_output_; 53 | }; 54 | 55 | class SimpleCmdExecutor: public Executor { 56 | public: 57 | SimpleCmdExecutor(Executor* parent, SymbolTableStack& symbol_table_stack) 58 | : Executor(parent, symbol_table_stack) {} 59 | 60 | std::vector Exec(SimpleCmd *node); 61 | }; 62 | 63 | class SubShellExecutor: public Executor { 64 | public: 65 | SubShellExecutor(Executor* parent, SymbolTableStack& symbol_table_stack) 66 | : Executor(parent, symbol_table_stack) {} 67 | 68 | int Exec(SubShell *node, bool background); 69 | 70 | CmdExprData Exec(SubShell *node); 71 | }; 72 | 73 | class CmdIoRedirectListExecutor: public Executor { 74 | public: 75 | CmdIoRedirectListExecutor(Executor* parent, 76 | SymbolTableStack& symbol_table_stack) 77 | : Executor(parent, symbol_table_stack) {} 78 | 79 | int Exec(CmdIoRedirectList *node, bool wait); 80 | 81 | CmdExprData Exec(CmdIoRedirectList *node); 82 | 83 | int GetInteger(Literal* integer); 84 | 85 | static std::string FileName(Executor *parent, FilePathCmd* file_path, 86 | bool trim = true); 87 | 88 | void PrepareData(Job &job, CmdIoRedirectList *node); 89 | 90 | int Var2Pipe(std::string var); 91 | 92 | int Str2Pipe(const std::string& str); 93 | }; 94 | 95 | class CmdPipeSequenceExecutor: public Executor { 96 | public: 97 | CmdPipeSequenceExecutor(Executor* parent, 98 | SymbolTableStack& symbol_table_stack) 99 | : Executor(parent, symbol_table_stack) {} 100 | 101 | int Exec(CmdPipeSequence *node, bool wait); 102 | CmdExprData Exec(CmdPipeSequence *node); 103 | void PopulateCmd(Job& job, CmdPipeSequence *node); 104 | void AddCommand(Job& job, Cmd *cmd); 105 | }; 106 | 107 | // functions to manipulate command 108 | std::string ResolveCmdExpr(Executor *parent, CmdValueExpr *cmd_expr); 109 | 110 | // execute an expression for array types or simple string type 111 | std::vector ResolveFullTypeCmdExpr(Executor* parent, 112 | CmdValueExpr* cmd_expr); 113 | 114 | std::string ExtractCmdExprFromString(Executor* parent, const std::string& str); 115 | 116 | // functions to manipulate file 117 | int CreateFile(std::string file_name); 118 | int AppendFile(std::string file_name); 119 | int ReadFile(std::string file_name); 120 | 121 | } 122 | } 123 | 124 | #endif // SHPP_CMD_EXECUTOR_H 125 | -------------------------------------------------------------------------------- /src/interpreter/executor.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Alex Silva Torres 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef SHPP_EXECUTOR_H 16 | #define SHPP_EXECUTOR_H 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "ast/ast.h" 24 | #include "objects/obj-type.h" 25 | #include "symbol-table.h" 26 | 27 | namespace shpp { 28 | namespace internal { 29 | 30 | class Executor { 31 | public: 32 | enum class StopFlag { 33 | kGo, 34 | kReturn, 35 | kBreak, 36 | kContinue, 37 | kThrow 38 | }; 39 | 40 | Executor(Executor* parent, SymbolTableStack& symbol_table_stack) 41 | : parent_(parent) 42 | , symbol_table_stack_(symbol_table_stack) 43 | , is_root_(false) {} 44 | 45 | Executor(Executor* parent, SymbolTableStack& symbol_table_stack, bool is_root) 46 | : parent_(parent) 47 | , symbol_table_stack_(symbol_table_stack) 48 | , is_root_(is_root) {} 49 | 50 | virtual void set_stop(StopFlag flag) { 51 | if (parent_ != nullptr) { 52 | parent_->set_stop(flag); 53 | } 54 | } 55 | 56 | Executor* parent() const noexcept { 57 | return parent_; 58 | } 59 | 60 | virtual Executor* GetMainExecutor() { 61 | if (parent_ != nullptr) { 62 | return parent_->GetMainExecutor(); 63 | } 64 | 65 | return nullptr; 66 | } 67 | 68 | SymbolTableStack& symbol_table_stack() { 69 | return symbol_table_stack_; 70 | } 71 | 72 | virtual bool inside_root_scope() { 73 | return false; 74 | } 75 | 76 | protected: 77 | inline bool is_root() const{ 78 | return is_root_; 79 | } 80 | 81 | // this method is used for decide if some statement 82 | // can be called on that moment, for example 83 | // break statement can be called only inside loops 84 | // and switch control 85 | virtual bool inside_loop() { 86 | if (parent_ != nullptr) { 87 | return parent_->inside_loop(); 88 | } 89 | 90 | return false; 91 | } 92 | 93 | // this method is used by switch control flow 94 | // because some statement can be called only 95 | // inside a switch block 96 | virtual bool inside_switch() { 97 | if (parent_ != nullptr) { 98 | return parent_->inside_switch(); 99 | } 100 | 101 | return false; 102 | } 103 | 104 | virtual bool inside_func() { 105 | if (parent_ != nullptr) { 106 | return parent_->inside_func(); 107 | } 108 | 109 | return false; 110 | } 111 | 112 | virtual Executor* GetBlockParent() { 113 | if (parent_ != nullptr) { 114 | return parent_->GetBlockParent(); 115 | } 116 | 117 | return nullptr; 118 | } 119 | 120 | private: 121 | Executor* parent_; 122 | SymbolTableStack& symbol_table_stack_; 123 | bool is_root_; 124 | }; 125 | 126 | } 127 | } 128 | 129 | #endif // SHPP_EXECUTOR_H 130 | -------------------------------------------------------------------------------- /src/interpreter/interpreter.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Alex Silva Torres 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef SHPP_INTERPRETER_H 16 | #define SHPP_INTERPRETER_H 17 | 18 | #include 19 | #include 20 | 21 | #include "symbol-table.h" 22 | 23 | namespace shpp { 24 | namespace internal { 25 | 26 | class Executor; 27 | 28 | class ScriptStream { 29 | public: 30 | ScriptStream(const std::string& filename) 31 | : filename_(filename), fs_(filename) {} 32 | 33 | ScriptStream(const std::string& filename, std::ifstream&& fs) 34 | : filename_(filename), fs_(std::move(fs)) {} 35 | 36 | std::ifstream& fs() { return fs_; } 37 | const std::string& filename() const { return filename_; } 38 | bool IsOpen() const { return fs_.is_open(); } 39 | private: 40 | std::string filename_; 41 | std::ifstream fs_; 42 | }; 43 | 44 | class Interpreter { 45 | public: 46 | Interpreter(bool main = false); 47 | 48 | ~Interpreter() = default; 49 | 50 | inline SymbolTableStack& SymTableStack() { 51 | return symbol_table_stack_; 52 | } 53 | 54 | void Exec(ScriptStream& file, std::vector&& args = {}); 55 | void ExecInterative(const std::function &func); 56 | 57 | std::shared_ptr LookupSymbol(const std::string& name); 58 | 59 | void RegisterMainModule(const std::string& full_path); 60 | 61 | private: 62 | void RegisterVars(); 63 | void RegisterArgs(std::vector&& args); 64 | void InsertVar(const std::string& name, std::shared_ptr obj); 65 | void RegisterFileVars(const std::string& file); 66 | void RegisterSysVars(); 67 | void ShowErrors(RunTimeError& e, const std::string& code, 68 | const std::string& filename); 69 | 70 | SymbolTablePtr symbol_table_; 71 | SymbolTableStack symbol_table_stack_; 72 | SymbolTableStack sys_symbol_table_stack_; 73 | std::unique_ptr stmt_list_; 74 | bool main_; 75 | std::string full_path_; 76 | }; 77 | 78 | std::vector SplitFileLines(const std::string str_file); 79 | 80 | } 81 | } 82 | 83 | #endif // SHPP_INTERPRETER_H 84 | -------------------------------------------------------------------------------- /src/interpreter/scope-executor.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Alex Silva Torres 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "scope-executor.h" 16 | 17 | #include "stmt-executor.h" 18 | #include "utils/scope-exit.h" 19 | 20 | namespace shpp { 21 | namespace internal { 22 | 23 | void ScopeExecutor::PushDeferStmt(std::tuple s) { 24 | defer_stack_.push(s); 25 | } 26 | 27 | Executor* ScopeExecutor::GetMainExecutor() { 28 | if (main_exec_) { 29 | return this; 30 | } else { 31 | if (parent() != nullptr) { 32 | return parent()->GetMainExecutor(); 33 | } 34 | 35 | return nullptr; 36 | } 37 | } 38 | 39 | void ScopeExecutor::ExecuteDeferStack() { 40 | executed_defer_ = true; 41 | 42 | while (defer_stack_.size() > 0) { 43 | StmtExecutor stmt_exec(this, std::get<1>(defer_stack_.top())); 44 | stmt_exec.Exec(std::get<0>(defer_stack_.top())); 45 | defer_stack_.pop(); 46 | } 47 | } 48 | 49 | void RootExecutor::Exec(AstNode* node) { 50 | // scope exit case an excpetion thrown 51 | auto cleanup = MakeScopeExit([&]() { 52 | // remove the scope 53 | ExecuteDeferStack(); 54 | }); 55 | IgnoreUnused(cleanup); 56 | 57 | // say to StmtListExecutor execute the list of stmt from root scope 58 | StmtListExecutor executor(true, this, symbol_table_stack()); 59 | executor.Exec(node); 60 | } 61 | 62 | void BlockExecutor::Exec(AstNode* node) { 63 | Block* block_node = static_cast(node); 64 | 65 | StmtListExecutor executor(this, symbol_table_stack()); 66 | executor.Exec(block_node->stmt_list()); 67 | } 68 | 69 | void BlockExecutor::set_stop(StopFlag flag) { 70 | if (parent() == nullptr) { 71 | return; 72 | } 73 | 74 | parent()->set_stop(flag); 75 | } 76 | 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/interpreter/scope-executor.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Alex Silva Torres 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef SHPP_SCOPE_EXECUTOR_H 16 | #define SHPP_SCOPE_EXECUTOR_H 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "ast/ast.h" 24 | #include "executor.h" 25 | 26 | namespace shpp { 27 | namespace internal { 28 | 29 | class ScopeExecutor: public Executor { 30 | public: 31 | ScopeExecutor(Executor* parent, SymbolTableStack& symbol_table_stack, 32 | bool is_root, bool main_exec) 33 | : Executor(parent, symbol_table_stack, is_root) 34 | , main_exec_(main_exec) 35 | , executed_defer_(false) {} 36 | 37 | void PushDeferStmt(std::tuple s); 38 | 39 | void ExecuteDeferStack(); 40 | 41 | ~ScopeExecutor() = default; 42 | 43 | protected: 44 | Executor* GetMainExecutor() override; 45 | 46 | Executor* GetBlockParent() override { 47 | return this; 48 | } 49 | 50 | private: 51 | bool main_exec_; 52 | bool executed_defer_; 53 | std::stack> defer_stack_; 54 | }; 55 | 56 | class RootExecutor: public ScopeExecutor { 57 | public: 58 | // the last parameter on Executor constructor means this is the 59 | // root executor 60 | RootExecutor(SymbolTableStack& symbol_table_stack) 61 | : ScopeExecutor(nullptr, symbol_table_stack, true, true) {} 62 | 63 | void Exec(AstNode* node); 64 | 65 | void set_stop(StopFlag /*flag*/) override {} 66 | 67 | bool inside_loop() override { 68 | return false; 69 | } 70 | 71 | bool inside_switch() override { 72 | return false; 73 | } 74 | 75 | bool inside_func() override { 76 | return false; 77 | } 78 | 79 | bool inside_root_scope() override { 80 | return true; 81 | } 82 | 83 | protected: 84 | Executor* GetMainExecutor() override { 85 | return this; 86 | } 87 | }; 88 | 89 | class BlockExecutor: public ScopeExecutor { 90 | public: 91 | // the last parameter on Executor constructor means this is NOT the 92 | // root executor 93 | BlockExecutor(Executor* parent, SymbolTableStack& symbol_table_stack, 94 | bool main_exec = false) 95 | : ScopeExecutor(parent, symbol_table_stack, false, main_exec) {} 96 | 97 | void Exec(AstNode* node); 98 | 99 | void set_stop(StopFlag flag) override; 100 | }; 101 | 102 | } 103 | } 104 | 105 | #endif // SHPP_SCOPE_EXECUTOR_H 106 | -------------------------------------------------------------------------------- /src/modules/env.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Alex Silva Torres 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "env.h" 16 | 17 | #include 18 | 19 | #include "utils/check.h" 20 | 21 | namespace shpp { 22 | namespace internal { 23 | namespace module { 24 | namespace env { 25 | 26 | ObjectPtr SetFunc::Call(Executor*, Args&& params, KWArgs&&) { 27 | SHPP_FUNC_CHECK_NUM_PARAMS(params, 2, set) 28 | SHPP_FUNC_CHECK_PARAM_TYPE(params[0], var, STRING) 29 | SHPP_FUNC_CHECK_PARAM_TYPE(params[1], value, STRING) 30 | 31 | const std::string& var = static_cast(*params[0]).value(); 32 | const std::string& value = static_cast(*params[1]).value(); 33 | 34 | // the last parameter set overwrite flag 35 | int r = setenv(var.c_str(), value.c_str(), 1); 36 | 37 | return obj_factory_.NewBool(r == 0? true: false); 38 | } 39 | 40 | ObjectPtr GetFunc::Call(Executor*, Args&& params, KWArgs&&) { 41 | SHPP_FUNC_CHECK_NUM_PARAMS(params, 1, get) 42 | SHPP_FUNC_CHECK_PARAM_TYPE(params[0], var, STRING) 43 | 44 | const std::string& var = static_cast(*params[0]).value(); 45 | 46 | // the last parameter set overwrite flag 47 | char *r = getenv(var.c_str()); 48 | std::string str_var; 49 | 50 | if (r != nullptr) { 51 | str_var = r; 52 | } 53 | 54 | return obj_factory_.NewString(str_var); 55 | } 56 | 57 | ObjectPtr ExistsFunc::Call(Executor*, Args&& params, KWArgs&&) { 58 | SHPP_FUNC_CHECK_NUM_PARAMS(params, 1, get) 59 | SHPP_FUNC_CHECK_PARAM_TYPE(params[0], var, STRING) 60 | 61 | const std::string& var = static_cast(*params[0]).value(); 62 | 63 | // the last parameter set overwrite flag 64 | char *r = getenv(var.c_str()); 65 | 66 | bool v = false; 67 | 68 | if (r != nullptr) { 69 | v = true; 70 | } 71 | 72 | return obj_factory_.NewBool(v); 73 | } 74 | 75 | ObjectPtr AppendFunc::Call(Executor*, Args&& params, KWArgs&&) { 76 | SHPP_FUNC_CHECK_NUM_PARAMS(params, 2, append) 77 | SHPP_FUNC_CHECK_PARAM_TYPE(params[0], var, STRING) 78 | SHPP_FUNC_CHECK_PARAM_TYPE(params[1], value, STRING) 79 | 80 | const std::string& var = static_cast(*params[0]).value(); 81 | const std::string& value = static_cast(*params[1]).value(); 82 | 83 | // the last parameter set overwrite flag 84 | char *str_ret = getenv(var.c_str()); 85 | std::string str_value; 86 | 87 | if (str_ret != nullptr) { 88 | str_value = str_ret; 89 | } 90 | 91 | str_value += value; 92 | 93 | // the last parameter set overwrite flag 94 | int r = setenv(var.c_str(), str_value.c_str(), 1); 95 | 96 | return obj_factory_.NewBool(r == 0? true: false); 97 | } 98 | 99 | ObjectPtr UnsetFunc::Call(Executor*, Args&& params, KWArgs&&) { 100 | SHPP_FUNC_CHECK_NUM_PARAMS(params, 1, get) 101 | SHPP_FUNC_CHECK_PARAM_TYPE(params[0], var, STRING) 102 | 103 | const std::string& var = static_cast(*params[0]).value(); 104 | 105 | // the last parameter set overwrite flag 106 | int r = unsetenv(var.c_str()); 107 | 108 | return obj_factory_.NewBool(r == 0? true: false); 109 | } 110 | 111 | } 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/modules/env.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Alex Silva Torres 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef SHPP_ENV_FUNCS_H 16 | #define SHPP_ENV_FUNCS_H 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "objects/object-factory.h" 25 | 26 | namespace shpp { 27 | namespace internal { 28 | namespace module { 29 | namespace env { 30 | 31 | class SetFunc: public FuncObject { 32 | public: 33 | SetFunc(ObjectPtr obj_type, SymbolTableStack&& sym_table) 34 | : FuncObject(obj_type, std::move(sym_table)) 35 | , obj_factory_(symbol_table_stack()) {} 36 | 37 | ObjectPtr Call(Executor* /*parent*/, Args&& params, KWArgs&&); 38 | 39 | private: 40 | ObjectFactory obj_factory_; 41 | }; 42 | 43 | class GetFunc: public FuncObject { 44 | public: 45 | GetFunc(ObjectPtr obj_type, SymbolTableStack&& sym_table) 46 | : FuncObject(obj_type, std::move(sym_table)) 47 | , obj_factory_(symbol_table_stack()) {} 48 | 49 | ObjectPtr Call(Executor* /*parent*/, Args&& params, KWArgs&&); 50 | 51 | private: 52 | ObjectFactory obj_factory_; 53 | }; 54 | 55 | class ExistsFunc: public FuncObject { 56 | public: 57 | ExistsFunc(ObjectPtr obj_type, SymbolTableStack&& sym_table) 58 | : FuncObject(obj_type, std::move(sym_table)) 59 | , obj_factory_(symbol_table_stack()) {} 60 | 61 | ObjectPtr Call(Executor* /*parent*/, Args&& params, KWArgs&&); 62 | 63 | private: 64 | ObjectFactory obj_factory_; 65 | }; 66 | 67 | class AppendFunc: public FuncObject { 68 | public: 69 | AppendFunc(ObjectPtr obj_type, SymbolTableStack&& sym_table) 70 | : FuncObject(obj_type, std::move(sym_table)) 71 | , obj_factory_(symbol_table_stack()) {} 72 | 73 | ObjectPtr Call(Executor* /*parent*/, Args&& params, KWArgs&&); 74 | 75 | private: 76 | ObjectFactory obj_factory_; 77 | }; 78 | 79 | class UnsetFunc: public FuncObject { 80 | public: 81 | UnsetFunc(ObjectPtr obj_type, SymbolTableStack&& sym_table) 82 | : FuncObject(obj_type, std::move(sym_table)) 83 | , obj_factory_(symbol_table_stack()) {} 84 | 85 | ObjectPtr Call(Executor* /*parent*/, Args&& params, KWArgs&&); 86 | 87 | private: 88 | ObjectFactory obj_factory_; 89 | }; 90 | 91 | inline void RegisterModule(SymbolTableStack& sym_table) { 92 | ModuleCustonObject::MemberTable table = { 93 | {"set", ObjectMethod(sym_table)}, 94 | {"get", ObjectMethod(sym_table)}, 95 | {"append", ObjectMethod(sym_table)}, 96 | {"exists", ObjectMethod(sym_table)}, 97 | {"unset", ObjectMethod(sym_table)} 98 | }; 99 | 100 | ObjectFactory obj_factory(sym_table); 101 | ObjectPtr obj_module = obj_factory.NewModule("env", std::move(table)); 102 | SymbolAttr symbol(obj_module, true); 103 | sym_table.InsertSysEntry("env", std::move(symbol)); 104 | } 105 | 106 | } 107 | } 108 | } 109 | } 110 | 111 | #endif // SHPP_ENV_FUNCS_H 112 | -------------------------------------------------------------------------------- /src/modules/std-cmds.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Alex Silva Torres 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "std-cmds.h" 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | #include 23 | 24 | #include "objects/obj-type.h" 25 | 26 | namespace shpp { 27 | namespace internal { 28 | namespace cmds { 29 | namespace stdf { 30 | 31 | void CdCmd::Exec(Executor* /*parent*/, std::vector&& args) { 32 | int stdout, stderr, stdin; 33 | 34 | std::tie(stdout, stderr, stdin) = GetStdFd(); 35 | 36 | if (args.size() < 2) { 37 | struct passwd *pw = getpwuid(getuid()); 38 | const char *homedir = pw->pw_dir; 39 | 40 | if (chdir(homedir) < 0) { 41 | std::stringstream ss; 42 | ss << "cd: " << homedir << ": " << strerror(errno) << "\n"; 43 | write(stderr, ss.str().c_str(), ss.str().length()); 44 | SetStatus(-1); 45 | return; 46 | } 47 | 48 | SetStatus(0); 49 | return; 50 | } 51 | 52 | if (chdir(args[1].c_str()) < 0) { 53 | std::stringstream ss; 54 | ss << "cd: " << args[1] << ": " << strerror(errno) << "\n"; 55 | write(stderr, ss.str().c_str(), ss.str().length()); 56 | SetStatus(-1); 57 | return; 58 | } 59 | 60 | SetStatus(0); 61 | return; 62 | } 63 | 64 | void ExitCmd::Exec(Executor* /*parent*/, std::vector&& args) { 65 | if (args.size() < 2) { 66 | std::exit(0); 67 | } 68 | 69 | int status = 0; 70 | 71 | try { 72 | status = std::stoi(args[1]); 73 | } catch (std::invalid_argument&) { 74 | std::exit(-1); 75 | } 76 | 77 | std::exit(status); 78 | } 79 | 80 | } 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/modules/std-cmds.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Alex Silva Torres 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef SHPP_STD_CMDS_H 16 | #define SHPP_STD_CMDS_H 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "cmd-entry.h" 25 | 26 | namespace shpp { 27 | namespace internal { 28 | namespace cmds { 29 | namespace stdf { 30 | 31 | class CdCmd: public CmdInEntry { 32 | public: 33 | CdCmd(const SymbolTableStack& symbol_table) 34 | : CmdInEntry(symbol_table) {} 35 | 36 | void Exec(Executor* /*parent*/, std::vector&& args) override; 37 | }; 38 | 39 | class ExitCmd: public CmdInEntry { 40 | public: 41 | ExitCmd(const SymbolTableStack& symbol_table) 42 | : CmdInEntry(symbol_table) {} 43 | 44 | void Exec(Executor* /*parent*/, std::vector&& args) override; 45 | }; 46 | 47 | inline void RegisterCmds(SymbolTableStack& sym_table) { 48 | CmdSet("cd", sym_table); 49 | CmdSet("exit", sym_table); 50 | } 51 | 52 | } 53 | } 54 | } 55 | } 56 | 57 | #endif // SHPP_STD_CMDS_H 58 | 59 | 60 | -------------------------------------------------------------------------------- /src/modules/sys.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Alex Silva Torres 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "sys.h" 16 | 17 | #include 18 | 19 | #include "utils/check.h" 20 | 21 | namespace shpp { 22 | namespace internal { 23 | namespace module { 24 | namespace sys { 25 | 26 | SysModule::SysModule(ObjectPtr obj_type, SymbolTableStack&& sym_table) 27 | : Object(ObjectType::MODULE, obj_type, std::move(sym_table)) 28 | , obj_factory_(symbol_table_stack()) { 29 | symbol_table_stack().NewTable(); 30 | } 31 | 32 | std::shared_ptr SysModule::Attr(std::shared_ptr/*self*/, 33 | const std::string& name) { 34 | auto obj = symbol_table_stack().Lookup(name, false).Ref(); 35 | return PassVar(obj, symbol_table_stack()); 36 | } 37 | 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/modules/sys.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Alex Silva Torres 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef SHPP_SYS_MODULE_H 16 | #define SHPP_SYS_MODULE_H 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "objects/object-factory.h" 25 | 26 | namespace shpp { 27 | namespace internal { 28 | namespace module { 29 | namespace sys { 30 | 31 | class SysModule: public Object { 32 | public: 33 | SysModule(ObjectPtr obj_type, SymbolTableStack&& sym_table); 34 | 35 | virtual ~SysModule() {} 36 | 37 | ObjectPtr Attr(std::shared_ptr/*self*/, 38 | const std::string& name) override; 39 | 40 | std::string Print() override { 41 | return std::string("[moule: sys]\n"); 42 | } 43 | 44 | private: 45 | void InsertAttrFunc(); 46 | ObjectPtr Argv(); 47 | ObjectPtr Version(); 48 | 49 | std::unordered_map> attrs_; 50 | ObjectFactory obj_factory_; 51 | }; 52 | 53 | 54 | inline void RegisterModule(SymbolTableStack& sym_table) { 55 | // create a symbol table on the start 56 | SymbolTableStack table_stack; 57 | auto main_tab = sym_table.MainTable(); 58 | table_stack.Push(main_tab, true); 59 | 60 | auto obj_type = sym_table.LookupSys("module").SharedAccess(); 61 | ObjectPtr obj_module = ObjectPtr(new SysModule(obj_type, 62 | std::move(table_stack))); 63 | SymbolAttr symbol(obj_module, true); 64 | sym_table.InsertSysEntry("sys", std::move(symbol)); 65 | } 66 | 67 | } 68 | } 69 | } 70 | } 71 | 72 | #endif // SHPP_SYS_MODULE_H 73 | -------------------------------------------------------------------------------- /src/objects/cmd-object.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Alex Silva Torres 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef SHPP_CMD_OBJECT_H 16 | #define SHPP_CMD_OBJECT_H 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "abstract-obj.h" 24 | #include "ast/ast.h" 25 | #include "func-object.h" 26 | #include "interpreter/symbol-table.h" 27 | #include "obj-type.h" 28 | #include "run_time_error.h" 29 | 30 | namespace shpp { 31 | namespace internal { 32 | 33 | class CmdIterObject : public BaseIter { 34 | public: 35 | CmdIterObject(std::string delim, int outerr, ObjectPtr cmd_obj, 36 | ObjectPtr obj_type, SymbolTableStack&& sym_table); 37 | 38 | virtual ~CmdIterObject() {} 39 | 40 | ObjectPtr Next() override; 41 | 42 | ObjectPtr HasNext() override; 43 | 44 | std::string Print() override { return std::string("[cmd_iter]"); } 45 | 46 | private: 47 | size_t pos_; 48 | ObjectPtr cmd_obj_; 49 | std::vector str_split_; 50 | }; 51 | 52 | class CmdObject : public Object { 53 | public: 54 | CmdObject(int status, std::string&& str_stdout, std::string&& str_stderr, 55 | ObjectPtr obj_type, SymbolTableStack&& sym_table) 56 | : Object(ObjectType::CMD, obj_type, std::move(sym_table)), 57 | status_(status), 58 | str_stdout_(std::move(str_stdout)), 59 | str_stderr_(std::move(str_stderr)), 60 | delim_("\n") { 61 | boost::trim(str_stdout_); 62 | boost::trim(str_stderr_); 63 | } 64 | 65 | virtual ~CmdObject() {} 66 | 67 | ObjectPtr ObjIter(ObjectPtr obj) override; 68 | 69 | ObjectPtr ObjArray() override; 70 | 71 | ObjectPtr In(ObjectPtr obj) override; 72 | 73 | const std::string& str_stdout() const noexcept { return str_stdout_; } 74 | 75 | const std::string& str_stderr() const noexcept { return str_stderr_; } 76 | 77 | ObjectPtr ObjString() override; 78 | 79 | ObjectPtr ObjCmd() override; 80 | 81 | ObjectPtr ObjBool() override; 82 | 83 | ObjectPtr Not() override; 84 | 85 | bool Compare(ObjectPtr obj); 86 | 87 | ObjectPtr Equal(ObjectPtr obj) override; 88 | 89 | ObjectPtr NotEqual(ObjectPtr obj) override; 90 | 91 | std::shared_ptr Attr(std::shared_ptr self, 92 | const std::string& name) override; 93 | 94 | std::string Print() override { return str_stdout_; } 95 | 96 | long int Len() override { return str_stdout_.size(); } 97 | 98 | inline void set_delim(const std::string& delim) { delim_ = delim; } 99 | 100 | const std::string delim() { return delim_; } 101 | 102 | int status() const { return status_; } 103 | 104 | private: 105 | int status_; 106 | std::string str_stdout_; 107 | std::string str_stderr_; 108 | std::string delim_; 109 | }; 110 | 111 | class CmdType : public TypeObject { 112 | public: 113 | CmdType(ObjectPtr obj_type, SymbolTableStack&& sym_table); 114 | 115 | virtual ~CmdType() {} 116 | 117 | ObjectPtr Constructor(Executor*, Args&&, KWArgs&&) override; 118 | }; 119 | 120 | class CmdOutFunc : public FuncObject { 121 | public: 122 | CmdOutFunc(ObjectPtr obj_type, SymbolTableStack&& sym_table) 123 | : FuncObject(obj_type, std::move(sym_table)) {} 124 | 125 | ObjectPtr Call(Executor* /*parent*/, Args&& params, KWArgs&&); 126 | }; 127 | 128 | class CmdErrFunc : public FuncObject { 129 | public: 130 | CmdErrFunc(ObjectPtr obj_type, SymbolTableStack&& sym_table) 131 | : FuncObject(obj_type, std::move(sym_table)) {} 132 | 133 | ObjectPtr Call(Executor* /*parent*/, Args&& params, KWArgs&&); 134 | }; 135 | 136 | class CmdDelimFunc : public FuncObject { 137 | public: 138 | CmdDelimFunc(ObjectPtr obj_type, SymbolTableStack&& sym_table) 139 | : FuncObject(obj_type, std::move(sym_table)) {} 140 | 141 | ObjectPtr Call(Executor* /*parent*/, Args&& params, KWArgs&&); 142 | }; 143 | 144 | class CmdStatusFunc : public FuncObject { 145 | public: 146 | CmdStatusFunc(ObjectPtr obj_type, SymbolTableStack&& sym_table) 147 | : FuncObject(obj_type, std::move(sym_table)) {} 148 | 149 | ObjectPtr Call(Executor* /*parent*/, Args&& params, KWArgs&&); 150 | }; 151 | 152 | } // namespace internal 153 | } // namespace shpp 154 | 155 | #endif // SHPP_CMD_OBJECT_H 156 | -------------------------------------------------------------------------------- /src/objects/glob-object.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Alex Silva Torres 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef SHPP_GLOB_OBJECT_H 16 | #define SHPP_GLOB_OBJECT_H 17 | 18 | #include 19 | #include 20 | 21 | #include "abstract-obj.h" 22 | #include "ast/ast.h" 23 | #include "func-object.h" 24 | #include "interpreter/symbol-table.h" 25 | #include "obj-type.h" 26 | #include "run_time_error.h" 27 | 28 | namespace shpp { 29 | namespace internal { 30 | 31 | class GlobIterObject : public BaseIter { 32 | public: 33 | GlobIterObject(ObjectPtr glob_obj, ObjectPtr obj_type, 34 | SymbolTableStack&& sym_table); 35 | 36 | virtual ~GlobIterObject() {} 37 | 38 | ObjectPtr Next() override; 39 | 40 | ObjectPtr HasNext() override; 41 | 42 | std::string Print() override { return std::string("[glob_iter]"); } 43 | 44 | private: 45 | size_t pos_; 46 | ObjectPtr glob_obj_; 47 | }; 48 | 49 | class GlobObject : public Object { 50 | public: 51 | GlobObject(const std::string& str_glob_expr, bool recursive, 52 | ObjectPtr obj_type, SymbolTableStack&& sym_table); 53 | 54 | virtual ~GlobObject() {} 55 | 56 | ObjectPtr ObjIter(ObjectPtr obj) override; 57 | 58 | ObjectPtr ObjArray() override; 59 | 60 | ObjectPtr In(ObjectPtr obj) override; 61 | 62 | ObjectPtr ObjBool() override; 63 | 64 | ObjectPtr Not() override; 65 | 66 | std::shared_ptr Attr(std::shared_ptr self, 67 | const std::string& name) override; 68 | 69 | std::string Print() override; 70 | 71 | long int Len() override { return glob_result_vec_.size(); } 72 | 73 | ObjectPtr GetGlobItem(size_t pos) const noexcept { 74 | return glob_result_vec_[pos]; 75 | } 76 | 77 | std::vector& value() noexcept { return glob_result_vec_; } 78 | 79 | const std::vector& value() const noexcept { 80 | return glob_result_vec_; 81 | } 82 | 83 | private: 84 | std::string str_glob_expr_; 85 | bool full_; 86 | std::vector glob_result_vec_; 87 | }; 88 | 89 | class GlobType : public TypeObject { 90 | public: 91 | GlobType(ObjectPtr obj_type, SymbolTableStack&& sym_table); 92 | 93 | virtual ObjectPtr Constructor(Executor* /*parent*/, Args&& params, KWArgs&&); 94 | 95 | virtual ~GlobType() {} 96 | }; 97 | 98 | class GlobSearchFunc : public FuncObject { 99 | public: 100 | GlobSearchFunc(ObjectPtr obj_type, SymbolTableStack&& sym_table) 101 | : FuncObject(obj_type, std::move(sym_table)) {} 102 | 103 | ObjectPtr Call(Executor* /*parent*/, Args&& params, KWArgs&&); 104 | }; 105 | 106 | } // namespace internal 107 | } // namespace shpp 108 | 109 | #endif -------------------------------------------------------------------------------- /src/objects/object-factory.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Alex Silva Torres 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "object-factory.h" 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | namespace shpp { 22 | namespace internal { 23 | 24 | 25 | 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/objects/regex.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Alex Silva Torres 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "regex.h" 16 | 17 | #include "obj-type.h" 18 | #include "object-factory.h" 19 | #include "utils/check.h" 20 | 21 | namespace shpp { 22 | namespace internal { 23 | 24 | RegexObject::RegexObject(const std::string& str_expr, ObjectPtr obj_type, 25 | SymbolTableStack&& sym_table) 26 | try 27 | : Object(ObjectType::REGEX, obj_type, std::move(sym_table)) 28 | , str_expr_(str_expr) 29 | , re_(str_expr_) { 30 | } catch (std::regex_error& e) { 31 | throw RunTimeError(RunTimeError::ErrorCode::REGEX, 32 | boost::format("%1%")% e.what()); 33 | } 34 | 35 | ObjectPtr RegexObject::ObjString() { 36 | ObjectFactory obj_factory(symbol_table_stack()); 37 | return obj_factory.NewString(str_expr_); 38 | } 39 | 40 | std::vector> RegexObject::Search( 41 | const std::string& str_search) { 42 | std::string s = str_search; 43 | std::smatch m; 44 | std::vector> vet_ret; 45 | 46 | while (std::regex_search(s, m, re_)) { 47 | std::vector group; 48 | for (auto& x:m) { 49 | group.push_back(x); 50 | } 51 | 52 | vet_ret.push_back(std::move(group)); 53 | s = m.suffix().str(); 54 | } 55 | 56 | return vet_ret; 57 | } 58 | 59 | bool RegexObject::Match(const std::string& str) { 60 | return std::regex_match (str, re_); 61 | } 62 | 63 | ObjectPtr RegexObject::Attr(std::shared_ptr self, 64 | const std::string& name) { 65 | ObjectPtr obj_type = ObjType(); 66 | return static_cast(*obj_type).CallObject(name, self); 67 | } 68 | 69 | RegexType::RegexType(ObjectPtr obj_type, SymbolTableStack&& sym_table) 70 | : TypeObject("regex", obj_type, std::move(sym_table)) { 71 | RegisterMethod("match", symbol_table_stack(), *this); 72 | RegisterMethod("search", symbol_table_stack(), *this); 73 | } 74 | 75 | ObjectPtr RegexType::Constructor(Executor*, Args&& params, KWArgs&&) { 76 | SHPP_FUNC_CHECK_NUM_PARAMS(params, 1, regex) 77 | SHPP_FUNC_CHECK_PARAM_TYPE(params[0], regex, STRING) 78 | 79 | const std::string& str = static_cast(*params[0]).value(); 80 | 81 | ObjectFactory obj_factory(symbol_table_stack()); 82 | return obj_factory.NewRegex(str); 83 | } 84 | 85 | ObjectPtr RegexMatchFunc::Call(Executor*, Args&& params, KWArgs&&) { 86 | SHPP_FUNC_CHECK_NUM_PARAMS(params, 2, match) 87 | SHPP_FUNC_CHECK_PARAM_TYPE(params[1], match, STRING) 88 | 89 | RegexObject& regex_obj = static_cast(*params[0]); 90 | const std::string& str = static_cast(*params[1]).value(); 91 | 92 | ObjectFactory obj_factory(symbol_table_stack()); 93 | return obj_factory.NewBool(regex_obj.Match(str)); 94 | } 95 | 96 | ObjectPtr RegexSearchFunc::Call(Executor*, Args&& params, KWArgs&&) { 97 | SHPP_FUNC_CHECK_NUM_PARAMS(params, 2, search) 98 | SHPP_FUNC_CHECK_PARAM_TYPE(params[0], search, REGEX) 99 | SHPP_FUNC_CHECK_PARAM_TYPE(params[1], search, STRING) 100 | 101 | RegexObject& regex_obj = static_cast(*params[0]); 102 | const std::string& str = static_cast(*params[1]).value(); 103 | 104 | auto vec = regex_obj.Search(str); 105 | ObjectFactory obj_factory(symbol_table_stack()); 106 | 107 | std::vector vec_obj; 108 | 109 | for (auto& row: vec) { 110 | std::vector row_obj; 111 | for (auto& elem: row) { 112 | ObjectPtr obj_str = obj_factory.NewString(elem); 113 | row_obj.push_back(obj_str); 114 | } 115 | 116 | ObjectPtr row_vec_obj = obj_factory.NewArray(std::move(row_obj)); 117 | vec_obj.push_back(row_vec_obj); 118 | } 119 | 120 | return obj_factory.NewArray(std::move(vec_obj)); 121 | } 122 | 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/objects/regex.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Alex Silva Torres 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef SHPP_REGEX_OBJECT_H 16 | #define SHPP_REGEX_OBJECT_H 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "run_time_error.h" 24 | #include "ast/ast.h" 25 | #include "interpreter/symbol-table.h" 26 | #include "abstract-obj.h" 27 | #include "obj-type.h" 28 | #include "func-object.h" 29 | 30 | namespace shpp { 31 | namespace internal { 32 | 33 | class RegexObject: public Object { 34 | public: 35 | RegexObject(const std::string& str_expr, ObjectPtr obj_type, 36 | SymbolTableStack&& sym_table); 37 | 38 | virtual ~RegexObject() {} 39 | 40 | ObjectPtr ObjString() override; 41 | 42 | ObjectPtr Attr(std::shared_ptr self, 43 | const std::string& name) override; 44 | 45 | std::vector> Search(const std::string& str_search); 46 | 47 | bool Match(const std::string& str); 48 | 49 | std::string Print() override { 50 | return "regex<" + str_expr_ + ">"; 51 | } 52 | 53 | long int Len() override { 54 | return str_expr_.size(); 55 | } 56 | 57 | private: 58 | std::string str_expr_; 59 | std::regex re_; 60 | }; 61 | 62 | class RegexType: public TypeObject { 63 | public: 64 | RegexType(ObjectPtr obj_type, SymbolTableStack&& sym_table); 65 | 66 | virtual ~RegexType() {} 67 | 68 | ObjectPtr Constructor(Executor*, Args&& params, KWArgs&&) override; 69 | }; 70 | 71 | class RegexMatchFunc: public FuncObject { 72 | public: 73 | RegexMatchFunc(ObjectPtr obj_type, SymbolTableStack&& sym_table) 74 | : FuncObject(obj_type, std::move(sym_table)) {} 75 | 76 | ObjectPtr Call(Executor* /*parent*/, Args&& params, KWArgs&&); 77 | }; 78 | 79 | class RegexSearchFunc: public FuncObject { 80 | public: 81 | RegexSearchFunc(ObjectPtr obj_type, SymbolTableStack&& sym_table) 82 | : FuncObject(obj_type, std::move(sym_table)) {} 83 | 84 | ObjectPtr Call(Executor* /*parent*/, Args&& params, KWArgs&&); 85 | }; 86 | 87 | } 88 | } 89 | 90 | #endif // SHPP_REGEX_OBJECT_H 91 | -------------------------------------------------------------------------------- /src/objects/slice-object.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Alex Silva Torres 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "slice-object.h" 16 | 17 | #include "simple-object.h" 18 | 19 | namespace shpp { 20 | namespace internal { 21 | 22 | SliceObject::SliceObject(ObjectPtr obj_start, ObjectPtr obj_end, 23 | ObjectPtr obj_step, ObjectPtr obj_type, 24 | SymbolTableStack&& sym_table) 25 | : Object(ObjectType::SLICE, obj_type, std::move(sym_table)) { 26 | if (obj_start->type() == ObjectType::INT) { 27 | IntObject& int_start = static_cast(*obj_start); 28 | start_ = int_start.value(); 29 | has_start_ = true; 30 | } else if (obj_start->type() == ObjectType::NIL) { 31 | start_ = 0; 32 | has_start_ = false; 33 | } else { 34 | throw RunTimeError(RunTimeError::ErrorCode::INCOMPATIBLE_TYPE, 35 | boost::format("start parameter must be integer")); 36 | } 37 | 38 | if (obj_end->type() == ObjectType::INT) { 39 | IntObject& int_end = static_cast(*obj_end); 40 | end_ = int_end.value(); 41 | has_end_ = true; 42 | } else if (obj_end->type() == ObjectType::NIL) { 43 | end_ = 0; 44 | has_end_ = false; 45 | } else { 46 | throw RunTimeError(RunTimeError::ErrorCode::INCOMPATIBLE_TYPE, 47 | boost::format("end parameter must be integer")); 48 | } 49 | 50 | if (obj_step->type() == ObjectType::INT) { 51 | IntObject& int_step = static_cast(*obj_step); 52 | step_ = int_step.value(); 53 | has_step_ = true; 54 | } else if (obj_step->type() == ObjectType::NIL) { 55 | step_ = 0; 56 | has_step_ = false; 57 | } else { 58 | throw RunTimeError(RunTimeError::ErrorCode::INCOMPATIBLE_TYPE, 59 | boost::format("step parameter must be integer")); 60 | } 61 | } 62 | 63 | bool SliceObject::operator==(const Object& obj) { 64 | if (obj.type() != ObjectType::SLICE) { 65 | return false; 66 | } 67 | 68 | const SliceObject& slice = static_cast(obj); 69 | 70 | bool exp = (start_ == slice.start_) && (end_ == slice.end_) && 71 | (step_ == slice.step_); 72 | 73 | return exp; 74 | } 75 | 76 | std::tuple SliceLogic(const SliceObject& slice, int size) { 77 | int start = 0; 78 | int end = size; 79 | int step = 1; 80 | 81 | if (slice.has_start()) { 82 | start = slice.start(); 83 | } 84 | 85 | if (slice.has_end()) { 86 | end = slice.end(); 87 | } 88 | 89 | if (slice.has_step()) { 90 | step = slice.step(); 91 | } 92 | 93 | if (end < 0) { 94 | end = size - abs(end); 95 | } 96 | 97 | if (start < 0) { 98 | start = size - abs(start); 99 | } 100 | 101 | if (start > size) { 102 | start = size; 103 | } 104 | 105 | if (end > size) { 106 | end = size; 107 | } 108 | 109 | if (start > end) { 110 | start = end; 111 | } 112 | 113 | if (end < 0) { 114 | end = 0; 115 | } 116 | 117 | if (start < 0) { 118 | start = 0; 119 | } 120 | 121 | return std::tuple(start, end, step); 122 | } 123 | 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/objects/slice-object.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Alex Silva Torres 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef SHPP_SLICE_OBJECT_H 16 | #define SHPP_SLICE_OBJECT_H 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "run_time_error.h" 24 | #include "ast/ast.h" 25 | #include "interpreter/symbol-table.h" 26 | #include "abstract-obj.h" 27 | 28 | namespace shpp { 29 | namespace internal { 30 | 31 | class SliceObject: public Object { 32 | public: 33 | SliceObject(ObjectPtr obj_start, ObjectPtr obj_end, ObjectPtr obj_step, 34 | ObjectPtr obj_type, SymbolTableStack&& sym_table); 35 | 36 | SliceObject(const SliceObject& obj) 37 | : Object(obj), start_(obj.start_), end_(obj.end_), step_(obj.step_) {} 38 | 39 | virtual ~SliceObject() {} 40 | 41 | SliceObject& operator=(const SliceObject& obj) { 42 | start_ = obj.start_; 43 | end_ = obj.end_; 44 | step_ = obj.step_; 45 | return *this; 46 | } 47 | 48 | inline int start() const noexcept { return start_; } 49 | 50 | inline int end() const noexcept { return end_; } 51 | 52 | inline int step() const noexcept { return step_; } 53 | 54 | inline bool has_start() const noexcept { return has_start_; } 55 | 56 | inline bool has_end() const noexcept { return has_end_; } 57 | 58 | inline bool has_step() const noexcept { return has_step_; } 59 | 60 | std::size_t Hash() override { 61 | throw RunTimeError(RunTimeError::ErrorCode::INCOMPATIBLE_TYPE, 62 | boost::format("slice object has no hash method")); 63 | } 64 | 65 | bool operator==(const Object& obj) override; 66 | 67 | std::string Print() override { 68 | return std::string("[slice ") + std::to_string(start_ ) + ", " + 69 | std::to_string(end_); 70 | } 71 | 72 | private: 73 | int start_; 74 | int end_; 75 | int step_; 76 | 77 | bool has_start_; 78 | bool has_end_; 79 | bool has_step_; 80 | }; 81 | 82 | // calculates the logic between start end and step for string, array and tuple 83 | // the logic is the same fo all object, this function helps keep the consistence 84 | std::tuple SliceLogic(const SliceObject& slice, int size); 85 | 86 | } 87 | } 88 | 89 | #endif // SHPP_SLICE_OBJECT_H 90 | -------------------------------------------------------------------------------- /src/parser/extract_expr.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Alex Silva Torres 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "extract_expr.h" 16 | 17 | #include "parser.h" 18 | #include "run_time_error.h" 19 | 20 | namespace shpp { 21 | namespace internal { 22 | 23 | ExtractExpr::ExtractExpr(const std::string& src) 24 | : cursor_(0) 25 | , c_(src[0]) 26 | , src_(src) 27 | , start_pos_(-1) 28 | , end_pos_(-1) 29 | , num_open_(0) 30 | , valid_scope_(true) {} 31 | 32 | void ExtractExpr::Extract() { 33 | while (true) { 34 | switch (c_) { 35 | case '\\': 36 | break; 37 | 38 | case '$': { 39 | Advance(); 40 | if (c_ == '{' && start_pos_ == -1) { 41 | start_pos_ = cursor_; 42 | num_open_++; 43 | } 44 | } break; 45 | 46 | case '}': { 47 | if (num_open_ > 1 && valid_scope_) { 48 | num_open_--; 49 | } else if (start_pos_ >= 0 && valid_scope_) { 50 | end_pos_ = cursor_; 51 | return; 52 | } 53 | } break; 54 | 55 | case '{': { 56 | if (num_open_ > 0 && valid_scope_) { 57 | num_open_++; 58 | } 59 | } break; 60 | 61 | case '"': 62 | valid_scope_ = valid_scope_? false:true; 63 | break; 64 | } 65 | 66 | if (HasNextChar()) { 67 | Advance(); 68 | } else { 69 | return; 70 | } 71 | } 72 | } 73 | 74 | ParserResult ParserExpr(const std::string& src) { 75 | Lexer l(src); 76 | TokenStream ts = l.Scanner(); 77 | Parser p(std::move(ts)); 78 | auto res = p.AstGenCmdExpr(); 79 | 80 | if (p.nerrors() == 0) { 81 | return res; 82 | } else { 83 | Message msg = p.Msgs(); 84 | throw RunTimeError(RunTimeError::ErrorCode::PARSER, msg.msg(), 85 | Position{msg.line(), msg.pos()}); 86 | } 87 | } 88 | 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/parser/extract_expr.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Alex Silva Torres 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef SHPP_EXTRACT_EXPRESSION_H 16 | #define SHPP_EXTRACT_EXPRESSION_H 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "token.h" 25 | #include "msg.h" 26 | #include "parser_result.h" 27 | #include "ast/ast.h" 28 | 29 | namespace shpp { 30 | namespace internal { 31 | 32 | class ExtractExpr { 33 | public: 34 | ExtractExpr(const std::string& src); 35 | 36 | void Extract(); 37 | 38 | bool has_expr() { 39 | return start_pos_ != -1 && end_pos_ != -1; 40 | } 41 | 42 | int start_pos() { 43 | return start_pos_; 44 | } 45 | 46 | int end_pos() { 47 | return end_pos_; 48 | } 49 | 50 | private: 51 | inline void Advance() { 52 | c_ = src_[++cursor_]; 53 | } 54 | 55 | bool HasNextChar() { 56 | return cursor_ < (src_.length() - 1); 57 | } 58 | 59 | size_t cursor_; 60 | char c_; 61 | const std::string& src_; 62 | int start_pos_; 63 | int end_pos_; 64 | int num_open_; 65 | bool valid_scope_; 66 | }; 67 | 68 | ParserResult ParserExpr(const std::string& src); 69 | 70 | } 71 | } 72 | 73 | #endif // SHPP_EXTRACT_EXPRESSION_H 74 | -------------------------------------------------------------------------------- /src/parser/lexer.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Alex Silva Torres 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef SHPP_LEXER_H 16 | #define SHPP_LEXER_H 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "msg.h" 25 | #include "token.h" 26 | 27 | namespace shpp { 28 | namespace internal { 29 | 30 | class Lexer { 31 | public: 32 | static const char kEndOfInput = -1; 33 | 34 | Lexer(const std::string& str) 35 | : str_(str), 36 | strlen_(str.length()), 37 | c_(str_[0]), 38 | buffer_cursor_(0), 39 | line_(1), 40 | line_pos_(1), 41 | nerror_(0) {} 42 | 43 | TokenStream Scanner(); 44 | 45 | inline uint NumErrors() noexcept { return nerror_; } 46 | 47 | inline const Messages& GetMessages() const noexcept { return msgs_; } 48 | 49 | inline Messages& GetMessages() noexcept { return msgs_; } 50 | 51 | private: 52 | void SkipSingleLineComment(); 53 | Token ScanString(char string_end_char = '"'); 54 | Token ScanWord(const std::string& prestr = ""); 55 | Token ScanNumber(); 56 | std::string ScanStringEscape(); 57 | char ScanWordEscape(); 58 | char ScanAnsiEscapeCode(); 59 | std::string ScanUnicodeEscapeCode(); 60 | 61 | inline bool IsLetter(char c) { 62 | return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')); 63 | } 64 | 65 | inline bool IsOctalChar(char c) { return (c >= '0' && c < '8'); } 66 | 67 | inline bool IsHexChar(char c) { 68 | return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || 69 | (c >= 'A' && c <= 'F'); 70 | } 71 | 72 | inline bool IsDigit(char c) { return c >= '0' && c <= '9'; } 73 | 74 | inline bool IsIdentifierStart(char c) { return (IsLetter(c) || c == '_'); } 75 | 76 | inline bool IsSpecialChar(char c) { 77 | bool b = c != ' ' && c != '\t' && c != '\n' && c != ')' && c != ';' && 78 | c != '}' && c != '|' && c != '&' && c != '%' && c != kEndOfInput; 79 | return b; 80 | } 81 | 82 | inline void Advance() { 83 | if (buffer_cursor_ == strlen_ - 1) { 84 | c_ = kEndOfInput; 85 | return; 86 | } 87 | 88 | // Check new line and ser cursor position 89 | if (c_ == '\n') { 90 | line_++; 91 | line_pos_ = 0; 92 | } 93 | 94 | c_ = str_[++buffer_cursor_]; 95 | 96 | // Always increment line position, because the first char on line is '1' 97 | line_pos_++; 98 | } 99 | 100 | inline void Back() { --buffer_cursor_; } 101 | 102 | inline char PeekAhead() { 103 | if ((buffer_cursor_ + 1) == strlen_) return kEndOfInput; 104 | 105 | return str_[buffer_cursor_ + 1]; 106 | } 107 | 108 | inline Token GetToken(TokenKind k, char check_blank = 0) { 109 | if (check_blank == 0) { 110 | check_blank = c_; 111 | } 112 | 113 | bool blank_after = check_blank == ' '; 114 | 115 | Token t(k, blank_after, line_, start_pos_); 116 | return t; 117 | } 118 | 119 | // Make a new token, but it doesn't advance the cursor 120 | inline Token GetToken(TokenKind k, Token::Value v, char check_blank = 0) { 121 | if (check_blank == 0) { 122 | check_blank = c_; 123 | } 124 | 125 | bool blank_after = check_blank == ' '; 126 | Token t(k, v, blank_after, line_, start_pos_); 127 | return t; 128 | } 129 | 130 | // Make a new token and advance the cursor 131 | inline Token Select(TokenKind k, char check_blank = 0) { 132 | if (check_blank == 0) { 133 | check_blank = PeekAhead(); 134 | } 135 | 136 | Token t(GetToken(k, check_blank)); 137 | Advance(); 138 | return t; 139 | } 140 | 141 | inline Token Select(TokenKind k, Token::Value v, char check_blank = 0) { 142 | if (check_blank == 0) { 143 | check_blank = PeekAhead(); 144 | } 145 | 146 | Token t(GetToken(k, v, check_blank)); 147 | Advance(); 148 | return t; 149 | } 150 | 151 | Token ScanIdentifier(bool varenv = false); 152 | 153 | void ErrorMsg(const boost::format& fmt_msg); 154 | 155 | std::string str_; 156 | uint strlen_; 157 | char c_; 158 | uint buffer_cursor_; 159 | uint line_; 160 | uint line_pos_; 161 | uint start_pos_; 162 | uint nerror_; 163 | Messages msgs_; 164 | }; 165 | 166 | } // namespace internal 167 | } // namespace shpp 168 | 169 | #endif // SHPP_LEXER_H 170 | -------------------------------------------------------------------------------- /src/parser/parser_literal_string.cc: -------------------------------------------------------------------------------- 1 | #include "parser_literal_string.h" 2 | 3 | namespace shpp { 4 | namespace internal { 5 | 6 | ParserLiteralStringError::ParserLiteralStringError() {} 7 | 8 | void ParserLiteralString::Scanner() { 9 | while (!isEnd()) { 10 | if (interpreter_mode_) { 11 | switch (getChar()) { 12 | case '\'': 13 | case '"': 14 | if (cmd_mode_) { 15 | nextAndPushChar(); 16 | } else { 17 | string_inside_mode_ = !string_inside_mode_; 18 | nextAndPushChar(); 19 | } 20 | break; 21 | 22 | case '\\': 23 | if (string_inside_mode_ || cmd_mode_) { 24 | nextAndPushChar(); 25 | nextAndPushChar(); 26 | } else { 27 | nextAndPushChar(); 28 | } 29 | break; 30 | 31 | case '{': 32 | if (string_inside_mode_ || cmd_mode_) { 33 | nextAndPushChar(); 34 | } else { 35 | nextAndPushChar(); 36 | ++braces_count_; 37 | } 38 | break; 39 | 40 | case '}': 41 | if (string_inside_mode_ || cmd_mode_) { 42 | nextAndPushChar(); 43 | } else { 44 | --braces_count_; 45 | if (braces_count_ == 0) { 46 | interpreter_mode_ = false; 47 | pushStrToken(true); 48 | pos_++; 49 | } else { 50 | nextAndPushChar(); 51 | } 52 | } 53 | break; 54 | 55 | case '$': 56 | nextAndPushChar(); 57 | if (getChar() == '(') { 58 | cmd_mode_ = true; 59 | nextAndPushChar(); 60 | } 61 | break; 62 | 63 | case ')': 64 | if (cmd_mode_) { 65 | cmd_mode_ = !cmd_mode_; 66 | } 67 | nextAndPushChar(); 68 | break; 69 | 70 | default: 71 | nextAndPushChar(); 72 | break; 73 | } 74 | 75 | if (isEnd() && interpreter_mode_) { 76 | throw ParserLiteralStringError(); 77 | } 78 | } else { 79 | parserNotInterpretableStr(); 80 | } 81 | } 82 | } 83 | 84 | void ParserLiteralString::parserNotInterpretableStr() { 85 | switch (getChar()) { 86 | case '\\': 87 | nextAndPushChar(); 88 | nextAndPushChar(); 89 | break; 90 | 91 | case '{': 92 | interpreter_mode_ = true; 93 | ++braces_count_; 94 | pushStrToken(false); 95 | pos_++; 96 | break; 97 | 98 | default: 99 | nextAndPushChar(); 100 | break; 101 | } 102 | 103 | if (isEnd()) { 104 | if (interpreter_mode_) { 105 | throw ParserLiteralStringError(); 106 | } 107 | pushStrToken(false); 108 | } 109 | } 110 | 111 | } // namespace internal 112 | } // namespace shpp -------------------------------------------------------------------------------- /src/parser/parser_literal_string.h: -------------------------------------------------------------------------------- 1 | #ifndef SHPP_PARSER_LITERAL_STRING_H 2 | #define SHPP_PARSER_LITERAL_STRING_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "parser/lexer.h" 9 | 10 | namespace shpp { 11 | namespace internal { 12 | 13 | class ParserLiteralStringError : public std::exception { 14 | public: 15 | ParserLiteralStringError(); 16 | 17 | /** 18 | * @return the error description and the context as a text string. 19 | */ 20 | virtual const char* what() const noexcept { return msg_.c_str(); } 21 | 22 | const std::string& msg() const noexcept { return msg_; } 23 | 24 | std::string msg_; 25 | }; 26 | 27 | class LiteralStringToken { 28 | public: 29 | LiteralStringToken(const std::string str_token, bool interpretable) 30 | : str_token_(str_token), interpretable_(interpretable) {} 31 | 32 | LiteralStringToken(LiteralStringToken&& lit_str_tk) 33 | : str_token_(std::move(lit_str_tk.str_token_)), 34 | interpretable_(lit_str_tk.interpretable_) {} 35 | 36 | const std::string& GetStrToken() const noexcept { return str_token_; } 37 | 38 | std::string& GetStrToken() noexcept { return str_token_; } 39 | 40 | bool IsInterpretable() const noexcept { return interpretable_; } 41 | 42 | private: 43 | std::string str_token_; 44 | bool interpretable_; 45 | }; 46 | 47 | class ParserLiteralString { 48 | public: 49 | ParserLiteralString(const std::string& literal_str) 50 | : literal_str_(literal_str), 51 | interpreter_mode_(false), 52 | braces_count_(0), 53 | string_inside_mode_(false), 54 | escape_mode_(false), 55 | cmd_mode_(false), 56 | pos_(0) {} 57 | void Scanner(); 58 | 59 | std::vector& getStringTokens() { return str_tokens_; } 60 | 61 | const std::vector& getStringTokens() const noexcept { 62 | return str_tokens_; 63 | } 64 | 65 | private: 66 | char getChar() const noexcept { return literal_str_[pos_]; } 67 | 68 | bool isEnd() const noexcept { return pos_ >= literal_str_.length(); } 69 | 70 | void nextAndPushChar() noexcept { 71 | if (!isEnd()) str_token_ += literal_str_[pos_++]; 72 | } 73 | 74 | void pushStrToken(bool isInterpretable) { 75 | LiteralStringToken tk(str_token_, isInterpretable); 76 | str_tokens_.push_back(std::move(tk)); 77 | str_token_ = ""; 78 | } 79 | 80 | void parserNotInterpretableStr(); 81 | 82 | std::string literal_str_; 83 | bool interpreter_mode_; 84 | std::string str_token_; 85 | size_t braces_count_; 86 | bool string_inside_mode_; 87 | bool escape_mode_; 88 | bool cmd_mode_; 89 | size_t pos_; 90 | 91 | std::vector str_tokens_; 92 | }; 93 | 94 | } // namespace internal 95 | } // namespace shpp 96 | #endif -------------------------------------------------------------------------------- /src/parser/parser_result.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Alex Silva Torres 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef SHPP_PARSER_RESULT_H 16 | #define SHPP_PARSER_RESULT_H 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "token.h" 24 | #include "msg.h" 25 | #include "lexer.h" 26 | 27 | namespace shpp { 28 | namespace internal { 29 | 30 | template 31 | class ParserResult { 32 | public: 33 | explicit ParserResult(std::unique_ptr&& uptr) noexcept 34 | : uptr_(std::move(uptr)) {} 35 | 36 | template 37 | explicit ParserResult(std::unique_ptr&& uptr) noexcept 38 | : uptr_(std::move(std::unique_ptr( 39 | static_cast(uptr.release())))) {} 40 | 41 | ParserResult() noexcept: uptr_(nullptr) {} 42 | ParserResult(std::nullptr_t) noexcept : uptr_(nullptr) {} 43 | explicit ParserResult(T* p) noexcept: uptr_(p) {} 44 | ParserResult(ParserResult&& pres) noexcept: uptr_(std::move(pres.uptr_)) {} 45 | ParserResult(const ParserResult&) = delete; 46 | 47 | ~ParserResult() {} 48 | 49 | ParserResult& operator=(ParserResult&& pres) noexcept { 50 | uptr_ = std::move(pres.uptr_); 51 | return *this; 52 | } 53 | 54 | ParserResult& operator=(std::unique_ptr&& uptr) noexcept { 55 | uptr_ = std::move(uptr); 56 | return *this; 57 | } 58 | 59 | ParserResult& operator=(std::nullptr_t) noexcept { 60 | uptr_ = nullptr; 61 | return *this; 62 | } 63 | 64 | ParserResult& operator= (const ParserResult&) = delete; 65 | 66 | template 67 | std::unique_ptr MoveAstNode() noexcept { 68 | auto d = static_cast(uptr_.release()); 69 | return std::unique_ptr(d); 70 | } 71 | 72 | std::unique_ptr MoveAstNode() noexcept { 73 | return std::move(uptr_); 74 | } 75 | 76 | T* NodePtr() noexcept { 77 | return uptr_.get(); 78 | } 79 | 80 | operator bool() const { 81 | if (uptr_.get() != nullptr) { 82 | return true; 83 | } 84 | 85 | return false; 86 | } 87 | 88 | 89 | private: 90 | std::unique_ptr uptr_; 91 | }; 92 | 93 | } 94 | } 95 | 96 | #endif // SETTI_PARSER_RESULT_H 97 | 98 | 99 | -------------------------------------------------------------------------------- /src/utils/check.h: -------------------------------------------------------------------------------- 1 | #define SHPP_FUNC_CHECK_NUM_PARAMS(params, num, func) \ 2 | if (params.size() != num) { \ 3 | throw RunTimeError( \ 4 | RunTimeError::ErrorCode::FUNC_PARAMS, \ 5 | boost::format("%1% takes exactly %2% argument") % #func % num); \ 6 | } 7 | 8 | #define SHPP_FUNC_CHECK_NUM_PARAMS_UNTIL(params, num, func) \ 9 | if (params.size() > num) { \ 10 | throw RunTimeError( \ 11 | RunTimeError::ErrorCode::FUNC_PARAMS, \ 12 | boost::format("%1% takes at most %2% argument") % #func % num); \ 13 | } 14 | 15 | #define SHPP_FUNC_CHECK_NUM_PARAMS_AT_LEAST(params, num, func) \ 16 | if (params.size() < num) { \ 17 | throw RunTimeError( \ 18 | RunTimeError::ErrorCode::FUNC_PARAMS, \ 19 | boost::format("%1% takes at least %2% argument") % #func % num); \ 20 | } 21 | 22 | #define SHPP_FUNC_CHECK_NO_PARAMS(params, func) \ 23 | if (params.size() != 0) { \ 24 | throw RunTimeError(RunTimeError::ErrorCode::FUNC_PARAMS, \ 25 | boost::format("%1% takes no argument") % #func); \ 26 | } 27 | 28 | #define SHPP_FUNC_CHECK_PARAM_TYPE(param, param_name, obj_type) \ 29 | if (param->type() != Object::ObjectType::obj_type) { \ 30 | throw RunTimeError(RunTimeError::ErrorCode::INCOMPATIBLE_TYPE, \ 31 | boost::format("%1% must be " #obj_type) % #param_name); \ 32 | } 33 | -------------------------------------------------------------------------------- /src/utils/dir.h: -------------------------------------------------------------------------------- 1 | #ifndef SHPP_DIR_H 2 | #define SHPP_DIR_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | namespace shpp { 11 | namespace internal { 12 | 13 | std::string GetHome() { 14 | const char *homedir; 15 | 16 | if ((homedir = getenv("HOME")) == NULL) { 17 | homedir = getpwuid(getuid())->pw_dir; 18 | } 19 | 20 | return std::string(homedir); 21 | } 22 | 23 | } 24 | } 25 | 26 | #endif // SHPP_DIR_H 27 | -------------------------------------------------------------------------------- /src/utils/glob.cc: -------------------------------------------------------------------------------- 1 | #include "glob.h" 2 | 3 | #include "glob-cpp/file-glob.h" 4 | 5 | namespace shpp { 6 | namespace internal { 7 | 8 | std::string GetGlobStr(Glob* glob) { 9 | std::vector pieces = glob->children(); 10 | std::string str_glob = ""; 11 | 12 | // assign the glob to string 13 | for (AstNode* piece: pieces) { 14 | CmdPiece* part = static_cast(piece); 15 | 16 | str_glob += part->cmd_str(); 17 | 18 | if (part->blank_after()) { 19 | str_glob += " "; 20 | } 21 | } 22 | 23 | return str_glob; 24 | } 25 | 26 | std::vector ExecGlob(const std::string& glob_str, 27 | SymbolTableStack& symbol_table_stack) { 28 | ObjectFactory obj_factory(symbol_table_stack); 29 | std::vector glob_obj; 30 | 31 | glob::file_glob fglob{glob_str}; 32 | std::vector results = fglob.Exec(); 33 | 34 | for (auto& res : results) { 35 | ObjectPtr path_obj = obj_factory.NewPath(res.path()); 36 | std::vector sub_str_match; 37 | auto& match_res = res.match_result(); 38 | for (auto& token : match_res) { 39 | ObjectPtr str_token_obj = obj_factory.NewString(token); 40 | sub_str_match.push_back(str_token_obj); 41 | } 42 | 43 | ObjectPtr array_token_obj = obj_factory.NewArray(std::move(sub_str_match)); 44 | ObjectPtr tuple_obj = obj_factory.NewTuple( 45 | std::vector{path_obj, array_token_obj}); 46 | glob_obj.push_back(tuple_obj); 47 | } 48 | 49 | return glob_obj; 50 | } 51 | 52 | std::vector ExecGlobSimple(const std::string& glob_str, 53 | SymbolTableStack& symbol_table_stack) { 54 | ObjectFactory obj_factory(symbol_table_stack); 55 | std::vector glob_obj; 56 | 57 | glob::file_glob fglob{glob_str}; 58 | std::vector results = fglob.Exec(); 59 | 60 | for (auto& res : results) { 61 | ObjectPtr path_obj = obj_factory.NewPath(res.path()); 62 | glob_obj.push_back(path_obj); 63 | } 64 | 65 | if (glob_obj.empty()) { 66 | ObjectPtr default_obj = obj_factory.NewPath(glob_str); 67 | glob_obj.push_back(default_obj); 68 | } 69 | 70 | return glob_obj; 71 | } 72 | 73 | std::vector GlobArguments(const std::string& arg) { 74 | std::vector arg_vec; 75 | 76 | glob::file_glob fglob{arg}; 77 | std::vector results = fglob.Exec(); 78 | 79 | for (auto& res : results) { 80 | std::string str_path = res.path().string(); 81 | arg_vec.push_back(std::move(str_path)); 82 | } 83 | 84 | if (arg_vec.empty()) { 85 | arg_vec.push_back(arg); 86 | } 87 | 88 | return arg_vec; 89 | } 90 | 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/utils/glob.h: -------------------------------------------------------------------------------- 1 | #ifndef SHPP_GLOB_H 2 | #define SHPP_GLOB_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "interpreter/symbol-table.h" 13 | #include "ast/ast.h" 14 | #include "objects/obj-type.h" 15 | #include "interpreter/executor.h" 16 | #include "objects/object-factory.h" 17 | 18 | namespace shpp { 19 | namespace internal { 20 | 21 | std::string GetGlobStr(Glob* glob); 22 | 23 | std::vector ExecGlob(const std::string& glob_str, 24 | SymbolTableStack& symbol_table_stack); 25 | 26 | std::vector ExecGlobSimple(const std::string& glob_str, 27 | SymbolTableStack& symbol_table_stack); 28 | 29 | std::vector GlobArguments(const std::string& arg); 30 | 31 | } 32 | } 33 | 34 | #endif // SHPP_GLOB_H 35 | -------------------------------------------------------------------------------- /src/utils/scope-exit.h: -------------------------------------------------------------------------------- 1 | #ifndef SHPP_SCOPE_EXIT_H 2 | #define SHPP_SCOPE_EXIT_H 3 | 4 | namespace shpp { 5 | namespace internal { 6 | 7 | template 8 | class ScopeExit { 9 | public: 10 | ScopeExit(T &&f) : f_{std::move(f)} {} 11 | 12 | ~ScopeExit() { f_(); } 13 | 14 | private: 15 | T f_; 16 | }; 17 | 18 | template 19 | ScopeExit MakeScopeExit(T &&f) { 20 | return ScopeExit{std::move(f)}; 21 | } 22 | 23 | template 24 | void IgnoreUnused (T const &) {} 25 | 26 | } 27 | } 28 | 29 | #endif // SHPP_SCOPE_EXIT_H 30 | 31 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (BUILD_UNIT_TESTS) 2 | add_subdirectory(unit) 3 | endif() 4 | -------------------------------------------------------------------------------- /test/interpreter/cmds/binop_cmd.sh: -------------------------------------------------------------------------------- 1 | # execute simple cmds 2 | # --output:start 3 | # t1 4 | # t2 5 | # --- 6 | # t1 7 | # --- 8 | # t2 9 | # t3 10 | # --- 11 | # t2 12 | # --- 13 | # --output:end 14 | 15 | path_test = "test__3" 16 | mkdir ${path_test} 17 | 18 | touch ${path_test}/t1 19 | touch ${path_test}/t2 20 | touch ${path_test}/t3 21 | 22 | ls ${path_test} | grep t1 && ls ${path_test} | grep t2 23 | echo --- 24 | ls ${path_test} | grep t1 || ls ${path_test} | grep t2 25 | echo --- 26 | ls ${path_test} | grep t2 && ls ${path_test} | grep t3 27 | echo --- 28 | ls ${path_test} | grep t2 || ls ${path_test} | grep t3 29 | echo --- 30 | 31 | for f in $(ls ${path_test}) { 32 | rm ${path_test + "/" + f} 33 | } 34 | 35 | ls ${path_test} 36 | rm -rf ${path_test} 37 | 38 | ls | grep ${path_test} 39 | -------------------------------------------------------------------------------- /test/interpreter/cmds/cmd_decl.sh: -------------------------------------------------------------------------------- 1 | # execute decl command 2 | # --output:start 3 | # gone.txt 4 | # go.txt 5 | # --output:end 6 | 7 | cmd test { 8 | mkdir dcl_teste 9 | touch dcl_teste/go.txt 10 | touch dcl_teste/gone.txt 11 | touch dcl_teste/ok.txt 12 | 13 | ls dcl_teste | grep go 14 | 15 | rm -rf dcl_teste 16 | } 17 | 18 | test 19 | -------------------------------------------------------------------------------- /test/interpreter/cmds/cmd_decl_args.sh: -------------------------------------------------------------------------------- 1 | # execute decl command 2 | # --output:start 3 | # test 4 | # my_file.txt 5 | # other.txt 6 | # --output:end 7 | 8 | cmd test { 9 | mkdir dcl_teste 10 | touch dcl_teste/my_file.txt 11 | touch dcl_teste/other.txt 12 | touch dcl_teste/your.cmd 13 | 14 | echo ${args[0]} 15 | ls dcl_teste | grep ${args[1]} 16 | 17 | rm -rf dcl_teste 18 | } 19 | 20 | test txt 21 | -------------------------------------------------------------------------------- /test/interpreter/cmds/io_cmd.sh: -------------------------------------------------------------------------------- 1 | # execute simple cmds 2 | # --output:start 3 | # alex 4 | # xela 5 | # xael 6 | # --output:end 7 | 8 | path_test = "iotest__3" 9 | mkdir ${path_test} 10 | 11 | ls ${path_test} | grep t1 > ftest__3 12 | cat < ftest__3 13 | 14 | my_var = "alex\nxela\nxael\nlexa" 15 | 16 | cat << ${my_var} | grep alex > ${path_test}/falex__3 17 | cat << ${my_var} | grep xela >> ${path_test}/falex__3 18 | cat <<< my_var | grep xael >> ${path_test}/falex__3 19 | cat ${path_test}/falex__3 20 | 21 | rm -rf ${path_test} 22 | 23 | ls | grep ${path_test} 24 | -------------------------------------------------------------------------------- /test/interpreter/cmds/redirect.sh: -------------------------------------------------------------------------------- 1 | # execute simple cmds 2 | # --output:start 3 | # world 4 | # hello 5 | # 6 | # file: test_out.txt 7 | # hello 8 | # 9 | # file: test_err.txt 10 | # world 11 | # 12 | # file: test_all.txt 13 | # hello 14 | # world 15 | # 16 | # file: test2_out.txt 17 | # hello 18 | # world 19 | # --output:end 20 | 21 | cmd test { 22 | print("hello") 23 | print_err("world") 24 | } 25 | 26 | cmd test2 { 27 | test 1>&2 28 | } 29 | 30 | test > test_out.txt 31 | test 2> test_err.txt 32 | test &> test_all.txt 33 | echo 34 | print("file: test_out.txt") 35 | cat < test_out.txt 36 | echo 37 | 38 | print("file: test_err.txt") 39 | cat < test_err.txt 40 | echo 41 | 42 | print("file: test_all.txt") 43 | cat < test_all.txt 44 | echo 45 | 46 | test2 2> test2_out.txt 47 | print("file: test2_out.txt") 48 | cat < test2_out.txt 49 | 50 | rm test_all.txt test_err.txt test_out.txt test2_out.txt 51 | -------------------------------------------------------------------------------- /test/interpreter/cmds/simple_cmd.sh: -------------------------------------------------------------------------------- 1 | # execute simple cmds 2 | # --output:start 3 | # t1 4 | # t2 5 | # t3 6 | # t1 7 | # t2 8 | # --output:end 9 | 10 | path_test = "test__2" 11 | 12 | if path(path_test).exists() { 13 | rm -r ${path_test} 14 | } 15 | 16 | mkdir ${path_test} 17 | 18 | touch ${path_test}/t1 19 | touch ${path_test}/t2 20 | touch ${path_test}/t3 21 | 22 | ls ${path_test} 23 | ls ${path_test} | grep t1 24 | ls ${path_test} | grep t2 25 | 26 | for f in $(ls ${path_test}) { 27 | rm ${path_test + "/" + f} 28 | } 29 | 30 | ls ${path_test} 31 | rm -r ${path_test} 32 | 33 | ls | grep ${path_test} 34 | -------------------------------------------------------------------------------- /test/interpreter/cmds/status.sh: -------------------------------------------------------------------------------- 1 | # execute simple cmds 2 | # --output:start 3 | # true 4 | # not 5 | t = $(echo ok) 6 | 7 | if t { 8 | print("true") 9 | } 10 | 11 | assert(t.status() == 0, "error") 12 | 13 | t = $(echo ok | cat) 14 | assert(t.status() == 0, "error") 15 | 16 | cmd m { 17 | echo teste 18 | exit 0 19 | } 20 | 21 | t = $(m | cat) 22 | assert(t.status() == 0, "error") 23 | 24 | cmd err { 25 | exit 1 26 | } 27 | 28 | t = $(err) 29 | if !t { 30 | print("not") 31 | } 32 | 33 | assert(t.status() != 0, "error") 34 | -------------------------------------------------------------------------------- /test/interpreter/cmds/string.sh: -------------------------------------------------------------------------------- 1 | # execute simple cmds 2 | # --output:start 3 | # hey, hello world there 4 | # hello 5 | # hey hello 6 | # hello you 7 | # hello hello 8 | # one| |two 9 | # --output:end 10 | 11 | t = "hello" 12 | q = ["one", "two"] 13 | echo "hey, ${t + \" world\"} there" 14 | echo "${t}" 15 | echo "hey ${t}" 16 | echo "${t} you" 17 | echo "${t} ${t}" 18 | echo "${q[0]}| |${q[1]}" 19 | -------------------------------------------------------------------------------- /test/interpreter/cmds/subshell.sh: -------------------------------------------------------------------------------- 1 | # execute sub shell test 2 | # --output:start 3 | # 123 4 | # change 5 | # alex 6 | # uuu 7 | # asdf 8 | # yyy 9 | # test 10 | # --output:end 11 | 12 | 13 | shell { 14 | echo 123 15 | } > test.tx 16 | 17 | cat test.tx 18 | rm test.tx 19 | 20 | t = "alex" 21 | shell { 22 | t = "change" 23 | echo ${t} 24 | } 25 | 26 | echo ${t} 27 | 28 | shell { 29 | echo uuu 30 | } | cat 31 | 32 | echo asdf | shell { 33 | print(read()) 34 | } 35 | 36 | print("yyy") 37 | echo test 38 | -------------------------------------------------------------------------------- /test/interpreter/func/ellipsis.sh: -------------------------------------------------------------------------------- 1 | # execute simple cmds 2 | # --output:start 3 | # case 1 4 | # case 3 5 | # 98alex8 teste98 6 | # alex8 teste 7 | # --output:end 8 | 9 | func f(args...) { 10 | print(98, ...args, 98) 11 | } 12 | 13 | p = "alex" 14 | v = ["alex", "outro"] 15 | 16 | switch p { 17 | case ...v { 18 | print("case 1") 19 | } 20 | 21 | case "outro" { 22 | print("case 2") 23 | } 24 | 25 | case "alex" { 26 | print("case 3") 27 | } 28 | 29 | default { 30 | print("default") 31 | } 32 | } 33 | 34 | f("alex", 8, " teste") 35 | print("alex", 8, " teste") 36 | -------------------------------------------------------------------------------- /test/interpreter/func/err_func_num_param.sh: -------------------------------------------------------------------------------- 1 | # param error test 2 | # --output:start 3 | # * 4 | # --output:end 5 | 6 | func test(a, b) { 7 | print (a, b) 8 | } 9 | 10 | test(5, 6, 8, 9) 11 | -------------------------------------------------------------------------------- /test/interpreter/func/err_func_num_param2.sh: -------------------------------------------------------------------------------- 1 | # param error test 2 | # --output:start 3 | # * 4 | # --output:end 5 | 6 | func test(a, b, c) { 7 | print(a, b, c) 8 | } 9 | 10 | test(1, 2) 11 | -------------------------------------------------------------------------------- /test/interpreter/func/err_func_num_param3.sh: -------------------------------------------------------------------------------- 1 | # param error test 2 | # --output:start 3 | # * 4 | # --output:end 5 | 6 | func test(a, b, c...) { 7 | print(a, b, c) 8 | } 9 | 10 | test(1) 11 | -------------------------------------------------------------------------------- /test/interpreter/func/err_func_num_param4.sh: -------------------------------------------------------------------------------- 1 | # param error test 2 | # --output:start 3 | # * 4 | # --output:end 5 | 6 | func test(a, b, c = 5) { 7 | print(a, b, c) 8 | } 9 | 10 | test(1) 11 | -------------------------------------------------------------------------------- /test/interpreter/func/err_func_param.sh: -------------------------------------------------------------------------------- 1 | # param error test 2 | # --output:start 3 | # Error: 10: 10: test takes exactly 1 argument (2 given) 4 | # --output:end 5 | 6 | func test(a) { 7 | print(a) 8 | } 9 | 10 | test(5, 6) 11 | -------------------------------------------------------------------------------- /test/interpreter/func/err_func_variadic.sh: -------------------------------------------------------------------------------- 1 | # param error test 2 | # --output:start 3 | # Error: 6: 1: only last parameter can be variadic 4 | # --output:end 5 | 6 | func test(a, c..., d) { 7 | print(a) 8 | } 9 | 10 | test(5, 6, 8, 9) 11 | -------------------------------------------------------------------------------- /test/interpreter/func/fib.sh: -------------------------------------------------------------------------------- 1 | # fibnacci test 2 | # --output:start 3 | # 55 4 | # --output:end 5 | 6 | func f(i) { 7 | if i < 2 { 8 | return i 9 | } else { 10 | return f(i-1) + f(i - 2) 11 | } 12 | } 13 | 14 | res = f(10) 15 | assert(res == 55, "error on fib result") 16 | 17 | print(res) 18 | -------------------------------------------------------------------------------- /test/interpreter/func/func_args.sh: -------------------------------------------------------------------------------- 1 | # function parameters test 2 | # --output:start 3 | # 1(2, 3, 4)50 4 | # 1(2, 3, 4)10 5 | # 1(2, 3, 4, 5)10 6 | # 1()10 7 | # [a, b, c] 8 | # [c, b] 9 | # 10520 10 | # 10510 11 | # 10210 12 | # 1023 13 | # 1023 14 | # --output:end 15 | 16 | func test(a, c..., d=10) { 17 | print(a, c, d) 18 | } 19 | 20 | func test2(a, b=5, c=10) { 21 | print(a,b,c) 22 | } 23 | 24 | test(1, 2, 3, 4,d= 50) 25 | test(1, 2, 3, 4) 26 | test(1, 2, 3, 4, 5) 27 | test(1) 28 | 29 | print(test2.__params__) 30 | print(test2.__default_params__) 31 | 32 | test2(10, c = 20) 33 | test2(10) 34 | test2(10, b = 2) 35 | test2(a=10, b = 2, c= 3) 36 | test2(10, b = 2, c= 3) 37 | -------------------------------------------------------------------------------- /test/interpreter/func/func_default.sh: -------------------------------------------------------------------------------- 1 | # function default parameter test 2 | # --output:start 3 | # 87alex 4 | # 7testalex 5 | # 7xal 6 | # --output:end 7 | 8 | func f(i, a = 5 + 2, b = "alex") { 9 | print(i, a, b) 10 | } 11 | 12 | f(8) 13 | f(7, "test") 14 | f(7, "x", "al") 15 | -------------------------------------------------------------------------------- /test/interpreter/func/func_default_param.sh: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexst07/shell-plus-plus/516c280e468a196aa4f53eb4d5d1cc98702a67b6/test/interpreter/func/func_default_param.sh -------------------------------------------------------------------------------- /test/interpreter/func/func_empty.sh: -------------------------------------------------------------------------------- 1 | # param error test 2 | # --output:start 3 | # --output:end 4 | 5 | func test(a, b, c) {} 6 | func test2(){} 7 | func test3(t...){} 8 | func test4(a, b = 5){} 9 | 10 | test(5, 6) 11 | test2() 12 | test3(1, 2, 3) 13 | test4(1) 14 | -------------------------------------------------------------------------------- /test/interpreter/func/func_lambda.sh: -------------------------------------------------------------------------------- 1 | # lambda test 2 | # --output:start 3 | # (1, 2, (3, alex)) 4 | # 1 5 | # 2 6 | # --output:end 7 | 8 | f = func(t...) { 9 | print(t[0]) 10 | return t 11 | } 12 | 13 | g = func(a, b, c...) { 14 | return a, b, c 15 | } 16 | 17 | gres = g(1, 2, 3, "alex") 18 | print(gres) 19 | assert(gres[0] == 1, "error") 20 | 21 | print(f(1, 2, 3)[1]) 22 | -------------------------------------------------------------------------------- /test/interpreter/func/func_lambda2.sh: -------------------------------------------------------------------------------- 1 | # lambda test 2 | # --output:start 3 | # 2:3 4 | # --output:end 5 | 6 | func test(a) { 7 | return func(b) { 8 | print(a, ":", b) 9 | } 10 | } 11 | 12 | p = test(2) 13 | p(3) 14 | -------------------------------------------------------------------------------- /test/interpreter/func/func_params.sh: -------------------------------------------------------------------------------- 1 | # variables with same in different scopes 2 | # --output:start 3 | # 3:6 4 | # 10:20 5 | # --output:end 6 | 7 | func test(a, b) { 8 | print(a, ":", b) 9 | } 10 | 11 | a = 10 12 | b = 20 13 | 14 | test(3, 6) 15 | print(a, ":", b) 16 | -------------------------------------------------------------------------------- /test/interpreter/func/func_scope.sh: -------------------------------------------------------------------------------- 1 | # function default parameter test 2 | # --output:start 3 | # 10 4 | # --output:end 5 | 6 | a = 5 7 | 8 | func q(f) { 9 | f() 10 | } 11 | 12 | q(func() { 13 | a = 10 14 | }) 15 | 16 | print(a) 17 | -------------------------------------------------------------------------------- /test/interpreter/func/func_variadic.sh: -------------------------------------------------------------------------------- 1 | # function variadic test 2 | # --output:start 3 | # 12alex 4 | # 512alex 5 | # --output:end 6 | 7 | func f(t...) { 8 | print(t[0], t[1], t[2]) 9 | } 10 | 11 | func f2(value, t...) { 12 | print(value, t[0], t[1], t[2]) 13 | } 14 | 15 | alex = "alex" 16 | 17 | f(1, 2, "alex") 18 | f2(5, 1, 2, alex) 19 | -------------------------------------------------------------------------------- /test/interpreter/func/lambda_inside_class.sh: -------------------------------------------------------------------------------- 1 | # test the case where a lambda is returned by some method inside class 2 | # 3 | # BUG: It test is the bug that ocorred when a lambda was returned by some 4 | # some method on the class, and when this lambda function was called 5 | # the caller, tried to pass object of class like this parameter for the 6 | # lambda function 7 | # 8 | # --output:start 9 | # test 10 | # >> other 11 | # --output:end 12 | 13 | class Test { 14 | func __init__(b) { 15 | this.b = b 16 | this.x = func(a) { 17 | print(this.b) 18 | print(">> ", a) 19 | } 20 | } 21 | 22 | func get_x() { 23 | return this.x 24 | } 25 | } 26 | 27 | t = Test("test") 28 | a = t.get_x()("other") 29 | -------------------------------------------------------------------------------- /test/interpreter/import/import1.sh: -------------------------------------------------------------------------------- 1 | import "import12.sh" as imp12 2 | 3 | my_var = "asdfs" 4 | str = "change here" 5 | print(my_var instanceof string) 6 | print(str) 7 | -------------------------------------------------------------------------------- /test/interpreter/import/import12.sh: -------------------------------------------------------------------------------- 1 | import "import1.sh" as imp1 2 | 3 | 4 | -------------------------------------------------------------------------------- /test/interpreter/import/import_module1.sh: -------------------------------------------------------------------------------- 1 | # import with star 2 | # --output:start 3 | # my A 4 | # local test 5 | # test 6 | # class A 7 | # class B 8 | # --output:end 9 | 10 | func A() { 11 | print("my A") 12 | } 13 | 14 | A() 15 | 16 | func Test() { 17 | print("local test") 18 | } 19 | 20 | Test() 21 | 22 | import * from "module.sh" 23 | 24 | Test() 25 | 26 | a = A("class A") 27 | print(a.get()) 28 | 29 | b = B("class B") 30 | print(b.get()) 31 | 32 | cmd cmd_test { 33 | print("my cmd") 34 | } 35 | 36 | cmd_test 37 | -------------------------------------------------------------------------------- /test/interpreter/import/import_module2.sh: -------------------------------------------------------------------------------- 1 | # import with id list 2 | # --output:start 3 | # local test 4 | # test 5 | # class A 6 | # --output:end 7 | 8 | func Test() { 9 | print("local test") 10 | } 11 | 12 | Test() 13 | 14 | import A, Test from "module.sh" 15 | 16 | Test() 17 | 18 | a = A("class A") 19 | print(a.get()) 20 | -------------------------------------------------------------------------------- /test/interpreter/import/import_module3.sh: -------------------------------------------------------------------------------- 1 | # import with as keyword 2 | # --output:start 3 | # test 4 | # class A 5 | # class B 6 | # --output:end 7 | 8 | import "module.sh" as mod 9 | 10 | mod.Test() 11 | 12 | a = mod.A("class A") 13 | print(a.get()) 14 | 15 | b = mod.B("class B") 16 | print(b.get()) 17 | -------------------------------------------------------------------------------- /test/interpreter/import/main1.sh: -------------------------------------------------------------------------------- 1 | # simple circular import test 2 | # --output:start 3 | # true 4 | # change here 5 | # asdfs 6 | # change here 7 | # teste 8 | # true 9 | # true 10 | # --output:end 11 | 12 | str = "teste" 13 | 14 | import "import1.sh" as imp 15 | 16 | print(imp.my_var) 17 | print(imp.str) 18 | print(str) 19 | 20 | print(str instanceof string) 21 | print(imp.my_var instanceof string) 22 | -------------------------------------------------------------------------------- /test/interpreter/import/module.sh: -------------------------------------------------------------------------------- 1 | __all__ = ["A", "B", "Test"] 2 | 3 | class A { 4 | func __init__(a) { 5 | this.a = a 6 | } 7 | 8 | func get() { 9 | return this.a 10 | } 11 | } 12 | 13 | class B { 14 | func __init__(a) { 15 | this.a = a 16 | } 17 | 18 | func get() { 19 | return this.a 20 | } 21 | } 22 | 23 | func Test() { 24 | print("test") 25 | } 26 | 27 | cmd cmd_test { 28 | print("cmd test") 29 | } 30 | -------------------------------------------------------------------------------- /test/interpreter/lang_basic/argv.sh: -------------------------------------------------------------------------------- 1 | # test bug when import come before sys.argv 2 | # --output:start 3 | # 123 4 | # [../test/interpreter/lang_basic/argv.sh] 5 | # --output:end 6 | 7 | import "import-test.sh" as test 8 | 9 | print(test.test_var) 10 | print(sys.argv) 11 | -------------------------------------------------------------------------------- /test/interpreter/lang_basic/array.sh: -------------------------------------------------------------------------------- 1 | # if else test 2 | # --output:start 3 | # test1 test2 test3 4 | # [1, 3, 8, 4, 5, 2] 5 | # [1, 2, 3, 4, 5, 8] 6 | # [8, 5, 4, 3, 2, 1] 7 | # [1, 2, 3, 4, 5, 8] 8 | # [16, 10, 8, 6, 4, 2] 9 | # [8, 5, 4, 3, 2, 1] 10 | # [8, 4, 2] 11 | # 2 12 | # false 13 | # 4 14 | # [8, 2] 15 | # [8, 5, 4, 2, 1] 16 | # [] 17 | # [8, 5, 4, 2, 1, 70, 78, 84] 18 | # [8, 5, 45, 2, 15, 70, 78, 84] 19 | # [8, 5, 2, 70, 78, 84] 20 | # false 21 | # false 22 | # true 23 | # false 24 | # [1, 2, 3] 25 | # [5, 2, 70] 26 | # before del: [8, 5, 2, 70, 78, 84] 27 | # after del: [8, 78, 84] 28 | # 8 29 | # 78 30 | # 84 31 | # --output:end 32 | 33 | str_arr = ["test1", "test2", "test3"] 34 | echo $@{str_arr} 35 | 36 | arr = [1] 37 | arr.append(3, 4, 2) 38 | arr.insert(2, 8) 39 | arr.insert(4, 5) 40 | print(arr) 41 | arr.sort() 42 | print(arr) 43 | arr.sort(func(a, b) {return a > b}) 44 | print(arr) 45 | arr.sort(func(a, b) {return a < b}) 46 | print(arr) 47 | arr.map(func(i) {return i*2}) 48 | arr.reverse() 49 | print(arr) 50 | 51 | arr.map(func(e) { 52 | return e/2 53 | }) 54 | 55 | print(arr) 56 | 57 | q = [] 58 | arr.for_each(func(e) { 59 | if e % 2 == 0 { 60 | q.append(e) 61 | } 62 | }) 63 | 64 | print(q) 65 | q.append(4) 66 | print(q.count(4)) 67 | print(arr.index(6)) 68 | print(arr.index(2)) 69 | q.remove(4) 70 | print(q) 71 | arr.pop(3) 72 | print(arr) 73 | q.clear() 74 | print(q) 75 | arr.extend([70,78,84]) 76 | print(arr) 77 | arr[2] = 45 78 | arr[4] = 15 79 | print(arr) 80 | del arr[4] 81 | del arr[2] 82 | print(arr) 83 | print([1,3] == 4) 84 | print([1,3] == [2,4, 8]) 85 | print([1,3] == [1,3]) 86 | print([1,3] == [2,4]) 87 | p = type([])([1, 2, 3]) 88 | print(p) 89 | arr2 = arr[1:4] 90 | print(arr2) 91 | print("before del: ", arr) 92 | del arr[1:4] 93 | print("after del: ", arr) 94 | 95 | for i in arr { 96 | print(i) 97 | } 98 | -------------------------------------------------------------------------------- /test/interpreter/lang_basic/assign.sh: -------------------------------------------------------------------------------- 1 | # if else test 2 | # --output:start 3 | # 10 4 | # 15 5 | # --output:end 6 | 7 | x = 15 8 | assert(x == 15) 9 | x = "test" 10 | assert(x == "test") 11 | 12 | v = [1, 2, 3] 13 | assert(v == [1, 2, 3]) 14 | v[0] = 55 15 | assert(v == [55, 2, 3]) 16 | v[1] = [10, 11] 17 | assert(v == [55, [10, 11], 3]) 18 | v[1][0] = 50 19 | assert(v == [55, [50, 11], 3]) 20 | 21 | a, b = 4, 5 22 | assert(a == 4 && b == 5) 23 | c = 4 ,5 24 | assert(c[0] == 4) 25 | assert(c[1] == 5) 26 | 27 | m = {"test1": "value1", "test2":"value2"} 28 | assert(m["test1"] == "value1") 29 | 30 | m["test3"] = 10 31 | assert(m["test3"] == 10) 32 | 33 | func t1() { 34 | return 10, 15 35 | } 36 | 37 | a = t1() 38 | assert(a[0] == 10) 39 | assert(a[1] == 15) 40 | a, b = t1() 41 | assert(a == 10) 42 | assert(b == 15) 43 | for i in [a,b] { 44 | print(i) 45 | } 46 | -------------------------------------------------------------------------------- /test/interpreter/lang_basic/file.sh: -------------------------------------------------------------------------------- 1 | # while break test 2 | # --output:start 3 | # hello world 4 | # 5 | # lo wo 6 | # 8 7 | # ello world 8 | # 9 | # line: line1 10 | # line: line2 11 | # line: line3 12 | # line: line4 13 | # line-> it_line1 14 | # line-> it_line2 15 | # line-> it_line3 16 | # line-> it_line4 17 | # 18 | # --output:end 19 | 20 | echo hello world > hello.txt 21 | 22 | f = file("hello.txt") 23 | print(f.read()) 24 | f.seekg(3) 25 | print(f.read(5)) 26 | 27 | print(f.tellg()) 28 | f.seekg(1) 29 | print(f.readall()) 30 | 31 | echo line1 > other.txt 32 | echo line2 >> other.txt 33 | echo line3 >> other.txt 34 | echo line4 >> other.txt 35 | 36 | q = file("other.txt") 37 | while true { 38 | r = q.readline() 39 | if !r { 40 | break 41 | } 42 | print("line: ", r) 43 | } 44 | 45 | echo it_line1 > iter.txt 46 | echo it_line2 >> iter.txt 47 | echo it_line3 >> iter.txt 48 | echo it_line4 >> iter.txt 49 | 50 | for line in file("iter.txt") { 51 | print("line-> ", line) 52 | } 53 | 54 | rm iter.txt hello.txt other.txt 55 | -------------------------------------------------------------------------------- /test/interpreter/lang_basic/filter.sh: -------------------------------------------------------------------------------- 1 | # filter test 2 | # --output:start 3 | # [15, 12, 14, 47] 4 | # [8, 2, 12, 2, 14] 5 | # dic1 6 | # key: key4, value: 11 7 | # key: key6, value: 15 8 | # key: key5, value: 12 9 | # dic2 10 | # key: key1, value: 2 11 | # key: key3, value: 6 12 | # key: key5, value: 12 13 | # --output:end 14 | 15 | arr = [1, 3, 8, 2, 5, 9, 15, 12, 2, 14, 47] 16 | arr2 = copy(arr) 17 | 18 | arr.filter(lambda x: x > 10) 19 | arr2.filter(lambda x: x % 2 == 0) 20 | print(arr) 21 | print(arr2) 22 | 23 | dic = {"key1": 2, "key2": 3, "key3": 6, "key4": 11, "key5": 12, "key6": 15} 24 | dic2 = copy(dic) 25 | 26 | dic.filter(lambda k, v: v > 10) 27 | dic2.filter(lambda k, v: v % 2 == 0) 28 | 29 | print("dic1") 30 | for k, v in dic { 31 | print("key: ", k, ", value: ", v) 32 | } 33 | 34 | print("dic2") 35 | for k, v in dic2 { 36 | print("key: ", k, ", value: ", v) 37 | } 38 | -------------------------------------------------------------------------------- /test/interpreter/lang_basic/glob.sh: -------------------------------------------------------------------------------- 1 | # glob test 2 | # --output:start 3 | # ./b.txt ./c.txt ./a.txt 4 | # *.txt 5 | # *.err 6 | # ./adir ./testdir ./cdir ./bdir 7 | # ./bdir 8 | # *dir 9 | # --output:end 10 | 11 | files = ["a.txt", "b.txt", "c.txt", "file"] 12 | dirs = ["adir", "bdir", "cdir", "testdir"] 13 | 14 | rm -rf test 15 | mkdir test 16 | cd test 17 | 18 | for f in files { 19 | touch ${f} 20 | } 21 | 22 | for d in dirs { 23 | mkdir ${d} 24 | } 25 | 26 | echo *.txt 27 | echo "*.txt" 28 | echo *.err 29 | echo *dir 30 | echo bdir 31 | echo "*dir" 32 | 33 | for f in files { 34 | rm ${f} 35 | } 36 | 37 | for d in dirs { 38 | rm -rf ${d} 39 | } 40 | 41 | cd .. 42 | rm -rf test 43 | -------------------------------------------------------------------------------- /test/interpreter/lang_basic/global.sh: -------------------------------------------------------------------------------- 1 | # if else test 2 | # --output:start 3 | # a: local func 4 | # b: global func 5 | # a: local test 6 | # b: global func 7 | # start func2 8 | # x local 9 | # y global 10 | # x call in func2 11 | # y call in func2 12 | # start func1 13 | # x local 14 | # y call in func2 15 | # x call in func1 16 | # y call in func1 17 | # end func1 18 | # x call in func2 19 | # y call in func1 20 | # end func2 21 | # x local 22 | # y call in func1 23 | # --output:end 24 | 25 | a = "local test" 26 | global b = "global test" 27 | 28 | func test() { 29 | b = "global func" 30 | a = "local func" 31 | print("a: ", a) 32 | print("b: ", b) 33 | } 34 | 35 | test() 36 | print("a: ", a) 37 | print("b: ", b) 38 | 39 | global y = "y global" 40 | x = "x local" 41 | 42 | func func1() { 43 | print("start func1") 44 | print(x) 45 | print(y) 46 | y = "y call in func1" 47 | x = "x call in func1" 48 | print(x) 49 | print(y) 50 | print("end func1") 51 | } 52 | 53 | func func2() { 54 | print("start func2") 55 | print(x) 56 | print(y) 57 | y = "y call in func2" 58 | x = "x call in func2" 59 | print(x) 60 | print(y) 61 | func1() 62 | print(x) 63 | print(y) 64 | print("end func2") 65 | } 66 | 67 | func2() 68 | 69 | print(x) 70 | print(y) 71 | -------------------------------------------------------------------------------- /test/interpreter/lang_basic/if_else.sh: -------------------------------------------------------------------------------- 1 | # if else test 2 | # --output:start 3 | # test 4 | # --output:end 5 | 6 | a = 5 7 | b = "test" 8 | 9 | if a == 5 { 10 | if b != "test" { 11 | print("no test") 12 | } else { 13 | print("test") 14 | } 15 | } else { 16 | print("asdf") 17 | } 18 | -------------------------------------------------------------------------------- /test/interpreter/lang_basic/if_else2.sh: -------------------------------------------------------------------------------- 1 | # if else test 2 | # --output:start 3 | # test 4 | # elseif2 5 | # elseif1 6 | # else 7 | # --output:end 8 | 9 | a = 5 10 | b = "test" 11 | 12 | if a == 5 { 13 | if b != "test" { 14 | print("no test") 15 | } else { 16 | print("test") 17 | } 18 | } else { 19 | print("asdf") 20 | } 21 | 22 | if a == 6 { 23 | print("if") 24 | } else if a == 3 { 25 | print("elseif1") 26 | } else if a == 5 { 27 | print("elseif2") 28 | } else { 29 | print("else") 30 | } 31 | 32 | if a == 6 { 33 | print("if") 34 | } else if a == 5 { 35 | print("elseif1") 36 | } else if a == 3 { 37 | print("elseif2") 38 | } else { 39 | print("else") 40 | } 41 | 42 | if a == 6 { 43 | print("if") 44 | } else if a == 3 { 45 | print("elseif1") 46 | } else if a == 4 { 47 | print("elseif2") 48 | } else { 49 | print("else") 50 | } 51 | -------------------------------------------------------------------------------- /test/interpreter/lang_basic/if_else_empty.sh: -------------------------------------------------------------------------------- 1 | # if else test 2 | # --output:start 3 | # --output:end 4 | 5 | if true {} else {} 6 | -------------------------------------------------------------------------------- /test/interpreter/lang_basic/if_else_expr.sh: -------------------------------------------------------------------------------- 1 | # if else test 2 | # --output:start 3 | # 9 4 | # 2 5 | # first 6 | # first 7 | # first 8 | # second 9 | # second 10 | # --output:end 11 | 12 | a1 = 8 13 | 14 | b = 9 if a1 > 5 else 2 15 | print(b) 16 | 17 | b = 9 if a1 < 5 else 2 18 | print(b) 19 | 20 | c = $(echo first) if a1 == 8 else $(echo second) 21 | print(c) 22 | 23 | c = $(echo first) if a1 == 8 24 | else $(echo second) 25 | print(c) 26 | 27 | c = $(echo first) if a1 == 8 else $(echo second) 28 | print(c) 29 | 30 | c = $(echo first) if 31 | a1 != 8 else $(echo second) 32 | print(c) 33 | 34 | c = $(echo first) if a1 != 8 else 35 | $(echo second) 36 | print(c) 37 | -------------------------------------------------------------------------------- /test/interpreter/lang_basic/import-test.sh: -------------------------------------------------------------------------------- 1 | test_var = "123" 2 | -------------------------------------------------------------------------------- /test/interpreter/lang_basic/import.sh: -------------------------------------------------------------------------------- 1 | func test() { 2 | print("hello") 3 | } 4 | 5 | class Test { 6 | func __init__(a) { 7 | this.a = a 8 | } 9 | 10 | func __print__() { 11 | return this.a 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/interpreter/lang_basic/import_test.sh: -------------------------------------------------------------------------------- 1 | # import test 2 | # --output:start 3 | # hello 4 | # alex 5 | # --output:end 6 | 7 | import "import.sh" as imp 8 | 9 | imp.test() 10 | 11 | t = imp.Test("alex") 12 | print(t) 13 | -------------------------------------------------------------------------------- /test/interpreter/lang_basic/lambda.sh: -------------------------------------------------------------------------------- 1 | # simple lambda test 2 | # --output:start 3 | # 16 4 | # 64 5 | # 1 6 | # 2 7 | # 3 8 | # --output:end 9 | 10 | l = lambda x: x*x 11 | print(l(4)) 12 | 13 | v = [lambda x: print(x), lambda x: x*x*x] 14 | 15 | print(v[1](4)) 16 | t = [1,2,3] 17 | t.for_each(lambda x:print(x)) 18 | -------------------------------------------------------------------------------- /test/interpreter/lang_basic/let.sh: -------------------------------------------------------------------------------- 1 | # execute let expression test 2 | # --output:start 3 | # a: true 4 | # 46 5 | # test1 6 | # test2 7 | # test3 8 | # --output:end 9 | 10 | if let a = true { 11 | print("a: ", a) 12 | } 13 | 14 | a = let x, y = 4, 6 15 | 16 | print(a[0], a[1]) 17 | 18 | echo "test1" > test.txt 19 | echo "test2" >> test.txt 20 | echo "test3" >> test.txt 21 | 22 | shell { 23 | while let r = read() { 24 | print(r) 25 | } 26 | } < test.txt 27 | 28 | rm test.txt 29 | -------------------------------------------------------------------------------- /test/interpreter/lang_basic/let2.sh: -------------------------------------------------------------------------------- 1 | # execute let expression test 2 | # --output:start 3 | # counter: 0 4 | # exec p 5 | # counter: 1 6 | # exec p 7 | # counter: 2 8 | # exec p 9 | # counter: 3 10 | # exec p 11 | # counter: 4 12 | # exec p 13 | # counter: 5 14 | # --output:end 15 | 16 | counter = 0 17 | 18 | func test() { 19 | if counter == 5 { 20 | print("counter: ", counter) 21 | return false 22 | } else { 23 | print("counter: ", counter) 24 | counter += 1 25 | return true 26 | } 27 | } 28 | 29 | while let r = test() { 30 | print("exec p") 31 | } 32 | -------------------------------------------------------------------------------- /test/interpreter/lang_basic/list_comp.sh: -------------------------------------------------------------------------------- 1 | # if else test 2 | # --output:start 3 | # [2, 2, 4, 4] 4 | # [1, 2, 2, 3, 3, 4, 4, 5] 5 | # [[1, 2], [2, 3], [3, 4], [4, 5]] 6 | # [1, 4, 9, 16] 7 | # [9, 16] 8 | # --output:end 9 | 10 | 11 | a = [[1,2], [2,3], [3,4], [4,5]] 12 | b = [b for i in a for b in i if b % 2 == 0] 13 | print(b) 14 | 15 | b = [b for i in a for b in i] 16 | print(b) 17 | 18 | b = [i for i in a for b in i if b % 2 == 0] 19 | print(b) 20 | 21 | x = [1, 2, 3, 4] 22 | y = [i*i for i in x] 23 | print(y) 24 | 25 | x = [1, 2, 3, 4] 26 | y = [i*i for i in x if i > 2] 27 | print(y) 28 | -------------------------------------------------------------------------------- /test/interpreter/lang_basic/quicksort.sh: -------------------------------------------------------------------------------- 1 | func quicksort(A, lo, hi) { 2 | if (lo < hi) { 3 | p = partition(A, lo, hi) 4 | A = quicksort(A, lo, p - 1) 5 | A = quicksort(A, p + 1, hi) 6 | } 7 | 8 | return A 9 | } 10 | 11 | func partition(A, lo, hi) { 12 | pivot = A[hi] 13 | i = lo 14 | j = lo 15 | 16 | for j in range(lo, hi) { 17 | if A[j] <= pivot { 18 | A[i], A[j] = A[j], A[i] 19 | i = i + 1 20 | } 21 | } 22 | 23 | A[i], A[hi] = A[hi], A[i] 24 | return i 25 | } 26 | 27 | a = [1,4,6,4,10,11,22,100,3,3,9] 28 | print(quicksort(a, 0, len(a) - 1)) 29 | -------------------------------------------------------------------------------- /test/interpreter/lang_basic/range_it.sh: -------------------------------------------------------------------------------- 1 | # range iterator test 2 | # --output:start 3 | # 01234 4 | # 02468 5 | # 108642 6 | # 10987654321 7 | # -10-9-8-7-6-5-4-3-2-1 8 | # -10-7-4-1 9 | # --output:end 10 | 11 | str = "" 12 | for i in range(0, 5) { 13 | str += string(i) 14 | } 15 | print(str) 16 | 17 | str = "" 18 | for i in range(0, 10, 2) { 19 | str += string(i) 20 | } 21 | print(str) 22 | 23 | str = "" 24 | for i in range(10, 0, -2) { 25 | str += string(i) 26 | } 27 | print(str) 28 | 29 | str = "" 30 | for i in range(10, 0) { 31 | str += string(i) 32 | } 33 | print(str) 34 | 35 | str = "" 36 | for i in range(-10, 0) { 37 | str += string(i) 38 | } 39 | print(str) 40 | 41 | str = "" 42 | for i in range(-10, 0, 3) { 43 | str += string(i) 44 | } 45 | print(str) 46 | -------------------------------------------------------------------------------- /test/interpreter/lang_basic/string.sh: -------------------------------------------------------------------------------- 1 | # if else test 2 | # --output:start 3 | # --output:end 4 | 5 | str = "my test" 6 | 7 | print(str == "my test") 8 | print(str != "my test") 9 | 10 | print(str[1:3]) 11 | print(str[-3:-1]) 12 | print(str[-2:]) 13 | print(str[:-2]) 14 | 15 | print(str.to_upper()) 16 | print(str) 17 | print(str.to_lower()) 18 | print(str[0]) 19 | 20 | print(str.split(" ")) 21 | -------------------------------------------------------------------------------- /test/interpreter/lang_basic/tuple.sh: -------------------------------------------------------------------------------- 1 | # if else test 2 | # --output:start 3 | # 1 4 | # test 5 | # true 6 | # --output:end 7 | 8 | t = 1, "test", true 9 | 10 | assert(type(t) == tuple) 11 | assert(type(array(t)) == array) 12 | 13 | assert(t[0] == 1) 14 | assert(t[1] == "test") 15 | assert(t[2] == true) 16 | 17 | for i in t { 18 | print(i) 19 | } 20 | -------------------------------------------------------------------------------- /test/interpreter/lang_basic/tuple2.sh: -------------------------------------------------------------------------------- 1 | # tuple test 2 | # --output:start 3 | # () 4 | # (1, test) 5 | # (4, (1, 2)) 6 | # (4, (90, 2)) 7 | # {(n, ()), (as, (4, 2)), (other, ([function], 5))} 8 | # ((1, 2, 3, 10), 5) 9 | # --output:end 10 | 11 | a = () 12 | print(a) 13 | 14 | b, c = (1, "test") 15 | print((b, c)) 16 | 17 | t1, t2 = 4, (1, 2) 18 | print((t1, t2)) 19 | t2[0] = 90 20 | print((t1, t2)) 21 | 22 | d = {"as": (4, 2), 23 | "other": (lambda x: x, 5), 24 | "n": ()} 25 | print(d) 26 | 27 | a = (1, 2, 3) 28 | b = (...a, 10), 5 29 | print(b) 30 | -------------------------------------------------------------------------------- /test/interpreter/lang_basic/varenv.sh: -------------------------------------------------------------------------------- 1 | # while test 2 | # --output:start 3 | # cmd: teste var 4 | # inside cmd: teste var 5 | # cmd: cmd teste var 6 | # inside cmd: cmd teste var 7 | # varenv: teste var 8 | # cmd: change teste var 9 | # inside cmd: change teste var 10 | # cmd: cmd teste var 11 | # inside cmd: cmd teste var 12 | # varenv: change teste var 13 | # --output:end 14 | 15 | cmd test { 16 | echo "cmd: " ${$TESTVAR} 17 | inside_test 18 | varenv TESTVAR = "cmd teste var" 19 | echo "cmd: " ${$TESTVAR} 20 | inside_test 21 | } 22 | 23 | cmd inside_test { 24 | echo "inside cmd: " ${$TESTVAR} 25 | } 26 | 27 | varenv TESTVAR = "teste var" 28 | test 29 | print("varenv: ", $TESTVAR) 30 | 31 | varenv TESTVAR = "change teste var" 32 | test 33 | print("varenv: ", $TESTVAR) 34 | -------------------------------------------------------------------------------- /test/interpreter/lang_basic/while.sh: -------------------------------------------------------------------------------- 1 | # while test 2 | # --output:start 3 | # 4950 4 | # --output:end 5 | 6 | i = 0 7 | sum = 0 8 | while i < 100 { 9 | sum += i 10 | i += 1 11 | } 12 | 13 | assert(sum == 4950, "error on sum") 14 | print(sum) 15 | -------------------------------------------------------------------------------- /test/interpreter/lang_basic/while_break.sh: -------------------------------------------------------------------------------- 1 | # while break test 2 | # --output:start 3 | # 4950 4 | # --output:end 5 | 6 | i = 0 7 | sum = 0 8 | while true { 9 | if i == 100 { 10 | break 11 | } 12 | 13 | sum += i 14 | i += 1 15 | } 16 | 17 | assert(sum == 4950, "error on sum") 18 | print(sum) 19 | -------------------------------------------------------------------------------- /test/interpreter/lang_basic/while_break2.sh: -------------------------------------------------------------------------------- 1 | # while break test 2 | # --output:start 3 | # a: 0 4 | # a: 1 5 | # a: 2 6 | # a: 3 7 | # a: 4 8 | # 3 9 | # --output:end 10 | 11 | i = 0 12 | sum = 0 13 | while true { 14 | if i == 3 { 15 | break 16 | } 17 | 18 | sum += i 19 | i += 1 20 | } 21 | 22 | a = 0 23 | while a < 5 { 24 | print("a: ", a) 25 | a += 1 26 | } 27 | 28 | print(sum) 29 | -------------------------------------------------------------------------------- /test/interpreter/lang_basic/while_continue.sh: -------------------------------------------------------------------------------- 1 | # while break test 2 | # --output:start 3 | # 13579 4 | # --output:end 5 | 6 | i = 0 7 | sum = 0 8 | str = "" 9 | while i < 10 { 10 | if i % 2 == 0 { 11 | i += 1 12 | continue 13 | } 14 | 15 | str += string(i) 16 | i += 1 17 | } 18 | 19 | print(str) 20 | -------------------------------------------------------------------------------- /test/interpreter/objects/path.sh: -------------------------------------------------------------------------------- 1 | # path test 2 | # --output:start 3 | # /home/alex 4 | # /home/alex/books 5 | # true 6 | # --output:end 7 | 8 | p1 = path("/home") 9 | p_books = path("books") 10 | 11 | # test path concatanation 12 | p2 = p1 / "alex" 13 | p3 = p2 / p_books 14 | 15 | print(p2) 16 | print(p3) 17 | 18 | # test path comparation 19 | pwd1 = path.pwd() 20 | pwd2 = path.pwd() 21 | print(pwd1 == pwd2) -------------------------------------------------------------------------------- /test/interpreter/oop/abastract-method.sh: -------------------------------------------------------------------------------- 1 | # simple class with same name of attributes 2 | # --output:start 3 | # val a: 4 val b: 15 4 | # --output:end 5 | 6 | interface IFoo { 7 | func func_foo() 8 | } 9 | 10 | abstract class AbstractTest: IFoo { 11 | func __init__(a) { 12 | this.a = a 13 | } 14 | 15 | abstract func Fother(a) 16 | 17 | func func_foo() {} 18 | 19 | func ValA() { 20 | print("val a: ", this.a) 21 | } 22 | } 23 | 24 | class Test(AbstractTest) { 25 | func __init__(b) { 26 | AbstractTest.__init__(this, 4) 27 | this.b = b 28 | } 29 | 30 | func Val() { 31 | print("val a: ", this.a, " val b: ", this.b) 32 | } 33 | 34 | func Fother(a) { 35 | print("Fother") 36 | } 37 | } 38 | 39 | t = Test(15) 40 | t.Val() 41 | -------------------------------------------------------------------------------- /test/interpreter/oop/attr.sh: -------------------------------------------------------------------------------- 1 | # simple class with same name of attributes 2 | # --output:start 3 | # symbol a not found 4 | # {(b, 5), (a, 1)} 5 | # {(b, 5), (a, 1)} 6 | # [function] 7 | # [function] 8 | # [function] 9 | # [this, a, b, c] 10 | # symbol a not found 11 | # [a, b] 12 | # --output:end 13 | 14 | class Base { 15 | func Okkkk(a, b, c...) {} 16 | } 17 | 18 | class TestBase(Base) { 19 | func __init__(c) { 20 | this.a = c 21 | } 22 | 23 | func GetFuncs() { 24 | return get_attr_type(this) 25 | } 26 | } 27 | 28 | class Test(TestBase) { 29 | func __init__(a, b) { 30 | this.a = a 31 | this.b = b 32 | } 33 | 34 | func GetAttrObj() { 35 | return get_attr_obj(this) 36 | } 37 | 38 | func GetAttrType(b = 3) { 39 | return get_attr_type(this) 40 | } 41 | 42 | func GetA() { 43 | return this.a 44 | } 45 | 46 | func GetB() { 47 | return this.b 48 | } 49 | } 50 | 51 | a = 5 52 | t = Test(1, 5) 53 | 54 | try { 55 | b = [] 56 | print(b.a) 57 | } catch Exception as e { 58 | print(e) 59 | } 60 | 61 | print(get_attr_obj(t)) 62 | print(t.GetAttrObj()) 63 | print(get_attr_type(t)["GetA"]) 64 | print(t.GetAttrType()["GetFuncs"]) 65 | print(t.GetFuncs()["Okkkk"]) 66 | print(get_attr_type(t)["Okkkk"].__params__) 67 | 68 | try { 69 | print(get_attr_type(t)["Okkkk"].a) 70 | } catch Exception as e { 71 | print(e) 72 | } 73 | 74 | func fTest(a, b) {} 75 | 76 | print(fTest.__params__) 77 | -------------------------------------------------------------------------------- /test/interpreter/oop/class.sh: -------------------------------------------------------------------------------- 1 | # simple class with same name of attributes 2 | # --output:start 3 | # 5 4 | # 3 5 | # x: 10 6 | # --output:end 7 | 8 | class test { 9 | func __init__(x) { 10 | this.a = x 11 | } 12 | 13 | func get() { 14 | return this.a 15 | } 16 | 17 | func obj() { 18 | return this 19 | } 20 | } 21 | 22 | class test2 { 23 | func __init__(x) { 24 | this.a = x 25 | } 26 | 27 | func get() { 28 | return this.a 29 | } 30 | 31 | func obj() { 32 | return this 33 | } 34 | } 35 | 36 | x = 10 37 | 38 | t1 = test(5) 39 | t2 = test2(3) 40 | 41 | print(t1.obj().get()) 42 | print(t2.obj().get()) 43 | 44 | print("x: ", x) 45 | -------------------------------------------------------------------------------- /test/interpreter/oop/decorator.sh: -------------------------------------------------------------------------------- 1 | # simple class with same name of attributes 2 | # --output:start 3 | # TextField: 80, 24 4 | # ScrollDecorator 5 | # BorderDecorator 6 | # BorderDecorator 7 | # --output:end 8 | 9 | interface Widget { 10 | func draw() 11 | } 12 | 13 | class TextField: Widget { 14 | func __init__(w, h) { 15 | this.w = w 16 | this.h = h 17 | } 18 | 19 | func draw() { 20 | print("TextField: ", this.w, ", ", this.h) 21 | } 22 | } 23 | 24 | class Decorator: Widget { 25 | func __init__(wid) { 26 | this.wid = wid 27 | } 28 | 29 | func draw() { 30 | this.wid.draw() 31 | } 32 | } 33 | 34 | class BorderDecorator(Decorator) { 35 | func __init__(wid) { 36 | Decorator.__init__(this, wid) 37 | } 38 | 39 | func draw() { 40 | Decorator.draw(this) 41 | print("BorderDecorator") 42 | } 43 | } 44 | 45 | class ScrollDecorator(Decorator) { 46 | func __init__(wid) 47 | { 48 | Decorator.__init__(this, wid) 49 | } 50 | 51 | func draw() { 52 | Decorator.draw(this) 53 | print("ScrollDecorator") 54 | } 55 | } 56 | 57 | wid = BorderDecorator( 58 | BorderDecorator( 59 | ScrollDecorator( 60 | TextField(80, 24)))) 61 | 62 | wid.draw() 63 | -------------------------------------------------------------------------------- /test/interpreter/oop/err_try_catch5.sh: -------------------------------------------------------------------------------- 1 | try { 2 | echo asdf 3 | zXxers 4 | echo fdsa 5 | } catch GlobException as t { 6 | print(string(t)) 7 | } catch BadAllocException as t { 8 | print(type(t)) 9 | print("Exception: " + string(t)) 10 | } finally { 11 | print("finally") 12 | } 13 | -------------------------------------------------------------------------------- /test/interpreter/oop/exceptions.sh: -------------------------------------------------------------------------------- 1 | class TestException(Exception) { 2 | func __init__(msg, code) { 3 | Exception.__init__(this, msg) 4 | this.code = code 5 | } 6 | 7 | func showSymTable() { 8 | dump_symbol_table(this) 9 | } 10 | 11 | } 12 | 13 | t = TestException("tessdfte", 45) 14 | print(string(t)) 15 | t.showSymTable() 16 | -------------------------------------------------------------------------------- /test/interpreter/oop/facade.sh: -------------------------------------------------------------------------------- 1 | # simple class with same name of attributes 2 | # --output:start 3 | # Ouch! 4 | # Beep beep! 5 | # Loading.. 6 | # Ready to be used! 7 | # Bup bup bup buzzzz! 8 | # Haaah! 9 | # Zzzzz 10 | # --output:end 11 | 12 | class Computer { 13 | func getElectricShock() { 14 | echo "Ouch!" 15 | } 16 | 17 | func makeSound() { 18 | echo "Beep beep!" 19 | } 20 | 21 | func showLoadingScreen() { 22 | echo "Loading.." 23 | } 24 | 25 | func bam() { 26 | echo "Ready to be used!" 27 | } 28 | 29 | func closeEverything() { 30 | echo "Bup bup bup buzzzz!" 31 | } 32 | 33 | func sooth() { 34 | echo "Zzzzz" 35 | } 36 | 37 | func pullCurrent() { 38 | echo "Haaah!" 39 | } 40 | } 41 | 42 | class ComputerFacade { 43 | func __init__(computer) { 44 | this.computer = computer 45 | } 46 | 47 | func turnOn() { 48 | this.computer.getElectricShock(); 49 | this.computer.makeSound(); 50 | this.computer.showLoadingScreen(); 51 | this.computer.bam(); 52 | } 53 | 54 | func turnOff() { 55 | this.computer.closeEverything(); 56 | this.computer.pullCurrent(); 57 | this.computer.sooth(); 58 | } 59 | } 60 | 61 | computer = ComputerFacade(Computer()) 62 | computer.turnOn() 63 | computer.turnOff() 64 | -------------------------------------------------------------------------------- /test/interpreter/oop/final.sh: -------------------------------------------------------------------------------- 1 | # simple class with final keyword 2 | # --output:start 3 | # 'Test1' can't extends from final type 'Test' 4 | # --output:end 5 | 6 | final class Test {} 7 | 8 | try { 9 | class Test1(Test) {} 10 | } catch Exception as ex { 11 | print(ex) 12 | } 13 | -------------------------------------------------------------------------------- /test/interpreter/oop/ifaces.sh: -------------------------------------------------------------------------------- 1 | # interfaces tests 2 | # --output:start 3 | # Print 2:3 4 | # Ftest 7:8 5 | # Fother 9 6 | # --output:end 7 | 8 | interface ITest { 9 | func Print(a, b) 10 | } 11 | 12 | interface IOther { 13 | func Fother(a) 14 | } 15 | 16 | interface ITest2: IOther { 17 | func Ftest(x, y) 18 | } 19 | 20 | class Test: ITest, ITest2 { 21 | func Print(a, b = 3) { 22 | print("Print ", a, ":", b) 23 | } 24 | 25 | func Ftest(x, y) { 26 | print("Ftest ", x, ":", y) 27 | } 28 | 29 | func Fother(x) { 30 | print("Fother ", x) 31 | } 32 | } 33 | 34 | t = Test() 35 | t.Print(2) 36 | t.Ftest(7, 8) 37 | t.Fother(9) 38 | -------------------------------------------------------------------------------- /test/interpreter/oop/inheritance.sh: -------------------------------------------------------------------------------- 1 | class Base { 2 | func __init__(a) { 3 | print("__init__ type this: ", type(a)) 4 | this.a = a 5 | print("__init__ type this: ", type(this.a)) 6 | } 7 | 8 | func printA() { 9 | dump_symbol_table(this) 10 | print("type this: ", type(this.a)) 11 | print("str -> value a: ", this.a) 12 | } 13 | 14 | func __str__() { 15 | return "value :a" + string(this.a) 16 | } 17 | 18 | func __add__(x) { 19 | print("__add__ type this: ", type(this.a)) 20 | y = this.a + x 21 | print("__add__ type this: ", type(y)) 22 | return Base(y) 23 | } 24 | } 25 | 26 | class Derived(Base) { 27 | func __init__(b) { 28 | Base.__init__(this, b + 5) 29 | this.b = b 30 | } 31 | 32 | func printB() { 33 | print("value a: ", this.a) 34 | } 35 | } 36 | 37 | q = Derived(15) 38 | q.printA() 39 | print(string(q)) 40 | a = q + 9 41 | a.printA() 42 | -------------------------------------------------------------------------------- /test/interpreter/oop/inner_class.sh: -------------------------------------------------------------------------------- 1 | # inner class test 2 | # --output:start 3 | # >> THIS A: 15 4 | # >> THIS B: 11 5 | # >> THIS C: 12 6 | # --output:end 7 | 8 | class Test1 { 9 | func __init__(a) { 10 | this.a = a 11 | } 12 | 13 | func dump() { 14 | dump_symbol_table() 15 | } 16 | 17 | func Print() { 18 | print(">> THIS A: ", this.a) 19 | } 20 | 21 | class Test2 { 22 | func __init__(b) { 23 | this.b = b 24 | } 25 | 26 | func Print() { 27 | print(">> THIS B: ", this.b) 28 | } 29 | 30 | func dump() { 31 | dump_symbol_table() 32 | } 33 | 34 | class Test3 { 35 | func __init__(b) { 36 | this.c = b 37 | } 38 | 39 | func Print() { 40 | print(">> THIS C: ", this.c) 41 | } 42 | 43 | func dump() { 44 | dump_symbol_table() 45 | } 46 | } 47 | } 48 | } 49 | 50 | 51 | t = Test1(15) 52 | t.Print() 53 | t1 = Test1.Test2(11) 54 | t1.Print() 55 | t2 = Test1.Test2.Test3(12) 56 | t2.Print() 57 | -------------------------------------------------------------------------------- /test/interpreter/oop/instanceof.sh: -------------------------------------------------------------------------------- 1 | # simple test for instanceof 2 | # --output:start 3 | # true 4 | # false 5 | # true 6 | # true 7 | # false 8 | # false 9 | # false 10 | # true 11 | # true 12 | # true 13 | # true 14 | # false 15 | # --output:end 16 | 17 | interface Iface1 {} 18 | interface Iface2 {} 19 | 20 | class Test1: Iface1 {} 21 | class Test2(Test1): Iface2 {} 22 | 23 | class Foo {} 24 | 25 | f = Foo() 26 | t1 = Test1() 27 | t2 = Test2() 28 | 29 | print(f instanceof Foo) 30 | print(f instanceof Iface1) 31 | print(t1 instanceof Iface1) 32 | print(t1 instanceof Test1) 33 | print(t1 instanceof Test2) 34 | print(t1 instanceof Iface2) 35 | print(t1 instanceof Foo) 36 | print(t2 instanceof Iface1) 37 | print(t2 instanceof Test1) 38 | print(t2 instanceof Test2) 39 | print(t2 instanceof Iface2) 40 | print(t2 instanceof Foo) 41 | -------------------------------------------------------------------------------- /test/interpreter/oop/is.sh: -------------------------------------------------------------------------------- 1 | # simple test for is 2 | # --output:start 3 | # true 4 | # true 5 | # false 6 | # true 7 | # true 8 | # false 9 | # true 10 | # false 11 | # false 12 | # --output:end 13 | 14 | print(type("s") is string) 15 | print(type(8) is int) 16 | print(type(15) is bool) 17 | 18 | class Foo {} 19 | print(type(Foo()) is Foo) 20 | f = Foo() 21 | print(type(f) is Foo) 22 | 23 | interface ITest {} 24 | class Test: ITest {} 25 | 26 | t = Test() 27 | print(type(t) is ITest) 28 | print(type(t) is Test) 29 | print(type(t) is object) 30 | print(type(t) is Foo) 31 | -------------------------------------------------------------------------------- /test/interpreter/oop/methods.sh: -------------------------------------------------------------------------------- 1 | # lambda test 2 | # --output:start 3 | # symbol a not found 4 | # 15 5 | # sum: 6 6 | # base: 3 7 | # --output:end 8 | 9 | class TestBase { 10 | var x = 5 11 | 12 | func __init__(a) { 13 | this.c = a 14 | } 15 | 16 | func GetA() { 17 | return this.c 18 | } 19 | } 20 | 21 | class Test(TestBase) { 22 | func __init__(a, b) { 23 | TestBase.__init__(this, a+2) 24 | this.a = a 25 | this.b = b 26 | } 27 | 28 | func Sum() { 29 | return this.a + this.b 30 | } 31 | 32 | func PrintSum() { 33 | print("sum: ", this.Sum()) 34 | } 35 | 36 | func PrintBase() { 37 | print("base: ", this.GetA()) 38 | } 39 | } 40 | 41 | a = 4 42 | 43 | try { 44 | Test.a = 15 45 | print(Test.a) 46 | } catch Exception as e { 47 | print(e) 48 | } 49 | 50 | Test.x = 15 51 | print(Test.x) 52 | 53 | t = Test(1, 5) 54 | t.PrintSum() 55 | t.PrintBase() 56 | -------------------------------------------------------------------------------- /test/interpreter/oop/op_overload.sh: -------------------------------------------------------------------------------- 1 | # simple class with same name of attributes 2 | # --output:start 3 | # hello+world 4 | # 11 5 | # ok 6 | # l 7 | # a> 8 | # --output:end 9 | 10 | class test { 11 | func __init__(x) { 12 | this.a = x 13 | } 14 | 15 | func __add__(x) { 16 | this.a += "+" + x 17 | return this 18 | } 19 | 20 | func __sub__(x) { 21 | this.a += "-" + x 22 | return this 23 | } 24 | 25 | func __call__(a, b, c) { 26 | return "call:(" + a +", " + b + ", " + c + ")" 27 | } 28 | 29 | func __print__() { 30 | return this.a 31 | } 32 | 33 | func __hash__() { 34 | return 8 35 | } 36 | 37 | func __len__() { 38 | return len(this.a) 39 | } 40 | 41 | func __bool__() { 42 | return len(this.a) > 10 43 | } 44 | 45 | func __getitem__(i) { 46 | return this.a[i] 47 | } 48 | } 49 | 50 | t = test("hello") 51 | t = t+"world" 52 | print(t) 53 | print(len(t)) 54 | 55 | if t { 56 | print("ok") 57 | } 58 | 59 | print(t[3]) 60 | 61 | a = test("ok") 62 | if a { 63 | print("a<") 64 | } else { 65 | print("a>") 66 | } 67 | -------------------------------------------------------------------------------- /test/interpreter/oop/singleton.sh: -------------------------------------------------------------------------------- 1 | # simple class with same name of attributes 2 | # --output:start 3 | # value a: 5, value b: 10 4 | # value a: 8, value b: 9 5 | # --output:end 6 | 7 | class Singleton { 8 | var instance_ = null 9 | 10 | func __init__() { 11 | this.a = 5 12 | this.b = 10 13 | } 14 | 15 | func set_values(a, b) { 16 | this.a = a 17 | this.b = b 18 | } 19 | 20 | func print_values() { 21 | print("value a: ", this.a, ", value b: ", this.b) 22 | } 23 | 24 | static func instance() { 25 | if type(Singleton.instance_) == null_t { 26 | return let Singleton.instance_ = Singleton() 27 | } else { 28 | return Singleton.instance_ 29 | } 30 | } 31 | } 32 | 33 | t = Singleton.instance() 34 | t.print_values() 35 | q = Singleton.instance() 36 | q.set_values(8, 9) 37 | t.print_values() 38 | -------------------------------------------------------------------------------- /test/interpreter/oop/static-method.sh: -------------------------------------------------------------------------------- 1 | # interfaces tests 2 | # --output:start 3 | # static func 4 | # a: 15 5 | # --output:end 6 | 7 | class Test { 8 | func __init__(a) { 9 | this.a = a 10 | } 11 | 12 | func ValA() { 13 | print("a: ", this.a) 14 | } 15 | 16 | static func Print() { 17 | print("static func") 18 | } 19 | } 20 | 21 | Test.Print() 22 | 23 | t = Test(15) 24 | t.ValA() 25 | -------------------------------------------------------------------------------- /test/interpreter/oop/try_catch.sh: -------------------------------------------------------------------------------- 1 | # interfaces tests 2 | # --output:start 3 | # asdf 4 | # 5 | # Exception: tessdfte 6 | # finally 7 | # --output:end 8 | 9 | 10 | class TestException(Exception) { 11 | func __init__(msg, code) { 12 | Exception.__init__(this, msg) 13 | this.code = code 14 | } 15 | } 16 | 17 | try { 18 | echo asdf 19 | throw TestException("tessdfte", 5) 20 | echo fdsa 21 | } catch GlobException as t { 22 | print(string(t)) 23 | } catch TestException as t { 24 | print(type(t)) 25 | print("Exception: " + string(t)) 26 | } finally { 27 | print("finally") 28 | } 29 | -------------------------------------------------------------------------------- /test/interpreter/oop/try_catch2.sh: -------------------------------------------------------------------------------- 1 | # interfaces tests 2 | # --output:start 3 | # asdf 4 | # 5 | # Exception: tessdfte 6 | # --output:end 7 | 8 | class TestException(Exception) { 9 | func __init__(msg, code) { 10 | Exception.__init__(this, msg) 11 | this.code = code 12 | } 13 | } 14 | 15 | try { 16 | echo asdf 17 | throw TestException("tessdfte", 5) 18 | echo fdsa 19 | } catch GlobException as t { 20 | print(t) 21 | } catch Exception, TestException as t { 22 | print(type(t)) 23 | print("Exception: " + t) 24 | } 25 | -------------------------------------------------------------------------------- /test/interpreter/oop/try_catch3.sh: -------------------------------------------------------------------------------- 1 | # interfaces tests 2 | # --output:start 3 | # asdf 4 | # 5 | # Exception: zXxers: No such file or directory 6 | # finally 7 | # --output:end 8 | 9 | try { 10 | echo asdf 11 | zXxers 12 | echo fdsa 13 | } catch GlobException as t { 14 | print(string(t)) 15 | } catch Exception as t { 16 | print(type(t)) 17 | print("Exception: " + string(t)) 18 | } finally { 19 | print("finally") 20 | } 21 | -------------------------------------------------------------------------------- /test/interpreter/oop/try_catch4.sh: -------------------------------------------------------------------------------- 1 | # interfaces tests 2 | # --output:start 3 | # asdf 4 | # Exception 5 | # finally 6 | # --output:end 7 | 8 | try { 9 | echo asdf 10 | zXxers # not found command 11 | echo fdsa 12 | } catch GlobException as t { 13 | print(string(t)) 14 | } catch Exception { 15 | print("Exception") 16 | } finally { 17 | print("finally") 18 | } 19 | -------------------------------------------------------------------------------- /test/interpreter/oop/var_decl.sh: -------------------------------------------------------------------------------- 1 | # inner class test 2 | # --output:start 3 | # 8 4 | # oktest: 8 5 | # 8 6 | # [25, 10] 7 | # oktest: [25, 10] 8 | # 8 9 | # --output:end 10 | 11 | class Test { 12 | var x = 8 13 | 14 | func fn() { 15 | return "ok" 16 | } 17 | 18 | func Print() { 19 | print(this.fn(), "test: ", Test.x) 20 | } 21 | } 22 | 23 | t = Test() 24 | print(Test.x) 25 | t.Print() 26 | Test.x = [15, 10] 27 | print(t.x) 28 | Test.x[0] = 25 29 | print(Test.x) 30 | t.Print() 31 | print(t.x) 32 | -------------------------------------------------------------------------------- /test/interpreter/run.sh: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Alex Silva Torres 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # Run tests for shpp interpreter 16 | 17 | func get_file_lines(file) { 18 | content = $(cat ${file}) 19 | 20 | lines = [] 21 | 22 | for line in content { 23 | if line[0] == "#" { 24 | lines.append(line[1:]) 25 | } 26 | } 27 | 28 | return lines 29 | } 30 | 31 | func get_file_output(file) { 32 | 33 | lines = get_file_lines(file) 34 | 35 | get = false 36 | out_lines = [] 37 | 38 | for line in lines { 39 | if line.trim() == "--output:end" { 40 | get = false 41 | } 42 | 43 | if get { 44 | out_lines.append(line.trim()) 45 | } 46 | 47 | if line.trim() == "--output:start" { 48 | get = true 49 | } 50 | } 51 | 52 | return out_lines 53 | } 54 | 55 | func compare_lines(out_lines, expected_lines) { 56 | for out_line, expected_line in out_lines, expected_lines { 57 | if (len(expected_line) > 0) && (expected_line[0] == "*") { 58 | if out_line.find("Error") == false { 59 | return false 60 | } 61 | 62 | continue 63 | } 64 | if out_line != expected_line { 65 | return false 66 | } 67 | } 68 | 69 | return true 70 | } 71 | 72 | # shpp interpreter has several limitations at this moment 73 | # on the future this test must be improved 74 | 75 | shpp_path = "../build/shell/shpp" 76 | test_path = "../test/interpreter/" 77 | 78 | folders = [] 79 | 80 | for p in $(ls ${test_path}) { 81 | ptest = test_path + p 82 | 83 | if path.is_dir(ptest) { 84 | folders.append(ptest) 85 | } 86 | } 87 | 88 | for folder in folders { 89 | for f in $(ls ${folder}) { 90 | file = folder +"/"+ f 91 | fres = get_file_output(file) 92 | fout = $(./${shpp_path} ${file} 2>&1) 93 | 94 | if compare_lines(fout, fres) { 95 | echo test ${file} correct 96 | } else { 97 | echo test ${file} error 98 | expected = fres.join("\n") 99 | print("output: ", string(fout)) 100 | print("expected: ", expected) 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /test/lib/argparse_test.sh: -------------------------------------------------------------------------------- 1 | import ArgumentParser from "./../../lib/argparse.sh" 2 | 3 | argparse = ArgumentParser() 4 | argparse.add_argument("--test", "test") 5 | argparse.process(sys.argv) 6 | print(argparse.vars()) 7 | -------------------------------------------------------------------------------- /test/lib/textcolor_test.sh: -------------------------------------------------------------------------------- 1 | import * from "./../../lib/textcolor.sh" 2 | 3 | print(red("hello")) 4 | -------------------------------------------------------------------------------- /test/unit/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src) 2 | CHECK_CXX_COMPILER_FLAG("-std=c++14" COMPILER_SUPPORTS_CXX11) 3 | 4 | include_directories(SYSTEM ${GTEST_INCLUDE_DIRS}) 5 | 6 | file(GLOB SOURCES_TEST ${CMAKE_CURRENT_SOURCE_DIR}/*.cc) 7 | foreach(local_file ${SOURCES_TEST}) 8 | get_filename_component(local_filename ${local_file} NAME_WE) 9 | 10 | add_executable(${local_filename} ${local_file}) 11 | target_link_libraries(${local_filename} shpp ${CMAKE_THREAD_LIBS_INIT} 12 | ${googletest_STATIC_LIBRARIES} 13 | ${Boost_FILESYSTEM_LIBRARY} 14 | ${Boost_SYSTEM_LIBRARY}) 15 | add_test(UnitTests ${local_filename}) 16 | endforeach() 17 | -------------------------------------------------------------------------------- /test/unit/cmd.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "cmd-exec.h" 5 | 6 | TEST(Token, Print) { 7 | using namespace shpp::internal; 8 | } 9 | 10 | 11 | int main(int argc, char **argv) { 12 | ::testing::InitGoogleTest(&argc, argv); 13 | return RUN_ALL_TESTS(); 14 | } 15 | -------------------------------------------------------------------------------- /test/unit/extract_expr.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "interpreter/cmd-executor.h" 6 | 7 | TEST(Token, Print) { 8 | using namespace shpp::internal; 9 | std::cout << "adfsdf"; 10 | std::cout << "adfsdf"; 11 | std::cout << "adfsdf"; 12 | std::cout << "adfsdf"; 13 | std::cout << "adfsdf"; 14 | std::cout << "adfsdf"; 15 | 16 | 17 | 18 | 19 | } 20 | 21 | 22 | int main(int argc, char **argv) { 23 | ::testing::InitGoogleTest(&argc, argv); 24 | return RUN_ALL_TESTS(); 25 | } 26 | -------------------------------------------------------------------------------- /test/unit/lexer_test.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "parser/lexer.h" 5 | 6 | TEST(Lexer, Check) { 7 | using namespace shpp::internal; 8 | 9 | Lexer l(":! <= ->rwe\n asdf; \n4.5.7 af/ds /home/alex/tst ./test/qua\\ sdf/sdf.txt\n" 10 | "if test == 4.48 { while (true) {echo \"aasdf\"}}"); 11 | std::cout << "Lexer\n"; 12 | TokenStream ts = l.Scanner(); 13 | std::cout << "Scanner\n"; 14 | do { 15 | std::cout << "loop\n"; 16 | Token t = ts.CurrentToken(); 17 | std::cout << t; 18 | } while (ts.Advance()); 19 | } 20 | 21 | TEST(Lexer, Comment) { 22 | using namespace shpp::internal; 23 | 24 | Lexer l(" for asdf in sdf[4]\\\n hello {# comment\n echo oi}"); 25 | std::cout << "Lexer\n"; 26 | TokenStream ts = l.Scanner(); 27 | std::cout << "Scanner\n"; 28 | do { 29 | std::cout << "loop\n"; 30 | Token t = ts.CurrentToken(); 31 | std::cout << t; 32 | } while (ts.Advance()); 33 | } 34 | 35 | TEST(Lexer, Error) { 36 | using namespace shpp::internal; 37 | 38 | Lexer l("\"asdfsd\n in sdf[4] {# comment\n echo oi}"); 39 | std::cout << "Lexer\n"; 40 | TokenStream ts = l.Scanner(); 41 | std::cout << "Scanner\n"; 42 | do { 43 | std::cout << "loop\n"; 44 | Token t = ts.CurrentToken(); 45 | std::cout << t; 46 | } while (ts.Advance()); 47 | } 48 | 49 | int main(int argc, char **argv) { 50 | ::testing::InitGoogleTest(&argc, argv); 51 | return RUN_ALL_TESTS(); 52 | } 53 | 54 | -------------------------------------------------------------------------------- /test/unit/parser_test.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "parser/parser.h" 5 | #include "ast/ast-printer.h" 6 | 7 | TEST(Lexer, Check) { 8 | using namespace shpp::internal; 9 | 10 | Lexer l("z = (6 << 2), 5 +4* -4+51-(\n\n\n\"oi\" - wef[6*2]->sef[es[\n5+1\n]]) /\n78+t->p(\n4*3,\n ad[4])\n\np[1], a = a && b,t[p]->f()"); 11 | std::cout << "Lexer\n"; 12 | TokenStream ts = l.Scanner(); 13 | Parser p(std::move(ts)); 14 | auto res = p.AstGen(); 15 | 16 | if (p.nerrors() == 0) { 17 | std::cout << "Correct analysis\n"; 18 | AstPrinter visitor; 19 | visitor.Visit(res.NodePtr()); 20 | } else { 21 | std::cout << "Parser error analysis\n"; 22 | } 23 | } 24 | 25 | int main(int argc, char **argv) { 26 | ::testing::InitGoogleTest(&argc, argv); 27 | return RUN_ALL_TESTS(); 28 | } 29 | 30 | 31 | -------------------------------------------------------------------------------- /test/unit/token_test.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "parser/token.h" 5 | 6 | TEST(Token, Print) { 7 | using namespace shpp::internal; 8 | 9 | Token t1(TokenKind::KW_IF, true, 1, 1); 10 | Token t2(TokenKind::INT_LITERAL, 5, true, 1, 1); 11 | Token t3(TokenKind::REAL_LITERAL, float(2.54), true, 1, 1); 12 | Token t4(TokenKind::KW_WHILE, true, 1, 1); 13 | 14 | std::cout << t1 << t2 << t3 << t4; 15 | } 16 | 17 | TEST(Token, Comparing) { 18 | using namespace shpp::internal; 19 | 20 | Token t1(TokenKind::KW_IF, "if", true, 1, 1); 21 | Token t2(TokenKind::INT_LITERAL, 5, true, 1, 1); 22 | Token t3(TokenKind::REAL_LITERAL, float(2.54), true, 1, 1); 23 | 24 | ASSERT_TRUE(t1 == TokenKind::KW_IF); 25 | ASSERT_TRUE(t2.Is(TokenKind::INT_LITERAL)); 26 | ASSERT_TRUE(t3 == TokenKind::REAL_LITERAL); 27 | 28 | ASSERT_TRUE(t1 != TokenKind::INT_LITERAL); 29 | ASSERT_TRUE(t2.IsNot(TokenKind::REAL_LITERAL)); 30 | ASSERT_TRUE(t3 != TokenKind::KW_IF); 31 | 32 | ASSERT_TRUE(t1.IsAny(TokenKind::INT_LITERAL, TokenKind::KW_IF)); 33 | ASSERT_TRUE(t2.IsNot(TokenKind::REAL_LITERAL, TokenKind::KW_IF)); 34 | } 35 | 36 | TEST(TokenStream, Operations) { 37 | using namespace shpp::internal; 38 | 39 | Token t1(TokenKind::KW_IF, true, 1, 1); 40 | Token t2(TokenKind::INT_LITERAL, 5, true, 1, 1); 41 | Token t3(TokenKind::REAL_LITERAL, float(2.54), true, 1, 1); 42 | 43 | TokenStream ts; 44 | ts.PushToken(std::move(t1)); 45 | ts.PushToken(std::move(t2)); 46 | ts.PushToken(std::move(t3)); 47 | 48 | do { 49 | std::cout << ts.CurrentToken(); 50 | } while (ts.Advance()); 51 | } 52 | 53 | int main(int argc, char **argv) { 54 | ::testing::InitGoogleTest(&argc, argv); 55 | return RUN_ALL_TESTS(); 56 | } 57 | -------------------------------------------------------------------------------- /tools/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(print_ast) 2 | add_subdirectory(print_tokens) 3 | add_subdirectory(interpreter) 4 | add_subdirectory(print_parser_string) 5 | -------------------------------------------------------------------------------- /tools/interpreter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src) 2 | CHECK_CXX_COMPILER_FLAG("-std=c++14" COMPILER_SUPPORTS_CXX11) 3 | 4 | add_executable(interpreter interpreter.cc) 5 | target_link_libraries(interpreter 6 | shpp 7 | ${CMAKE_THREAD_LIBS_INIT} 8 | ${Boost_FILESYSTEM_LIBRARY} 9 | ${Boost_SYSTEM_LIBRARY}) 10 | 11 | -------------------------------------------------------------------------------- /tools/interpreter/interpreter.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "interpreter/interpreter.h" 6 | 7 | int main(int argc, char **argv) { 8 | using namespace shpp::internal; 9 | 10 | if (argc < 2) { 11 | std::cout << "usage: interpreter \n"; 12 | return -1; 13 | } 14 | 15 | std::string name = argv[1]; 16 | 17 | Interpreter i; 18 | try { 19 | ScriptStream file(name); 20 | i.Exec(file); 21 | } catch (shpp::RunTimeError& e) { 22 | std::cout << "Error: " << e.pos().line << ": " << e.pos().col 23 | << ": " << e.what() << "\n"; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tools/print_ast/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src) 2 | CHECK_CXX_COMPILER_FLAG("-std=c++14" COMPILER_SUPPORTS_CXX11) 3 | 4 | add_executable(print_ast print_ast.cc) 5 | target_link_libraries(print_ast shpp ${CMAKE_THREAD_LIBS_INIT}) 6 | 7 | 8 | -------------------------------------------------------------------------------- /tools/print_ast/print_ast.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "parser/parser.h" 6 | #include "ast/ast-printer.h" 7 | 8 | int main(int argc, char **argv) { 9 | using namespace shpp::internal; 10 | 11 | if (argc < 2) { 12 | std::cout << "usage: print_ast \n"; 13 | return -1; 14 | } 15 | 16 | std::ifstream file(argv[1]); 17 | std::stringstream buffer; 18 | buffer << file.rdbuf(); 19 | 20 | Lexer l(buffer.str()); 21 | std::cout << "Lexer\n"; 22 | TokenStream ts = l.Scanner(); 23 | Parser p(std::move(ts)); 24 | auto res = p.AstGen(); 25 | 26 | if (p.nerrors() == 0) { 27 | std::cout << "Correct analysis\n"; 28 | AstPrinter visitor; 29 | visitor.Visit(res.NodePtr()); 30 | } else { 31 | std::cout << "Parser error analysis:\n"; 32 | auto msgs = p.Msgs(); 33 | std::cout << msgs << "\n"; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tools/print_parser_string/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src) 2 | CHECK_CXX_COMPILER_FLAG("-std=c++14" COMPILER_SUPPORTS_CXX11) 3 | 4 | add_executable(print_parser_string print_parser_string.cc) 5 | target_link_libraries(print_parser_string shpp ${CMAKE_THREAD_LIBS_INIT}) 6 | -------------------------------------------------------------------------------- /tools/print_parser_string/print_parser_string.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "parser/parser_literal_string.h" 4 | int main(int argc, char* argv[]) { 5 | std::cout << "String: " << argv[1] << std::endl; 6 | shpp::internal::ParserLiteralString parser(argv[1]); 7 | parser.Scanner(); 8 | std::vector& vec = 9 | parser.getStringTokens(); 10 | 11 | std::cout << "Tokens count: " << vec.size() << std::endl; 12 | for (shpp::internal::LiteralStringToken& tk : vec) { 13 | std::cout << "String[" << tk.IsInterpretable() << "]:" << tk.GetStrToken() 14 | << std::endl; 15 | } 16 | return 0; 17 | } -------------------------------------------------------------------------------- /tools/print_tokens/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src) 2 | CHECK_CXX_COMPILER_FLAG("-std=c++14" COMPILER_SUPPORTS_CXX11) 3 | 4 | add_executable(print_tokens print_tokens.cc) 5 | target_link_libraries(print_tokens shpp ${CMAKE_THREAD_LIBS_INIT}) 6 | -------------------------------------------------------------------------------- /tools/print_tokens/print_tokens.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "parser/parser.h" 6 | #include "ast/ast-printer.h" 7 | 8 | int main(int argc, char **argv) { 9 | using namespace shpp::internal; 10 | 11 | if (argc < 2) { 12 | std::cout << "usage: print_ast \n"; 13 | return -1; 14 | } 15 | 16 | std::ifstream file(argv[1]); 17 | std::stringstream buffer; 18 | buffer << file.rdbuf(); 19 | 20 | Lexer l(buffer.str()); 21 | 22 | TokenStream ts = l.Scanner(); 23 | std::cout << "Scanner:\n"; 24 | do { 25 | Token t = ts.CurrentToken(); 26 | std::cout << t; 27 | } while (ts.Advance()); 28 | } 29 | 30 | --------------------------------------------------------------------------------