├── .github └── workflows │ └── ccpp.yml ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE ├── README.md ├── comet ├── CMakeLists.txt ├── comet.h └── main.c ├── docs ├── CNAME ├── _config.yml ├── _layouts │ └── default.html ├── index.md ├── stdlib │ ├── argument_exception.md │ ├── boolean.md │ ├── csv.md │ ├── datetime.md │ ├── directory.md │ ├── duration.md │ ├── enum.md │ ├── env.md │ ├── exception.md │ ├── file.md │ ├── function.md │ ├── hash.md │ ├── image.md │ ├── index.md │ ├── iterable.md │ ├── json.md │ ├── list.md │ ├── module.md │ ├── nil.md │ ├── number.md │ ├── object.md │ ├── set.md │ ├── socket.md │ ├── string.md │ ├── string_builder.md │ ├── thread.md │ ├── thread_synchronisation.md │ └── unittest.md └── syntax │ └── index.md ├── grammar.md ├── sample.cmt ├── stdlib ├── CMakeLists.txt ├── _init.c ├── boolean.c ├── colour.cpp ├── comet │ ├── CMakeLists.txt │ ├── coverage.cmt │ ├── csv.cmt │ ├── json.cmt │ └── unittest.cmt ├── cometlib.h ├── datetime_unix.cpp ├── datetime_win.cpp ├── directory.cpp ├── duration.cpp ├── enum.cpp ├── env.cpp ├── exception.c ├── file_common.cpp ├── file_unix.cpp ├── file_win.cpp ├── function.c ├── hash.cpp ├── image.c ├── iterable.c ├── list.cpp ├── module.c ├── native_functions.c ├── nil.c ├── number.c ├── object.c ├── private │ ├── colour.h │ ├── comet_stdlib.h │ ├── comet_string.h │ ├── datetime.hpp │ ├── directory.hpp │ ├── file_common.h │ ├── list.h │ ├── string_builder.h │ └── thread_sync_common.h ├── process.cpp ├── set.cpp ├── socket.c ├── string.c ├── string_builder.c ├── system.c ├── test │ ├── CMakeLists.txt │ ├── main.c │ ├── test_list.c │ ├── test_string.c │ └── tests.h ├── thread.c ├── thread_synchronisation_common.c ├── thread_synchronisation_unix.c ├── thread_synchronisation_win.c └── unittest │ └── _init.cmt ├── test_scripts ├── argument_exception_test.cmt ├── attribute_test.cmt ├── boolean.cmt ├── class_inherits_constructor.cmt ├── class_inherits_imported_class.cmt ├── class_inherits_object.cmt ├── class_operator_overload.cmt ├── class_with_field.cmt ├── colour.cmt ├── complex_exception_with_finally.cmt ├── datetime_test.cmt ├── default_params.cmt ├── directory_test.cmt ├── duration_test.cmt ├── enum_declaration.cmt ├── env_test.cmt ├── exception_handler.cmt ├── file_test.cmt ├── for_loop_test.cmt ├── foreach_loops.cmt ├── hash_test.cmt ├── image.cmt ├── import_test.cmt ├── import_test │ ├── main.cmt │ └── secondary.cmt ├── is.cmt ├── json_test.cmt ├── lambda.cmt ├── list_test.cmt ├── main_test.cmt ├── native_field_accessor.cmt ├── numbers_test.cmt ├── object_test.cmt ├── process_test.cmt ├── rest_params_test.cmt ├── set_test.cmt ├── socket_threads.cmt ├── splat_test.cmt ├── string_builder_test.cmt ├── string_test.cmt ├── ternary_test.cmt ├── throw_exception.cmt ├── variables.cmt └── while_loop_test.cmt └── vmlib ├── CMakeLists.txt ├── chunk.c ├── chunk.h ├── common.h ├── compiler ├── CMakeLists.txt ├── inc │ ├── compiler.h │ └── debug.h ├── src │ ├── compiler.c │ ├── compiler_defs.h │ ├── constants.c │ ├── constants.h │ ├── debug.c │ ├── declarations.c │ ├── declarations.h │ ├── emitter.c │ ├── emitter.h │ ├── expressions.c │ ├── expressions.h │ ├── source_files.c │ ├── sources.cmake │ ├── statements.c │ ├── statements.h │ ├── variables.c │ └── variables.h └── test │ ├── CMakeLists.txt │ ├── main.c │ ├── test_pratt_parser.c │ └── tests.h ├── import.cpp ├── import.h ├── lexer ├── CMakeLists.txt ├── inc │ └── scanner.h ├── src │ ├── scanner.c │ └── sources.cmake └── test │ ├── CMakeLists.txt │ ├── main.c │ ├── test_scanner.c │ └── tests.h ├── mem.c ├── mem.h ├── native.c ├── native.h ├── object_defs.h ├── objects.c ├── objects.h ├── table.c ├── table.h ├── test └── test_table.c ├── value.c ├── value.h ├── vm.c ├── vm.h └── vm_globals.c /.github/workflows/ccpp.yml: -------------------------------------------------------------------------------- 1 | name: C/C++ CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build-linux: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v4 11 | with: 12 | submodules: recursive 13 | - name: build-test 14 | shell: bash 15 | run: | 16 | cmake -B build -S . 17 | cmake --build build --target all --target test --target stdlib_test 18 | 19 | 20 | build-windows: 21 | runs-on: windows-latest 22 | 23 | steps: 24 | - uses: actions/checkout@v4 25 | with: 26 | submodules: recursive 27 | - name: build-test 28 | run: | 29 | cmake -B build -S . -DCMAKE_SYSTEM_VERSION="10.0.20348.0" 30 | cmake --build build --target ALL_BUILD --target RUN_TESTS 31 | # - run: cmake --build build --target ALL_BUILD --target RUN_TESTS --target stdlib_test 32 | 33 | 34 | build-release-windows: 35 | runs-on: windows-latest 36 | 37 | steps: 38 | - uses: actions/checkout@v4 39 | with: 40 | submodules: recursive 41 | - name: build 42 | shell: bash 43 | run: | 44 | cmake -B release -S . -DCMAKE_BUILD_TYPE=Release -DCMAKE_SYSTEM_VERSION="10.0.20348.0" -DVERSION=${{ github.ref_name }} 45 | cmake --build release --target ALL_BUILD --config Release 46 | 47 | - name: Create-Archive 48 | shell: powershell 49 | if: ${{ startsWith(github.ref, 'refs/tags/') }} 50 | run: | 51 | mkdir output 52 | cp release\comet\Release\comet.exe output 53 | cp stdlib\comet\*.cmt output 54 | Compress-Archive -Path output\* -DestinationPath comet_windows_${{ github.ref_name }}.zip 55 | 56 | - name: Release-Windows 57 | uses: softprops/action-gh-release@v0.1.15 58 | if: ${{ startsWith(github.ref, 'refs/tags/') }} 59 | with: 60 | files: comet_windows_${{ github.ref_name }}.zip 61 | 62 | build-release-linux: 63 | runs-on: ubuntu-latest 64 | 65 | steps: 66 | - uses: actions/checkout@v4 67 | with: 68 | submodules: recursive 69 | 70 | - name: build 71 | shell: bash 72 | run: | 73 | cmake -B release -S . -DCMAKE_BUILD_TYPE=Release -DVERSION=${{ github.ref_name }} 74 | cmake --build release --target all 75 | 76 | - name: Create-Archive 77 | if: ${{ startsWith(github.ref, 'refs/tags/') }} 78 | shell: bash 79 | run: zip -j comet_linux_${{ github.ref_name }}.zip release/comet/comet stdlib/comet/*.cmt 80 | 81 | - name: Release-Linux 82 | uses: softprops/action-gh-release@v0.1.15 83 | if: ${{ startsWith(github.ref, 'refs/tags/') }} 84 | with: 85 | files: comet_linux_${{ github.ref_name }}.zip -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | .vscode/ 3 | build/ 4 | release/ 5 | .vs/ 6 | .coverage.json -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "date"] 2 | path = date 3 | url = https://github.com/HowardHinnant/date 4 | [submodule "utf8proc"] 5 | path = utf8proc 6 | url = https://github.com/JuliaStrings/utf8proc 7 | [submodule "stb"] 8 | path = stb 9 | url = https://github.com/nothings/stb 10 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.10) 2 | project (comet) 3 | enable_language(C) 4 | enable_language(CXX) 5 | enable_testing() 6 | 7 | if (WIN32) 8 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") 9 | elseif (CMAKE_BUILD_TYPE MATCHES DEBUG) 10 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0 -g -ggdb3 -Wall -Wextra -Werror") 11 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -g -ggdb3 -Wall -Wextra -Werror") 12 | endif () 13 | 14 | set(CMAKE_C_STANDARD 11 CACHE INTERNAL "Set C standard to C11") 15 | set(CMAKE_CXX_STANDARD 20 CACHE INTERNAL "Set C++ standard to C++20") 16 | set(CMAKE_LINKER_FLAGS "${CMAKE_LINKER_FLAGS}") 17 | 18 | find_package(Threads REQUIRED) 19 | SET( BUILD_SHARED_LIBS OFF CACHE INTERNAL "Build static libraries") 20 | SET( BUILD_TZ_LIB ON CACHE INTERNAL "Build the timezone library") 21 | SET( USE_SYSTEM_TZ_DB ON CACHE INTERNAL "Use the system TimeZone database, removing need for libcurl") 22 | SET(UTF8PROC_INSTALL OFF CACHE INTERNAL "Don't install the utf8proc lib") 23 | set(MEMORYCHECK_COMMAND_OPTIONS "--track-origins=yes --leak-check=full --errors-for-leak-kinds=all --error-exitcode=1 --log-fd=2") 24 | add_subdirectory(date) 25 | add_subdirectory(utf8proc) 26 | add_subdirectory(stdlib) 27 | add_subdirectory(vmlib) 28 | add_subdirectory(comet) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The Comet Programming Language 2 | 3 | This is mostly built for fun and a way of taking things apart to see how they work. It is based on the interpreter from [Crafting Interpreters](https://github.com/munificent/craftinginterpreters) 4 | 5 | After programming for a while, there are specific things about the dynamic languages that I've 6 | used that really annoy me, so I'm making my own language to improve some of those. No doubt I will add things that are annoying. 7 | 8 | It takes inspiration from the languages I've used most heavily: 9 | 10 | - C 11 | - JavaScript 12 | - Ruby 13 | - Python 14 | - C# 15 | 16 | I don't have a firm idea of the syntax or semantics yet - I will be making those up as I go along. I'd like to think that I'll document it, but it will likely be half-assed, and incomplete. Have fun! 17 | 18 | [Current working sample](sample.cmt) 19 | -------------------------------------------------------------------------------- /comet/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.10) 2 | enable_language(C) 3 | 4 | add_executable(comet main.c) 5 | if (WIN32) 6 | add_compile_definitions(WIN32=1) 7 | target_link_libraries(comet PRIVATE stdlib vmlib) 8 | SET_TARGET_PROPERTIES(comet PROPERTIES LINK_FLAGS "/link setargv.obj") 9 | else() 10 | target_link_libraries(comet PRIVATE stdlib vmlib m c) 11 | endif (WIN32) 12 | target_include_directories(comet PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") 13 | 14 | install(TARGETS comet 15 | RUNTIME DESTINATION bin 16 | LIBRARY DESTINATION lib) 17 | 18 | set (git_cmd "git") 19 | execute_process(COMMAND "git" "describe" "--tags" 20 | WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} 21 | OUTPUT_VARIABLE VERSION) 22 | 23 | target_compile_definitions(comet PRIVATE VERSION_STRING=${VERSION}) 24 | 25 | set (toplevel_source_dir "${CMAKE_CURRENT_SOURCE_DIR}/..") 26 | add_custom_target(stdlib_test 27 | ${CMAKE_COMMAND} -E env "COMET_LIB_DIR=${toplevel_source_dir}/stdlib/comet" 28 | "$" 29 | "${toplevel_source_dir}/stdlib/comet/unittest.cmt" 30 | "--coverage" 31 | "${toplevel_source_dir}/test_scripts/*.cmt" 32 | WORKING_DIRECTORY "${toplevel_source_dir}" 33 | DEPENDS comet 34 | ) 35 | 36 | add_custom_target(memtest 37 | ${CMAKE_COMMAND} -E env "COMET_LIB_DIR=${toplevel_source_dir}/stdlib/comet" 38 | "valgrind" 39 | "$" 40 | "${toplevel_source_dir}/stdlib/comet/unittest.cmt" 41 | "--coverage" 42 | "${toplevel_source_dir}/test_scripts/*.cmt" 43 | WORKING_DIRECTORY "${toplevel_source_dir}" 44 | DEPENDS comet 45 | ) -------------------------------------------------------------------------------- /comet/comet.h: -------------------------------------------------------------------------------- 1 | #ifndef _COMET_H_ 2 | #define _COMET_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include "value.h" 9 | #include "objects.h" 10 | #include "native.h" 11 | #include "mem.h" 12 | 13 | void init_comet(VM* vm); 14 | void init_stdlib(VM* vm); 15 | 16 | VALUE obj_hash(VM* vm, VALUE self, int arg_count, VALUE* arguments); 17 | 18 | VALUE string_create(VM* vm, char* chars, int length); 19 | int string_compare_to_cstr(VALUE self, const char* cstr); 20 | const char* string_get_cstr(VALUE self); 21 | VALUE string_hash(VM* vm, VALUE self, int arg_count, VALUE* arguments); 22 | uint32_t string_hash_cstr(const char* string, int length); 23 | 24 | void exception_set_stacktrace(VM* vm, VALUE self, VALUE stacktrace); 25 | VALUE exception_get_stacktrace(VM* vm, VALUE self); 26 | VALUE exception_get_message(VM* vm, VALUE self, int arg_count, VALUE* arguments); 27 | VALUE instanceof(VALUE self, VALUE klass); 28 | 29 | bool bool_is_falsey(Value value); 30 | 31 | VALUE create_number(VM* vm, double number); 32 | double number_get_value(VALUE self); 33 | VALUE number_operator(VM* vm, VALUE self, VALUE* arguments, OPERATOR op); 34 | 35 | VALUE list_create(VM* vm); 36 | VALUE list_add(VM* vm, VALUE self, int arg_count, VALUE* arguments); 37 | VALUE list_get_at(VM* vm, VALUE self, int arg_count, VALUE* arguments); 38 | VALUE list_length(VM* vm, VALUE self, int arg_count, VALUE* arguments); 39 | 40 | VALUE hash_create(VM *vm); 41 | VALUE hash_add(VM *vm, VALUE self, int arg_count, VALUE *arguments); 42 | VALUE hash_has_key_q(VM* vm, VALUE self, int arg_count, VALUE* arguments); 43 | VALUE hash_get(VM *vm, VALUE self, int arg_count, VALUE *arguments); 44 | 45 | void hash_mark_contents(VALUE self); 46 | void list_mark_contents(VALUE self); 47 | void set_mark_contents(VALUE self); 48 | void enum_mark_contents(VALUE self); 49 | void thread_mark_contents(VALUE self); 50 | void module_mark_contents(VALUE self); 51 | 52 | VALUE module_create(VM* vm, const char* filename); 53 | bool module_is_initialized(VALUE module); 54 | void module_set_initialized(VALUE module); 55 | const char* module_filename(VALUE module); 56 | void module_set_main(VALUE module, ObjFunction* function); 57 | ObjFunction* module_get_main(VALUE module); 58 | VALUE module_functions(VM *vm, VALUE self, int arg_count, VALUE *arguments); 59 | 60 | VALUE duration_create(VM *vm, int64_t count); 61 | #ifdef __cplusplus 62 | } 63 | #endif 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /comet/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "common.h" 5 | #include "chunk.h" 6 | #include "debug.h" 7 | #include "vm.h" 8 | #include "cometlib.h" 9 | #include "compiler.h" 10 | #include "import.h" 11 | 12 | #define stringify(s) #s 13 | #define stringify_value(s) stringify(s) 14 | 15 | static VM virtualMachine; 16 | 17 | static int startingArg = 2; 18 | 19 | static void repl() 20 | { 21 | SourceFile source; 22 | char line[1024]; 23 | source.source = line; 24 | source.path = ""; 25 | for (;;) 26 | { 27 | printf("> "); 28 | 29 | if (!fgets(line, sizeof(line), stdin)) 30 | { 31 | printf("\n"); 32 | break; 33 | } 34 | 35 | Value module = compile(&source, &virtualMachine); 36 | if (module == NIL_VAL) 37 | { 38 | runtimeError(&virtualMachine, "compilation error"); 39 | } 40 | else 41 | { 42 | interpret(&virtualMachine, module); 43 | } 44 | } 45 | } 46 | 47 | static void defineMain(Value main) 48 | { 49 | Value main_varname = copyString(&virtualMachine, "__MAIN__", 8); 50 | push(&virtualMachine, main_varname); 51 | // This is the fully resolved, canonical path - the same as how __FILE__ will be defined 52 | // to_import might be relative or an unresolved library path 53 | const char *main_filename = module_filename(main); 54 | Value main_varvalue = copyString(&virtualMachine, main_filename, strlen(main_filename)); 55 | push(&virtualMachine, main_varvalue); 56 | addGlobal(main_varname, main_varvalue); 57 | popMany(&virtualMachine, 2); 58 | } 59 | 60 | static void runFile(const char *path) 61 | { 62 | Value to_import = copyString(&virtualMachine, path, strlen(path)); 63 | push(&virtualMachine, to_import); 64 | Value main = import_from_file(&virtualMachine, NULL, to_import); 65 | defineMain(main); 66 | pop(&virtualMachine); 67 | 68 | InterpretResult result = interpret(&virtualMachine, main); 69 | 70 | if (result == INTERPRET_COMPILE_ERROR) exit(65); 71 | if (result == INTERPRET_RUNTIME_ERROR) exit(70); 72 | } 73 | 74 | void initArgv(VM *vm, int argc, const char **argv) 75 | { 76 | VALUE argv_list = list_create(vm); 77 | push(vm, argv_list); 78 | for (int i = startingArg; i < argc; i++) 79 | { 80 | VALUE arg = copyString(vm, argv[i], strlen(argv[i])); 81 | push(vm, arg); 82 | list_add(vm, argv_list, 1, &arg); 83 | pop(vm); 84 | } 85 | addGlobal(copyString(vm, "ARGV", 4), argv_list); 86 | pop(vm); 87 | } 88 | 89 | int main(int argc, const char **argv) 90 | { 91 | if (argc >= 2) 92 | { 93 | for (int i = 1; i < argc; i++) 94 | { 95 | if (strncmp(argv[i], "--version", 9) == 0) 96 | { 97 | printf("comet programming language, %s\n", stringify_value(VERSION_STRING)); 98 | return 0; 99 | } 100 | } 101 | } 102 | init_comet(&virtualMachine); 103 | 104 | initArgv(&virtualMachine, argc, argv); 105 | const char *version_string = stringify_value(VERSION_STRING); 106 | addGlobal( 107 | copyString(&virtualMachine, "COMET_VERSION", 13), 108 | copyString(&virtualMachine, version_string, strlen(version_string))); 109 | 110 | if (argc == 1) 111 | { 112 | repl(); 113 | } 114 | else if (argc >= 2) 115 | { 116 | runFile(argv[startingArg-1]); 117 | } 118 | else 119 | { 120 | fprintf(stderr, "Usage: comet [path]\n"); 121 | exit(64); 122 | } 123 | 124 | #ifdef WIN32 125 | socket_cleanup(); 126 | #endif 127 | finalizeGarbageCollection(); 128 | freeVM(&virtualMachine); 129 | freeGlobals(); 130 | return 0; 131 | } -------------------------------------------------------------------------------- /docs/CNAME: -------------------------------------------------------------------------------- 1 | cometlang.com -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | remote_theme: pages-themes/dinky@v0.2.0 2 | plugins: 3 | - jekyll-remote-theme # add this line to the plugins list if you already have one 4 | github: 5 | zip_url: https://github.com/cometlang/comet/releases/latest -------------------------------------------------------------------------------- /docs/_layouts/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {% seo %} 8 | 9 | 10 | 11 | 14 | {% include head-custom.html %} 15 | 16 | 17 |
18 |
19 |

{{ site.title | default: site.github.repository_name }}

20 |

{{ site.description | default: site.github.project_tagline }}

21 | 22 | 28 | 29 | {% if site.github.is_project_page %} 30 |

This project is maintained by {{ site.github.owner_name }}

31 | {% endif %} 32 | 33 | {% if site.github.is_user_page %} 34 | 37 | {% endif %} 38 |
39 | 40 |
41 | {{ content }} 42 |
43 | 44 | 47 |
48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # The Comet Programming Language 2 | 3 | # [Syntax](syntax/index.md) 4 | # [Stdlib](stdlib/index.md) 5 | 6 | Comet was created with the help of [Crafting Interpreters](https://www.craftinginterpreters.com) by Bob Nystrom 7 | 8 | Comet is in the C family of languages and is object-oriented. It liberally borrows from other languages, such as Python, Ruby, and JavaScript to name a few. I've tried to keep the semantics unsurprising and the novelty-factor low. So why would you want to use this instead of say, Python, Ruby or JavaScript? 9 | 10 | 1. It's small. At less than 2MB (including the standard libary in a single executable) it's ideal for an embedded environment 11 | 2. It's a self-contained language. Many scripting languages require you to understand the low-level C programming 12 | techniques in order to use them (like the file modes for fopen, or the socket dance). I've attempted to abstract 13 | those APIs into a more naturally comet style. 14 | 3. It's learned from (some of) the mistakes of some of its inspirational languages 15 | - Ruby's heavy use of optional syntax often makes it difficult to understand what's happening 16 | - Python attempts to search large portions of the filesystem when it imports modules 17 | - JavaScript's type-coercion makes for error-prone code 18 | - C's (lack of) memory management is one of the major sources of security bugs in the world 19 | - Python's dogmatic approach to whitespace means that many useful constructs just aren't reasonable to implement 20 | - C#'s empty `throw;` statement is often forgotten 21 | - JavaScript's many exceptions to the rule make programming it very error-prone 22 | - Java's verbose syntax make it very unfriendly to programmers and keyboards alike 23 | - C++'s compilation rules and templating system make reasoning about programs very challenging 24 | 25 | ## Drawbacks 26 | - Currently, the math operations are _very_ slow. Because of how operator overloading is implemented, all math operations incur the cost of an object function call with virtual method resolution. 27 | - The garbage collection is pretty basic, naive and slow. For every garbage collection, we iterate through every object in the system, which is _much_ more than necessary. A relatively straight forward improvement would be to implement a generational system. Also, all memory operations on all threads come to a halt when the GC is running (which essentially means all execution stops). 28 | -------------------------------------------------------------------------------- /docs/stdlib/argument_exception.md: -------------------------------------------------------------------------------- 1 | [up](index.md) 2 | 3 | ## ArgumentException 4 | inherits [Exception](exception.md) 5 | 6 | ### static methods 7 | - `throw_if_nil()` throw an ArgumentException if the given value is `nil` 8 | - `throw_if_empty()` throw an ArgumentException if the given value returns `true` to `empty?` (which `nil` does) 9 | - `throw_if_nil_or_empty()` throw an ArgumentException if the given value is `nil` or returns `true` to `empty?` 10 | - `throw_if_nil_or_whitespace()` throw an ArgumentException if the given value is `nil`, returns `true` to `empty?` or is a string made up of entirely whitespace characters. This will also throw an ArgumentException itself if the given value is neither `nil` nor a [String](string.md). 11 | -------------------------------------------------------------------------------- /docs/stdlib/boolean.md: -------------------------------------------------------------------------------- 1 | [up](index.md) 2 | 3 | ## Boolean 4 | inherits [Object](object.md) 5 | 6 | There are two singleton instances of Boolean in the interpreter accessible via the keywords `true` and `false` 7 | 8 | ### constructor 9 | Boolean(value) 10 | Takes a single argument, and converts to a boolean, based on the truthiness of the value. 11 | 12 | ### methods 13 | - `to_string()` returns either "true" or "false" 14 | 15 | ### static methods 16 | - `parse(value)` parses either a string (case insensitively) or an integer (following the C rules) or falling back to the truthiness of the value 17 | 18 | ### operators 19 | - `==` compares by truthiness 20 | -------------------------------------------------------------------------------- /docs/stdlib/csv.md: -------------------------------------------------------------------------------- 1 | [up](index.md) 2 | 3 | ## csv 4 | This is currently an aspirational csv parser. It is aspirational, because the implementation 5 | as it stands, is `string.split(',')`. It will get better. 6 | 7 | ### functions 8 | 9 | - `parse_from_string(str, has_header = false)` parse the csv using an in-memory string 10 | - `parse_from_file(filename, has_header = false)` parse the csv using the given filename to open the file 11 | 12 | Both of these functions return an iterable containing a `CsvRow` which is itself iterable, and will maintain 13 | the order as specified in the source. -------------------------------------------------------------------------------- /docs/stdlib/datetime.md: -------------------------------------------------------------------------------- 1 | [up](index.md) 2 | 3 | ## Datetime 4 | inherits [Object](object.md) 5 | 6 | ## methods 7 | - `init([year],[month],[day],[hour],[minute],[seconds],[milliseconds])` Create a datetime in the current timezone from the given values. 8 | - `to_string()` outputs a string representation of the datetime in a json-compliant ISO8601 format. yyyy-mm-ddTHH:MM:ss.mmmZ 9 | - `year()` get the year portion of the datetime 10 | - `month()` get the month portion of the datetime 11 | - `day()` get the day portion of the datetime 12 | - `hours()` get the hours portion of the datetime 13 | - `minutes()` get the minutes portion of the datetime 14 | - `seconds()` get the seconds portion of the datetime 15 | - `milliseconds()` get the milliseconds portion of the datetime 16 | 17 | ### static methods 18 | - `now()` returns a DateTime representing the current local time 19 | - `parse('to_parse', ['format'])` parse a [String](string.md) into a DateTime. See [here](https://en.cppreference.com/w/cpp/chrono/parse) for format string specifiers. `format` defaults to '%Y-%m-%dT%H:%M:%6S%z' which will parse the output of `DateTime::to_string()` 20 | 21 | ### operators 22 | - `-` subtracts either a datetime or a duration to the existing datetime. The returned type is the same as the operand. 23 | - `==` compares two datetimes and returns true if they both represent the same point in time 24 | -------------------------------------------------------------------------------- /docs/stdlib/directory.md: -------------------------------------------------------------------------------- 1 | [up](index.md) 2 | 3 | ## Directory 4 | inherits [Object](object.md) 5 | 6 | ### methods 7 | - `parent()` converts the directory to an absolute path and returns the parent Directory 8 | - `absolute()` gets the Directory representing the absolute path 9 | - `list()` returns a [List](list.md) of Directory objects containing the contents of the directory. Does not include the pseudo paths of '.' and '..' 10 | - `to_string()` returns the path of the directory as a [String](string.md) 11 | 12 | ### static methods 13 | - `list(path)` returns a [List](list.md) of Directory objects containing the contents of the directory. Does not include the pseudo paths of '.' and '..' 14 | - `directory?(path)` returns a [Boolean](boolean.md) if the given path is a directory 15 | - `remove(path, [recursively])` deletes a directory from the filesystem, optionally deleting the contents first 16 | - `delete(path, [recursively])` alias for remove 17 | - `create(path)` creates the given directory and all parent directories required 18 | -------------------------------------------------------------------------------- /docs/stdlib/duration.md: -------------------------------------------------------------------------------- 1 | [up](index.md) 2 | 3 | ## Duration 4 | inherits [Object](object.md) 5 | 6 | Represents a span of time, but not a point in time. 7 | 8 | 9 | ### constructor 10 | - `Duration([years], [months], [days], [hours], [minutes], [seconds], [milliseconds])` creates a duration representing the given time periods. If none of the time periods are given, a duration of 0 is created. 11 | 12 | ### methods 13 | - `to_string()` returns a representation of the duration in nanoseconds with the ns unit appended 14 | 15 | ### static methods 16 | - `from_years(years)` creates a duration representing the number of years given 17 | - `from_months(months)` creates a duration representing the number of months given 18 | - `from_days(days)` creates a duration representing the number of days given 19 | - `from_hours(hours)` creates a duration representing the number of hours given 20 | - `from_minutes(minutes)` creates a duration representing the number of minutes given 21 | - `from_seconds(seconds)` creates a duration representing the number of seconds given 22 | - `from_milliseconds(milliseconds)` creates a duration representing the number of milliseconds given 23 | 24 | ### operators 25 | - `+` adds either a datetime or a duration to the existing duration. The returned type is the same as the operand. 26 | -------------------------------------------------------------------------------- /docs/stdlib/enum.md: -------------------------------------------------------------------------------- 1 | [up](index.md) 2 | 3 | ## Enum 4 | inherits [Iterable](iterable.md) 5 | 6 | Not intended to be instantiated directly, but rather through the `enum NAME {}` syntax 7 | 8 | ### methods 9 | - `add(name, value)` adds a value to the enum with the given name string and number value 10 | - `length()` returns the number of values in the enum 11 | - `contains?(value)` returns `true` if the given [EnumValue](#enumvalue) is part of this enum 12 | 13 | ### static methods 14 | - `parse(value)` parses either an enum name, or an integer to the corresponding [EnumValue](#enumvalue). Returns `nil` if no corresponding [EnumValue](#enumvalue) could be found in this enum. 15 | 16 | 17 | ## EnumValue 18 | inherits [Number](number.md) 19 | 20 | Not intended to be instantiated directly. 21 | 22 | Acts like a number, because that's pretty useful in an enum value, but has a couple of extra methods / fields. 23 | 24 | ### constructor 25 | - `EnumValue(name, value)` sets the corresponding fields name and value, along with initialising the number 26 | 27 | ### fields 28 | - `name` returns the [String](string.md) representing the enum name 29 | - `value` returns the [Number](number.md) representing the enum value 30 | 31 | ### methods 32 | - `to_string()` returns a string of the form `[name]:[value]`, e.g. `my_enum_value:64` 33 | -------------------------------------------------------------------------------- /docs/stdlib/env.md: -------------------------------------------------------------------------------- 1 | [up](index.md) 2 | 3 | ## ENV 4 | A singleton object that gives access to the environment variables in the system. 5 | 6 | ## operators 7 | - `[varname]` returns a string with the value of the environment variable. Throws an [Exception](exception.md) if there was no environment variable with that name. 8 | - `[varname]=value` Sets an environment variable of name `varname` to `value`. Throws an [Exception](exception.md) if the value is not a [String](string.md). If `value` is [nil](nil.md) it unsets the environment variable -------------------------------------------------------------------------------- /docs/stdlib/exception.md: -------------------------------------------------------------------------------- 1 | [up](index.md) 2 | 3 | ## Exception 4 | inherits [Object](object.md) 5 | 6 | ### constructor 7 | Exception(message) 8 | 9 | ### fields 10 | - `stacktrace` the string representation of the stacktrace 11 | 12 | ### methods 13 | - `message()` gets the message set in the constructor 14 | 15 | ## Known Subclasses 16 | - `AssertionException` 17 | - [ArgumentException](argument_exception.md) 18 | - `IOException` 19 | - `SocketException` 20 | - `TimeoutException` 21 | - `KeyNotFoundException` 22 | - `IndexOutOfBoundsException` 23 | - `MethodNotFoundException` 24 | - `ImportException` 25 | - `ThreadException` 26 | - `FormatException` 27 | - `InvokeException` -------------------------------------------------------------------------------- /docs/stdlib/file.md: -------------------------------------------------------------------------------- 1 | [up](index.md) 2 | 3 | ## File 4 | inherits [Object](object.md) 5 | 6 | ## Enum - FOPEN 7 | - `READ_ONLY` 8 | - `READ_WRITE` 9 | - `APPEND` 10 | - `BINARY` 11 | 12 | ### methods 13 | - `close()` closes the file 14 | - `write(value)` writes the value to the file 15 | - `read()` reads the entire content of the file into a [String](string.md) and return it 16 | - `sync()` calls the system call `fsync` to synchronize the filesystem into which the file is written 17 | - `flush()` calls the system call `fflush` to ensure the file buffers are written to the physical media 18 | 19 | ### static methods 20 | - `open(path, flags)` opens a file with an FOPEN flag optionally bitwise-or'd with `FOPEN.BINARY` (binary is not implemented correctly and will still attempt to return a string) 21 | - `exists?(path)` returns a [Boolean](boolean.md) value if the current process can see the existence of the path, per the rules of the OS/filesystem 22 | - `file?(path)` returns a [Boolean](boolean.md) if the given path is a regular file 23 | - `read_all_lines(path)` opens the path for reading as text, returning a [List](list.md) of the individual lines, closing the file when finished 24 | - `delete(path)` deletes the file at the path 25 | - `join_path(path, [components]*)` joins the path and the following path components with the OS-dependent path separator and returns a string representation of it 26 | -------------------------------------------------------------------------------- /docs/stdlib/function.md: -------------------------------------------------------------------------------- 1 | [up](index.md) 2 | 3 | ## Function 4 | A class representing comet Functions defined 5 | 6 | ## methods 7 | - `name()` returns a [String](string.md) with the name of the function object 8 | - `attributes()` returns a [List](list.md) of any attributes declared on the function -------------------------------------------------------------------------------- /docs/stdlib/hash.md: -------------------------------------------------------------------------------- 1 | [up](index.md) 2 | 3 | ## Hash 4 | inherits [Iterable](iterable.md) 5 | final 6 | 7 | This is known collectively as a Hash, Hash Table, Dictionary and probably other names. It can be instantiated with a literal `{}` or statically initalized with `{key: value, ...}` 8 | 9 | ### methods 10 | - `add(key, value)` add a value to the hash with key 11 | - `remove(key)` removes the value and key from the hash 12 | - `to_string()` returns a String representation of the hash 13 | - `get(key, default=nil)` returns the value found by `key` otherwise returns the default value if key doesn't have an entry in the hash. 14 | - `has_key?(key)` returns a boolean for whether the hash has the given key 15 | 16 | ### operators 17 | - `==` iterates through the keys and values of each hash and returns true if they have the same contents (or are the same instance) 18 | - `[]` takes an object as the key and returns the associated value, e.g. `hash[some_object]` 19 | - `[]=` assigns a value to the given key, e.g. `hash[some_object] = "some string"` 20 | -------------------------------------------------------------------------------- /docs/stdlib/image.md: -------------------------------------------------------------------------------- 1 | [up](index.md) 2 | 3 | ## Image 4 | inherits [Object](object.md) 5 | 6 | The coordinate system for the images starts with 0,0 at the bottom-left and ends with (width-1),(height-1) at the top-right. It is always in 24-bit colour, 8 bits per channel and does not currently support an alpha channel. Jpegs are written at a constant 90 quality. 7 | 8 | ## Enum - IMAGE_FORMAT 9 | - `PNG` 10 | - `JPEG` 11 | - `BMP` 12 | 13 | ### constructor 14 | - `Image(width, height)` creates an image of width x height pixels 15 | 16 | ### methods 17 | - `set_pixel(x, y, r, g, b)` sets the pixel colour at coordinates x, y to the colour values (0-255) of r,g,b 18 | - `write_to_file(filename, format)` writes the image to the filename, in the given format (a value of the `IMAGE_FORMAT` enum) 19 | - `width()` gets the number of pixels in the x axis of the image 20 | - `height()` gets the number of pixels in the y axis of the image 21 | 22 | ### static methods 23 | - `read(filename)` reads the image 24 | -------------------------------------------------------------------------------- /docs/stdlib/index.md: -------------------------------------------------------------------------------- 1 | [up](../index.md) 2 | 3 | # Classes 4 | 5 | - [Object](object.md) 6 | - [ArgumentException](argument_exception.md) 7 | - [Boolean](boolean.md) 8 | - [Datetime](datetime.md) 9 | - [Directory](directory.md) 10 | - [Duration](duration.md) 11 | - [Enum](enum.md) 12 | - [ENV](env.md) 13 | - [Exception](exception.md) 14 | - [File](file.md) 15 | - [Function](function.md) 16 | - [Hash](hash.md) 17 | - [Image](image.md) 18 | - [Iterable](iterable.md) 19 | - [List](list.md) 20 | - [Module](module.md) 21 | - [Nil](nil.md) 22 | - [Number](number.md) 23 | - [Set](set.md) 24 | - [Socket](socket.md) 25 | - [String](string.md) 26 | - [StringBuilder](string_builder.md) 27 | - [Thread](thread.md) 28 | - [Thread Synchronisation Primitives](thread_synchronisation.md) 29 | - [UnitTest](unittest.md) 30 | 31 | # Modules 32 | - [csv](csv.md) 33 | - [json](json.md) 34 | - [unittest](unittest.md) 35 | 36 | # Functions 37 | ## clock 38 | - `clock()` returns a [Number](number.md) representing the fractional number of seconds of CPU time the process has used 39 | 40 | ## print 41 | - `print([...])` print every argument by first calling `to_string()` on it first, ending with a newline 42 | 43 | ## print_to 44 | - `print_to(STD_STREAM, [...])` print every argument by first calling `to_string()` on it first, ending with a newline. It will print it to the given stream. The options are STD_STREAM.OUT and STD_STREAM.ERR 45 | 46 | ## input 47 | - `input([prompt])` print the prompt (no newline) if given and then wait for a line of input, returning it as a [String](string.md). Gets a maximum of 512 bytes in one line. 48 | 49 | ## get_password 50 | - `get_password([prompt])` print the prompt (no newline) if given and then wait for a line of input, returning it as a [String](string.md). Does not echo the input. Gets a maximum of 512 bytes in one line. 51 | 52 | ## assert 53 | - `assert(value)` if value is falsy, then an AssertionException is thrown 54 | 55 | ## callable? 56 | - `callable?(value)` returns `true` if the value can be called (like a function) 57 | 58 | ## sleep 59 | - `sleep(time)` stops the execution of the current thread for at least the fractional seconds provided. 60 | 61 | ## exit 62 | - `exit(exit_status)` simple wrapper for the C function to exit the program with the given return code. Makes no attempt to clean anything up. 63 | 64 | ## get_imported_modules 65 | - `get_imported_modules()` gets all of the [Modules](module.md) currently imported to the interpreter 66 | 67 | ## call_function 68 | - `call_function(receiver, method, [arguments, ...])` calls the method on the receiver. If the method is a function object, then the receiver may be `nil`. If the method is a name of a method (a string), then the receiver must be an instance. 69 | -------------------------------------------------------------------------------- /docs/stdlib/iterable.md: -------------------------------------------------------------------------------- 1 | [up](index.md) 2 | 3 | ## Iterable 4 | inherits [Object](object.md) 5 | 6 | Functions as an abstract class in other languages, where it has only minimal functionality and relies on the concrete implementation 7 | to provide the basic methods. 8 | 9 | ### methods 10 | - `contains?(value)` (abstract) returns `true` if the value is contained in the iterable 11 | - `empty?()` (abstract) returns `true` if the iterable has no items 12 | - `iterator()` (abstract) returns an [Iterator](#iterator) 13 | - `min()` returns the minimum value as determined by the `<` operator 14 | - `max()` returns the maximum value as determined by the `<` operator 15 | 16 | ## Iterator 17 | inherits [Object](object.md) 18 | 19 | This is just an interface, but shows what should be returned by the `iterator` method of [Iterable](#iterable) 20 | 21 | ### methods 22 | - `has_next?()` (abstract) returns `true` if there is another item in the sequence 23 | - `get_next()` (abstract) returns the next item in the sequence 24 | -------------------------------------------------------------------------------- /docs/stdlib/json.md: -------------------------------------------------------------------------------- 1 | [up](index.md) 2 | 3 | ## json 4 | This is a pure comet json parser. It might one day grow to include a json serialiser. 5 | 6 | ### functions 7 | 8 | - `parse_from_string(str)` parse the json using an in-memory string 9 | - `parse_from_file(filename)` parse the json using the given filename to open the file 10 | 11 | Both of these functions return a hash for objects and a list for arrays. It does NOT detect datetimes 12 | and parse those - they will be returned as strings. -------------------------------------------------------------------------------- /docs/stdlib/list.md: -------------------------------------------------------------------------------- 1 | [up](index.md) 2 | 3 | ## List 4 | inherits [Iterable](iterable.md) 5 | final 6 | 7 | This is technically an ArrayList, i.e. the memory is contiguous which makes indexing faster, at the cost of adding to the list might trigger a reallocation and that performance will linearly degrade as the list size increases. It can be instantiated with a literal `[]` or statically initialized with `[value, "some string", ...]` 8 | 9 | ### constructor 10 | - `List([initial_capacity])` 11 | If given an `initial_capactiy` then the list will be pre-allocated to contain that number of values. This means the index-assign operator can then be used to sparsely populate the list. 12 | 13 | ### methods 14 | - `add(value)` appends the value(s) to the end of the list - takes multiple arguments and will add them all to the list, in order. 15 | - `push(value)` alias for `add` 16 | - `pop()` removes and returns the value at the end of the list 17 | - `get_at(index)` returns the value at the given index, but does not remove it from the list 18 | - `to_string()` returns a string representation of the list 19 | - `size()` returns the number of items stored in the list 20 | - `length()` alias for `size()` 21 | - `sort()` sorts the list in-place and returns a reference to the list. It uses Timsort, a stable sort running in O(n log n) time. 22 | - `filter()` takes a callable object which is called with every item, returning `true` if the item should be part of the list returned. The list returned is a new object and the initial list is left unchanged. 23 | - `map(lambda)` returns a list of values as mapped by the lambda, which is called with each item in the list 24 | - `reduce(initial, lambda)` given an intial value, the lambda is called with the current reduction, each item in the list, and the index in the list, e.g. `list.reduce(0, |current, item, index| { return curent + 1 })` 25 | 26 | ### operators 27 | - `==` compares the contents of the list to another list to see if the contents (and order!) are identical 28 | - `[]` returns the value at the given index (must be an integer) 29 | - `[]=` assigns the given value to the given index (which must be an integer) 30 | - `+` appends all the items in the argument list to the original -------------------------------------------------------------------------------- /docs/stdlib/module.md: -------------------------------------------------------------------------------- 1 | [up](index.md) 2 | 3 | ## Module 4 | inherits [Object](object.md) 5 | final 6 | 7 | Not intended to be instantiated directly. All imported modules will be an instance of this class. 8 | 9 | ### methods 10 | - `functions()` returns a [List](list.md) of the functions defined in the module. This is defined as all fields that are callable and everything defined with the `function` keyword. Does not include classes, even though they are considered callable. 11 | - `fields()` returns a [List](list.md) of names ([Strings](string.md)) of the fields defined in the module. 12 | - `filename()` returns a [String](string.md) representing the absolute path of the module 13 | 14 | ### operators 15 | - `[key]` an index to the fields and functions using the name of the function (as a string) as the key 16 | -------------------------------------------------------------------------------- /docs/stdlib/nil.md: -------------------------------------------------------------------------------- 1 | [up](index.md) 2 | 3 | ## Nil 4 | inherits [Iterable](iterable.md) 5 | final 6 | 7 | Nil is a special class with exactly one instance - it cannot be instantiated. It can be treated as a permanently empty iterable that will never have contents to iterate, but does not require checking for nil? before using in a foreach loop. 8 | 9 | ### methods 10 | - `nil?()` Returns true 11 | - `to_string()` Returns the empty string "" (because checking for `nil?` can be cumbersome) 12 | - `empty?()` returns true 13 | 14 | ### iterable 15 | Nil is an iterable that is permanently empty (because checking for `nil?` can be cumbersome) 16 | -------------------------------------------------------------------------------- /docs/stdlib/number.md: -------------------------------------------------------------------------------- 1 | [up](index.md) 2 | 3 | ## Number 4 | inherits [Object](object.md) 5 | final 6 | 7 | A number is a 64 bit floating point number. 8 | 9 | ### methods 10 | - `to_string()` returns a [String](string.md) representing the number 11 | - `square_root()` returns the square root of the number 12 | - `even?()` returns `true` if the number is evenly divisible by 2 13 | - `floor()` returns the nearest whole integer, searching lower numbers 14 | - `ceiling()` returns the nearest whole integer, searching higher numbers 15 | - `power(n)` returns number raised to the power of `n` 16 | 17 | ### static methods 18 | - `parse()` takes a string and parses it into a `Number`. Returns [nil](nil.md) if the string couldn't be parsed into a number. 19 | - `max(n, m)` returns the maximum value between `n` and `m` or `m` if they are the same 20 | - `min(n, m)` returns the minimum value between `n` and `m` or `m` if they are the same 21 | - `random([seed])` returns a random value between 0 and 1.0 - this is NOT suitable for cryptography. Optionally 22 | allows a seed to be specified. The seed should be able to fit into a 32 bit integer. 23 | 24 | ### operators 25 | - `==` compares the two objects to see if they're the same instance 26 | - `-` performs the mathmatical subtraction operation 27 | - `+` performs the mathmatical addition operation 28 | - `*` performs the mathmatical multiplication operation 29 | - `/` performs the mathmatical division operation (floating point) 30 | - `%` performs the mathmatical remainder operation (casts both operands to uint64_t) 31 | - `>` performs the strict greater than comparision 32 | - `>=` performs the greater than or equal to comparision 33 | - `<` performs the strict less than comparision 34 | - `<=` performs the less than or equal to comparision 35 | - `~` bitwise negates the number 36 | - `<<` bitwise left shift the argument number of bits 37 | - `>>` bitwise right shift the argument number of bits 38 | - `|` bitwise inclusive or 39 | - `&` bitwise and 40 | - `^` bitwise exclusive or 41 | 42 | All bitwise operations are performed as though the number is a signed 64 bit integer -------------------------------------------------------------------------------- /docs/stdlib/object.md: -------------------------------------------------------------------------------- 1 | [up](index.md) 2 | 3 | ## Object 4 | The base class for all Objects 5 | 6 | ### methods 7 | - `nil?()` Returns a Boolean as to whether the instance is nil or not 8 | - `hash()` Returns a hash value for this object 9 | - `to_string()` Returns a String representation of this object 10 | - `methods()` Returns a [List](list.md) of [Strings](string.md) with the names of all callables on the object 11 | - `fields()` Returns a [List](list.md) of [Strings](string.md) of all the fields on the object 12 | 13 | ### static methods 14 | - `to_string()` Returns a string representation of the class 15 | - `hash()` This is identical to the non-static method, but added such that classes can be used as a key to a hash 16 | 17 | ### operators 18 | - `==` compares if the two objects are the same instance 19 | -------------------------------------------------------------------------------- /docs/stdlib/set.md: -------------------------------------------------------------------------------- 1 | [up](index.md) 2 | 3 | ## Set 4 | inherits [Iterable](iterable.md) 5 | final 6 | 7 | WARNING: known to be broken 8 | 9 | ### methods 10 | - `add(value)` adds a value to a set. Returns `true` if the value was added, `false` if the value was already contained in the set 11 | - `remove(value)` removes a value from a set. Silently does nothing if the value was never in the set 12 | - `union(set)` Returns a new Set with the combined values of both sets 13 | - `intersect(set)` Returns a new Set containing the values that were in both sets 14 | - `difference(set)` Returns a new Set with the values in the callee that are not in the argument Set 15 | - `to_list()` Returns a list of the values in the Set. Not guaranteed to be in any specific order 16 | - `to_string()` Returns a string representation of the Set. 17 | - `empty?()` Returns `true` if there are no values in the Set. 18 | - `count()` Returns the number of values in the Set.` 19 | 20 | ### operators 21 | - `|` performs a union 22 | - `&` performs an intersection 23 | - `-` performs a difference 24 | 25 | -------------------------------------------------------------------------------- /docs/stdlib/socket.md: -------------------------------------------------------------------------------- 1 | [up](index.md) 2 | 3 | ## Enum - SOCKET_TYPE 4 | - `TCP` 5 | - `UDP` 6 | - `RAW` 7 | 8 | ## Enum - ADDRESS_FAMILY 9 | - `UNIX` 10 | - `IPv4` 11 | - `IPv6` 12 | - `NETLINK` 13 | 14 | ## Socket 15 | 16 | ### constructor 17 | Socket(SOCKET_TYPE, ADDRESS_FAMILY) 18 | 19 | ## static methods 20 | - `open(SOCKET_TYPE, ADDRESS_FAMILY)` 21 | 22 | ## methods 23 | - `close()` Closes the socket 24 | - `bind(address, port)` binds the socket to an address and port. `address` is a string-based representation of the address. The format must match the address family specified when creating the socket. 25 | - `accept()` accepts incoming connections, returning a new [Socket](#socket) for each successful connection 26 | - `listen(queue_length)` marks the socket as listening, ready to `accept` incoming connections. 27 | - `connect(address, port)` make an outbound connection to a specific endpoint 28 | - `read()` returns a [String](string.md) of any values read 29 | - `write(string)` writes a [String](string.md) to the socket 30 | -------------------------------------------------------------------------------- /docs/stdlib/string.md: -------------------------------------------------------------------------------- 1 | [up](index.md) 2 | 3 | ## String 4 | inherits [Iterable](iterable.md) 5 | final 6 | 7 | All strings are UTF-8 encoded 8 | 9 | ### methods 10 | - `left_trim()` returns a new string with all whitespace characters from the left of the string removed 11 | - `right_trim()` returns a new string with all whitespace characters from the right of the string removed 12 | - `trim()` returns a new string with all whitespace characters from the left and right sides removed 13 | - `split(str)` returns a [List](list.md) of the string portions in between any instances of `str` 14 | - `replace(value, replacement)` returns a new string with all instances of `value` replaced by `replacement` 15 | - `starts_with?(value)` returns `true` if the string begins exactly with value, otherwise `false` 16 | - `ends_with?(value)` returns `true` if the string ends exactly with value, otherwise `false` 17 | - `to_lower()` returns a new string with all uppercase letters replaced with their lowercase counterparts 18 | - `to_upper()` returns a new string with all lowercase letters replaced with their uppercase counterparts 19 | - `to_string()` returns self 20 | - `value()` returns the numerical value of the string. e.g. `'a'.value()` returns `97` 21 | - `substring(start, [length])` returns the substring starting at the (zero-based) index of the codepoint of either the length specified or to the end of the string 22 | - `length()` returns the number of codepoints (~letters) in the string. 23 | - `count()` alias of length() 24 | - `number?()` returns true if the string _only_ contains characters that can be parsed into a single number 25 | - `whitespace?()` returns true if the string _only_ contains whitespace characters 26 | 27 | ### static methods 28 | - `format(msg, ...)` formats a string, replacing instances of `{n}` with the nth (zero-based) index of 29 | argument after msg. e.g. `String.format('this is {0} string with {0} replacement', 'a')` results in 30 | `'this is a string with a replacement'` 31 | a `{` or `}` can be escaped by doubling them up. e.g. `String.format('this is {{0}} string with {0} replacement', a)` results in `'this is {0} string with a replacement'` 32 | 33 | ### operators 34 | - `==` compares if the two strings are equal in a case-sensitive manner 35 | - `+` concatenates an object (calling its `to_string()` method) onto this string, returning a new String. The existing string is left unchanged 36 | - `[]` gets the character (codepoint) at the given index, returned as a new string -------------------------------------------------------------------------------- /docs/stdlib/string_builder.md: -------------------------------------------------------------------------------- 1 | [up](index.md) 2 | 3 | ## String 4 | inherits [Object](object.md) 5 | final 6 | 7 | A StringBuilder is a way of creating a string that can be appended to without the overhead of extra allocations and general interning that each regular string will incur. 8 | 9 | ### methods 10 | - `append()` concatenates a [String](string.md) onto this string builder in place 11 | 12 | ### operators 13 | - `+` concatenates a [String](string.md) onto this string builder in place 14 | -------------------------------------------------------------------------------- /docs/stdlib/thread.md: -------------------------------------------------------------------------------- 1 | [up](index.md) 2 | 3 | WARNING: 4 | Threads are known to not work particularly well. They cause problems with the Garbage Collection, so 5 | random segfaults that don't occur with non-threaded code, is likely because of threads. One day I might 6 | re-write the GC to be reference-counted, so this issue will go away. 7 | 8 | ## Thread 9 | inherits [Object](object.md) 10 | final 11 | 12 | This is a real Thread with all the problems and performance that come with that. See also [Thread Synchronisation](thread_synchronisation.md) 13 | 14 | ### methods 15 | - `start(callable, arg)` starts a new OS thread by calling the callable with the argument 16 | - `join()` waits until the thread finishes and collects the thread 17 | -------------------------------------------------------------------------------- /docs/stdlib/thread_synchronisation.md: -------------------------------------------------------------------------------- 1 | [up](index.md) 2 | 3 | ## Mutex 4 | inherits [Object](object.md) 5 | 6 | A mutual exclusion thread synchronisation primitive. If locked, then only the thread holding the lock may enter the critical section, safeguarding against multiple threads mutating the state simultaneously. 7 | 8 | ### methods 9 | - `lock()` block until the lock can be obtained 10 | - `timed_lock(timeout)` block for (timeout) seconds (may be fractional) and either the lock is obtained or a `TimeoutException` is thrown. 11 | - `unlock()` release the lock, allowing the next waiting thread to gain the lock. The thread owning the lock must call unlock. 12 | - `try_lock()` Attempt to get the lock or immediately return. A [Boolean](boolean.md) value will be returned indicating whether the lock was gained successfully. 13 | 14 | 15 | ## ConditionVariable 16 | inherits [Object](object.md) 17 | 18 | ### methods 19 | - `wait()` Waits until the condition is signalled (blocking) 20 | - `signal_one()` signals a single waiting thread that the condition is met 21 | - `signal_all()` signals all waiting threads that the condition is met. The kernel scheduler will determine which thread gains the lock. 22 | - `timed_wait(timeout)` Wait for a limited amount of fraction seconds. A `TimeoutException` is thrown if the interval elapses before the condition is signalled. 23 | 24 | -------------------------------------------------------------------------------- /docs/stdlib/unittest.md: -------------------------------------------------------------------------------- 1 | [up](index.md) 2 | 3 | ## unittest 4 | A basic unit testing framework 5 | 6 | Can be run as a script to run other tests. All `function`s in the module that start with the name `test_` are determined to be tests to run. 7 | 8 | test_script.cmt: 9 | ``` 10 | import 'unittest' as unittest 11 | 12 | function test_a_thing() { 13 | unittest.Assert.that('this is a string').is_not_nil() 14 | } 15 | ``` 16 | 17 | `comet unittest [--coverage] test_script.cmt` 18 | 19 | The script will exit with a non-zero exit code if any of the tests fail. 20 | 21 | If the `--coverage` parameter is specified, then a json document with the count of the number of times each line was executed, along with the per-function and per-file totals and percentages called `.coverage.json` in the current directory is created. 22 | 23 | ### classes 24 | - `Assert` 25 | 26 | #### static methods 27 | - `that(value)` returns an instance of `Assert`, initialised with the value to be tested against 28 | - `fail([message])` fails the test, optionally giving a message for why the test failed 29 | - `pass()` 30 | 31 | #### methods 32 | - `is_nil(message)` asserts that the value is nil, optionally giving a message when the assertion fails 33 | - `is_not_nil(message)` asserts that the value is not nil, optionally giving a message when the assertion fails 34 | - `is_equal_to(expected, message)` asserts that the value is equal to (as determined by the `==` operator) to the expected, optionally giving a message when the assertion fails 35 | - `is_not_equal_to(expected, message)` asserts that the value is not equal to (as determined by the negation of the `==` operator) to the expected, optionally giving a message when the assertion fails 36 | - `is_true(message)` asserts that the value is true, optionally giving a message when the assertion fails 37 | - `is_false(message)` asserts that the value is false, optionally giving a message when the assertion fails 38 | - `contains(expected, message)` asserts that the value contains the expected (as determined by calling `value.contains?(expected)`), optionally giving a message when the assertion fails 39 | - `does_not_contain(expected, message)` asserts that the value does not contain the expected (as determined by calling `!value.contains?(expected)`), optionally giving a message when the assertion fails 40 | - `matches(expected, message)` asserts that the value matches the expect. Determined by either calling expected if the expected is callable, returning a boolean or falling back to the `==` operator, optionally giving a message when the assertion fails 41 | - `throws(message)` asserts that the value throws an exception (other than an AssertionException) when it is called, optionally giving a message when the assertion fails. Throws an ArgumentException itself if the value is not callable. 42 | - `does_not_throw(message)` asserts that the value does not throw an exception when called, optionally giving a message when the assertion fails. Throws an ArgumentException itself if the value is not callable. 43 | - `is_of_type(type, message)` asserts that the value `is` the given type, optionally giving a message for when the assertion fails 44 | - `is_empty(message)` asserts that the value returns `true` to `empty?()`, optionally giving a message for when the assertion fails 45 | - `is_not_empty(message)` asserts that the value returns `false` to `empty?()`, optionally giving a message for when the assertion fails 46 | - `is_callable(message)` asserts that the value is callable 47 | 48 | -------------------------------------------------------------------------------- /grammar.md: -------------------------------------------------------------------------------- 1 | ``` 2 | program → declaration* EOF ; 3 | 4 | declaration → classDecl 5 | | funDecl 6 | | varDecl 7 | | statement ; 8 | 9 | classDecl → "class" IDENTIFIER ( "<" IDENTIFIER )? 10 | "{" method* "}" ; 11 | funDecl → "fun" function ; 12 | varDecl → "var" IDENTIFIER ( "=" expression )? EOL ; 13 | 14 | statement → exprStmt 15 | | forStmt 16 | | ifStmt 17 | | printStmt 18 | | returnStmt 19 | | whileStmt 20 | | throwStmt 21 | | block ; 22 | 23 | exprStmt → expression EOL ; 24 | forStmt → "for" "(" ( varDecl | exprStmt | ";" ) 25 | expression? ";" 26 | expression? ")" EOL? statement ; 27 | ifStmt → "if" "(" expression ")" statement ( "else" statement )? ; 28 | printStmt → "print" expression EOL ; 29 | returnStmt → "return" expression? EOL ; 30 | whileStmt → "while" "(" expression ")" statement EOL? ; 31 | throwStmt → "throw" expression EOL ; 32 | block → "{" declaration* "}" ; 33 | 34 | expression → assignment ; 35 | 36 | assignment → ( call "." )? IDENTIFIER "=" assignment 37 | | logic_or; 38 | 39 | logic_or → logic_and ( "or" logic_and )* ; 40 | logic_and → equality ( "and" equality )* ; 41 | equality → comparison ( ( "!=" | "==" ) comparison )* ; 42 | comparison → addition ( ( ">" | ">=" | "<" | "<=" ) addition )* ; 43 | addition → multiplication ( ( "-" | "+" ) multiplication )* ; 44 | multiplication → unary ( ( "/" | "*" ) unary )* ; 45 | 46 | unary → ( "!" | "-" ) unary | call ; 47 | call → primary ( "(" arguments? ")" | "." IDENTIFIER | "[" arguments? "]")* ; 48 | primary → "true" | "false" | "nil" | "self" 49 | | NUMBER | STRING | IDENTIFIER | "(" expression ")" 50 | | "super" "." IDENTIFIER ; 51 | 52 | method → scope function 53 | function → IDENTIFIER "(" parameters? ")" block ; 54 | parameters → IDENTIFIER ( "," IDENTIFIER )* ; 55 | arguments → expression ( "," expression )* ; 56 | 57 | scope → private | protected | public 58 | 59 | NUMBER → DIGIT+ ( "." DIGIT+ )? ; 60 | STRING → '"' * '"' ; 61 | IDENTIFIER → ALPHA ( ALPHA | DIGIT )* ; 62 | ALPHA → 'a' ... 'z' | 'A' ... 'Z' | '_' ; 63 | DIGIT → '0' ... '9' ; 64 | EOL → '\n' 65 | ``` 66 | -------------------------------------------------------------------------------- /sample.cmt: -------------------------------------------------------------------------------- 1 | #!/usr/bin/comet 2 | import 'module' as Module 3 | 4 | class MyClass : SomeOtherClass 5 | { 6 | init(a) 7 | { 8 | self.a = a 9 | self.list = [] 10 | self.hash = {} 11 | self.set = Set() 12 | } 13 | 14 | this_is_a_method() 15 | { 16 | print("interpolated string ${self.a}") 17 | print('literal string') 18 | } 19 | 20 | this_is_a_different_method(a, b=1, c=nil) 21 | { 22 | while (expression) 23 | { 24 | self.my_iterative_method((|something|) { 25 | print(something) 26 | }) 27 | } 28 | } 29 | 30 | this_method_is_destructive!() 31 | { 32 | 33 | } 34 | 35 | this_method_returns_a_bool?() 36 | { 37 | return false 38 | } 39 | 40 | operator [] (arg) 41 | { 42 | print('This overloads how the operator identifier[1] will function') 43 | } 44 | 45 | my_iterative_method(func) 46 | { 47 | foreach(var item in self.list) 48 | { 49 | func(item) 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /stdlib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.10) 2 | enable_language(C) 3 | enable_language(CXX) 4 | project (stdlib) 5 | 6 | include_directories( 7 | "${CMAKE_CURRENT_SOURCE_DIR}" 8 | ../comet 9 | ../vmlib 10 | ../eral 11 | ../stb) 12 | 13 | set(SOURCE 14 | _init.c 15 | boolean.c 16 | directory.cpp 17 | duration.cpp 18 | enum.cpp 19 | env.cpp 20 | exception.c 21 | file_common.cpp 22 | function.c 23 | hash.cpp 24 | image.c 25 | iterable.c 26 | list.cpp 27 | module.c 28 | native_functions.c 29 | nil.c 30 | number.c 31 | object.c 32 | process.cpp 33 | set.cpp 34 | socket.c 35 | string.c 36 | string_builder.c 37 | system.c 38 | thread.c 39 | thread_synchronisation_common.c 40 | colour.cpp 41 | ) 42 | 43 | if (WIN32) 44 | add_compile_definitions(WIN32=1) 45 | list(APPEND SOURCE file_win.cpp thread_synchronisation_win.c datetime_win.cpp) 46 | else() 47 | list(APPEND SOURCE file_unix.cpp thread_synchronisation_unix.c datetime_unix.cpp) 48 | endif() 49 | 50 | add_library(stdlib STATIC "${SOURCE}") 51 | 52 | if (WIN32) 53 | target_link_libraries(stdlib vmlib utf8proc ws2_32 date date-tz) 54 | else() 55 | target_link_libraries(stdlib vmlib utf8proc m date date-tz) 56 | endif (WIN32) 57 | 58 | target_include_directories(stdlib PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/private" Threads::Threads) 59 | target_include_directories(stdlib PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") 60 | 61 | add_subdirectory(test) 62 | 63 | add_subdirectory(comet) 64 | -------------------------------------------------------------------------------- /stdlib/_init.c: -------------------------------------------------------------------------------- 1 | #include "comet.h" 2 | #include "cometlib.h" 3 | 4 | void init_comet(VM* vm) 5 | { 6 | initGlobals(); 7 | initVM(vm); 8 | init_stdlib(vm); 9 | common_strings[STRING_INIT] = copyString(vm, "init", 4); 10 | common_strings[STRING_HASH] = copyString(vm, "hash", 4); 11 | common_strings[STRING_TO_STRING] = copyString(vm, "to_string", 9); 12 | common_strings[STRING_LAMBDA] = copyString(vm, "(|lambda|)", 10); 13 | common_strings[STRING_EMPTY_Q] = copyString(vm, "empty?", 6); 14 | common_strings[STRING_NUMBER] = copyString(vm, "Number", 6); 15 | common_strings[STRING_SCRIPT] = copyString(vm, "