├── .appveyor.yml ├── .gitignore ├── .gitlab-ci.yml ├── CMakeLists.txt ├── Findlily.cmake ├── README.md ├── RELEASES.md ├── license.txt ├── manifest ├── pkg_core.lily ├── pkg_coroutine.lily ├── pkg_fs.lily ├── pkg_introspect.lily ├── pkg_math.lily ├── pkg_prelude.lily ├── pkg_random.lily ├── pkg_subprocess.lily ├── pkg_sys.lily └── pkg_time.lily ├── run ├── CMakeLists.txt └── lily.c ├── scripts ├── common.lily ├── parser_data.lily ├── prelude.lily └── token.lily ├── src ├── CMakeLists.txt ├── csiphash.c ├── lily.h ├── lily_alloc.c ├── lily_alloc.h ├── lily_api.c ├── lily_buffer_u16.c ├── lily_buffer_u16.h ├── lily_code_iter.c ├── lily_code_iter.h ├── lily_core_types.h ├── lily_emitter.c ├── lily_emitter.h ├── lily_expr.c ├── lily_expr.h ├── lily_generic_pool.c ├── lily_generic_pool.h ├── lily_lexer.c ├── lily_lexer.h ├── lily_lexer_data.h ├── lily_library.c ├── lily_library.h ├── lily_msgbuf.c ├── lily_opcode.h ├── lily_parser.c ├── lily_parser.h ├── lily_parser_data.h ├── lily_pkg_core.c ├── lily_pkg_coroutine.c ├── lily_pkg_coroutine_bindings.h ├── lily_pkg_fs.c ├── lily_pkg_fs_bindings.h ├── lily_pkg_introspect.c ├── lily_pkg_introspect_bindings.h ├── lily_pkg_math.c ├── lily_pkg_math_bindings.h ├── lily_pkg_prelude.c ├── lily_pkg_prelude_bindings.h ├── lily_pkg_random.c ├── lily_pkg_random_bindings.h ├── lily_pkg_subprocess.c ├── lily_pkg_subprocess_bindings.h ├── lily_pkg_sys.c ├── lily_pkg_sys_bindings.h ├── lily_pkg_time.c ├── lily_pkg_time_bindings.h ├── lily_platform.h ├── lily_raiser.c ├── lily_raiser.h ├── lily_string_pile.c ├── lily_string_pile.h ├── lily_symtab.c ├── lily_symtab.h ├── lily_token.h ├── lily_type_maker.c ├── lily_type_maker.h ├── lily_type_system.c ├── lily_type_system.h ├── lily_utf8.c ├── lily_utf8.h ├── lily_value.h ├── lily_vm.c ├── lily_vm.h └── st.c ├── test ├── CMakeLists.txt ├── benchmark │ ├── README.md │ ├── bench.lily │ ├── binary_trees.lily │ ├── binary_trees.lua │ ├── binary_trees.py │ ├── binary_trees.rb │ ├── fib.lily │ ├── fib.lua │ ├── fib.py │ ├── fib.rb │ ├── for.lily │ ├── for.lua │ ├── for.py │ ├── for.rb │ ├── map_numeric.lily │ ├── map_numeric.lua │ ├── map_numeric.py │ ├── map_numeric.rb │ ├── map_string.lily │ ├── map_string.lua │ ├── map_string.py │ ├── map_string.rb │ └── test_benchmark.lily ├── call │ ├── test_bad_call.lily │ ├── test_bad_keyargs.lily │ ├── test_bad_optargs.lily │ ├── test_call_pipe.lily │ ├── test_keyargs.lily │ ├── test_optargs.lily │ └── test_varargs.lily ├── class │ ├── test_bad_class.lily │ └── test_class.lily ├── closure │ └── test_verify_closures.lily ├── constant │ └── test_verify_constant.lily ├── coroutine │ └── test_verify_coroutine.lily ├── coverage │ ├── test_dynaload.lily │ └── test_verify_coverage.lily ├── enum │ ├── test_bad_enum.lily │ └── test_enum.lily ├── exception │ ├── test_bad_exception.lily │ └── test_exception.lily ├── file_for_io.txt ├── format │ └── test_verify_format.lily ├── forward │ ├── test_bad_forward.lily │ └── test_forward.lily ├── gc │ └── test_verify_gc.lily ├── import │ ├── enum_export_main.lily │ ├── enum_export_user.lily │ ├── enum_exporter.lily │ ├── nest │ │ ├── deep_target.lily │ │ └── rooted_include.lily │ ├── packages │ │ └── fakepackage │ │ │ └── src │ │ │ └── fakepackage.lily │ ├── rooted_base.lily │ ├── test_bad_import.lily │ └── test_import.lily ├── lambda │ ├── test_bad_lambda.lily │ └── test_lambda.lily ├── manifest │ ├── test_bad_manifest.lily │ └── test_manifest.lily ├── method │ ├── test_boolean.lily │ ├── test_byte.lily │ ├── test_bytestring.lily │ ├── test_file.lily │ ├── test_hash.lily │ ├── test_integer.lily │ ├── test_list.lily │ ├── test_option.lily │ ├── test_result.lily │ └── test_string.lily ├── prelude │ ├── test_pkg_coroutine.lily │ ├── test_pkg_fs.lily │ ├── test_pkg_introspect.lily │ ├── test_pkg_math.lily │ ├── test_pkg_random.lily │ ├── test_pkg_subprocess.lily │ ├── test_pkg_sys.lily │ └── test_pkg_time.lily ├── rewind │ └── test_verify_rewind.lily ├── sandbox │ └── test_verify_sandbox.lily ├── syntax │ ├── test_bad_syntax.lily │ ├── test_bad_token.lily │ └── test_basics.lily ├── t │ ├── manifest │ │ ├── backbone.lily │ │ ├── covlib.lily │ │ └── farm.lily │ ├── src │ │ ├── lily_backbone.c │ │ ├── lily_backbone_bindings.h │ │ ├── lily_covlib.c │ │ ├── lily_covlib_bindings.h │ │ ├── lily_farm.c │ │ └── lily_farm_bindings.h │ └── testing.lily ├── template │ └── test_verify_template.lily ├── test_driver.c └── types │ ├── test_generics.lily │ ├── test_inference.lily │ ├── test_narrowing.lily │ ├── test_quantification.lily │ ├── test_scoop.lily │ └── test_variance.lily └── try ├── coroutine_tasks.lily ├── fib.lily ├── fizzbuzz.lily ├── one_hundred_doors.lily ├── rpn_calculator.lily ├── show_classes.lily ├── show_how_to_combine_functions.lily ├── show_off_enums.lily └── sum_numbers_from_argv.lily /.appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 1.0.{build} 2 | image: Visual Studio 2017 3 | platform: 4 | - x64 5 | 6 | build_script: 7 | - cmd: cmake . 8 | - cmd: cmake --build . 9 | - cmd: pre-commit-tests.exe 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries. 2 | 3 | lily 4 | pre-commit-tests 5 | test/t/backbone.* 6 | 7 | # Generated by CMake. 8 | 9 | CMakeCache.txt 10 | CMakeFiles/ 11 | cmake_install.cmake 12 | compile_commands.json 13 | CTestTestfile.cmake 14 | DartConfiguration.tcl 15 | install_manifest.txt 16 | lib/ 17 | Makefile 18 | Testing/ 19 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | cache: 2 | key: ${CI_COMMIT_REF_SLUG} 3 | paths: 4 | - .public/ 5 | 6 | test: 7 | stage: test 8 | before_script: 9 | - apt-get update && apt-get -y install cmake lcov 10 | script: 11 | - cmake -DWITH_COVERAGE=on . 12 | - make 13 | - ./pre-commit-tests 14 | - lcov --directory . --capture --output-file coverage.info 15 | - >- 16 | lcov --remove coverage.info "/usr/*" \ 17 | "${PWD}/run/*" \ 18 | "${PWD}/test/*" \ 19 | "${PWD}/src/csiphash.c" \ 20 | "${PWD}/src/lily_alloc.c" \ 21 | --output-file coverage.info 22 | - genhtml -o .public coverage.info 23 | - lcov --list coverage.info 24 | only: 25 | - main 26 | 27 | pages: 28 | stage: deploy 29 | script: 30 | - mv .public public 31 | artifacts: 32 | paths: 33 | - public 34 | only: 35 | - main 36 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project("Lily" C) 3 | 4 | if(MSVC) 5 | string(REPLACE "/W3" "/W4" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") 6 | add_definitions(/wd4201) 7 | add_definitions(/wd4204) 8 | add_definitions(/wd4214) 9 | add_definitions(/MP) 10 | set(CMAKE_CTEST_COMMAND copy lily.exe ..) 11 | else() 12 | set(CMAKE_CTEST_COMMAND cp lily ..) 13 | if(DEBUG) 14 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g3") 15 | else() 16 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2") 17 | endif(DEBUG) 18 | 19 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wno-implicit-fallthrough -Wsign-compare -Wshadow") 20 | 21 | if(WITH_COVERAGE) 22 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0 -fprofile-arcs -ftest-coverage") 23 | endif(WITH_COVERAGE) 24 | endif() 25 | 26 | set(LIBRARY_OUTPUT_PATH "${PROJECT_BINARY_DIR}/lib") 27 | set(EXECUTABLE_OUTPUT_PATH "${PROJECT_BINARY_DIR}") 28 | 29 | set(LILY_MAJOR "2") 30 | set(LILY_MINOR "0") 31 | 32 | # BSD libc includes the dl* functions and there's no libdl on them. 33 | # Unfortunately, CMake doesn't seem to distinguish *BSD from the other *nixen. 34 | STRING(REGEX MATCH "BSD" IS_BSD ${CMAKE_SYSTEM_NAME}) 35 | 36 | if(IS_BSD OR MINGW OR MSVC OR APPLE) 37 | set(LILY_NEED_DL 0) 38 | else() 39 | set(LILY_NEED_DL 1) 40 | endif() 41 | 42 | # Windows doesn't have a math library to link against. 43 | 44 | if(WIN32) 45 | set(LILY_NEED_M 0) 46 | else() 47 | set(LILY_NEED_M 1) 48 | endif() 49 | 50 | add_subdirectory(src) 51 | add_subdirectory(run) 52 | add_subdirectory(test) 53 | 54 | set(TEST_COMMAND cd .. && ./pre-commit-tests) 55 | add_custom_target(check ${CMAKE_CTEST_COMMAND} 56 | COMMAND ${TEST_COMMAND} 57 | DEPENDS lily VERBATIM) 58 | -------------------------------------------------------------------------------- /Findlily.cmake: -------------------------------------------------------------------------------- 1 | # Try to find where Lily has been installed. 2 | # 3 | # The following variables are defined: 4 | # 5 | # LILY_FOUND - Boolean indicating if Lily was found. 6 | # LILY_INCLUDE_DIRS - Path to where lily.h is. 7 | # 8 | # The following functions are defined: 9 | # 10 | # lily_add_library(name, targets...) 11 | # Create an extension library for Lily. Under the hood, this calls 12 | # `add_library(name SHARED targets)`, and sets common options that most 13 | # extension libraries will need. Most importantly, this links to liblily.lib 14 | # on Windows. If that is not done, the resulting dll will not work. 15 | # 16 | # This function is intended for projects that export a single extension 17 | # library for the interpreter to load. 18 | 19 | if(WIN32) 20 | find_library(_LILY_STATIC_LIBRARY 21 | NAMES 22 | Lily/lib/liblily.lib) 23 | 24 | get_filename_component(_LILY_BASE_PATH ${_LILY_STATIC_LIBRARY} DIRECTORY) 25 | set(_LILY_BASE_PATH ${_LILY_BASE_PATH}/..) 26 | else() 27 | set(_LILY_BASE_PATH ${CMAKE_INSTALL_PREFIX}) 28 | endif() 29 | 30 | find_path(LILY_INCLUDE_DIRS 31 | NAMES 32 | lily.h 33 | PATHS 34 | "${_LILY_BASE_PATH}/include/lily") 35 | 36 | if(WIN32) 37 | if(LILY_INCLUDE_DIRS AND _LILY_STATIC_LIBRARY) 38 | set(LILY_FOUND true) 39 | endif() 40 | else() 41 | if(LILY_INCLUDE_DIRS) 42 | set(LILY_FOUND true) 43 | endif() 44 | endif() 45 | 46 | if(NOT LILY_FOUND) 47 | message(FATAL_ERROR "Could not find Lily.") 48 | endif() 49 | 50 | function(lily_add_library _LIBRARY_NAME) 51 | set(_LIBRARY_SOURCES ${ARGV}) 52 | set(_OUT_DIR ${PROJECT_BINARY_DIR}/src) 53 | 54 | list(REMOVE_AT _LIBRARY_SOURCES 0) 55 | add_library(${_LIBRARY_NAME} SHARED ${_LIBRARY_SOURCES}) 56 | 57 | if(WIN32) 58 | target_link_libraries(${_LIBRARY_NAME} ${_LILY_STATIC_LIBRARY}) 59 | endif() 60 | 61 | target_include_directories(${_LIBRARY_NAME} PUBLIC ${LILY_INCLUDE_DIRS}) 62 | 63 | set_target_properties( 64 | ${_LIBRARY_NAME} 65 | PROPERTIES 66 | PREFIX "" 67 | LIBRARY_OUTPUT_DIRECTORY ${_OUT_DIR} 68 | LIBRARY_OUTPUT_DIRECTORY_DEBUG ${_OUT_DIR} 69 | LIBRARY_OUTPUT_DIRECTORY_RELEASE ${_OUT_DIR} 70 | RUNTIME_OUTPUT_DIRECTORY ${_OUT_DIR} 71 | RUNTIME_OUTPUT_DIRECTORY_DEBUG ${_OUT_DIR} 72 | RUNTIME_OUTPUT_DIRECTORY_RELEASE ${_OUT_DIR} 73 | ) 74 | endfunction(lily_add_library) 75 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Pipeline Status](https://gitlab.com/FascinatedBox/lily/badges/main/pipeline.svg)](https://gitlab.com/FascinatedBox/lily/-/commits/main) [![Windows Build](https://ci.appveyor.com/api/projects/status/gitlab/FascinatedBox/lily?svg=true)](https://ci.appveyor.com/project/FascinatedBox/lily) [![Coverage Report](https://gitlab.com/FascinatedBox/lily/badges/main/coverage.svg)](https://FascinatedBox.gitlab.io/lily/src/index.html) 2 | 3 | ## Lily 4 | 5 | Lily is a programming language focused on expressiveness and type safety. 6 | 7 | ## Sample 8 | 9 | ``` 10 | scoped enum Color { Black, Blue, Cyan, Green, Magenta, Red, White, Yellow } 11 | 12 | class Terminal(public var @foreground: Color, width_str: String) 13 | { 14 | public var @width = width_str.parse_i().unwrap_or(80) 15 | 16 | public define set_fg(new_color: Color) { 17 | @foreground = new_color 18 | } 19 | } 20 | 21 | var terms = [Terminal(Color.White, "A"), Terminal(Color.Red, "40")] 22 | 23 | terms.each(|e| e.width += 20 ) 24 | |> print 25 | ``` 26 | 27 | ## Features 28 | 29 | #### Templating 30 | 31 | By default, Lily runs in **standalone** mode where all content is code to 32 | execute. But Lily can also be run in **template** mode. In **template** mode, 33 | code is between `` tags. When a file is imported, it's always 34 | loaded in **standalone** mode, so that it doesn't accidentally send headers. 35 | Files that are imported are also namespaced (no 'global namespace'). 36 | 37 | #### Embeddable 38 | 39 | Lily may be a statically-typed language, but the reference implementation is an 40 | interpreter. The interpreter as well as its API have been carefully designed 41 | with sandboxing in mind. As a result, it's possible to have multiple 42 | interpreters exist alongside each other. 43 | 44 | #### Shorter edit cycle 45 | 46 | Another benefit from having the reference implementation as an interpreter is a 47 | shorter turn around time. The interpreter's parser is comparable in speed to 48 | that of languages using an interpreter as their reference. 49 | 50 | ## Building 51 | 52 | You need a C compiler and CMake (3.0.0 +). There are no external dependencies. 53 | 54 | To build Lily, execute the following in a terminal: 55 | 56 | ``` 57 | cmake . 58 | 59 | make 60 | ``` 61 | 62 | Note: Windows users may need to add `-G"Unix Makefiles"` to the end of the cmake 63 | invocation. 64 | 65 | The above will build the `lily` executable, as well as a liblily that you can 66 | use with your program. It also builds `pre-commit-tests`. 67 | 68 | ## Running tests 69 | 70 | The centerpiece of Lily's testing is `test_main.lily` in the `test` directory. 71 | That file imports and invokes a large number of tests that cover a lot of Lily. 72 | 73 | The `make` command also builds `covlib` and `pre-commit-tests`. No additional 74 | commands are necessary. `covlib` is a library that tests some parts of Lily that 75 | native code can't test. `pre-commit-tests` is a special runner that executes 76 | `test_main.lily`. 77 | 78 | To run Lily's tests, execute `pre-commit-tests` from the directory it's in after 79 | building Lily. 80 | 81 | ## Resources 82 | 83 | * [Documentation](http://lily-lang.org) 84 | 85 | * [Builtin module reference](http://lily-lang.org/core/module.core.html) 86 | 87 | * [Try it in your browser](http://lily-lang.org/intro-sandbox.html) 88 | 89 | ## Packaging 90 | 91 | The [lily-garden](https://gitlab.com/FascinatedBox/lily-garden) repository 92 | contains a package manager (Garden) that simplifies the install of Lily 93 | packages. 94 | 95 | ## License 96 | 97 | [MIT](https://gitlab.com/FascinatedBox/lily/blob/main/license.txt) 98 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011-2021 Jesse Adkins (FascinatedBox) 2 | 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | -------------------------------------------------------------------------------- /manifest/pkg_core.lily: -------------------------------------------------------------------------------- 1 | import manifest 2 | 3 | # This file is automatically generated by scrips/prelude.lily. 4 | 5 | ### Pseudo root of all pre-registered modules. 6 | ### 7 | ### This is a fake module created to link together all of the modules that are 8 | ### automatically loaded in the interpreter. 9 | ### 10 | ### Except for the `prelude` module, all modules listed here can be imported 11 | ### from any file inside of the interpreter. This is unlike other modules, which 12 | ### are loaded using relative paths. 13 | ### 14 | ### The `prelude` module is unique because the classes, enums, and functions are 15 | ### the foundation of the interpreter. Instead of requiring it to be imported, 16 | ### the contents of `prelude` are instead available without a namespace. 17 | library core 18 | 19 | import pkg_prelude 20 | import pkg_coroutine 21 | import pkg_fs 22 | import pkg_introspect 23 | import pkg_math 24 | import pkg_random 25 | import pkg_subprocess 26 | import pkg_sys 27 | import pkg_time 28 | -------------------------------------------------------------------------------- /manifest/pkg_coroutine.lily: -------------------------------------------------------------------------------- 1 | import manifest 2 | 3 | ### The coroutine package provides a suspendable `Function`. 4 | library coroutine 5 | 6 | ### A `Coroutine` is similar to a `Function`, except that it can also yield 7 | ### values at different points along its lifetime. Every `Coroutine` has a 8 | ### callstack that belongs to it, as well as an exception state. A `Coroutine`'s 9 | ### status can be discovered by one of the is_ methods. 10 | ### 11 | ### The `Coroutine` type takes two types. The first is the type that the 12 | ### `Coroutine` will be returning or yielding. The second is the type that the 13 | ### `Coroutine` takes as a message. A `Coroutine` can take empty `Unit` messages 14 | ### for simplicity, or a more interesting type if a more bidirectional kind of 15 | ### messaging is wanted. A `Coroutine` can get the value resumed using 16 | ### `Coroutine.receive` while within the `Coroutine`. 17 | ### 18 | ### The first argument of a `Function` to be made a `Coroutine` is always the 19 | ### `Coroutine` itself. If the `Function` specifies extra arguments, those 20 | ### arguments are to be passed to the intermediate result of `Coroutine.create`. 21 | foreign static class Coroutine[A, B] { 22 | ### Build a new `Coroutine` that wraps over the `Function` provided. 23 | ### 24 | ### # Errors 25 | ### 26 | ### * `RuntimeError`: If 'fn' is not a native function. 27 | public static define build(fn: Function(Coroutine[A, B])): Coroutine[A, B] 28 | 29 | ### Build a new Coroutine that wraps over the `Function` provided. The base 30 | ### `Function` has the second argument set to 'value' exactly once before 31 | ### any resumption takes place. This method is provided so that a 32 | ### `Coroutine` can take an extra value (perhaps a `Tuple`) without needing 33 | ### to be a closure. 34 | ### 35 | ### # Errors 36 | ### 37 | ### * `RuntimeError`: If 'fn' is not a native function. 38 | public static define build_with_value[C](fn: Function(Coroutine[A, B], C), 39 | value: C): Coroutine[A, B] 40 | 41 | ### Returns `true` if the `Coroutine` has returned a value instead of 42 | ### yielding, `false` otherwise. 43 | public define is_done: Boolean 44 | 45 | ### Returns `true` if the `Coroutine` raised an exception, `false` 46 | ### otherwise. 47 | public define is_failed: Boolean 48 | 49 | ### Returns `true` if the `Coroutine` is running, `false` otherwise. Note 50 | ### that this does not mean that the `Coroutine` is the one currently 51 | ### running, only that it is running. 52 | public define is_running: Boolean 53 | 54 | ### Returns `true` if the `Coroutine` is ready to be resumed, `false` 55 | ### otherwise. 56 | public define is_waiting: Boolean 57 | 58 | ### This function returns the value that the `Coroutine` is holding, so long 59 | ### as the `Coroutine` is the one currently running. 60 | ### 61 | ### The value stored by the `Coroutine` is initially the first argument sent 62 | ### to the intermediate builder. Following that, it is the last value that 63 | ### was sent to the `Coroutine` using `Coroutine.resume_with`. 64 | ### 65 | ### # Errors 66 | ### 67 | ### * `RuntimeError`: If 'self' is not the current `Coroutine`. 68 | public define receive: B 69 | 70 | ### Attempt to resume the `Coroutine` provided. A `Coroutine` can be resumed 71 | ### only if it is currently in the 'waiting' state. 72 | ### 73 | ### This function does not send a value to the `Coroutine` which is why it 74 | ### requires the second parameter to be `Unit`. 75 | ### 76 | ### If the `Coroutine` is suspended and yields a value, the result is a 77 | ### `Some` of that value. 78 | ### 79 | ### Otherwise, this returns `None`. 80 | ### 81 | ### Note that if a `Coroutine` returns a value instead of yielding, the 82 | ### value is ignored and the result is `None`. 83 | public static define resume(self: Coroutine[A, Unit]): Option[A] 84 | 85 | ### Attempt to resume the `Coroutine` provided. A `Coroutine` can be resumed 86 | ### only if it is currently in the 'waiting' state. 87 | ### 88 | ### This function includes a value for the `Coroutine` to store. The value 89 | ### is stored only if the `Coroutine` is resumed. If stored, the old value 90 | ### is ejected from the `Coroutine` provided. 91 | ### 92 | ### If the `Coroutine` is suspended and yields (or returns) a value, the 93 | ### result is a `Some` of that value. 94 | ### 95 | ### Otherwise, this returns `None`. 96 | public define resume_with(value: B): Option[A] 97 | 98 | ### Yield 'value' from the `Coroutine` given. Control returns to whatever 99 | ### invoked 'self'. 100 | ### 101 | ### # Errors 102 | ### 103 | ### * `RuntimeError` if `self` is the current `Coroutine`, or within a 104 | ### foreign call. 105 | public define yield(value: A) 106 | } 107 | -------------------------------------------------------------------------------- /manifest/pkg_fs.lily: -------------------------------------------------------------------------------- 1 | import manifest 2 | 3 | ### Functions for interacting with the underlying filesystem. 4 | library fs 5 | 6 | ### Change the current working directory to `dirname`. 7 | ### 8 | ### # Errors 9 | ### 10 | ### * `IOError` if `dirname` is not valid, or does not exist. 11 | define change_dir(dirname: String) 12 | 13 | ### Create directory `dirname` with `mode` permissions. 14 | ### 15 | ### Note: On Windows, `mode` is ignored. 16 | ### 17 | ### # Errors 18 | ### 19 | ### * `IOError` if `dirname` cannot be created. 20 | define create_dir(dirname: String, mode: *Integer=0c777) 21 | 22 | ### Return the current directory. 23 | define current_dir: String 24 | 25 | ### Remove directory `dirname`. 26 | ### 27 | ### Note: The directory must be empty. 28 | ### 29 | ### # Errors 30 | ### 31 | ### * `IOError` if `dirname` cannot be removed. 32 | define remove_dir(dirname: String) 33 | -------------------------------------------------------------------------------- /manifest/pkg_math.lily: -------------------------------------------------------------------------------- 1 | import manifest 2 | 3 | ### The math package provides access to some useful math functions and 4 | ### constants. 5 | library math 6 | 7 | ### Calculates the absolute value of an integer. 8 | define abs(x: Integer): Integer 9 | 10 | ### Calculates the arc cosine of a double in radians. 11 | define acos(x: Double): Double 12 | 13 | ### Calculates the arc sine of a double in radians. 14 | define asin(x: Double): Double 15 | 16 | ### Calculates the arc tangent of a double in radians. 17 | define atan(x: Double): Double 18 | 19 | ### Round a double up to the nearest integer. 20 | define ceil(x: Double): Double 21 | 22 | ### Calculate the cosine of a double in radians. 23 | define cos(x: Double): Double 24 | 25 | ### Calculate the hyperbolic cosine of a double in radians. 26 | define cosh(x: Double): Double 27 | 28 | ### Calculate e^x. 29 | define exp(x: Double): Double 30 | 31 | ### Calculates the absolute value of a double. 32 | define fabs(x: Double): Double 33 | 34 | ### Round a double down to the nearest integer. 35 | define floor(x: Double): Double 36 | 37 | ### Calculate the remainder of x/y. 38 | define fmod(x: Double, y: Double): Double 39 | 40 | ### Check if a number is infinity 41 | define is_infinity(x: Double): Boolean 42 | 43 | ### Check if a number is nan since if x = nan, then x == nan is false 44 | define is_nan(x: Double): Boolean 45 | 46 | ### Calculate x * 2^y. 47 | define ldexp(x: Double, y: Integer): Double 48 | 49 | ### Calculate the log of a double with base e. 50 | define log(x: Double): Double 51 | 52 | ### Calculate the log of a double with base 10. 53 | define log10(x: Double): Double 54 | 55 | ### Split a double into an integer and a fractional part <[ipart, fpart]>. 56 | define modf(x: Double): Tuple[Double, Double] 57 | 58 | ### Calculate x^y. 59 | define pow(x: Double, y: Double): Double 60 | 61 | ### Calculate the sine of a double in radians. 62 | define sin(x: Double): Double 63 | 64 | ### Calculate the hyperbolic sine of a double in radians. 65 | define sinh(x: Double): Double 66 | 67 | ### Calculate the square root of a double. 68 | define sqrt(x: Double): Double 69 | 70 | ### Calculate the tangent of a double in radians. 71 | define tan(x: Double): Double 72 | 73 | ### Calculate the hyperbolic tangent of a double in radians. 74 | define tanh(x: Double): Double 75 | 76 | ### Convert a double in radians to degrees. 77 | define to_deg(x: Double): Double 78 | 79 | ### Convert a double in degrees to radians. 80 | define to_rad(x: Double): Double 81 | 82 | ### Value used as an error returned by math functions. +infinity on systems 83 | ### supporting IEEE Std 754-1985. 84 | var huge: Double 85 | 86 | ### Value representing unsigned infinity 87 | var infinity: Double 88 | 89 | ### Value representing not a number 90 | var nan: Double 91 | 92 | ### Value of pi 93 | var pi: Double 94 | -------------------------------------------------------------------------------- /manifest/pkg_random.lily: -------------------------------------------------------------------------------- 1 | import manifest 2 | 3 | ### The random package provides access to a Mersenne Twister for pseudo-random 4 | ### number generation. 5 | library random 6 | 7 | ### The `Random` class provides access to the random number generator. Each 8 | ### instance is completely separate from all others. 9 | ### 10 | ### The constructor for this class takes a seed. If the seed provided is 0 or 11 | ### less, then the current time (`time(NULL)` in C) is used instead. 12 | foreign class Random(seed: *Integer = 0) 13 | { 14 | ### Generate a random `Integer` value between `lower` and `upper`. 15 | ### 16 | ### # Errors 17 | ### 18 | ### * `ValueError` is raised if the range is empty, or reversed. 19 | public define between(lower: Integer, upper: Integer): Integer 20 | } 21 | -------------------------------------------------------------------------------- /manifest/pkg_subprocess.lily: -------------------------------------------------------------------------------- 1 | import manifest 2 | 3 | ### This package provides subprocess creation. 4 | library subprocess 5 | 6 | ### Launch `command` as a subprocess in `mode`. The result of this function is 7 | ### a `File` that can be read from or written to like any other `File`. 8 | ### 9 | ### The mode provided must be one of the following: 10 | ### 11 | ### * `"r"` Read mode. This allows reading from the stdout of the subprocess. 12 | ### 13 | ### * `"w"` Write mode. This allows writing to the stdin of the subprocess. 14 | ### 15 | ### # Errors 16 | ### 17 | ### * `ValueError` if mode is not one of the modes listed above. 18 | ### 19 | ### * `RuntimeError` if unable to start the subprocess. 20 | define popen(command: String, mode: *String="r"): File 21 | -------------------------------------------------------------------------------- /manifest/pkg_sys.lily: -------------------------------------------------------------------------------- 1 | import manifest 2 | 3 | ### Interpreter and system environment access. 4 | library sys 5 | 6 | ### Exit the interpreter with `status` exit code. 7 | ### 8 | ### This function will always exit the interpreter, because it does NOT raise an 9 | ### exception to exit. 10 | ### 11 | ### Following an exit, the interpreter will yield control to the embedder. The 12 | ### interpreter will block subsequent attempts to load content to prevent 13 | ### further usage. Regardless of exit code, the interpreter will return to the 14 | ### embedder successfully (no error is recorded). 15 | ### 16 | ### The interpreter does not exit on behalf of the embedder. Instead, the 17 | ### interpreter provides functionality through the api to retrieve a suitable 18 | ### exit code. 19 | define exit(status: Byte) 20 | 21 | ### Convenience function for calling `sys.exit` with EXIT_FAILURE. 22 | define exit_failure 23 | 24 | ### Convenience function for calling `sys.exit` with EXIT_SUCCESS. 25 | define exit_success 26 | 27 | ### Search the environment for `name`, returning either a `Some` with the 28 | ### contents, or `None`. Internally, this is a wrapper over C's getenv. 29 | define getenv(name: String): Option[String] 30 | 31 | ### Return the current recursion limit. 32 | define recursion_limit: Integer 33 | 34 | ### Attempt to set `limit` as the maximum recursion limit. 35 | ### 36 | ### # Errors 37 | ### 38 | ### * `ValueError` if `limit` is lower than the current recursion depth, or an 39 | ### unreasonable value (too high or low). 40 | define set_recursion_limit(limit: Integer) 41 | 42 | ### This contains arguments sent to the program through the command-line. If 43 | ### Lily was not invoked from the command-line (ex: mod_lily), then this is 44 | ### empty. 45 | var argv: List[String] 46 | -------------------------------------------------------------------------------- /manifest/pkg_time.lily: -------------------------------------------------------------------------------- 1 | import manifest 2 | 3 | ### The time package provides access to basic time information on the system. 4 | library time 5 | 6 | ### Instances of this class represent a single point in time. This class also 7 | ### includes static methods to provide a few extra features. 8 | foreign static class Time 9 | { 10 | ### Returns the number of seconds of CPU time the interpreter has used. 11 | public static define clock: Double 12 | 13 | ### Returns a `Time` instance representing the current system time. 14 | public static define now: Time 15 | 16 | ### Returns the value of `self` as a number of seconds since the epoch. 17 | public define since_epoch: Integer 18 | 19 | ### Return a `String` representation of a `Time` instance. 20 | ### 21 | ### Internally, this calls strftime with `"%Y-%m-%d %H:%M:%S %z"`. 22 | ### 23 | ### Example output: `"2016-7-10 16:30:00 -0800"`. 24 | public define to_s: String 25 | } 26 | -------------------------------------------------------------------------------- /run/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories("${PROJECT_SOURCE_DIR}/src/") 2 | 3 | add_executable(lily lily.c $) 4 | set_target_properties(lily PROPERTIES ENABLE_EXPORTS TRUE) 5 | 6 | if(LILY_NEED_M) 7 | target_link_libraries(lily m) 8 | endif() 9 | 10 | if(LILY_NEED_DL) 11 | target_link_libraries(lily dl) 12 | endif() 13 | 14 | install(TARGETS lily DESTINATION bin) 15 | -------------------------------------------------------------------------------- /run/lily.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "lily.h" 7 | 8 | static void usage() 9 | { 10 | fputs("Usage: lily [option] ...\n" 11 | "Options:\n" 12 | "-h : Print this help and exit.\n" 13 | "-t : Code is between tags.\n" 14 | " Everything else is printed to stdout.\n" 15 | " By default, everything is treated as code.\n" 16 | "-s string : The program is a string (end of options).\n" 17 | "-gstart N : Initial # of objects allowed before a gc sweep.\n" 18 | "-gmul N : (# allowed * N) when sweep can't free anything.\n" 19 | "file : The program is the given filename.\n", stderr); 20 | exit(EXIT_FAILURE); 21 | } 22 | 23 | int is_file; 24 | int do_tags = 0; 25 | int gc_start = -1; 26 | int gc_multiplier = -1; 27 | char *to_process = NULL; 28 | 29 | static void process_args(int argc, char **argv, int *argc_offset) 30 | { 31 | int i; 32 | for (i = 1;i < argc;i++) { 33 | char *arg = argv[i]; 34 | if (strcmp("-h", arg) == 0) 35 | usage(); 36 | else if (strcmp("-t", arg) == 0) 37 | do_tags = 1; 38 | else if (strcmp("-gstart", arg) == 0) { 39 | i++; 40 | if (i + 1 == argc) 41 | usage(); 42 | 43 | gc_start = atoi(argv[i]); 44 | } 45 | else if (strcmp("-gmul", arg) == 0) { 46 | i++; 47 | if (i + 1 == argc) 48 | usage(); 49 | 50 | gc_multiplier = atoi(argv[i]); 51 | } 52 | else if (strcmp("-s", arg) == 0) { 53 | i++; 54 | if (i == argc) 55 | usage(); 56 | 57 | is_file = 0; 58 | break; 59 | } 60 | else { 61 | is_file = 1; 62 | break; 63 | } 64 | } 65 | 66 | to_process = argv[i]; 67 | *argc_offset = i; 68 | } 69 | 70 | int main(int argc, char **argv) 71 | { 72 | int argc_offset; 73 | process_args(argc, argv, &argc_offset); 74 | if (to_process == NULL) 75 | usage(); 76 | 77 | lily_config config; 78 | 79 | lily_config_init(&config); 80 | 81 | if (gc_start != -1) 82 | config.gc_start = gc_start; 83 | if (gc_multiplier != -1) 84 | config.gc_multiplier = gc_multiplier; 85 | 86 | config.argc = argc - argc_offset; 87 | config.argv = argv + argc_offset; 88 | 89 | lily_state *state = lily_new_state(&config); 90 | 91 | int result; 92 | 93 | if (is_file == 1) 94 | result = lily_load_file(state, to_process); 95 | else 96 | result = lily_load_string(state, "[cli]", to_process); 97 | 98 | if (result) { 99 | if (do_tags == 0) 100 | result = lily_parse_content(state); 101 | else 102 | result = lily_render_content(state); 103 | } 104 | 105 | if (result == 0) 106 | fputs(lily_error_message(state), stderr); 107 | 108 | int exit_code = lily_exit_code(state); 109 | 110 | lily_free_state(state); 111 | exit(exit_code); 112 | } 113 | -------------------------------------------------------------------------------- /scripts/common.lily: -------------------------------------------------------------------------------- 1 | var script_name = "" 2 | 3 | # Token records are in three parts: 4 | # 5 | # 1) A single character representation of the token, if one exists. 6 | # If this token does not map to a single Byte, this is ' '. 7 | # 8 | # 2) What to write when printing the token. 9 | # 10 | # 3) The name to write in the lily_token enum. 11 | # 12 | var token_data = [ 13 | <[')', ")", "tk_right_parenth"]>, 14 | <[',', ",", "tk_comma"]>, 15 | <['{', "{", "tk_left_curly"]>, 16 | <['}', "}", "tk_right_curly"]>, 17 | <['[', "[", "tk_left_bracket"]>, 18 | <[':', ":", "tk_colon"]>, 19 | <['~', "~", "tk_tilde"]>, 20 | 21 | # These five have their eq at +1 from their base. 22 | <['^', "^", "tk_bitwise_xor"]>, 23 | <[' ', "^=", "tk_bitwise_xor_eq"]>, 24 | <['!', "!", "tk_not"]>, 25 | <[' ', "!=", "tk_not_eq"]>, 26 | <['%', "%", "tk_modulo"]>, 27 | <[' ', "%=", "tk_modulo_eq"]>, 28 | <['*', "*", "tk_multiply"]>, 29 | <[' ', "*=", "tk_multiply_eq"]>, 30 | <['/', "/", "tk_divide"]>, 31 | <[' ', "/=", "tk_divide_eq"]>, 32 | 33 | <['+', "+", "tk_plus"]>, 34 | <[' ', "++", "tk_plus_plus"]>, 35 | <[' ', "+=", "tk_plus_eq"]>, 36 | <['-', "-", "tk_minus"]>, 37 | <[' ', "-=", "tk_minus_eq"]>, 38 | <['<', "<", "tk_lt"]>, 39 | <[' ', "<=", "tk_lt_eq"]>, 40 | <[' ', "<<", "tk_left_shift"]>, 41 | <[' ', "<<=", "tk_left_shift_eq"]>, 42 | <['>', ">", "tk_gt"]>, 43 | <[' ', ">=", "tk_gt_eq"]>, 44 | <[' ', ">>", "tk_right_shift"]>, 45 | <[' ', ">>=", "tk_right_shift_eq"]>, 46 | <['=', "=", "tk_equal"]>, 47 | <[' ', "==", "tk_eq_eq"]>, 48 | <['(', "(", "tk_left_parenth"]>, 49 | <[' ', "a lambda", "tk_lambda"]>, 50 | <[' ', "<[", "tk_tuple_open"]>, 51 | <[' ', "]>", "tk_tuple_close"]>, 52 | <[']', "]", "tk_right_bracket"]>, 53 | <[' ', "=>", "tk_arrow"]>, 54 | <[' ', "a label", "tk_word"]>, 55 | <[' ', "a property name", "tk_prop_word"]>, 56 | <['"', "a string", "tk_double_quote"]>, 57 | <[' ', "a bytestring", "tk_bytestring"]>, 58 | <['\'', "a byte", "tk_byte"]>, 59 | <[' ', "an integer", "tk_integer"]>, 60 | <[' ', "a double", "tk_double"]>, 61 | <[' ', "a docblock", "tk_docblock"]>, 62 | <[' ', "a named argument", "tk_keyword_arg"]>, 63 | <['.', ".", "tk_dot"]>, 64 | <['&', "&", "tk_bitwise_and"]>, 65 | <[' ', "&=", "tk_bitwise_and_eq"]>, 66 | <[' ', "&&", "tk_logical_and"]>, 67 | <['|', "|", "tk_bitwise_or"]>, 68 | <[' ', "|=", "tk_bitwise_or_eq"]>, 69 | <[' ', "||", "tk_logical_or"]>, 70 | <[' ', "@(", "tk_typecast_parenth"]>, 71 | <[' ', "...", "tk_three_dots"]>, 72 | <[' ', "|>", "tk_func_pipe"]>, 73 | <[' ', "$1", "tk_scoop"]>, 74 | <[' ', "invalid token", "tk_invalid"]>, 75 | <[' ', "end of lambda", "tk_end_lambda"]>, 76 | <[' ', "?>", "tk_end_tag"]>, 77 | <[' ', "end of file", "tk_eof"]>, 78 | ] 79 | 80 | define open_file(path: String): File 81 | { 82 | var f = File.open(path, "w") 83 | 84 | # Turn "src/lily_xyz.h" into LILY_XYZ_H. 85 | var header_name = path.split("/")[-1] 86 | .replace(".", "_") 87 | .upper() 88 | 89 | f.write("""\ 90 | #ifndef {0} 91 | # define {0} 92 | 93 | /* Generated by {1}. */\n\n\ 94 | """.format(header_name, script_name)) 95 | 96 | return f 97 | } 98 | 99 | define finish_file(f: File, path: String) 100 | { 101 | f.write("#endif\n") 102 | f.close() 103 | print(script_name ++ ": Updated " ++ path ++ ".") 104 | } 105 | 106 | define write_table_header(f: File, info: String) 107 | { 108 | f.write("static const " ++ info ++ "[] = {\n") 109 | } 110 | 111 | define write_numeric_table[A](f: File, table: List[A]) 112 | { 113 | for i in 0...table.size() - 1 by 16: { 114 | var slice = table.slice(i, i + 16) 115 | 116 | if slice.size() == 0: { 117 | slice = table.slice(i, table.size()) 118 | } 119 | 120 | var slice_str = " " ++ slice.join(", ") ++ ",\n" 121 | 122 | f.write(slice_str) 123 | } 124 | 125 | f.write("};\n\n") 126 | } 127 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | file(GLOB lily_SOURCES *.c *.h) 2 | 3 | # The objects in this library are used to build several targets, but the library 4 | # itself is never installed. 5 | add_library(liblily_obj OBJECT ${lily_SOURCES}) 6 | 7 | if(WIN32) 8 | # C extensions that use interpreter functions need to have their own copy of 9 | # interpreter functions due to how DLLs work. This builds that. 10 | add_library(liblily STATIC $) 11 | else() 12 | add_library(liblily SHARED $) 13 | 14 | if(LILY_NEED_DL) 15 | target_link_libraries(liblily dl) 16 | endif() 17 | 18 | # -fPIC is needed for it to work properly as a shared library. 19 | set_target_properties(liblily_obj PROPERTIES COMPILE_FLAGS "-fPIC") 20 | 21 | # This prevents the shared library being named libliblily 22 | set_target_properties(liblily PROPERTIES PREFIX "") 23 | endif() 24 | 25 | if(LILY_NEED_M) 26 | target_link_libraries(liblily m) 27 | endif() 28 | 29 | install(TARGETS liblily 30 | ARCHIVE DESTINATION lib 31 | LIBRARY DESTINATION lib 32 | COMPONENT library) 33 | 34 | install(FILES lily.h 35 | DESTINATION "include/lily") 36 | -------------------------------------------------------------------------------- /src/csiphash.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013 Marek Majkowski 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | 22 | 23 | Original location: 24 | https://github.com/majek/csiphash/ 25 | 26 | Solution inspired by code from: 27 | Samuel Neves (supercop/crypto_auth/siphash24/little) 28 | djb (supercop/crypto_auth/siphash24/little2) 29 | Jean-Philippe Aumasson (https://131002.net/siphash/siphash24.c) 30 | */ 31 | 32 | #include 33 | 34 | #if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \ 35 | __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 36 | # define _le64toh(x) ((uint64_t)(x)) 37 | #elif defined(_WIN32) 38 | /* Windows is always little endian, unless you're on xbox360 39 | http://msdn.microsoft.com/en-us/library/b0084kay(v=vs.80).aspx */ 40 | # define _le64toh(x) ((uint64_t)(x)) 41 | #elif defined(__APPLE__) 42 | # include 43 | # define _le64toh(x) OSSwapLittleToHostInt64(x) 44 | #else 45 | 46 | /* See: http://sourceforge.net/p/predef/wiki/Endianness/ */ 47 | # if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) 48 | # include 49 | # else 50 | # include 51 | # endif 52 | # if defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \ 53 | __BYTE_ORDER == __LITTLE_ENDIAN 54 | # define _le64toh(x) ((uint64_t)(x)) 55 | # else 56 | # define _le64toh(x) le64toh(x) 57 | # endif 58 | 59 | #endif 60 | 61 | 62 | #define ROTATE(x, b) (uint64_t)( ((x) << (b)) | ( (x) >> (64 - (b))) ) 63 | 64 | #define HALF_ROUND(a,b,c,d,s,t) \ 65 | a += b; c += d; \ 66 | b = ROTATE(b, s) ^ a; \ 67 | d = ROTATE(d, t) ^ c; \ 68 | a = ROTATE(a, 32); 69 | 70 | #define DOUBLE_ROUND(v0,v1,v2,v3) \ 71 | HALF_ROUND(v0,v1,v2,v3,13,16); \ 72 | HALF_ROUND(v2,v1,v0,v3,17,21); \ 73 | HALF_ROUND(v0,v1,v2,v3,13,16); \ 74 | HALF_ROUND(v2,v1,v0,v3,17,21); 75 | 76 | 77 | uint64_t siphash24(const void *src, unsigned long src_sz, const char key[16]) { 78 | const uint64_t *_key = (uint64_t *)key; 79 | uint64_t k0 = _le64toh(_key[0]); 80 | uint64_t k1 = _le64toh(_key[1]); 81 | uint64_t b = (uint64_t)src_sz << 56; 82 | const uint64_t *in = (uint64_t*)src; 83 | 84 | uint64_t v0 = k0 ^ 0x736f6d6570736575ULL; 85 | uint64_t v1 = k1 ^ 0x646f72616e646f6dULL; 86 | uint64_t v2 = k0 ^ 0x6c7967656e657261ULL; 87 | uint64_t v3 = k1 ^ 0x7465646279746573ULL; 88 | 89 | while (src_sz >= 8) { 90 | uint64_t mi = _le64toh(*in); 91 | in += 1; src_sz -= 8; 92 | v3 ^= mi; 93 | DOUBLE_ROUND(v0,v1,v2,v3); 94 | v0 ^= mi; 95 | } 96 | 97 | uint64_t t = 0; uint8_t *pt = (uint8_t *)&t; uint8_t *m = (uint8_t *)in; 98 | switch (src_sz) { 99 | case 7: pt[6] = m[6]; 100 | case 6: pt[5] = m[5]; 101 | case 5: pt[4] = m[4]; 102 | case 4: *((uint32_t*)&pt[0]) = *((uint32_t*)&m[0]); break; 103 | case 3: pt[2] = m[2]; 104 | case 2: pt[1] = m[1]; 105 | case 1: pt[0] = m[0]; 106 | } 107 | b |= _le64toh(t); 108 | 109 | v3 ^= b; 110 | DOUBLE_ROUND(v0,v1,v2,v3); 111 | v0 ^= b; v2 ^= 0xff; 112 | DOUBLE_ROUND(v0,v1,v2,v3); 113 | DOUBLE_ROUND(v0,v1,v2,v3); 114 | return (v0 ^ v1) ^ (v2 ^ v3); 115 | } 116 | -------------------------------------------------------------------------------- /src/lily_alloc.c: -------------------------------------------------------------------------------- 1 | #include "lily_alloc.h" 2 | 3 | void *lily_malloc(size_t size) 4 | { 5 | void *result = malloc(size); 6 | if (result == NULL) 7 | abort(); 8 | 9 | return result; 10 | } 11 | 12 | void *lily_realloc(void *ptr, size_t new_size) 13 | { 14 | void *result = realloc(ptr, new_size); 15 | if (result == NULL) 16 | abort(); 17 | 18 | return result; 19 | } 20 | 21 | void lily_free(void *ptr) 22 | { 23 | free(ptr); 24 | } 25 | -------------------------------------------------------------------------------- /src/lily_alloc.h: -------------------------------------------------------------------------------- 1 | #ifndef LILY_API_ALLOC_H 2 | # define LILY_API_ALLOC_H 3 | 4 | # include 5 | 6 | void *lily_malloc(size_t); 7 | void *lily_realloc(void *, size_t); 8 | void lily_free(void *); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /src/lily_buffer_u16.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "lily_alloc.h" 4 | #include "lily_buffer_u16.h" 5 | 6 | lily_buffer_u16 *lily_new_buffer_u16(uint16_t initial) 7 | { 8 | lily_buffer_u16 *b = lily_malloc(sizeof(*b)); 9 | 10 | b->data = lily_malloc(initial * sizeof(*b->data)); 11 | b->pos = 0; 12 | b->size = initial; 13 | return b; 14 | } 15 | 16 | void lily_u16_write_1(lily_buffer_u16 *b, uint16_t one) 17 | { 18 | if (b->pos + 1 > b->size) { 19 | b->size *= 2; 20 | b->data = lily_realloc(b->data, b->size * sizeof(*b->data)); 21 | } 22 | 23 | b->data[b->pos] = one; 24 | b->pos++; 25 | } 26 | 27 | void lily_u16_write_2(lily_buffer_u16 *b, uint16_t one, uint16_t two) 28 | { 29 | if (b->pos + 2 > b->size) { 30 | b->size *= 2; 31 | b->data = lily_realloc(b->data, b->size * sizeof(*b->data)); 32 | } 33 | 34 | b->data[b->pos ] = one; 35 | b->data[b->pos + 1] = two; 36 | b->pos += 2; 37 | } 38 | 39 | void lily_u16_write_3(lily_buffer_u16 *b, uint16_t one, uint16_t two, 40 | uint16_t three) 41 | { 42 | if (b->pos + 3 > b->size) { 43 | b->size *= 2; 44 | b->data = lily_realloc(b->data, b->size * sizeof(*b->data)); 45 | } 46 | 47 | b->data[b->pos ] = one; 48 | b->data[b->pos + 1] = two; 49 | b->data[b->pos + 2] = three; 50 | b->pos += 3; 51 | } 52 | 53 | void lily_u16_write_4(lily_buffer_u16 *b, uint16_t one, uint16_t two, 54 | uint16_t three, uint16_t four) 55 | { 56 | if (b->pos + 4 > b->size) { 57 | b->size *= 2; 58 | b->data = lily_realloc(b->data, b->size * sizeof(*b->data)); 59 | } 60 | 61 | b->data[b->pos ] = one; 62 | b->data[b->pos + 1] = two; 63 | b->data[b->pos + 2] = three; 64 | b->data[b->pos + 3] = four; 65 | b->pos += 4; 66 | } 67 | 68 | void lily_u16_write_5(lily_buffer_u16 *b, uint16_t one, uint16_t two, 69 | uint16_t three, uint16_t four, uint16_t five) 70 | { 71 | if (b->pos + 5 > b->size) { 72 | b->size *= 2; 73 | b->data = lily_realloc(b->data, b->size * sizeof(*b->data)); 74 | } 75 | 76 | b->data[b->pos ] = one; 77 | b->data[b->pos + 1] = two; 78 | b->data[b->pos + 2] = three; 79 | b->data[b->pos + 3] = four; 80 | b->data[b->pos + 4] = five; 81 | b->pos += 5; 82 | } 83 | 84 | void lily_u16_write_6(lily_buffer_u16 *b, uint16_t one, uint16_t two, 85 | uint16_t three, uint16_t four, uint16_t five, uint16_t six) 86 | { 87 | if (b->pos + 6 > b->size) { 88 | b->size *= 2; 89 | b->data = lily_realloc(b->data, b->size * sizeof(*b->data)); 90 | } 91 | 92 | b->data[b->pos ] = one; 93 | b->data[b->pos + 1] = two; 94 | b->data[b->pos + 2] = three; 95 | b->data[b->pos + 3] = four; 96 | b->data[b->pos + 4] = five; 97 | b->data[b->pos + 5] = six; 98 | b->pos += 6; 99 | } 100 | 101 | void lily_u16_write_prep(lily_buffer_u16 *b, uint16_t needed) 102 | { 103 | if (b->pos + needed > b->size) { 104 | while ((b->pos + needed) > b->size) 105 | b->size *= 2; 106 | 107 | b->data = lily_realloc(b->data, sizeof(*b->data) * b->size); 108 | } 109 | } 110 | 111 | uint16_t lily_u16_pop(lily_buffer_u16 *b) 112 | { 113 | uint16_t result = b->data[b->pos - 1]; 114 | b->pos--; 115 | return result; 116 | } 117 | 118 | void lily_u16_inject(lily_buffer_u16 *b, int where, uint16_t value) 119 | { 120 | if (b->pos + 1 > b->size) { 121 | b->size *= 2; 122 | b->data = lily_realloc(b->data, b->size * sizeof(*b->data)); 123 | } 124 | 125 | int move_by = b->pos - where; 126 | 127 | memmove(b->data+where+1, b->data+where, move_by * sizeof(*b->data)); 128 | b->pos++; 129 | b->data[where] = value; 130 | } 131 | 132 | void lily_free_buffer_u16(lily_buffer_u16 *b) 133 | { 134 | lily_free(b->data); 135 | lily_free(b); 136 | } 137 | -------------------------------------------------------------------------------- /src/lily_buffer_u16.h: -------------------------------------------------------------------------------- 1 | #ifndef LILY_BUFFER_U16_H 2 | # define LILY_BUFFER_U16_H 3 | 4 | # include 5 | 6 | typedef struct { 7 | uint16_t *data; 8 | uint16_t pos; 9 | uint16_t size; 10 | uint32_t pad; 11 | } lily_buffer_u16; 12 | 13 | lily_buffer_u16 *lily_new_buffer_u16(uint16_t); 14 | 15 | void lily_u16_write_1(lily_buffer_u16 *, uint16_t); 16 | void lily_u16_write_2(lily_buffer_u16 *, uint16_t, uint16_t); 17 | void lily_u16_write_3(lily_buffer_u16 *, uint16_t, uint16_t, uint16_t); 18 | void lily_u16_write_4(lily_buffer_u16 *, uint16_t, uint16_t, uint16_t, uint16_t); 19 | void lily_u16_write_5(lily_buffer_u16 *, uint16_t, uint16_t, uint16_t, uint16_t, 20 | uint16_t); 21 | void lily_u16_write_6(lily_buffer_u16 *, uint16_t, uint16_t, uint16_t, uint16_t, 22 | uint16_t, uint16_t); 23 | 24 | void lily_u16_write_prep(lily_buffer_u16 *, uint16_t); 25 | 26 | uint16_t lily_u16_pop(lily_buffer_u16 *); 27 | 28 | #define lily_u16_pos(b) b->pos 29 | #define lily_u16_get(b, pos) b->data[pos] 30 | #define lily_u16_set_pos(b, what) b->pos = what 31 | #define lily_u16_set_at(b, where, what) b->data[where] = what 32 | void lily_u16_inject(lily_buffer_u16 *, int, uint16_t); 33 | 34 | void lily_free_buffer_u16(lily_buffer_u16 *); 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /src/lily_code_iter.h: -------------------------------------------------------------------------------- 1 | #ifndef LILY_CODE_ITER_H 2 | # define LILY_CODE_ITER_H 3 | 4 | # include 5 | 6 | typedef struct { 7 | uint16_t *buffer; 8 | 9 | uint16_t offset; 10 | uint16_t stop; 11 | uint16_t round_total; 12 | uint16_t opcode; 13 | 14 | uint16_t special_1; 15 | uint16_t counter_2; 16 | uint16_t inputs_3; 17 | uint16_t outputs_4; 18 | 19 | uint16_t jumps_5; 20 | uint16_t line_6; 21 | uint32_t pad; 22 | } lily_code_iter; 23 | 24 | void lily_ci_init(lily_code_iter *, uint16_t *, uint16_t, uint16_t); 25 | int lily_ci_next(lily_code_iter *); 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /src/lily_generic_pool.c: -------------------------------------------------------------------------------- 1 | #include "lily_alloc.h" 2 | #include "lily_core_types.h" 3 | #include "lily_generic_pool.h" 4 | #include "lily_symtab.h" 5 | #include "lily_type_maker.h" 6 | 7 | lily_generic_pool *lily_new_generic_pool(void) 8 | { 9 | lily_generic_pool *gp = lily_malloc(sizeof(*gp)); 10 | lily_class **cache_generics = lily_malloc(4 * sizeof(*cache_generics)); 11 | lily_class **scope_generics = lily_malloc(4 * sizeof(*scope_generics)); 12 | 13 | gp->cache_generics = cache_generics; 14 | gp->cache_size = 4; 15 | 16 | int i; 17 | for (i = 0;i < 4;i++) 18 | cache_generics[i] = NULL; 19 | 20 | gp->scope_generics = scope_generics; 21 | gp->scope_start = 0; 22 | gp->scope_end = 0; 23 | gp->scope_size = 4; 24 | 25 | return gp; 26 | } 27 | 28 | void lily_rewind_generic_pool(lily_generic_pool *gp) 29 | { 30 | gp->scope_start = 0; 31 | gp->scope_end = 0; 32 | } 33 | 34 | void lily_free_generic_pool(lily_generic_pool *gp) 35 | { 36 | int i; 37 | for (i = 0;i < gp->cache_size;i++) { 38 | lily_class *c = gp->cache_generics[i]; 39 | if (c == NULL) 40 | break; 41 | 42 | lily_free(c->self_type); 43 | lily_free(c->name); 44 | lily_free(c); 45 | } 46 | 47 | lily_free(gp->cache_generics); 48 | lily_free(gp->scope_generics); 49 | lily_free(gp); 50 | } 51 | 52 | static lily_class *find_in_cache(lily_generic_pool *gp, const char *name, 53 | int *next_pos) 54 | { 55 | int i = 0; 56 | lily_class *c = gp->cache_generics[i]; 57 | 58 | while (c) { 59 | if (c->name[0] == name[0]) 60 | return c; 61 | 62 | i++; 63 | c = gp->cache_generics[i]; 64 | } 65 | 66 | *next_pos = i; 67 | return NULL; 68 | } 69 | 70 | lily_type *lily_gp_push(lily_generic_pool *gp, const char *name, uint16_t pos) 71 | { 72 | int i; 73 | lily_class *result = find_in_cache(gp, name, &i); 74 | 75 | if (result == NULL) { 76 | lily_class *new_generic = lily_new_raw_class(name, 0); 77 | lily_type *t = lily_new_raw_type(new_generic); 78 | 79 | t->flags |= TYPE_IS_UNRESOLVED; 80 | t->generic_pos = pos; 81 | 82 | new_generic->id = LILY_ID_GENERIC; 83 | new_generic->self_type = t; 84 | new_generic->all_subtypes = t; 85 | new_generic->flags |= CLS_GC_SPECULATIVE; 86 | 87 | result = new_generic; 88 | gp->cache_generics[i] = new_generic; 89 | 90 | if (i + 1 == gp->cache_size) { 91 | gp->cache_size *= 2; 92 | lily_class **new_cache = lily_realloc(gp->cache_generics, 93 | gp->cache_size * sizeof(*new_cache)); 94 | 95 | for (i = i + 1;i < gp->cache_size;i++) 96 | new_cache[i] = NULL; 97 | 98 | gp->cache_generics = new_cache; 99 | } 100 | } 101 | 102 | if (gp->scope_end == gp->scope_size) { 103 | gp->scope_size *= 2; 104 | lily_class **new_scope = lily_realloc(gp->scope_generics, 105 | gp->scope_size * sizeof(*new_scope)); 106 | 107 | gp->scope_generics = new_scope; 108 | } 109 | 110 | gp->scope_generics[gp->scope_end] = result; 111 | gp->scope_end++; 112 | 113 | return result->self_type; 114 | } 115 | 116 | lily_class *lily_gp_find(lily_generic_pool *gp, const char *name) 117 | { 118 | char ch = name[0]; 119 | int i; 120 | 121 | for (i = gp->scope_start;i < gp->scope_end;i++) { 122 | lily_class *c = gp->scope_generics[i]; 123 | if (c->name[0] == ch) 124 | return c; 125 | } 126 | 127 | return NULL; 128 | } 129 | 130 | int lily_gp_num_in_scope(lily_generic_pool *gp) 131 | { 132 | return gp->scope_end - gp->scope_start; 133 | } 134 | 135 | uint16_t lily_gp_save(lily_generic_pool *gp) 136 | { 137 | return gp->scope_end; 138 | } 139 | 140 | void lily_gp_restore(lily_generic_pool *gp, uint16_t old_end) 141 | { 142 | gp->scope_end = old_end; 143 | } 144 | 145 | uint16_t lily_gp_save_and_hide(lily_generic_pool *gp) 146 | { 147 | uint16_t result = gp->scope_start; 148 | gp->scope_start = gp->scope_end; 149 | return result; 150 | } 151 | 152 | void lily_gp_restore_and_unhide(lily_generic_pool *gp, uint16_t old_start) 153 | { 154 | gp->scope_end = gp->scope_start; 155 | gp->scope_start = old_start; 156 | } 157 | -------------------------------------------------------------------------------- /src/lily_generic_pool.h: -------------------------------------------------------------------------------- 1 | #ifndef LILY_GENERIC_POOL_H 2 | # define LILY_GENERIC_POOL_H 3 | 4 | /* The generic pool is used to store generic classes. It handles management of 5 | what generics are available, and of creating them if need be. 6 | There's no index for 'cache_generics'. Instead, it will always have one class 7 | set aside as NULL as a sentinel. It's not ideal, but it prevents having to 8 | use another uint64_t. */ 9 | typedef struct { 10 | struct lily_class_ **cache_generics; 11 | struct lily_class_ **scope_generics; 12 | uint16_t cache_size; 13 | 14 | uint16_t scope_start; 15 | uint16_t scope_end; 16 | uint16_t scope_size; 17 | } lily_generic_pool; 18 | 19 | lily_generic_pool *lily_new_generic_pool(void); 20 | void lily_rewind_generic_pool(lily_generic_pool *); 21 | void lily_free_generic_pool(lily_generic_pool *); 22 | 23 | /* Try to find a generic of the given name in the pool. */ 24 | struct lily_class_ *lily_gp_find(lily_generic_pool *, const char *); 25 | 26 | /* Add a generic to the current scope. If a generic that matches the name and 27 | the generic position is given, then that generic is used. Otherwise, a new 28 | generic is created. The generic is returned. */ 29 | lily_type *lily_gp_push(lily_generic_pool *, const char *, uint16_t); 30 | 31 | /* How many generics are in the current scope? */ 32 | int lily_gp_num_in_scope(lily_generic_pool *); 33 | 34 | /* Save the end of the current scope, to restore later. The generics from the 35 | old scope are still visible. */ 36 | uint16_t lily_gp_save(lily_generic_pool *); 37 | 38 | /* Save the start of the current scope, to unhide later. You should only use 39 | this if there cannot be a prior scope (such as how classes and enums do not 40 | allow nesting OR when running a dynaload). */ 41 | uint16_t lily_gp_save_and_hide(lily_generic_pool *); 42 | 43 | /* Restore the end of the current scope. */ 44 | void lily_gp_restore(lily_generic_pool *, uint16_t); 45 | 46 | /* Unhide generics from the old scope. */ 47 | void lily_gp_restore_and_unhide(lily_generic_pool *, uint16_t); 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /src/lily_lexer.h: -------------------------------------------------------------------------------- 1 | #ifndef LILY_LEXER_H 2 | # define LILY_LEXER_H 3 | 4 | # include 5 | 6 | # include "lily_raiser.h" 7 | # include "lily_string_pile.h" 8 | # include "lily_token.h" 9 | 10 | typedef enum { 11 | et_copied_string, 12 | et_file, 13 | et_lambda, 14 | et_shallow_string, 15 | et_unused, 16 | } lily_lex_entry_type; 17 | 18 | typedef union { 19 | int64_t integer_val; 20 | double double_val; 21 | } lily_lex_number; 22 | 23 | typedef struct lily_lex_entry_ { 24 | union { 25 | FILE *entry_file; 26 | /* For string-based entries, where to read the next line from. */ 27 | const char *entry_cursor; 28 | }; 29 | 30 | /* For copied strings, this holds the origin so that it can be free'd when 31 | the entry is done. */ 32 | char *cursor_origin; 33 | 34 | /* These fields are aligned with fields of the same name in lily_lex_state. 35 | When another entry becomes current, the lexer's state is saved into the 36 | entry. */ 37 | lily_token token: 8; 38 | uint8_t pad; 39 | uint16_t line_num; 40 | uint16_t expand_start_line; 41 | uint16_t string_length; 42 | lily_lex_number n; 43 | 44 | /* Where this entry starts in lexer's string pile. */ 45 | uint16_t pile_start; 46 | /* How long the identifier saved is. */ 47 | uint16_t ident_length; 48 | /* How far the read cursor is from the source line. */ 49 | uint16_t cursor_offset; 50 | lily_lex_entry_type entry_type: 16; 51 | 52 | struct lily_lex_entry_ *prev; 53 | struct lily_lex_entry_ *next; 54 | } lily_lex_entry; 55 | 56 | typedef struct { 57 | /* Where the next token read should begin. */ 58 | char *read_cursor; 59 | /* A buffer for storing the last identifier read in. This buffer's size is 60 | always at least the size of the source, so that identifier reading 61 | doesn't need to do buffer size checks when copying over. */ 62 | char *label; 63 | 64 | uint32_t source_size; 65 | uint32_t label_size; 66 | 67 | lily_token token: 8; 68 | uint8_t pad; 69 | uint16_t line_num; 70 | /* For tokens that can span multiple lines, this is their starting line. */ 71 | uint16_t expand_start_line; 72 | union { 73 | /* How many bytes are in the String/ByteString literal. */ 74 | uint16_t string_length; 75 | /* If the last digit scanned had a sign, then that sign is at source 76 | plus this offset. 77 | If there wasn't a sign, this is uint16 max. */ 78 | uint16_t number_sign_offset; 79 | }; 80 | 81 | lily_lex_number n; 82 | char *source; 83 | lily_lex_entry *entry; 84 | 85 | /* This holds the source lines and identifiers of entries that aren't 86 | current. This pile is not shared anywhere else. */ 87 | lily_string_pile *string_pile; 88 | 89 | lily_raiser *raiser; 90 | } lily_lex_state; 91 | 92 | lily_lex_state *lily_new_lex_state(lily_raiser *); 93 | void lily_rewind_lex_state(lily_lex_state *, uint16_t); 94 | void lily_free_lex_state(lily_lex_state *); 95 | 96 | void lily_lexer_load(lily_lex_state *, lily_lex_entry_type, const void *); 97 | void lily_pop_lex_entry(lily_lex_state *); 98 | 99 | void lily_next_token(lily_lex_state *); 100 | 101 | char *lily_read_template_content(lily_lex_state *, int *); 102 | int lily_read_manifest_header(lily_lex_state *); 103 | int lily_read_template_header(lily_lex_state *); 104 | 105 | int lily_lexer_digit_rescan(lily_lex_state *); 106 | void lily_lexer_verify_path_string(lily_lex_state *); 107 | 108 | int64_t lily_scan_number(char *, int *); 109 | 110 | #endif 111 | 112 | -------------------------------------------------------------------------------- /src/lily_lexer_data.h: -------------------------------------------------------------------------------- 1 | #ifndef LILY_LEXER_DATA_H 2 | # define LILY_LEXER_DATA_H 3 | 4 | /* Generated by scripts/token.lily. */ 5 | 6 | # define CC_AT 63 7 | # define CC_B 64 8 | # define CC_CASH 65 9 | # define CC_DIGIT 66 10 | # define CC_NEWLINE 67 11 | # define CC_QUESTION 68 12 | # define CC_SHARP 69 13 | 14 | static const uint8_t ch_table[] = { 15 | 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 67, 58, 58, 58, 58, 58, 16 | 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 17 | 58, 9, 40, 69, 65, 11, 48, 42, 32, 0, 13, 17, 1, 20, 47, 15, 18 | 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 5, 58, 22, 30, 26, 68, 19 | 63, 38, 64, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 20 | 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 4, 58, 36, 7, 38, 21 | 58, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 22 | 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 2, 51, 3, 6, 58, 23 | 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 24 | 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 25 | 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 26 | 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 27 | 58, 58, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 28 | 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 29 | 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 30 | 38, 38, 38, 38, 38, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 31 | }; 32 | 33 | # define IS_IDENT_START(x) (ident_table[x] == 1) 34 | 35 | static const uint8_t ident_table[] = { 36 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 40 | 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 41 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 42 | 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 43 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 44 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 45 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 46 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 47 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 48 | 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 49 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 50 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 51 | 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52 | }; 53 | 54 | static const char *token_name_table[] = { 55 | ")", ",", "{", "}", "[", ":", "~", "^", "^=", "!", "!=", "%", "%=", "*", 56 | "*=", "/", "/=", "+", "++", "+=", "-", "-=", "<", "<=", "<<", "<<=", ">", 57 | ">=", ">>", ">>=", "=", "==", "(", "a lambda", "<[", "]>", "]", "=>", 58 | "a label", "a property name", "a string", "a bytestring", "a byte", 59 | "an integer", "a double", "a docblock", "a named argument", ".", "&", "&=", 60 | "&&", "|", "|=", "||", "@(", "...", "|>", "$1", "invalid token", 61 | "end of lambda", "?>", "end of file", 62 | }; 63 | 64 | static const uint8_t priority_table[] = { 65 | 0, 0, 0, 0, 0, 0, 0, 7, 1, 0, 4, 10, 1, 10, 1, 10, 66 | 1, 9, 5, 1, 9, 1, 4, 4, 8, 1, 4, 4, 8, 1, 1, 4, 67 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68 | 7, 1, 3, 7, 1, 2, 0, 0, 6, 0, 0, 0, 0, 0, 69 | }; 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /src/lily_library.c: -------------------------------------------------------------------------------- 1 | #include "lily_alloc.h" 2 | #include "lily_library.h" 3 | 4 | #ifdef _WIN32 5 | #include 6 | 7 | void *lily_library_load(const char *path) 8 | { 9 | return LoadLibraryA(path); 10 | } 11 | 12 | void *lily_library_get(void *source, const char *target) 13 | { 14 | return GetProcAddress((HMODULE)source, target); 15 | } 16 | 17 | void lily_library_free(void *source) 18 | { 19 | FreeLibrary((HMODULE)source); 20 | } 21 | #else 22 | #include 23 | 24 | void *lily_library_load(const char *path) 25 | { 26 | return dlopen(path, RTLD_LAZY); 27 | } 28 | 29 | void *lily_library_get(void *source, const char *name) 30 | { 31 | return dlsym(source, name); 32 | } 33 | 34 | void lily_library_free(void *source) 35 | { 36 | dlclose(source); 37 | } 38 | #endif 39 | -------------------------------------------------------------------------------- /src/lily_library.h: -------------------------------------------------------------------------------- 1 | #ifndef LILY_LIBRARY_H 2 | # define LILY_LIBRARY_H 3 | 4 | # include "lily_core_types.h" 5 | 6 | void *lily_library_load(const char *); 7 | void *lily_library_get(void *, const char *); 8 | void lily_library_free(void *); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /src/lily_parser.h: -------------------------------------------------------------------------------- 1 | #ifndef LILY_PARSER_H 2 | # define LILY_PARSER_H 3 | 4 | # include "lily.h" 5 | # include "lily_buffer_u16.h" 6 | # include "lily_emitter.h" 7 | # include "lily_expr.h" 8 | # include "lily_generic_pool.h" 9 | # include "lily_lexer.h" 10 | # include "lily_raiser.h" 11 | # include "lily_symtab.h" 12 | # include "lily_type_maker.h" 13 | # include "lily_vm.h" 14 | 15 | /* This is used to prevent multiple content loads and to make sure content 16 | handling has content. */ 17 | #define PARSER_HAS_CONTENT 0x01 18 | 19 | /* Manifest files have different rules. */ 20 | #define PARSER_IN_MANIFEST 0x02 21 | 22 | /* This is used by symtab's rewind to determine what happens to the new symbols 23 | from the failed parse. If set, symbols are hidden in case old symbols refer 24 | to them (unlikely but possible). Otherwise, the symbols are dropped. */ 25 | #define PARSER_IS_EXECUTING 0x04 26 | 27 | /* This is used to make sure the end token is correct. */ 28 | #define PARSER_IS_RENDERING 0x08 29 | 30 | /* Don't allow complex expressions (currently only blocks lambdas). */ 31 | #define PARSER_SIMPLE_EXPR 0x10 32 | 33 | /* Consider `class Error(message: String) < Exception(message) { ... }`. A 34 | typical expression call would allow calls or subscripts against the 35 | `Exception` call. This tells expression to not allow that. */ 36 | #define PARSER_SUPER_EXPR 0x20 37 | 38 | /* This is set when parser has a docblock to store when writing the doc section 39 | for a symbol. */ 40 | #define PARSER_HAS_DOCBLOCK 0x40 41 | 42 | /* This is set when parser is processing a manifest, or if a parse starts with 43 | the config struct's extra_info set to 1. If set, parser will store docblocks, 44 | parameter names, and other useful information for introspection. By default, 45 | the information is not saved, resulting in some introspection functions 46 | returning empty strings. */ 47 | #define PARSER_EXTRA_INFO 0x80 48 | 49 | struct lily_rewind_state_; 50 | struct lily_import_state_; 51 | 52 | typedef struct { 53 | char ***data; 54 | uint16_t pos; 55 | uint16_t size; 56 | uint32_t pad; 57 | } lily_doc_stack; 58 | 59 | typedef struct lily_parse_state_ { 60 | lily_module_entry *module_start; 61 | lily_module_entry *module_top; 62 | 63 | lily_module_entry *main_module; 64 | 65 | /* This stores positions in data_strings for fetching out later. */ 66 | lily_buffer_u16 *data_stack; 67 | 68 | /* The next insertion position into data_strings. */ 69 | uint16_t data_string_pos; 70 | 71 | /* See PARSER_* flags. */ 72 | uint16_t flags; 73 | 74 | uint16_t modifiers; 75 | uint16_t pad; 76 | 77 | /* The current expression state. */ 78 | lily_expr_state *expr; 79 | 80 | /* Pile strings are stored here. */ 81 | lily_string_pile *expr_strings; 82 | 83 | /* This holds intermediate strings for keyword arguments and import. */ 84 | lily_string_pile *data_strings; 85 | 86 | /* The parser uses this to hold and register generic classes. */ 87 | lily_generic_pool *generics; 88 | 89 | lily_function_val *toplevel_func; 90 | 91 | lily_class *current_class; 92 | lily_msgbuf *msgbuf; 93 | lily_lex_state *lex; 94 | lily_emit_state *emit; 95 | lily_symtab *symtab; 96 | lily_vm_state *vm; 97 | lily_type_maker *tm; 98 | lily_raiser *raiser; 99 | lily_config *config; 100 | struct lily_rewind_state_ *rs; 101 | struct lily_import_state_ *ims; 102 | lily_doc_stack *doc; 103 | } lily_parse_state; 104 | 105 | lily_var *lily_parser_lambda_eval(lily_parse_state *, uint16_t, const char *, 106 | lily_type *); 107 | lily_item *lily_find_or_dl_member(lily_parse_state *, lily_class *, 108 | const char *); 109 | lily_class *lily_dynaload_exception(lily_parse_state *, const char *); 110 | 111 | #endif 112 | -------------------------------------------------------------------------------- /src/lily_pkg_core.c: -------------------------------------------------------------------------------- 1 | #include "lily.h" 2 | #include "lily_vm.h" 3 | 4 | /* This file is automatically generated by scripts/prelude.lily. */ 5 | 6 | extern const char *lily_prelude_info_table[]; 7 | extern const char *lily_coroutine_info_table[]; 8 | extern const char *lily_fs_info_table[]; 9 | extern const char *lily_introspect_info_table[]; 10 | extern const char *lily_math_info_table[]; 11 | extern const char *lily_random_info_table[]; 12 | extern const char *lily_subprocess_info_table[]; 13 | extern const char *lily_sys_info_table[]; 14 | extern const char *lily_time_info_table[]; 15 | 16 | extern lily_call_entry_func lily_prelude_call_table[]; 17 | extern lily_call_entry_func lily_coroutine_call_table[]; 18 | extern lily_call_entry_func lily_fs_call_table[]; 19 | extern lily_call_entry_func lily_introspect_call_table[]; 20 | extern lily_call_entry_func lily_math_call_table[]; 21 | extern lily_call_entry_func lily_random_call_table[]; 22 | extern lily_call_entry_func lily_subprocess_call_table[]; 23 | extern lily_call_entry_func lily_sys_call_table[]; 24 | extern lily_call_entry_func lily_time_call_table[]; 25 | 26 | void lily_prelude_register(lily_vm_state *vm) 27 | { 28 | lily_module_register(vm, "prelude", lily_prelude_info_table, lily_prelude_call_table); 29 | lily_module_register(vm, "coroutine", lily_coroutine_info_table, lily_coroutine_call_table); 30 | lily_module_register(vm, "fs", lily_fs_info_table, lily_fs_call_table); 31 | lily_module_register(vm, "introspect", lily_introspect_info_table, lily_introspect_call_table); 32 | lily_module_register(vm, "math", lily_math_info_table, lily_math_call_table); 33 | lily_module_register(vm, "random", lily_random_info_table, lily_random_call_table); 34 | lily_module_register(vm, "subprocess", lily_subprocess_info_table, lily_subprocess_call_table); 35 | lily_module_register(vm, "sys", lily_sys_info_table, lily_sys_call_table); 36 | lily_module_register(vm, "time", lily_time_info_table, lily_time_call_table); 37 | } 38 | -------------------------------------------------------------------------------- /src/lily_pkg_coroutine.c: -------------------------------------------------------------------------------- 1 | #include "lily.h" 2 | #include "lily_value.h" 3 | #include "lily_vm.h" 4 | #define LILY_NO_EXPORT 5 | #include "lily_pkg_coroutine_bindings.h" 6 | 7 | typedef lily_coroutine_val lily_coroutine_Coroutine; 8 | 9 | void lily_coroutine_Coroutine_build(lily_state *s) 10 | { 11 | lily_vm_state *base_vm = lily_vm_coroutine_build(s, ID_Coroutine(s)); 12 | 13 | lily_vm_coroutine_call_prep(base_vm, 1); 14 | lily_return_top(s); 15 | } 16 | 17 | void lily_coroutine_Coroutine_build_with_value(lily_state *s) 18 | { 19 | lily_vm_state *base_vm = lily_vm_coroutine_build(s, ID_Coroutine(s)); 20 | 21 | lily_push_value(base_vm, lily_arg_value(s, 1)); 22 | lily_vm_coroutine_call_prep(base_vm, 2); 23 | lily_return_top(s); 24 | } 25 | 26 | void lily_coroutine_Coroutine_is_failed(lily_state *s) 27 | { 28 | lily_coroutine_val *co_val = ARG_Coroutine(s, 0); 29 | lily_return_boolean(s, co_val->status == co_failed); 30 | } 31 | 32 | void lily_coroutine_Coroutine_is_done(lily_state *s) 33 | { 34 | lily_coroutine_val *co_val = ARG_Coroutine(s, 0); 35 | lily_return_boolean(s, co_val->status == co_done); 36 | } 37 | 38 | void lily_coroutine_Coroutine_is_running(lily_state *s) 39 | { 40 | lily_coroutine_val *co_val = ARG_Coroutine(s, 0); 41 | lily_return_boolean(s, co_val->status == co_running); 42 | } 43 | 44 | void lily_coroutine_Coroutine_is_waiting(lily_state *s) 45 | { 46 | lily_coroutine_val *co_val = ARG_Coroutine(s, 0); 47 | lily_return_boolean(s, co_val->status == co_waiting); 48 | } 49 | 50 | void lily_coroutine_Coroutine_receive(lily_state *s) 51 | { 52 | lily_coroutine_val *co_val = ARG_Coroutine(s, 0); 53 | 54 | if (co_val->vm != s) 55 | lily_RuntimeError(s, 56 | "Attempt to receive a value from another coroutine."); 57 | 58 | lily_push_value(s, co_val->receiver); 59 | lily_return_top(s); 60 | } 61 | 62 | void lily_coroutine_Coroutine_resume(lily_state *s) 63 | { 64 | lily_vm_coroutine_resume(s, ARG_Coroutine(s, 0), NULL); 65 | lily_return_top(s); 66 | } 67 | 68 | void lily_coroutine_Coroutine_resume_with(lily_state *s) 69 | { 70 | lily_vm_coroutine_resume(s, ARG_Coroutine(s, 0), lily_arg_value(s, 1)); 71 | lily_return_top(s); 72 | } 73 | 74 | void lily_coroutine_Coroutine_yield(lily_state *s) 75 | { 76 | lily_coroutine_val *co_target = ARG_Coroutine(s, 0); 77 | lily_value *to_yield = lily_arg_value(s, 1); 78 | 79 | lily_vm_state *co_vm = co_target->vm; 80 | 81 | if (co_vm != s) 82 | lily_RuntimeError(s, "Cannot yield from another coroutine."); 83 | 84 | lily_raiser *co_raiser = co_vm->raiser; 85 | 86 | /* A vm always has at least two jumps currently active: 87 | * 1: Parser, or the coroutine base. 88 | * 2: vm main loop. 89 | If there are any more jumps, the vm is in a foreign call. A Coroutine in 90 | a foreign call cannot be restored, because restoration happens by calling 91 | the vm main loop again. */ 92 | if (co_raiser->all_jumps->prev->prev != NULL) 93 | lily_RuntimeError(s, "Cannot yield while in a foreign call."); 94 | 95 | /* The yield will not come back, so the return must come first. */ 96 | lily_return_unit(s); 97 | 98 | /* Push the value to be yielded so that the caller has an obvious place to 99 | find it (top of the stack). The value must be popped before the 100 | Coroutine is resumed. */ 101 | lily_push_value(co_vm, to_yield); 102 | 103 | /* Since yield is jumping back into the base jump, the main loop is never 104 | properly exited. The main loop's jump needs to be popped so that it can 105 | be properly restored when the main loop is entered again. */ 106 | lily_release_jump(co_raiser); 107 | 108 | longjmp(co_raiser->all_jumps->jump, 1); 109 | } 110 | 111 | LILY_DECLARE_COROUTINE_CALL_TABLE 112 | -------------------------------------------------------------------------------- /src/lily_pkg_coroutine_bindings.h: -------------------------------------------------------------------------------- 1 | #ifndef LILY_COROUTINE_BINDINGS_H 2 | #define LILY_COROUTINE_BINDINGS_H 3 | /* Generated by lily-bindgen, do not edit. */ 4 | 5 | #if defined(_WIN32) && !defined(LILY_NO_EXPORT) 6 | #define LILY_COROUTINE_EXPORT __declspec(dllexport) 7 | #else 8 | #define LILY_COROUTINE_EXPORT 9 | #endif 10 | 11 | #define ARG_Coroutine(s_, i_) \ 12 | (lily_coroutine_Coroutine *)lily_arg_generic(s_, i_) 13 | #define AS_Coroutine(v_) \ 14 | (lily_coroutine_Coroutine *)lily_as_generic(v_) 15 | #define ID_Coroutine(s_) \ 16 | lily_cid_at(s_, 0) 17 | #define INIT_Coroutine(s_) \ 18 | (lily_coroutine_Coroutine *)lily_push_foreign(s_, ID_Coroutine(s_), (lily_destroy_func)destroy_Coroutine, sizeof(lily_coroutine_Coroutine)) 19 | 20 | LILY_COROUTINE_EXPORT 21 | const char *lily_coroutine_info_table[] = { 22 | "\01Coroutine\0" 23 | ,"C\012Coroutine\0[A,B]" 24 | ,"m\0build\0[A,B](Function(Coroutine[A,B])): Coroutine[A,B]" 25 | ,"m\0build_with_value\0[A,B,C](Function(Coroutine[A,B],C),C): Coroutine[A,B]" 26 | ,"m\0is_done\0[A,B](Coroutine[A,B]): Boolean" 27 | ,"m\0is_failed\0[A,B](Coroutine[A,B]): Boolean" 28 | ,"m\0is_running\0[A,B](Coroutine[A,B]): Boolean" 29 | ,"m\0is_waiting\0[A,B](Coroutine[A,B]): Boolean" 30 | ,"m\0receive\0[A,B](Coroutine[A,B]): B" 31 | ,"m\0resume\0[A,B](Coroutine[A,Unit]): Option[A]" 32 | ,"m\0resume_with\0[A,B](Coroutine[A,B],B): Option[A]" 33 | ,"m\0yield\0[A,B](Coroutine[A,B],A)" 34 | ,"Z" 35 | }; 36 | #define LILY_DECLARE_COROUTINE_CALL_TABLE \ 37 | LILY_COROUTINE_EXPORT \ 38 | lily_call_entry_func lily_coroutine_call_table[] = { \ 39 | NULL, \ 40 | NULL, \ 41 | lily_coroutine_Coroutine_build, \ 42 | lily_coroutine_Coroutine_build_with_value, \ 43 | lily_coroutine_Coroutine_is_done, \ 44 | lily_coroutine_Coroutine_is_failed, \ 45 | lily_coroutine_Coroutine_is_running, \ 46 | lily_coroutine_Coroutine_is_waiting, \ 47 | lily_coroutine_Coroutine_receive, \ 48 | lily_coroutine_Coroutine_resume, \ 49 | lily_coroutine_Coroutine_resume_with, \ 50 | lily_coroutine_Coroutine_yield, \ 51 | }; 52 | #endif 53 | -------------------------------------------------------------------------------- /src/lily_pkg_fs.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #ifdef _WIN32 6 | # include 7 | #else 8 | # include 9 | # include 10 | #endif 11 | 12 | #include "lily.h" 13 | #include "lily_platform.h" 14 | #define LILY_NO_EXPORT 15 | #include "lily_pkg_fs_bindings.h" 16 | 17 | extern void lily_mb_reserve(lily_msgbuf *, uint32_t); 18 | extern int lily_mb_size(lily_msgbuf *); 19 | 20 | #define HANDLE_RESULT \ 21 | if (result == -1) { \ 22 | char buffer[LILY_STRERROR_BUFFER_SIZE]; \ 23 | \ 24 | lily_strerror(buffer); \ 25 | lily_IOError(s, "Errno %d: %s (%s).", errno, buffer, dirname_raw); \ 26 | } \ 27 | 28 | void lily_fs__change_dir(lily_state *s) 29 | { 30 | char *dirname_raw = lily_arg_string_raw(s, 0); 31 | int result; 32 | 33 | errno = 0; 34 | 35 | #ifdef _WIN32 36 | result = _chdir(dirname_raw); 37 | #else 38 | result = chdir(dirname_raw); 39 | #endif 40 | 41 | HANDLE_RESULT 42 | lily_return_unit(s); 43 | } 44 | 45 | void lily_fs__create_dir(lily_state *s) 46 | { 47 | char *dirname_raw = lily_arg_string_raw(s, 0); 48 | int result; 49 | 50 | #ifndef _WIN32 51 | int mode = 0777; 52 | 53 | if (lily_arg_count(s) == 2) 54 | mode = (int)lily_arg_integer(s, 1); 55 | #endif 56 | 57 | errno = 0; 58 | 59 | #ifdef _WIN32 60 | result = _mkdir(dirname_raw); 61 | #else 62 | result = mkdir(dirname_raw, mode); 63 | #endif 64 | 65 | HANDLE_RESULT 66 | lily_return_unit(s); 67 | } 68 | 69 | void lily_fs__current_dir(lily_state *s) 70 | { 71 | lily_msgbuf *vm_buffer = lily_msgbuf_get(s); 72 | uint32_t size = lily_mb_size(vm_buffer); 73 | char *buffer, *result; 74 | 75 | while (1) { 76 | buffer = (char *)lily_mb_raw(vm_buffer); 77 | 78 | #ifdef _WIN32 79 | result = _getcwd(buffer, size); 80 | #else 81 | result = getcwd(buffer, size); 82 | #endif 83 | 84 | if (result) 85 | break; 86 | 87 | size *= 2; 88 | lily_mb_reserve(vm_buffer, size); 89 | } 90 | 91 | lily_return_string(s, buffer); 92 | } 93 | 94 | void lily_fs__remove_dir(lily_state *s) 95 | { 96 | char *dirname_raw = lily_arg_string_raw(s, 0); 97 | int result; 98 | 99 | errno = 0; 100 | 101 | #ifdef _WIN32 102 | result = _rmdir(dirname_raw); 103 | #else 104 | result = rmdir(dirname_raw); 105 | #endif 106 | 107 | HANDLE_RESULT 108 | lily_return_unit(s); 109 | } 110 | 111 | LILY_DECLARE_FS_CALL_TABLE 112 | -------------------------------------------------------------------------------- /src/lily_pkg_fs_bindings.h: -------------------------------------------------------------------------------- 1 | #ifndef LILY_FS_BINDINGS_H 2 | #define LILY_FS_BINDINGS_H 3 | /* Generated by lily-bindgen, do not edit. */ 4 | 5 | #if defined(_WIN32) && !defined(LILY_NO_EXPORT) 6 | #define LILY_FS_EXPORT __declspec(dllexport) 7 | #else 8 | #define LILY_FS_EXPORT 9 | #endif 10 | 11 | LILY_FS_EXPORT 12 | const char *lily_fs_info_table[] = { 13 | "\0\0" 14 | ,"F\0change_dir\0(String)" 15 | ,"F\0create_dir\0(String,*Integer)" 16 | ,"F\0current_dir\0: String" 17 | ,"F\0remove_dir\0(String)" 18 | ,"Z" 19 | }; 20 | #define LILY_DECLARE_FS_CALL_TABLE \ 21 | LILY_FS_EXPORT \ 22 | lily_call_entry_func lily_fs_call_table[] = { \ 23 | NULL, \ 24 | lily_fs__change_dir, \ 25 | lily_fs__create_dir, \ 26 | lily_fs__current_dir, \ 27 | lily_fs__remove_dir, \ 28 | }; 29 | #endif 30 | -------------------------------------------------------------------------------- /src/lily_pkg_math.c: -------------------------------------------------------------------------------- 1 | /* Make sure this is defined so math.h defines M_* constants. */ 2 | #ifndef _USE_MATH_DEFINES 3 | # define _USE_MATH_DEFINES 4 | #endif 5 | 6 | #include 7 | #include 8 | 9 | #include "lily.h" 10 | #define LILY_NO_EXPORT 11 | #include "lily_pkg_math_bindings.h" 12 | 13 | void lily_math__abs(lily_state *s) 14 | { 15 | int64_t x = lily_arg_integer(s, 0); 16 | 17 | lily_return_integer(s, llabs(x)); 18 | } 19 | 20 | void lily_math__acos(lily_state *s) 21 | { 22 | double x = lily_arg_double(s, 0); 23 | 24 | lily_return_double(s, acos(x)); 25 | } 26 | 27 | void lily_math__asin(lily_state *s) 28 | { 29 | double x = lily_arg_double(s, 0); 30 | 31 | lily_return_double(s, asin(x)); 32 | } 33 | 34 | void lily_math__atan(lily_state *s) 35 | { 36 | double x = lily_arg_double(s, 0); 37 | 38 | lily_return_double(s, atan(x)); 39 | } 40 | 41 | void lily_math__ceil(lily_state *s) 42 | { 43 | double x = lily_arg_double(s, 0); 44 | 45 | lily_return_double(s, ceil(x)); 46 | } 47 | 48 | void lily_math__cos(lily_state *s) 49 | { 50 | double x = lily_arg_double(s, 0); 51 | 52 | lily_return_double(s, cos(x)); 53 | } 54 | 55 | void lily_math__cosh(lily_state *s) 56 | { 57 | double x = lily_arg_double(s, 0); 58 | 59 | lily_return_double(s, cosh(x)); 60 | } 61 | 62 | void lily_math__exp(lily_state *s) 63 | { 64 | double x = lily_arg_double(s, 0); 65 | 66 | lily_return_double(s, exp(x)); 67 | } 68 | 69 | void lily_math__fabs(lily_state *s) 70 | { 71 | double x = lily_arg_double(s, 0); 72 | 73 | lily_return_double(s, fabs(x)); 74 | } 75 | 76 | void lily_math__floor(lily_state *s) 77 | { 78 | double x = lily_arg_double(s, 0); 79 | 80 | lily_return_double(s, floor(x)); 81 | } 82 | 83 | void lily_math__fmod(lily_state *s) 84 | { 85 | double x = lily_arg_double(s, 0); 86 | double y = lily_arg_double(s, 1); 87 | 88 | lily_return_double(s, fmod(x, y)); 89 | } 90 | 91 | void lily_math__is_infinity(lily_state *s) 92 | { 93 | double x = lily_arg_double(s, 0); 94 | 95 | lily_return_boolean(s, isinf(x)); 96 | } 97 | 98 | void lily_math__is_nan(lily_state *s) 99 | { 100 | double x = lily_arg_double(s, 0); 101 | 102 | lily_return_boolean(s, isnan(x)); 103 | } 104 | 105 | void lily_math__ldexp(lily_state *s) 106 | { 107 | double x = lily_arg_double(s, 0); 108 | int y = (int)lily_arg_integer(s, 1); 109 | 110 | lily_return_double(s, ldexp(x, y)); 111 | } 112 | 113 | void lily_math__log(lily_state *s) 114 | { 115 | double x = lily_arg_double(s, 0); 116 | 117 | lily_return_double(s, log(x)); 118 | } 119 | 120 | void lily_math__log10(lily_state *s) 121 | { 122 | double x = lily_arg_double(s, 0); 123 | 124 | lily_return_double(s, log10(x)); 125 | } 126 | 127 | void lily_math__modf(lily_state *s) 128 | { 129 | double i, f; 130 | double x = lily_arg_double(s, 0); 131 | 132 | f = modf(x, &i); 133 | 134 | lily_container_val *tpl = lily_push_tuple(s, 2); 135 | lily_push_double(s, i); 136 | lily_con_set_from_stack(s, tpl, 0); 137 | lily_push_double(s, f); 138 | lily_con_set_from_stack(s, tpl, 1); 139 | 140 | lily_return_top(s); 141 | } 142 | 143 | void lily_math__pow(lily_state *s) 144 | { 145 | double x = lily_arg_double(s, 0); 146 | double y = lily_arg_double(s, 1); 147 | 148 | lily_return_double(s, pow(x, y)); 149 | } 150 | 151 | void lily_math__sin(lily_state *s) 152 | { 153 | double x = lily_arg_double(s, 0); 154 | 155 | lily_return_double(s, sin(x)); 156 | } 157 | 158 | void lily_math__sinh(lily_state *s) 159 | { 160 | double x = lily_arg_double(s, 0); 161 | 162 | lily_return_double(s, sinh(x)); 163 | } 164 | 165 | void lily_math__sqrt(lily_state *s) 166 | { 167 | double x = lily_arg_double(s, 0); 168 | 169 | lily_return_double(s, sqrt(x)); 170 | } 171 | 172 | void lily_math__tan(lily_state *s) 173 | { 174 | double x = lily_arg_double(s, 0); 175 | 176 | lily_return_double(s, tan(x)); 177 | } 178 | 179 | void lily_math__tanh(lily_state *s) 180 | { 181 | double x = lily_arg_double(s, 0); 182 | 183 | lily_return_double(s, tanh(x)); 184 | } 185 | 186 | void lily_math__to_deg(lily_state *s) 187 | { 188 | double x = lily_arg_double(s, 0); 189 | 190 | lily_return_double(s, x * (180 / M_PI)); 191 | } 192 | 193 | void lily_math__to_rad(lily_state *s) 194 | { 195 | double x = lily_arg_double(s, 0); 196 | 197 | lily_return_double(s, x * (M_PI / 180)); 198 | } 199 | 200 | void lily_math_var_huge(lily_state *s) 201 | { 202 | lily_push_double(s, HUGE_VAL); 203 | } 204 | 205 | void lily_math_var_infinity(lily_state *s) 206 | { 207 | lily_push_double(s, INFINITY); 208 | } 209 | 210 | void lily_math_var_nan(lily_state *s) 211 | { 212 | lily_push_double(s, NAN); 213 | } 214 | 215 | void lily_math_var_pi(lily_state *s) 216 | { 217 | lily_push_double(s, M_PI); 218 | } 219 | 220 | LILY_DECLARE_MATH_CALL_TABLE 221 | -------------------------------------------------------------------------------- /src/lily_pkg_math_bindings.h: -------------------------------------------------------------------------------- 1 | #ifndef LILY_MATH_BINDINGS_H 2 | #define LILY_MATH_BINDINGS_H 3 | /* Generated by lily-bindgen, do not edit. */ 4 | 5 | #if defined(_WIN32) && !defined(LILY_NO_EXPORT) 6 | #define LILY_MATH_EXPORT __declspec(dllexport) 7 | #else 8 | #define LILY_MATH_EXPORT 9 | #endif 10 | 11 | LILY_MATH_EXPORT 12 | const char *lily_math_info_table[] = { 13 | "\0\0" 14 | ,"F\0abs\0(Integer): Integer" 15 | ,"F\0acos\0(Double): Double" 16 | ,"F\0asin\0(Double): Double" 17 | ,"F\0atan\0(Double): Double" 18 | ,"F\0ceil\0(Double): Double" 19 | ,"F\0cos\0(Double): Double" 20 | ,"F\0cosh\0(Double): Double" 21 | ,"F\0exp\0(Double): Double" 22 | ,"F\0fabs\0(Double): Double" 23 | ,"F\0floor\0(Double): Double" 24 | ,"F\0fmod\0(Double,Double): Double" 25 | ,"F\0is_infinity\0(Double): Boolean" 26 | ,"F\0is_nan\0(Double): Boolean" 27 | ,"F\0ldexp\0(Double,Integer): Double" 28 | ,"F\0log\0(Double): Double" 29 | ,"F\0log10\0(Double): Double" 30 | ,"F\0modf\0(Double): Tuple[Double,Double]" 31 | ,"F\0pow\0(Double,Double): Double" 32 | ,"F\0sin\0(Double): Double" 33 | ,"F\0sinh\0(Double): Double" 34 | ,"F\0sqrt\0(Double): Double" 35 | ,"F\0tan\0(Double): Double" 36 | ,"F\0tanh\0(Double): Double" 37 | ,"F\0to_deg\0(Double): Double" 38 | ,"F\0to_rad\0(Double): Double" 39 | ,"R\0huge\0Double" 40 | ,"R\0infinity\0Double" 41 | ,"R\0nan\0Double" 42 | ,"R\0pi\0Double" 43 | ,"Z" 44 | }; 45 | #define LILY_DECLARE_MATH_CALL_TABLE \ 46 | LILY_MATH_EXPORT \ 47 | lily_call_entry_func lily_math_call_table[] = { \ 48 | NULL, \ 49 | lily_math__abs, \ 50 | lily_math__acos, \ 51 | lily_math__asin, \ 52 | lily_math__atan, \ 53 | lily_math__ceil, \ 54 | lily_math__cos, \ 55 | lily_math__cosh, \ 56 | lily_math__exp, \ 57 | lily_math__fabs, \ 58 | lily_math__floor, \ 59 | lily_math__fmod, \ 60 | lily_math__is_infinity, \ 61 | lily_math__is_nan, \ 62 | lily_math__ldexp, \ 63 | lily_math__log, \ 64 | lily_math__log10, \ 65 | lily_math__modf, \ 66 | lily_math__pow, \ 67 | lily_math__sin, \ 68 | lily_math__sinh, \ 69 | lily_math__sqrt, \ 70 | lily_math__tan, \ 71 | lily_math__tanh, \ 72 | lily_math__to_deg, \ 73 | lily_math__to_rad, \ 74 | lily_math_var_huge, \ 75 | lily_math_var_infinity, \ 76 | lily_math_var_nan, \ 77 | lily_math_var_pi, \ 78 | }; 79 | #endif 80 | -------------------------------------------------------------------------------- /src/lily_pkg_random.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "lily.h" 6 | #define LILY_NO_EXPORT 7 | #include "lily_pkg_random_bindings.h" 8 | 9 | /* This uses code from libmtwist which uses The Unlicense: 10 | https://github.com/dajobe/libmtwist 11 | https://unlicense.org/ 12 | 13 | This randomness library is limited to 32 bit values. It's assumed that most 14 | ranges will be within 32 bits (4 billion-ish). */ 15 | 16 | #define MTWIST_N 624 17 | #define MTWIST_M 397 18 | #define MTWIST_UPPER_MASK ((uint32_t)0x80000000) 19 | #define MTWIST_LOWER_MASK ((uint32_t)0x7FFFFFFF) 20 | #define MTWIST_FULL_MASK ((uint32_t)0xFFFFFFFF) 21 | #define MTWIST_MATRIX_A ((uint32_t)0x9908B0DF) 22 | 23 | #define MTWIST_MIXBITS(u, v) ( ( (u) & MTWIST_UPPER_MASK) | ( (v) & MTWIST_LOWER_MASK) ) 24 | #define MTWIST_TWIST(u, v) ( (MTWIST_MIXBITS(u, v) >> 1) ^ ( (v) & UINT32_C(1) ? MTWIST_MATRIX_A : UINT32_C(0)) ) 25 | 26 | typedef struct { 27 | LILY_FOREIGN_HEADER 28 | uint32_t state[MTWIST_N]; 29 | uint32_t *next; 30 | int remaining; 31 | } lily_random_Random; 32 | 33 | static void destroy_Random(lily_random_Random *r) 34 | { 35 | (void)r; 36 | } 37 | 38 | void lily_random_Random_new(lily_state *s) 39 | { 40 | lily_random_Random* mt = INIT_Random(s); 41 | int64_t seed = 0; 42 | int i; 43 | 44 | if (lily_arg_count(s) == 2) 45 | seed = lily_arg_integer(s, 1); 46 | 47 | if (seed <= 0) 48 | seed = (int64_t)time(NULL); 49 | 50 | mt->state[0] = (uint32_t)(seed & MTWIST_FULL_MASK); 51 | for(i = 1; i < MTWIST_N; i++) { 52 | mt->state[i] = (((uint32_t)1812433253) * (mt->state[i - 1] ^ (mt->state[i - 1] >> 30)) + i); 53 | mt->state[i] &= MTWIST_FULL_MASK; 54 | } 55 | 56 | mt->remaining = 0; 57 | mt->next = NULL; 58 | 59 | lily_return_top(s); 60 | } 61 | 62 | static void mtwist_update(lily_random_Random* mt) 63 | { 64 | int count; 65 | uint32_t *p = mt->state; 66 | 67 | for (count = (MTWIST_N - MTWIST_M + 1); --count; p++) 68 | *p = p[MTWIST_M] ^ MTWIST_TWIST(p[0], p[1]); 69 | 70 | for (count = MTWIST_M; --count; p++) 71 | *p = p[MTWIST_M - MTWIST_N] ^ MTWIST_TWIST(p[0], p[1]); 72 | 73 | *p = p[MTWIST_M - MTWIST_N] ^ MTWIST_TWIST(p[0], mt->state[0]); 74 | 75 | mt->remaining = MTWIST_N; 76 | mt->next = mt->state; 77 | } 78 | 79 | uint32_t mtwist_u32rand(lily_random_Random* mt) 80 | { 81 | uint32_t r; 82 | 83 | if (mt->remaining == 0) 84 | mtwist_update(mt); 85 | 86 | r = *mt->next++; 87 | mt->remaining--; 88 | 89 | /* Tempering. */ 90 | r ^= (r >> 11); 91 | r ^= (r << 7) & ((uint32_t)0x9D2C5680); 92 | r ^= (r << 15) & ((uint32_t)0xEFC60000); 93 | r ^= (r >> 18); 94 | 95 | r &= MTWIST_FULL_MASK; 96 | 97 | return (uint32_t)r; 98 | } 99 | 100 | void lily_random_Random_between(lily_state *s) 101 | { 102 | lily_random_Random *r = ARG_Random(s, 0); 103 | uint64_t rng = (uint64_t)mtwist_u32rand(r); 104 | 105 | int64_t start = lily_arg_integer(s, 1); 106 | int64_t end = lily_arg_integer(s, 2); 107 | 108 | if (start >= end) 109 | lily_ValueError(s, "Interval range is empty."); 110 | 111 | int64_t distance = end - start + 1; 112 | 113 | if (distance < INT32_MIN || 114 | distance > INT32_MAX) 115 | lily_ValueError(s, "Interval exceeds 32 bits in size."); 116 | 117 | uint64_t result = start + (rng % distance); 118 | 119 | lily_return_integer(s, result); 120 | } 121 | 122 | LILY_DECLARE_RANDOM_CALL_TABLE 123 | -------------------------------------------------------------------------------- /src/lily_pkg_random_bindings.h: -------------------------------------------------------------------------------- 1 | #ifndef LILY_RANDOM_BINDINGS_H 2 | #define LILY_RANDOM_BINDINGS_H 3 | /* Generated by lily-bindgen, do not edit. */ 4 | 5 | #if defined(_WIN32) && !defined(LILY_NO_EXPORT) 6 | #define LILY_RANDOM_EXPORT __declspec(dllexport) 7 | #else 8 | #define LILY_RANDOM_EXPORT 9 | #endif 10 | 11 | #define ARG_Random(s_, i_) \ 12 | (lily_random_Random *)lily_arg_generic(s_, i_) 13 | #define AS_Random(v_) \ 14 | (lily_random_Random *)lily_as_generic(v_) 15 | #define ID_Random(s_) \ 16 | lily_cid_at(s_, 0) 17 | #define INIT_Random(s_) \ 18 | (lily_random_Random *)lily_push_foreign(s_, ID_Random(s_), (lily_destroy_func)destroy_Random, sizeof(lily_random_Random)) 19 | 20 | LILY_RANDOM_EXPORT 21 | const char *lily_random_info_table[] = { 22 | "\01Random\0" 23 | ,"C\02Random\0" 24 | ,"m\0\0(*Integer): Random" 25 | ,"m\0between\0(Random,Integer,Integer): Integer" 26 | ,"Z" 27 | }; 28 | #define LILY_DECLARE_RANDOM_CALL_TABLE \ 29 | LILY_RANDOM_EXPORT \ 30 | lily_call_entry_func lily_random_call_table[] = { \ 31 | NULL, \ 32 | NULL, \ 33 | lily_random_Random_new, \ 34 | lily_random_Random_between, \ 35 | }; 36 | #endif 37 | -------------------------------------------------------------------------------- /src/lily_pkg_subprocess.c: -------------------------------------------------------------------------------- 1 | #include "lily.h" 2 | #include "lily_value.h" 3 | #define LILY_NO_EXPORT 4 | #include "lily_pkg_subprocess_bindings.h" 5 | 6 | #ifdef _WIN32 7 | # define lily_popen(command_, mode_) (_popen(command_, mode_)) 8 | # define lily_pclose(f_) _pclose(f_) 9 | #else 10 | # define lily_popen(command_, mode_) (fflush(NULL), popen(command_, mode_)) 11 | # define lily_pclose(f_) pclose(f_) 12 | #endif 13 | 14 | static void file_pclose(FILE *f) 15 | { 16 | lily_pclose(f); 17 | } 18 | 19 | void lily_subprocess__popen(lily_state *s) 20 | { 21 | char *command = lily_arg_string_raw(s, 0); 22 | const char *mode = lily_optional_string_raw(s, 1, "r"); 23 | const char *mode_ch = mode; 24 | int ok; 25 | 26 | if (*mode_ch == 'r' || *mode_ch == 'w') { 27 | mode_ch++; 28 | ok = (*mode_ch == '\0'); 29 | } 30 | else 31 | ok = 0; 32 | 33 | if (ok == 0) 34 | lily_ValueError(s, "Invalid mode '%s' given.", mode); 35 | 36 | FILE *f = lily_popen(command, mode); 37 | 38 | if (f == NULL) 39 | lily_RuntimeError(s, "Failed to run command."); 40 | 41 | lily_push_file(s, f, mode, file_pclose); 42 | lily_return_top(s); 43 | } 44 | 45 | LILY_DECLARE_SUBPROCESS_CALL_TABLE 46 | -------------------------------------------------------------------------------- /src/lily_pkg_subprocess_bindings.h: -------------------------------------------------------------------------------- 1 | #ifndef LILY_SUBPROCESS_BINDINGS_H 2 | #define LILY_SUBPROCESS_BINDINGS_H 3 | /* Generated by lily-bindgen, do not edit. */ 4 | 5 | #if defined(_WIN32) && !defined(LILY_NO_EXPORT) 6 | #define LILY_SUBPROCESS_EXPORT __declspec(dllexport) 7 | #else 8 | #define LILY_SUBPROCESS_EXPORT 9 | #endif 10 | 11 | LILY_SUBPROCESS_EXPORT 12 | const char *lily_subprocess_info_table[] = { 13 | "\0\0" 14 | ,"F\0popen\0(String,*String): File" 15 | ,"Z" 16 | }; 17 | #define LILY_DECLARE_SUBPROCESS_CALL_TABLE \ 18 | LILY_SUBPROCESS_EXPORT \ 19 | lily_call_entry_func lily_subprocess_call_table[] = { \ 20 | NULL, \ 21 | lily_subprocess__popen, \ 22 | }; 23 | #endif 24 | -------------------------------------------------------------------------------- /src/lily_pkg_sys.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "lily.h" 6 | #include "lily_vm.h" 7 | #define LILY_NO_EXPORT 8 | #include "lily_pkg_sys_bindings.h" 9 | 10 | extern void lily_parser_exit(lily_state *, uint8_t); 11 | 12 | void lily_sys_var_argv(lily_state *s) 13 | { 14 | lily_config *config = lily_config_get(s); 15 | int opt_argc = config->argc; 16 | char **opt_argv = config->argv; 17 | lily_container_val *lv = lily_push_list(s, opt_argc); 18 | 19 | int i; 20 | for (i = 0;i < opt_argc;i++) { 21 | lily_push_string(s, opt_argv[i]); 22 | lily_con_set_from_stack(s, lv, i); 23 | } 24 | } 25 | 26 | void lily_sys__exit(lily_state *s) 27 | { 28 | lily_parser_exit(s, lily_arg_byte(s, 0)); 29 | } 30 | 31 | void lily_sys__exit_failure(lily_state *s) 32 | { 33 | lily_parser_exit(s, EXIT_FAILURE); 34 | } 35 | 36 | void lily_sys__exit_success(lily_state *s) 37 | { 38 | lily_parser_exit(s, EXIT_SUCCESS); 39 | } 40 | 41 | void lily_sys__getenv(lily_state *s) 42 | { 43 | char *env = getenv(lily_arg_string_raw(s, 0)); 44 | 45 | if (env) { 46 | lily_push_string(s, env); 47 | lily_return_some_of_top(s); 48 | } 49 | else 50 | lily_return_none(s); 51 | } 52 | 53 | void lily_sys__recursion_limit(lily_state *s) 54 | { 55 | lily_return_integer(s, s->depth_max); 56 | } 57 | 58 | void lily_sys__set_recursion_limit(lily_state *s) 59 | { 60 | int64_t limit = lily_arg_integer(s, 0); 61 | 62 | if (limit < 1 || limit > INT32_MAX) 63 | lily_ValueError(s, "Limit value (%ld) is not reasonable.", limit); 64 | 65 | if (limit < s->call_depth) 66 | lily_ValueError(s, 67 | "Limit value (%ld) is lower than the current recursion depth.", 68 | limit); 69 | 70 | s->depth_max = (int32_t)limit; 71 | lily_return_unit(s); 72 | } 73 | 74 | LILY_DECLARE_SYS_CALL_TABLE 75 | -------------------------------------------------------------------------------- /src/lily_pkg_sys_bindings.h: -------------------------------------------------------------------------------- 1 | #ifndef LILY_SYS_BINDINGS_H 2 | #define LILY_SYS_BINDINGS_H 3 | /* Generated by lily-bindgen, do not edit. */ 4 | 5 | #if defined(_WIN32) && !defined(LILY_NO_EXPORT) 6 | #define LILY_SYS_EXPORT __declspec(dllexport) 7 | #else 8 | #define LILY_SYS_EXPORT 9 | #endif 10 | 11 | LILY_SYS_EXPORT 12 | const char *lily_sys_info_table[] = { 13 | "\0\0" 14 | ,"F\0exit\0(Byte)" 15 | ,"F\0exit_failure\0" 16 | ,"F\0exit_success\0" 17 | ,"F\0getenv\0(String): Option[String]" 18 | ,"F\0recursion_limit\0: Integer" 19 | ,"F\0set_recursion_limit\0(Integer)" 20 | ,"R\0argv\0List[String]" 21 | ,"Z" 22 | }; 23 | #define LILY_DECLARE_SYS_CALL_TABLE \ 24 | LILY_SYS_EXPORT \ 25 | lily_call_entry_func lily_sys_call_table[] = { \ 26 | NULL, \ 27 | lily_sys__exit, \ 28 | lily_sys__exit_failure, \ 29 | lily_sys__exit_success, \ 30 | lily_sys__getenv, \ 31 | lily_sys__recursion_limit, \ 32 | lily_sys__set_recursion_limit, \ 33 | lily_sys_var_argv, \ 34 | }; 35 | #endif 36 | -------------------------------------------------------------------------------- /src/lily_pkg_time.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "lily.h" 4 | #define LILY_NO_EXPORT 5 | #include "lily_pkg_time_bindings.h" 6 | 7 | typedef struct { 8 | LILY_FOREIGN_HEADER 9 | struct tm local; 10 | } lily_time_Time; 11 | 12 | void destroy_Time(lily_time_Time *t) 13 | { 14 | (void)t; 15 | } 16 | 17 | void lily_time_Time_clock(lily_state *s) 18 | { 19 | lily_return_double(s, ((double)clock())/(double)CLOCKS_PER_SEC); 20 | } 21 | 22 | void lily_time_Time_now(lily_state *s) 23 | { 24 | lily_time_Time *t = INIT_Time(s); 25 | 26 | time_t raw_time; 27 | struct tm *time_info; 28 | 29 | time(&raw_time); 30 | time_info = localtime(&raw_time); 31 | t->local = *time_info; 32 | 33 | lily_return_top(s); 34 | } 35 | 36 | void lily_time_Time_to_s(lily_state *s) 37 | { 38 | lily_time_Time *t = ARG_Time(s, 0); 39 | char buf[64]; 40 | 41 | strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S %z", &t->local); 42 | lily_return_string(s, buf); 43 | } 44 | 45 | void lily_time_Time_since_epoch(lily_state *s) 46 | { 47 | lily_time_Time *t = ARG_Time(s, 0); 48 | 49 | lily_return_integer(s, (int64_t) mktime(&t->local)); 50 | } 51 | 52 | LILY_DECLARE_TIME_CALL_TABLE 53 | -------------------------------------------------------------------------------- /src/lily_pkg_time_bindings.h: -------------------------------------------------------------------------------- 1 | #ifndef LILY_TIME_BINDINGS_H 2 | #define LILY_TIME_BINDINGS_H 3 | /* Generated by lily-bindgen, do not edit. */ 4 | 5 | #if defined(_WIN32) && !defined(LILY_NO_EXPORT) 6 | #define LILY_TIME_EXPORT __declspec(dllexport) 7 | #else 8 | #define LILY_TIME_EXPORT 9 | #endif 10 | 11 | #define ARG_Time(s_, i_) \ 12 | (lily_time_Time *)lily_arg_generic(s_, i_) 13 | #define AS_Time(v_) \ 14 | (lily_time_Time *)lily_as_generic(v_) 15 | #define ID_Time(s_) \ 16 | lily_cid_at(s_, 0) 17 | #define INIT_Time(s_) \ 18 | (lily_time_Time *)lily_push_foreign(s_, ID_Time(s_), (lily_destroy_func)destroy_Time, sizeof(lily_time_Time)) 19 | 20 | LILY_TIME_EXPORT 21 | const char *lily_time_info_table[] = { 22 | "\01Time\0" 23 | ,"C\04Time\0" 24 | ,"m\0clock\0: Double" 25 | ,"m\0now\0: Time" 26 | ,"m\0since_epoch\0(Time): Integer" 27 | ,"m\0to_s\0(Time): String" 28 | ,"Z" 29 | }; 30 | #define LILY_DECLARE_TIME_CALL_TABLE \ 31 | LILY_TIME_EXPORT \ 32 | lily_call_entry_func lily_time_call_table[] = { \ 33 | NULL, \ 34 | NULL, \ 35 | lily_time_Time_clock, \ 36 | lily_time_Time_now, \ 37 | lily_time_Time_since_epoch, \ 38 | lily_time_Time_to_s, \ 39 | }; 40 | #endif 41 | -------------------------------------------------------------------------------- /src/lily_platform.h: -------------------------------------------------------------------------------- 1 | #ifndef LILY_PLATFORM_H 2 | # define LILY_PLATFORM_H 3 | 4 | # ifdef _WIN32 5 | # define LILY_PATH_CHAR '\\' 6 | # define LILY_PATH_SLASH "\\" 7 | # else 8 | # define LILY_PATH_CHAR '/' 9 | # define LILY_PATH_SLASH "/" 10 | # endif 11 | 12 | # ifdef _WIN32 13 | # define LILY_LIB_SUFFIXES {"dll", NULL} 14 | # elif __APPLE__ 15 | # define LILY_LIB_SUFFIXES {"dylib", "so", NULL} 16 | # else 17 | # define LILY_LIB_SUFFIXES {"so", NULL} 18 | # endif 19 | 20 | # define LILY_STRERROR_BUFFER_SIZE 128 21 | 22 | # ifdef _WIN32 23 | # define lily_strerror(_buffer) \ 24 | strerror_s(_buffer, sizeof(_buffer), errno) 25 | # else 26 | # define lily_strerror(_buffer) \ 27 | strerror_r(errno, _buffer, sizeof(_buffer)) 28 | # endif 29 | #endif 30 | -------------------------------------------------------------------------------- /src/lily_raiser.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "lily_alloc.h" 5 | #include "lily_raiser.h" 6 | 7 | #define HANDLE_VARARGS \ 8 | lily_mb_flush(raiser->msgbuf); \ 9 | \ 10 | va_list var_args; \ 11 | va_start(var_args, fmt); \ 12 | lily_mb_add_fmt_va(raiser->msgbuf, fmt, var_args); \ 13 | va_end(var_args); 14 | 15 | lily_raiser *lily_new_raiser(void) 16 | { 17 | lily_raiser *raiser = lily_malloc(sizeof(*raiser)); 18 | lily_jump_link *first_jump = lily_malloc(sizeof(*first_jump)); 19 | first_jump->prev = NULL; 20 | first_jump->next = NULL; 21 | 22 | raiser->msgbuf = lily_new_msgbuf(64); 23 | raiser->aux_msgbuf = lily_new_msgbuf(64); 24 | raiser->all_jumps = first_jump; 25 | raiser->source = err_from_none; 26 | 27 | return raiser; 28 | } 29 | 30 | void lily_rewind_raiser(lily_raiser *raiser) 31 | { 32 | lily_mb_flush(raiser->msgbuf); 33 | lily_mb_flush(raiser->aux_msgbuf); 34 | raiser->source = err_from_none; 35 | } 36 | 37 | void lily_free_raiser(lily_raiser *raiser) 38 | { 39 | lily_jump_link *jump_next; 40 | 41 | /* The vm pulls back all_jumps during exception capture and native function 42 | exit. By the time this function is called, all_jumps is always the first 43 | in the chain. */ 44 | while (raiser->all_jumps) { 45 | jump_next = raiser->all_jumps->next; 46 | lily_free(raiser->all_jumps); 47 | raiser->all_jumps = jump_next; 48 | } 49 | 50 | lily_free_msgbuf(raiser->aux_msgbuf); 51 | lily_free_msgbuf(raiser->msgbuf); 52 | lily_free(raiser); 53 | } 54 | 55 | /* This ensures that there is space for a jump for the caller. It will first try 56 | to reuse a jump, then to allocate a new one. */ 57 | lily_jump_link *lily_jump_setup(lily_raiser *raiser) 58 | { 59 | if (raiser->all_jumps->next) 60 | raiser->all_jumps = raiser->all_jumps->next; 61 | else { 62 | lily_jump_link *new_link = lily_malloc(sizeof(*new_link)); 63 | new_link->prev = raiser->all_jumps; 64 | raiser->all_jumps->next = new_link; 65 | 66 | new_link->next = NULL; 67 | 68 | raiser->all_jumps = new_link; 69 | } 70 | 71 | return raiser->all_jumps; 72 | } 73 | 74 | /* Releases the jump provided by lily_jump_setup. */ 75 | void lily_release_jump(lily_raiser *raiser) 76 | { 77 | raiser->all_jumps = raiser->all_jumps->prev; 78 | } 79 | 80 | void lily_raise_class(lily_raiser *raiser, struct lily_class_ *error_class, 81 | const char *message) 82 | { 83 | raiser->source = err_from_vm; 84 | raiser->error_class = error_class; 85 | 86 | /* This function doesn't need multiple arguments because it's copying over 87 | the message that the vm already processed. */ 88 | 89 | lily_mb_flush(raiser->msgbuf); 90 | lily_mb_add(raiser->msgbuf, message); 91 | 92 | longjmp(raiser->all_jumps->jump, 1); 93 | } 94 | 95 | void lily_raise_raw(lily_raiser *raiser, const char *fmt, ...) 96 | { 97 | raiser->source = err_from_raw; 98 | 99 | HANDLE_VARARGS 100 | 101 | longjmp(raiser->all_jumps->jump, 1); 102 | } 103 | 104 | void lily_raise_syn(lily_raiser *raiser, const char *fmt, ...) 105 | { 106 | raiser->source = err_from_parse; 107 | 108 | HANDLE_VARARGS 109 | 110 | longjmp(raiser->all_jumps->jump, 1); 111 | } 112 | 113 | void lily_raise_tree(lily_raiser *raiser, struct lily_ast_ *error_ast, 114 | const char *fmt, ...) 115 | { 116 | raiser->source = err_from_emit; 117 | raiser->error_ast = error_ast; 118 | 119 | HANDLE_VARARGS 120 | 121 | longjmp(raiser->all_jumps->jump, 1); 122 | } 123 | -------------------------------------------------------------------------------- /src/lily_raiser.h: -------------------------------------------------------------------------------- 1 | #ifndef LILY_RAISER_H 2 | # define LILY_RAISER_H 3 | 4 | # include 5 | 6 | # include "lily.h" 7 | # include "lily_core_types.h" 8 | 9 | typedef enum { 10 | err_from_emit, 11 | err_from_none, 12 | err_from_parse, 13 | err_from_raw, 14 | err_from_vm, 15 | } lily_error_source; 16 | 17 | typedef struct lily_jump_link_ { 18 | struct lily_jump_link_ *prev; 19 | struct lily_jump_link_ *next; 20 | 21 | jmp_buf jump; 22 | } lily_jump_link; 23 | 24 | struct lily_class_; 25 | struct lily_ast_; 26 | 27 | typedef struct lily_raiser_ { 28 | lily_jump_link *all_jumps; 29 | 30 | /* The error message is stored here. */ 31 | lily_msgbuf *msgbuf; 32 | 33 | /* This is a spare msgbuf for building error messages. */ 34 | lily_msgbuf *aux_msgbuf; 35 | 36 | union { 37 | struct lily_class_ *error_class; 38 | struct lily_ast_ *error_ast; 39 | }; 40 | 41 | lily_error_source source; 42 | } lily_raiser; 43 | 44 | lily_raiser *lily_new_raiser(void); 45 | void lily_rewind_raiser(lily_raiser *); 46 | void lily_free_raiser(lily_raiser *); 47 | 48 | void lily_raise_class(lily_raiser *, struct lily_class_ *, const char *); 49 | void lily_raise_tree(lily_raiser *, struct lily_ast_ *, const char *, ...); 50 | void lily_raise_syn(lily_raiser *, const char *, ...); 51 | void lily_raise_raw(lily_raiser *, const char *, ...); 52 | lily_jump_link *lily_jump_setup(lily_raiser *); 53 | void lily_release_jump(lily_raiser *); 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /src/lily_string_pile.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "lily_alloc.h" 4 | #include "lily_string_pile.h" 5 | 6 | lily_string_pile *lily_new_string_pile(void) 7 | { 8 | lily_string_pile *sp = lily_malloc(sizeof(*sp)); 9 | 10 | sp->buffer = lily_malloc(64 * sizeof(*sp->buffer)); 11 | sp->size = 64; 12 | return sp; 13 | } 14 | 15 | void lily_free_string_pile(lily_string_pile *sp) 16 | { 17 | lily_free(sp->buffer); 18 | lily_free(sp); 19 | } 20 | 21 | void lily_sp_insert(lily_string_pile *sp, const char *new_str, uint16_t *pos) 22 | { 23 | uint16_t want_size = *pos + 1 + (uint16_t)strlen(new_str); 24 | 25 | if (sp->size < want_size) { 26 | while (sp->size < want_size) 27 | sp->size *= 2; 28 | 29 | char *new_buffer = lily_realloc(sp->buffer, 30 | sp->size * sizeof(*new_buffer)); 31 | sp->buffer = new_buffer; 32 | } 33 | 34 | strcpy(sp->buffer + *pos, new_str); 35 | *pos = want_size; 36 | } 37 | 38 | void lily_sp_insert_bytes(lily_string_pile *sp, const char *new_str, 39 | uint16_t *pos, uint16_t new_str_size) 40 | { 41 | uint16_t want_size = *pos + 1 + new_str_size; 42 | 43 | if (sp->size < want_size) { 44 | while (sp->size < want_size) 45 | sp->size *= 2; 46 | 47 | char *new_buffer = lily_realloc(sp->buffer, 48 | sp->size * sizeof(*new_buffer)); 49 | sp->buffer = new_buffer; 50 | } 51 | 52 | memcpy(sp->buffer + *pos, new_str, new_str_size); 53 | *pos = want_size; 54 | } 55 | 56 | char *lily_sp_get(lily_string_pile *sp, uint16_t pos) 57 | { 58 | return sp->buffer + pos; 59 | } 60 | -------------------------------------------------------------------------------- /src/lily_string_pile.h: -------------------------------------------------------------------------------- 1 | #ifndef LILY_STRING_PILE_H 2 | # define LILY_STRING_PILE_H 3 | 4 | # include 5 | 6 | /* lily_string_pile is a storage for strings needed by expressions. The 7 | expression state injects strings at a given index. Later, emitter can fetch 8 | out the strings by index. The point of this is to have a string storage with 9 | no waste that is not harmed by the underlying buffer resizing/moving. */ 10 | 11 | typedef struct { 12 | char *buffer; 13 | uint16_t size; 14 | uint16_t pad; 15 | uint32_t pad2; 16 | } lily_string_pile; 17 | 18 | lily_string_pile *lily_new_string_pile(void); 19 | 20 | void lily_free_string_pile(lily_string_pile *); 21 | 22 | /* Insert a string into the pile at the index given. The index is updated to 23 | start after the inserted string. */ 24 | void lily_sp_insert(lily_string_pile *, const char *, uint16_t *); 25 | 26 | /* Same as lily_sp_insert, but for sources that may have embedded zeroes. This 27 | takes an extra size and uses memcpy. */ 28 | void lily_sp_insert_bytes(lily_string_pile *, const char *, uint16_t *, 29 | uint16_t); 30 | 31 | /* Fetch a string from the pile that starts from the given index. The string is 32 | a shallow copy of the buffer, and is thus invalidated if the underlying 33 | buffer happens to grow. */ 34 | char *lily_sp_get(lily_string_pile *, uint16_t); 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /src/lily_symtab.h: -------------------------------------------------------------------------------- 1 | #ifndef LILY_SYMTAB_H 2 | # define LILY_SYMTAB_H 3 | 4 | # include "lily_core_types.h" 5 | # include "lily_value.h" 6 | 7 | typedef struct lily_value_stack_ { 8 | lily_value **data; 9 | uint16_t pos; 10 | uint16_t size; 11 | uint32_t pad; 12 | } lily_value_stack; 13 | 14 | typedef struct lily_symtab_ { 15 | lily_module_entry *prelude_module; 16 | lily_module_entry *active_module; 17 | 18 | /* Defined functions that go out of scope are stuffed in here, unless 19 | they're class methods. */ 20 | lily_var *hidden_function_chain; 21 | 22 | /* Ditto, for classes. */ 23 | lily_class *hidden_class_chain; 24 | 25 | lily_value_stack *literals; 26 | 27 | /* Each class gets a unique id. This is mostly for the builtin classes 28 | which have some special behavior sometimes. */ 29 | uint16_t next_class_id; 30 | 31 | uint16_t next_global_id; 32 | 33 | uint32_t pad; 34 | 35 | /* These classes are used frequently throughout the interpreter, so they're 36 | kept here for easy, fast access. */ 37 | lily_class *integer_class; 38 | lily_class *double_class; 39 | lily_class *string_class; 40 | lily_class *byte_class; 41 | lily_class *bytestring_class; 42 | lily_class *boolean_class; 43 | lily_class *function_class; 44 | lily_class *list_class; 45 | lily_class *hash_class; 46 | lily_class *tuple_class; 47 | lily_class *optarg_class; 48 | } lily_symtab; 49 | 50 | lily_symtab *lily_new_symtab(void); 51 | void lily_set_prelude(lily_symtab *, lily_module_entry *); 52 | void lily_free_module_symbols(lily_symtab *, lily_module_entry *); 53 | void lily_free_properties(lily_class *); 54 | void lily_rewind_symtab(lily_symtab *, lily_module_entry *, lily_class *, 55 | lily_var *, lily_boxed_sym *, int); 56 | void lily_free_symtab(lily_symtab *); 57 | 58 | lily_value *lily_literal_at(lily_symtab *, uint16_t); 59 | lily_literal *lily_get_integer_literal(lily_symtab *, int64_t); 60 | lily_literal *lily_get_double_literal(lily_symtab *, double); 61 | lily_literal *lily_get_bytestring_literal(lily_symtab *, const char *, 62 | uint32_t); 63 | lily_literal *lily_get_string_literal(lily_symtab *, const char *); 64 | lily_literal *lily_get_unit_literal(lily_symtab *); 65 | void lily_new_function_literal(lily_symtab *, lily_var *, lily_value *); 66 | 67 | lily_class *lily_find_class(lily_module_entry *, const char *); 68 | lily_var *lily_find_var(lily_module_entry *, const char *); 69 | lily_named_sym *lily_find_member(lily_class *, const char *); 70 | lily_named_sym *lily_find_member_in_class(lily_class *, const char *); 71 | lily_variant_class *lily_find_variant(lily_class *, const char *); 72 | lily_module_entry *lily_find_module(lily_module_entry *, const char *); 73 | lily_module_entry *lily_find_module_by_path(lily_symtab *, const char *); 74 | lily_module_entry *lily_find_registered_module(lily_symtab *, const char *); 75 | 76 | lily_class *lily_new_raw_class(const char *, uint16_t); 77 | lily_class *lily_new_class(lily_symtab *, const char *, uint16_t); 78 | lily_class *lily_new_enum_class(lily_symtab *, const char *, uint16_t); 79 | lily_variant_class *lily_new_variant_class(lily_class *, const char *, 80 | uint16_t); 81 | 82 | lily_prop_entry *lily_add_class_property(lily_class *, lily_type *, 83 | const char *, uint16_t, uint16_t); 84 | void lily_add_symbol_ref(lily_module_entry *, lily_sym *); 85 | 86 | void lily_fix_enum_variant_ids(lily_symtab *, lily_class *); 87 | void lily_register_classes(lily_symtab *, struct lily_vm_state_ *); 88 | #endif 89 | -------------------------------------------------------------------------------- /src/lily_token.h: -------------------------------------------------------------------------------- 1 | #ifndef LILY_TOKEN_H 2 | # define LILY_TOKEN_H 3 | 4 | /* Generated by scripts/token.lily. */ 5 | 6 | typedef enum { 7 | tk_right_parenth, 8 | tk_comma, 9 | tk_left_curly, 10 | tk_right_curly, 11 | tk_left_bracket, 12 | tk_colon, 13 | tk_tilde, 14 | tk_bitwise_xor, 15 | tk_bitwise_xor_eq, 16 | tk_not, 17 | tk_not_eq, 18 | tk_modulo, 19 | tk_modulo_eq, 20 | tk_multiply, 21 | tk_multiply_eq, 22 | tk_divide, 23 | tk_divide_eq, 24 | tk_plus, 25 | tk_plus_plus, 26 | tk_plus_eq, 27 | tk_minus, 28 | tk_minus_eq, 29 | tk_lt, 30 | tk_lt_eq, 31 | tk_left_shift, 32 | tk_left_shift_eq, 33 | tk_gt, 34 | tk_gt_eq, 35 | tk_right_shift, 36 | tk_right_shift_eq, 37 | tk_equal, 38 | tk_eq_eq, 39 | tk_left_parenth, 40 | tk_lambda, 41 | tk_tuple_open, 42 | tk_tuple_close, 43 | tk_right_bracket, 44 | tk_arrow, 45 | tk_word, 46 | tk_prop_word, 47 | tk_double_quote, 48 | tk_bytestring, 49 | tk_byte, 50 | tk_integer, 51 | tk_double, 52 | tk_docblock, 53 | tk_keyword_arg, 54 | tk_dot, 55 | tk_bitwise_and, 56 | tk_bitwise_and_eq, 57 | tk_logical_and, 58 | tk_bitwise_or, 59 | tk_bitwise_or_eq, 60 | tk_logical_or, 61 | tk_typecast_parenth, 62 | tk_three_dots, 63 | tk_func_pipe, 64 | tk_scoop, 65 | tk_invalid, 66 | tk_end_lambda, 67 | tk_end_tag, 68 | tk_eof, 69 | } lily_token; 70 | 71 | # define IS_ASSIGN_TOKEN(t) (lily_priority_for_token(t) == 1) 72 | # define IS_COMPARE_TOKEN(t) (lily_priority_for_token(t) == 4) 73 | 74 | uint8_t lily_priority_for_token(lily_token); 75 | const char *tokname(lily_token); 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /src/lily_type_maker.h: -------------------------------------------------------------------------------- 1 | #ifndef LILY_TYPE_MAKER_H 2 | # define LILY_TYPE_MAKER_H 3 | 4 | # include "lily_core_types.h" 5 | 6 | typedef struct { 7 | lily_type **types; 8 | uint16_t pos; 9 | uint16_t size; 10 | uint32_t pad; 11 | } lily_type_maker; 12 | 13 | lily_type_maker *lily_new_type_maker(void); 14 | void lily_tm_add(lily_type_maker *, lily_type *); 15 | void lily_tm_add_unchecked(lily_type_maker *, lily_type *); 16 | void lily_tm_insert(lily_type_maker *, uint16_t, lily_type *); 17 | void lily_tm_reserve(lily_type_maker *, uint16_t); 18 | lily_type *lily_tm_pop(lily_type_maker *); 19 | lily_type *lily_tm_make(lily_type_maker *, lily_class *, uint16_t); 20 | lily_type *lily_tm_make_call(lily_type_maker *, uint16_t, lily_class *, 21 | uint16_t); 22 | uint16_t lily_tm_pos(lily_type_maker *); 23 | void lily_tm_restore(lily_type_maker *, uint16_t); 24 | 25 | void lily_free_type_maker(lily_type_maker *); 26 | 27 | lily_type *lily_new_raw_type(lily_class *); 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /src/lily_type_system.h: -------------------------------------------------------------------------------- 1 | # ifndef LILY_TYPE_SYSTEM_H 2 | # define LILY_TYPE_SYSTEM_H 3 | 4 | # include "lily_core_types.h" 5 | # include "lily_type_maker.h" 6 | 7 | /* This is where the brains of Lily's type system lies. This handles type 8 | matching, unification, and solving. 9 | The type system (ts for short) works by having parser always tell it the max 10 | number of generics that any one function or class needs at once. 11 | How it works is by establishing a series of scope. Each scope has the max 12 | number of generics seen reserved into it. Matching will lay values down into 13 | the current scope, and will handle generics as it goes along. Resolving then 14 | later uses those filled-in types. */ 15 | 16 | typedef struct { 17 | uint16_t pos; 18 | uint16_t num_used; 19 | uint16_t scoop_count; 20 | uint16_t pad; 21 | } lily_ts_save_point; 22 | 23 | typedef struct { 24 | lily_type **types; 25 | lily_type **base; 26 | uint16_t pos; 27 | uint16_t num_used; 28 | uint16_t max_seen; 29 | uint16_t max; 30 | 31 | uint16_t scoop_count; 32 | uint16_t pad1; 33 | uint32_t pad2; 34 | 35 | lily_type_maker *tm; 36 | } lily_type_system; 37 | 38 | lily_type_system *lily_new_type_system(lily_type_maker *); 39 | void lily_rewind_type_system(lily_type_system *); 40 | void lily_free_type_system(lily_type_system *); 41 | 42 | /* The first type (left) is the type that is wanted. 43 | The second type (right) is the type that is given. 44 | Determine if the two types match. Additionally, if 'left' provides generics, 45 | and those generics are unresolved, then right's types solve those generics. */ 46 | int lily_ts_check(lily_type_system *, lily_type *, lily_type *); 47 | 48 | lily_type *lily_ts_unify(lily_type_system *, lily_type *, lily_type *); 49 | 50 | /* This uses the type system to determine if the first type can be assigned 51 | to the second type. This understands variance, but will not solve any generics. */ 52 | int lily_ts_type_greater_eq(lily_type_system *, lily_type *, lily_type *); 53 | 54 | /* This recurses through the given type, building up a new, completely resolved 55 | type whereever the given type has generics. 56 | In the event that the given type specifies generics that are not solved, 57 | this function will solve them as ?. 58 | The result is never NULL. */ 59 | lily_type *lily_ts_resolve(lily_type_system *, lily_type *); 60 | 61 | /* This function is called when the first type (left) needs to be solved BUT 62 | the generics within left are not within the type stack. 63 | This situation happens when class member accesses wherein the class member 64 | has a generic type. In such a case, the left is the class instance (which 65 | provides all generic info), and the right is the type of the member. 66 | The result is a solved type, and never NULL. */ 67 | lily_type *lily_ts_resolve_by_second(lily_type_system *, lily_type *, lily_type *); 68 | 69 | /* This does what lily_ts_resolve does as well as replacing the result of scoop 70 | if neccessary. Most callers don't want this and should use the regular 71 | resolve instead. */ 72 | lily_type *lily_ts_resolve_unscoop(lily_type_system *, lily_type *); 73 | 74 | /* This saves information for the current scope down to the save point, and 75 | reserves a fresh set of types for a new scope. */ 76 | void lily_ts_scope_save(lily_type_system *, lily_ts_save_point *); 77 | 78 | /* This restores ts to a previously-established scope. */ 79 | void lily_ts_scope_restore(lily_type_system *, lily_ts_save_point *); 80 | 81 | /* The parser calls this each time that generics are collected. This reports 82 | how many were collected. max_seen may or may not be updated. */ 83 | void lily_ts_generics_seen(lily_type_system *, uint16_t); 84 | 85 | /* Determine if the first class passed is either a base class or the same class 86 | as the second one. This doesn't take the ts because the information needed 87 | is within the classes themselves. */ 88 | int lily_class_greater_eq(lily_class *, lily_class *); 89 | 90 | /* The same as lily_class_greater_eq, except that an id is passed in place of a 91 | first class. */ 92 | int lily_class_greater_eq_id(int, lily_class *); 93 | 94 | #endif 95 | -------------------------------------------------------------------------------- /src/lily_utf8.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2008-2009 Bjoern Hoehrmann 2 | // See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. 3 | 4 | #include 5 | 6 | #include "lily_utf8.h" 7 | 8 | #define UTF8_ACCEPT 0 9 | #define UTF8_REJECT 1 10 | 11 | static const uint8_t utf8d[] = { 12 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1f 13 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f 14 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5f 15 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7f 16 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f 17 | 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf 18 | 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // c0..df 19 | 0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, // e0..ef 20 | 0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, // f0..ff 21 | 0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, // s0..s0 22 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, // s1..s2 23 | 1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, // s3..s4 24 | 1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, // s5..s6 25 | 1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // s7..s8 26 | }; 27 | 28 | uint32_t 29 | decode(uint32_t* state, uint32_t* codep, uint32_t byte) { 30 | uint32_t type = utf8d[byte]; 31 | 32 | *codep = (*state != UTF8_ACCEPT) ? 33 | (byte & 0x3fu) | (*codep << 6) : 34 | (0xff >> type) & (byte); 35 | 36 | *state = utf8d[256 + *state*16 + type]; 37 | return *state; 38 | } 39 | 40 | /* Only check if 'input' is valid utf-8 (\0 termination is assumed). */ 41 | int lily_is_valid_utf8(const char *input) 42 | { 43 | uint8_t *s = (uint8_t *)input; 44 | uint32_t codepoint; 45 | uint32_t state = 0; 46 | 47 | for (;*s; ++s) 48 | if (decode(&state, &codepoint, *s) == UTF8_REJECT) 49 | break; 50 | 51 | return state == UTF8_ACCEPT; 52 | } 53 | 54 | /* Check if 'input' is valid utf-8 and \0 terminated. */ 55 | int lily_is_valid_sized_utf8(const char *input, uint32_t size) 56 | { 57 | uint8_t *s = (uint8_t *)input; 58 | uint8_t *end = s + size; 59 | uint32_t codepoint; 60 | uint32_t state = 0; 61 | 62 | for (;*s; ++s) 63 | if (decode(&state, &codepoint, *s) == UTF8_REJECT) 64 | break; 65 | 66 | return (state == UTF8_ACCEPT) && (s == end); 67 | } 68 | -------------------------------------------------------------------------------- /src/lily_utf8.h: -------------------------------------------------------------------------------- 1 | #ifndef LILY_UTF8_H 2 | # define LILY_UTF8_H 3 | 4 | # include 5 | 6 | int lily_is_valid_utf8(const char *); 7 | int lily_is_valid_sized_utf8(const char *, uint32_t); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories("${PROJECT_SOURCE_DIR}/src/") 2 | 3 | function(set_output_dirs TARGET_NAME PATH) 4 | # Extra dirs are set for multi-config generators (like MSBuild) to make 5 | # sure they build to the right directory. 6 | set_target_properties("${TARGET_NAME}" PROPERTIES 7 | PREFIX "" 8 | LIBRARY_OUTPUT_DIRECTORY "${PATH}" 9 | LIBRARY_OUTPUT_DIRECTORY_DEBUG "${PATH}" 10 | LIBRARY_OUTPUT_DIRECTORY_RELEASE "${PATH}" 11 | RUNTIME_OUTPUT_DIRECTORY "${PATH}" 12 | RUNTIME_OUTPUT_DIRECTORY_DEBUG "${PATH}" 13 | RUNTIME_OUTPUT_DIRECTORY_RELEASE "${PATH}") 14 | endfunction(set_output_dirs) 15 | 16 | set(backbone_SOURCES 17 | t/src/lily_backbone.c 18 | t/src/lily_covlib.c 19 | t/src/lily_farm.c) 20 | 21 | if(WIN32 OR APPLE) 22 | add_library(backbone SHARED ${backbone_SOURCES} $) 23 | else() 24 | add_library(backbone SHARED ${backbone_SOURCES}) 25 | endif() 26 | 27 | add_executable(pre-commit-tests test_driver.c $) 28 | set_target_properties(pre-commit-tests PROPERTIES ENABLE_EXPORTS TRUE) 29 | 30 | if(LILY_NEED_DL) 31 | target_link_libraries(pre-commit-tests dl) 32 | endif() 33 | 34 | if(LILY_NEED_M) 35 | target_link_libraries(pre-commit-tests m) 36 | endif() 37 | 38 | set_output_dirs(backbone "${PROJECT_BINARY_DIR}/test/t/") 39 | set_output_dirs(pre-commit-tests "${PROJECT_BINARY_DIR}") 40 | -------------------------------------------------------------------------------- /test/benchmark/README.md: -------------------------------------------------------------------------------- 1 | Each of these benchmarks attempts to replicate the same algorithm in a different 2 | language. Lily may be a statically-typed language, but the reference 3 | implementation is an interpreter. I've therefore decided to pit Lily up against 4 | similar languages: Python, Ruby, and Lua. 5 | 6 | The benchmarks here are copied from the 7 | [Wren programming language](https://github.com/munificent/wren). 8 | 9 | ### binary_trees 10 | 11 | This benchmark stresses object creation and garbage collection. It builds a few 12 | big, deeply nested binaries and then traverses them. 13 | 14 | ### fib 15 | 16 | This benchmark runs a naive Fibonacci a few times. This stresses heavy function 17 | entry/exit, and arithmetic. It's not very representative of real-world code 18 | though. 19 | 20 | ### for 21 | 22 | This was originally made to test the speed of for loops. It serves as a useful 23 | baseline of hash performance, since it builds a large integer-based hash and 24 | does a single accumulating loop through it. In Lily's case, this also stresses 25 | a foreign function calling back into a native one. 26 | 27 | ### map_numeric 28 | 29 | This does the same work as 'for', but finishes by deleting the elements one at 30 | a time. 31 | 32 | ### map_string 33 | 34 | This builds a large hash string to int hash, then does the same as map_numeric 35 | (iterate and manually delete elements). Together they're useful for isolating 36 | problems in the performance of hashes. 37 | -------------------------------------------------------------------------------- /test/benchmark/bench.lily: -------------------------------------------------------------------------------- 1 | import sys, time 2 | 3 | var is_quiet = (sys.argv.get(1).unwrap_or("") == "--quiet") 4 | var start_time = 0 5 | 6 | define log[A](message: A) 7 | { 8 | if is_quiet: { 9 | return 10 | } 11 | 12 | print(message) 13 | } 14 | 15 | define finish(begin: Double) 16 | { 17 | log("Elapsed: {0}".format(time.Time.clock() - begin)) 18 | } 19 | 20 | define parameter(usual: Integer, :quiet quiet_value: Integer): Integer 21 | { 22 | if is_quiet: { 23 | return quiet_value 24 | else: 25 | return usual 26 | } 27 | } 28 | 29 | define run(f: Function()) 30 | { 31 | # Skip the first pass when running from the test suite. The suite will run 32 | # the bench call explicitly under an assert. 33 | if sys.argv[0] == "": { 34 | return 35 | } 36 | 37 | f() 38 | } 39 | 40 | define start: Double 41 | { 42 | return time.Time.clock() 43 | } 44 | -------------------------------------------------------------------------------- /test/benchmark/binary_trees.lily: -------------------------------------------------------------------------------- 1 | import bench 2 | 3 | enum Tree { 4 | Leaf(Integer), 5 | Branch(Integer, Tree, Tree) 6 | } 7 | 8 | define make_tree(item: Integer, depth: Integer): Tree 9 | { 10 | if depth == 0: { 11 | return Leaf(item) 12 | } 13 | 14 | var item2 = item + item 15 | depth -= 1 16 | return Branch(item, make_tree(item2 - 1, depth), make_tree(item2, depth)) 17 | } 18 | 19 | define check_tree(t: Tree): Integer 20 | { 21 | match t: { 22 | case Leaf(i): 23 | return i 24 | case Branch(i, left, right): 25 | return i + check_tree(left) - check_tree(right) 26 | } 27 | } 28 | 29 | define run_stretch(min: Integer, max: Integer) 30 | { 31 | var long_lived_tree = make_tree(0, max + 1) 32 | var iterations = 1 33 | var stretch_depth = max + 1 34 | 35 | for i in 1...max: { 36 | iterations *= 2 37 | } 38 | 39 | for depth in min...stretch_depth by 2: { 40 | var check = 0 41 | 42 | for i in 1...iterations: { 43 | var k = 1 44 | check += check_tree(make_tree(i, depth)) + check_tree(make_tree(-i, depth)) 45 | } 46 | 47 | iterations /= 4 48 | bench.log("{0} trees of depth {1} check: {2}".format(iterations * 2, depth, check)) 49 | } 50 | 51 | bench.log("long lived tree of depth {0} check: {1}".format(max, check_tree(long_lived_tree))) 52 | } 53 | 54 | define run_depth(max: Integer) 55 | { 56 | var stretch_depth = max + 1 57 | var tree = make_tree(0, stretch_depth) 58 | var check = check_tree(tree) 59 | 60 | bench.log("stretch tree of depth {0} check: {1}".format(stretch_depth, check)) 61 | } 62 | 63 | define bench_test 64 | { 65 | var min = bench.parameter(4, :quiet 2) 66 | var max = bench.parameter(12, :quiet 6) 67 | var start = bench.start() 68 | 69 | run_depth(max + 1) 70 | run_stretch(min, max) 71 | bench.finish(start) 72 | } 73 | 74 | bench.run(bench_test) 75 | -------------------------------------------------------------------------------- /test/benchmark/binary_trees.lua: -------------------------------------------------------------------------------- 1 | -- The Computer Language Benchmarks Game 2 | -- http://shootout.alioth.debian.org/ 3 | -- contributed by Mike Pall 4 | 5 | local function BottomUpTree(item, depth) 6 | if depth > 0 then 7 | local i = item + item 8 | depth = depth - 1 9 | local left, right = BottomUpTree(i-1, depth), BottomUpTree(i, depth) 10 | return { item, left, right } 11 | else 12 | return { item } 13 | end 14 | end 15 | 16 | local function ItemCheck(tree) 17 | if tree[2] then 18 | return tree[1] + ItemCheck(tree[2]) - ItemCheck(tree[3]) 19 | else 20 | return tree[1] 21 | end 22 | end 23 | 24 | local N = 12 25 | local mindepth = 4 26 | local maxdepth = mindepth + 2 27 | if maxdepth < N then maxdepth = N end 28 | 29 | local start = os.clock() 30 | 31 | do 32 | local stretchdepth = maxdepth + 1 33 | local stretchtree = BottomUpTree(0, stretchdepth) 34 | io.write(string.format("stretch tree of depth %d check: %d\n", 35 | stretchdepth, ItemCheck(stretchtree))) 36 | end 37 | 38 | local longlivedtree = BottomUpTree(0, maxdepth) 39 | 40 | for depth=mindepth,maxdepth,2 do 41 | local iterations = 2 ^ (maxdepth - depth + mindepth) 42 | local check = 0 43 | for i=1,iterations do 44 | check = check + ItemCheck(BottomUpTree(1, depth)) + 45 | ItemCheck(BottomUpTree(-1, depth)) 46 | end 47 | io.write(string.format("%d trees of depth %d check: %d\n", 48 | iterations*2, depth, check)) 49 | end 50 | 51 | io.write(string.format("long lived tree of depth %d check: %d\n", 52 | maxdepth, ItemCheck(longlivedtree))) 53 | 54 | io.write(string.format("elapsed: %.8f\n", os.clock() - start)) 55 | -------------------------------------------------------------------------------- /test/benchmark/binary_trees.py: -------------------------------------------------------------------------------- 1 | # The Computer Language Benchmarks Game 2 | # http://shootout.alioth.debian.org/ 3 | # 4 | # contributed by Antoine Pitrou 5 | # modified by Dominique Wahli 6 | # modified by Heinrich Acker 7 | from __future__ import print_function 8 | 9 | import time 10 | 11 | # Map "range" to an efficient range in both Python 2 and 3. 12 | try: 13 | range = xrange 14 | except NameError: 15 | pass 16 | 17 | def make_tree(item, depth): 18 | if not depth: return item, None, None 19 | item2 = item + item 20 | depth -= 1 21 | return item, make_tree(item2 - 1, depth), make_tree(item2, depth) 22 | 23 | def check_tree(node): 24 | item, left, right = node 25 | if not left: return item 26 | return item + check_tree(left) - check_tree(right) 27 | 28 | min_depth = 4 29 | max_depth = 12 30 | stretch_depth = max_depth + 1 31 | 32 | start = time.clock() 33 | print("stretch tree of depth %d check:" % stretch_depth, check_tree(make_tree(0, stretch_depth))) 34 | 35 | long_lived_tree = make_tree(0, max_depth) 36 | 37 | iterations = 2 ** max_depth 38 | for depth in range(min_depth, stretch_depth, 2): 39 | 40 | check = 0 41 | for i in range(1, iterations + 1): 42 | check += check_tree(make_tree(i, depth)) + check_tree(make_tree(-i, depth)) 43 | 44 | print("%d trees of depth %d check:" % (iterations * 2, depth), check) 45 | iterations //= 4 46 | 47 | print("long lived tree of depth %d check:" % max_depth, check_tree(long_lived_tree)) 48 | print("elapsed: " + str(time.clock() - start)) -------------------------------------------------------------------------------- /test/benchmark/binary_trees.rb: -------------------------------------------------------------------------------- 1 | # The Computer Language Shootout Benchmarks 2 | # http://shootout.alioth.debian.org 3 | # 4 | # contributed by Jesse Millikan 5 | # Modified by Wesley Moxam 6 | 7 | 8 | def item_check(left, item, right) 9 | return item if left.nil? 10 | item + item_check(*left) - item_check(*right) 11 | end 12 | 13 | def bottom_up_tree(item, depth) 14 | return [nil, item, nil] unless depth > 0 15 | item_item = 2 * item 16 | depth -= 1 17 | [bottom_up_tree(item_item - 1, depth), item, bottom_up_tree(item_item, depth)] 18 | end 19 | 20 | max_depth = 12 21 | min_depth = 4 22 | 23 | max_depth = min_depth + 2 if min_depth + 2 > max_depth 24 | 25 | stretch_depth = max_depth + 1 26 | stretch_tree = bottom_up_tree(0, stretch_depth) 27 | 28 | start = Time.now 29 | puts "stretch tree of depth #{stretch_depth} check: #{item_check(*stretch_tree)}" 30 | stretch_tree = nil 31 | 32 | long_lived_tree = bottom_up_tree(0, max_depth) 33 | 34 | min_depth.step(max_depth + 1, 2) do |depth| 35 | iterations = 2**(max_depth - depth + min_depth) 36 | 37 | check = 0 38 | 39 | for i in 1..iterations 40 | temp_tree = bottom_up_tree(i, depth) 41 | check += item_check(*temp_tree) 42 | 43 | temp_tree = bottom_up_tree(-i, depth) 44 | check += item_check(*temp_tree) 45 | end 46 | 47 | puts "#{iterations * 2} trees of depth #{depth} check: #{check}" 48 | end 49 | 50 | puts "long lived tree of depth #{max_depth} check: #{item_check(*long_lived_tree)}" 51 | puts "elapsed: " + (Time.now - start).to_s 52 | -------------------------------------------------------------------------------- /test/benchmark/fib.lily: -------------------------------------------------------------------------------- 1 | import bench 2 | 3 | define fib(n: Integer): Integer 4 | { 5 | if n < 2: { 6 | return n 7 | else: 8 | return fib(n - 1) + fib(n - 2) 9 | } 10 | } 11 | 12 | define bench_test 13 | { 14 | var begin = 1 15 | var end = bench.parameter(5, :quiet 1) 16 | var n = bench.parameter(28, :quiet 20) 17 | var start = bench.start() 18 | 19 | for i in begin...end: { 20 | n |> fib |> bench.log 21 | } 22 | 23 | bench.finish(start) 24 | } 25 | 26 | bench.run(bench_test) 27 | -------------------------------------------------------------------------------- /test/benchmark/fib.lua: -------------------------------------------------------------------------------- 1 | function fib(n) 2 | if n < 2 then return n end 3 | return fib(n - 2) + fib(n - 1) 4 | end 5 | 6 | local start = os.clock() 7 | for i = 1, 5 do 8 | io.write(fib(28) .. "\n") 9 | end 10 | io.write(string.format("elapsed: %.8f\n", os.clock() - start)) 11 | -------------------------------------------------------------------------------- /test/benchmark/fib.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | import time 4 | 5 | def fib(n): 6 | if n < 2: return n 7 | return fib(n - 1) + fib(n - 2) 8 | 9 | start = time.clock() 10 | for i in range(0, 5): 11 | print(fib(28)) 12 | print("elapsed: " + str(time.clock() - start)) -------------------------------------------------------------------------------- /test/benchmark/fib.rb: -------------------------------------------------------------------------------- 1 | def fib(n) 2 | if n < 2 then 3 | n 4 | else 5 | fib(n - 1) + fib(n - 2) 6 | end 7 | end 8 | 9 | start = Time.now 10 | for i in 0...5 11 | puts fib(28) 12 | end 13 | puts "elapsed: " + (Time.now - start).to_s 14 | -------------------------------------------------------------------------------- /test/benchmark/for.lily: -------------------------------------------------------------------------------- 1 | import bench 2 | 3 | define bench_test 4 | { 5 | var begin = 0 6 | var end = bench.parameter(999999, :quiet 50000) 7 | var map: Hash[Integer, Integer] = [] 8 | var sum = 0 9 | var start = bench.start() 10 | 11 | for i in begin...end: { 12 | map[i] = i 13 | } 14 | 15 | map.each_pair(|k, v| sum += v ) 16 | 17 | bench.log(sum) 18 | bench.finish(start) 19 | } 20 | 21 | bench.run(bench_test) 22 | -------------------------------------------------------------------------------- /test/benchmark/for.lua: -------------------------------------------------------------------------------- 1 | local start = os.clock() 2 | local list = {} 3 | for i = 0, 999999 do 4 | list[i] = i 5 | end 6 | 7 | local sum = 0 8 | for k, i in pairs(list) do 9 | sum = sum + i 10 | end 11 | io.write(sum .. "\n") 12 | io.write(string.format("elapsed: %.8f\n", os.clock() - start)) 13 | -------------------------------------------------------------------------------- /test/benchmark/for.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | import time 4 | 5 | # Map "range" to an efficient range in both Python 2 and 3. 6 | try: 7 | range = xrange 8 | except NameError: 9 | pass 10 | 11 | start = time.clock() 12 | list = [] 13 | for i in range(0, 1000000): 14 | list.append(i) 15 | 16 | sum = 0 17 | for i in list: 18 | sum += i 19 | print(sum) 20 | print("elapsed: " + str(time.clock() - start)) -------------------------------------------------------------------------------- /test/benchmark/for.rb: -------------------------------------------------------------------------------- 1 | start = Time.now 2 | list = [] 3 | 1000000.times {|i| list << i} 4 | 5 | sum = 0 6 | list.each {|i| sum += i} 7 | puts sum 8 | puts "elapsed: " + (Time.now - start).to_s 9 | -------------------------------------------------------------------------------- /test/benchmark/map_numeric.lily: -------------------------------------------------------------------------------- 1 | import bench 2 | 3 | define bench_test 4 | { 5 | var begin = 0 6 | var end = bench.parameter(999999, :quiet 50000) 7 | var map: Hash[Integer, Integer] = [] 8 | var sum = 0 9 | var start = bench.start() 10 | 11 | for i in begin...end: { 12 | map[i] = i 13 | } 14 | 15 | for i in begin...end: { 16 | sum += map[i] 17 | } 18 | 19 | for i in begin...end: { 20 | map.delete(i) 21 | } 22 | 23 | bench.log(sum) 24 | bench.finish(start) 25 | } 26 | 27 | bench.run(bench_test) 28 | -------------------------------------------------------------------------------- /test/benchmark/map_numeric.lua: -------------------------------------------------------------------------------- 1 | local start = os.clock() 2 | 3 | local map = {} 4 | 5 | for i = 1, 1000000 do 6 | map[i] = i 7 | end 8 | 9 | local sum = 0 10 | for i = 1, 1000000 do 11 | sum = sum + map[i] 12 | end 13 | io.write(string.format("%d\n", sum)) 14 | 15 | for i = 1, 1000000 do 16 | map[i] = nil 17 | end 18 | 19 | io.write(string.format("elapsed: %.8f\n", os.clock() - start)) 20 | -------------------------------------------------------------------------------- /test/benchmark/map_numeric.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | import time 4 | 5 | start = time.clock() 6 | 7 | map = {} 8 | 9 | for i in range(1, 1000001): 10 | map[i] = i 11 | 12 | sum = 0 13 | for i in range(1, 1000001): 14 | sum = sum + map[i] 15 | print(sum) 16 | 17 | for i in range(1, 1000001): 18 | del map[i] 19 | 20 | print("elapsed: " + str(time.clock() - start)) -------------------------------------------------------------------------------- /test/benchmark/map_numeric.rb: -------------------------------------------------------------------------------- 1 | start = Time.now 2 | 3 | map = Hash.new 4 | 5 | for i in (1..1000000) 6 | map[i] = i 7 | end 8 | 9 | sum = 0 10 | for i in (1..1000000) 11 | sum = sum + map[i] 12 | end 13 | puts sum 14 | 15 | for i in (1..1000000) 16 | map.delete(i) 17 | end 18 | 19 | puts "elapsed: " + (Time.now - start).to_s 20 | -------------------------------------------------------------------------------- /test/benchmark/test_benchmark.lily: -------------------------------------------------------------------------------- 1 | import (Interpreter, 2 | TestCase) "../t/testing" 3 | 4 | class TestBenchmarks < TestCase 5 | { 6 | private define run_bench(name: String) 7 | { 8 | var t = Interpreter() 9 | 10 | t.parse_string("test\/benchmark\/__run__.lily", """ 11 | import sys 12 | sys.argv = ["", "--quiet"] 13 | 14 | import (bench_test) \ 15 | """ ++ name) 16 | 17 | assert_parse_string(t, "bench_test()") 18 | } 19 | 20 | public define test_binary_trees 21 | { 22 | run_bench("binary_trees") 23 | } 24 | 25 | public define test_fib 26 | { 27 | run_bench("fib") 28 | } 29 | 30 | public define test_for 31 | { 32 | run_bench("for") 33 | } 34 | 35 | public define test_map_numeric 36 | { 37 | run_bench("map_numeric") 38 | } 39 | 40 | public define test_map_string 41 | { 42 | run_bench("map_string") 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /test/call/test_bad_keyargs.lily: -------------------------------------------------------------------------------- 1 | import (Interpreter, 2 | TestCase) "../t/testing" 3 | 4 | class TestBadKeyarg < TestCase 5 | { 6 | public define test_keyarg_rules 7 | { 8 | var t = Interpreter() 9 | 10 | # keyarg rules (no posargs after keyargs) 11 | # Blocked to make keyargs easier. 12 | 13 | assert_parse_fails(t, """\ 14 | SyntaxError: Positional argument after keyword argument.\n \ 15 | from [test]:5:\n\ 16 | """, 17 | """ 18 | define f(:a a: Integer, :b b: String) { 19 | } 20 | 21 | f(:b 1, 5) 22 | """) 23 | 24 | # keyarg rules (no keyargs with forward) 25 | # Blocked to make keyargs easier, and limited use. 26 | 27 | assert_parse_fails(t, """\ 28 | SyntaxError: Forward declarations not allowed to have keyword arguments.\n \ 29 | from [test]:1:\n\ 30 | """, 31 | """\ 32 | forward define f(:a Integer) { ... } 33 | """) 34 | 35 | # keyarg rules (no duplicates) 36 | 37 | assert_parse_fails(t, """\ 38 | SyntaxError: A keyword named :a has already been declared.\n \ 39 | from [test]:1:\n\ 40 | """, 41 | """\ 42 | define f(:a a: String, :a b: String) {} 43 | """) 44 | } 45 | 46 | public define test_unsupported 47 | { 48 | var t = Interpreter() 49 | 50 | # unsupported (on plain define) 51 | 52 | assert_parse_fails(t, """\ 53 | SyntaxError: f does not specify any keyword arguments.\n \ 54 | from [test]:3:\n\ 55 | """, 56 | """\ 57 | define f(a: Integer) {} 58 | 59 | f(:a 1) 60 | """) 61 | 62 | # unsupported (on class method) 63 | 64 | assert_parse_fails(t, """\ 65 | SyntaxError: Example.f does not specify any keyword arguments.\n \ 66 | from [test]:5:\n\ 67 | """, 68 | """\ 69 | class Example { 70 | public define f(a: Integer) {} 71 | } 72 | 73 | Example().f(:a 1) 74 | """) 75 | 76 | # unsupported (on enum method) 77 | 78 | assert_parse_fails(t, """\ 79 | SyntaxError: Example.f does not specify any keyword arguments.\n \ 80 | from [test]:7:\n\ 81 | """, 82 | """\ 83 | enum Example { 84 | One, 85 | Two 86 | define f(a: Integer) {} 87 | } 88 | 89 | One.f(:a 1) 90 | """) 91 | 92 | # unsupported (on lambda) 93 | 94 | assert_parse_fails(t, """\ 95 | SyntaxError: v is not capable of receiving keyword arguments.\n \ 96 | from [test]:3:\n\ 97 | """, 98 | """\ 99 | var v = (|a: Integer| 0 ) 100 | 101 | v(:a 1) 102 | """) 103 | 104 | # unsupported (on nested define) 105 | 106 | assert_parse_fails(t, """\ 107 | SyntaxError: g is not capable of receiving keyword arguments.\n \ 108 | from [test]:4:\n\ 109 | """, 110 | """\ 111 | define f(:a a: Integer) { 112 | var g = f 113 | define h { 114 | g(:a 1) 115 | } 116 | } 117 | """) 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /test/call/test_bad_optargs.lily: -------------------------------------------------------------------------------- 1 | import (Interpreter, 2 | TestCase) "../t/testing" 3 | 4 | class TestBadOptargs < TestCase 5 | { 6 | public define test_bad_value 7 | { 8 | var t = Interpreter() 9 | 10 | # bad value (enum given non-member) 11 | 12 | assert_parse_fails(t, """\ 13 | SyntaxError: what has not been declared.\n \ 14 | from [test]:6:\n\ 15 | """, 16 | """\ 17 | enum Test { 18 | One, 19 | Two 20 | } 21 | 22 | define f(a: *Test = what) 23 | """) 24 | 25 | # bad value (Boolean given non-Boolean) 26 | 27 | assert_parse_fails(t, """\ 28 | SyntaxError: hello has not been declared.\n \ 29 | from [test]:1:\n\ 30 | """, 31 | """\ 32 | define f(a: *Boolean=hello) { } 33 | """) 34 | } 35 | 36 | public define test_optarg_rules 37 | { 38 | var t = Interpreter() 39 | 40 | # optarg rules (no default arguments for lambdas) 41 | 42 | assert_parse_fails(t, """\ 43 | SyntaxError: Expected 'a label', not '*'.\n \ 44 | from [test]:1:\n\ 45 | """, 46 | """\ 47 | var v = (|a: *Integer=10| a ) 48 | """) 49 | 50 | # optarg rules (no optargs for variants) 51 | # Blocked because these aren't functions. 52 | 53 | assert_parse_fails(t, """\ 54 | SyntaxError: Variant types cannot have default values.\n \ 55 | from [test]:2:\n\ 56 | """, 57 | """\ 58 | enum Test { 59 | Check(*Integer), 60 | Blank 61 | } 62 | """) 63 | 64 | # optarg rules (default value cannot be a lambda) 65 | 66 | assert_parse_fails(t, """\ 67 | SyntaxError: Not allowed to use a lambda here.\n \ 68 | from [test]:1:\n\ 69 | """, 70 | """\ 71 | define f(a: *Integer=(|| 10)()) { 72 | 73 | } 74 | 75 | f(1) 76 | """) 77 | 78 | # optarg rules (optarg self reference in constructor) 79 | 80 | assert_parse_fails(t, """\ 81 | SyntaxError: Constructor for class Test is not initialized.\n \ 82 | from [test]:1:\n\ 83 | """, 84 | """\ 85 | class Test(a: *Test=Test()) { 86 | } 87 | """) 88 | 89 | # optarg rules (optarg self reference in define) 90 | 91 | assert_parse_fails(t, """\ 92 | SyntaxError: Attempt to use uninitialized value 'f'.\n \ 93 | from [test]:1:\n\ 94 | """, 95 | """\ 96 | define f(a: *Integer=f(1)): Integer { 97 | return 10 98 | } 99 | """) 100 | 101 | # optarg rules (required argument after optional) 102 | # Blocked because it makes the type system more difficult. 103 | 104 | assert_parse_fails(t, """\ 105 | SyntaxError: Non-optional argument follows optional argument.\n \ 106 | from [test]:1:\n\ 107 | """, 108 | """\ 109 | define f(a: *Integer=10, b: Integer) { } 110 | """) 111 | 112 | # optarg rules (wrong token before value) 113 | 114 | assert_parse_fails(t, """\ 115 | SyntaxError: Expected '=', not ','.\n \ 116 | from [test]:1:\n\ 117 | """, 118 | """\ 119 | define f(a: *Integer,1) { } 120 | """) 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /test/call/test_call_pipe.lily: -------------------------------------------------------------------------------- 1 | import (Interpreter, 2 | TestCase) "../t/testing" 3 | 4 | class TestCallPipe < TestCase 5 | { 6 | public define test_basic 7 | { 8 | var t = Interpreter() 9 | 10 | # basic (pipe to global) 11 | 12 | assert_parse_string(t, """ 13 | define square(a: Integer): Integer { return a * a } 14 | 15 | if (10 |> square) != 100: { 16 | 0 / 0 17 | } 18 | """) 19 | 20 | # basic (chain of pipes) 21 | 22 | assert_parse_string(t, """ 23 | if (10 |> square |> square) != 10000: { 24 | 0 / 0 25 | } 26 | """) 27 | 28 | # basic (order of operations) 29 | 30 | assert_parse_string(t, """ 31 | var order: List[Integer] = [] 32 | 33 | define fn(a: Integer): Integer { 34 | order.push(a) 35 | return a 36 | } 37 | 38 | fn(3) |> [fn(1), fn(2)].push 39 | 40 | if order != [1, 2, 3]: { 41 | 0 / 0 42 | } 43 | """) 44 | } 45 | 46 | public define test_pipe_self 47 | { 48 | var t = Interpreter() 49 | 50 | # pipe self (adding self when outside class) 51 | 52 | assert_parse_string(t, """ 53 | var l: List[Integer] = [] 54 | 55 | 10 |> l.push 56 | 57 | if l != [10]: { 58 | 0 / 0 59 | } 60 | """) 61 | 62 | # pipe self (adding self when inside class) 63 | 64 | assert_parse_string(t, """ 65 | var check = [0] 66 | class Test { 67 | public define f(target: List[Integer]) { 68 | target.push(target.size()) 69 | } 70 | 71 | check |> f 72 | } 73 | 74 | Test() 75 | 76 | if check != [0, 1]: { 77 | 0 / 0 78 | } 79 | """) 80 | } 81 | 82 | public define test_pipe_variant 83 | { 84 | var t = Interpreter() 85 | 86 | assert_parse_string(t, """ 87 | if (10 |> Some).unwrap() != 10: { 88 | 0 / 0 89 | } 90 | """) 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /test/call/test_varargs.lily: -------------------------------------------------------------------------------- 1 | import (Interpreter, 2 | TestCase) "../t/testing" 3 | 4 | class TestVarargs < TestCase 5 | { 6 | public define test_basic 7 | { 8 | var t = Interpreter() 9 | 10 | # basic (vararg to global define) 11 | 12 | assert_parse_string(t, """ 13 | define padded_vararg_total(unused: String, args: Integer...): Integer 14 | { 15 | var va_total = 0 16 | args.each(|a| va_total += a ) 17 | return va_total 18 | } 19 | 20 | define just_vararg_total(args: Integer...): Integer 21 | { 22 | var va_total = 0 23 | args.each(|a| va_total += a ) 24 | return va_total 25 | } 26 | 27 | if padded_vararg_total("", 1, 2, 3) != 6: { 28 | 0 / 0 29 | } 30 | 31 | if padded_vararg_total("") != 0: { 32 | 0 / 0 33 | } 34 | 35 | if just_vararg_total(1, 2, 3) != 6: { 36 | 0 / 0 37 | } 38 | 39 | if just_vararg_total() != 0: { 40 | 0 / 0 41 | } 42 | """) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /test/constant/test_verify_constant.lily: -------------------------------------------------------------------------------- 1 | import (Interpreter, 2 | TestCase) "../t/testing" 3 | 4 | class TestVerifyConstant < TestCase 5 | { 6 | public define test_basics 7 | { 8 | var t = Interpreter() 9 | 10 | # basics (verify assignment) 11 | 12 | assert_parse_string(t, """ 13 | constant integer_a = 10 14 | 15 | if integer_a != 10: { 16 | 0/0 17 | } 18 | """) 19 | 20 | # basics (multiple constants) 21 | 22 | assert_parse_string(t, """ 23 | constant integer_b = 20, 24 | integer_c = 30 25 | 26 | if integer_b != 20 && 27 | integer_c != 30: { 28 | 0/0 29 | } 30 | """) 31 | 32 | # basics (all other constant types) 33 | 34 | assert_parse_string(t, """ 35 | constant double_a = 1.0, 36 | string_a = "2" 37 | 38 | if double_a != 1.0 || 39 | string_a != "2": { 40 | 0/0 41 | } 42 | """) 43 | 44 | # basics (truthy constant fold) 45 | 46 | assert_parse_string(t, """ 47 | constant truthy = 1 48 | 49 | if truthy: { 50 | else: 51 | 0 / 0 52 | } 53 | """) 54 | 55 | # basics (falsey constant fold) 56 | 57 | assert_parse_string(t, """ 58 | constant falsey = 0 59 | 60 | if falsey: { 61 | 0 / 0 62 | } 63 | """) 64 | 65 | # basics (tuple subscript by constant) 66 | 67 | assert_parse_string(t, """ 68 | var t = <[1, "2", 3.0]> 69 | constant index = 2 70 | 71 | if t[index] != 3.0: { 72 | 0 / 0 73 | } 74 | """) 75 | 76 | # basics (constant with open forward) 77 | 78 | assert_parse_string(t, """ 79 | forward define f { ... } 80 | constant x = 1 81 | define f {} 82 | """) 83 | } 84 | 85 | public define test_failure 86 | { 87 | var t = Interpreter() 88 | 89 | # failure (specify a type) 90 | 91 | assert_parse_fails(t, """\ 92 | SyntaxError: Constants cannot explicitly specify a type.\n \ 93 | from [test]:1:\n\ 94 | """, 95 | """\ 96 | constant a: Integer = 1 97 | """) 98 | 99 | # failure (no initialization) 100 | 101 | assert_parse_fails(t, """\ 102 | SyntaxError: An initialization expression is required here.\n \ 103 | from [test]:2:\n\ 104 | """, 105 | """\ 106 | constant a 107 | """) 108 | 109 | # failure (init by another constant) 110 | 111 | assert_parse_fails(t, """\ 112 | SyntaxError: Expected a Double, Integer, or String literal, not 'a label'.\n \ 113 | from [test]:2:\n\ 114 | """, 115 | """\ 116 | constant a = 10 117 | constant b = a 118 | """) 119 | 120 | # failure (bad value) 121 | 122 | assert_parse_fails(t, """\ 123 | SyntaxError: Expected a Double, Integer, or String literal, not 'a lambda'.\n \ 124 | from [test]:1:\n\ 125 | """, 126 | """\ 127 | constant a = (|b: Integer| b + b ) 128 | """) 129 | 130 | # failure (bad location) 131 | 132 | assert_parse_fails(t, """\ 133 | SyntaxError: Cannot declare a constant here.\n \ 134 | from [test]:2:\n\ 135 | """, 136 | """\ 137 | if 1: { 138 | constant a: Integer = 1 139 | } 140 | """) 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /test/coverage/test_dynaload.lily: -------------------------------------------------------------------------------- 1 | import (Interpreter, 2 | TestCase) "../t/testing" 3 | 4 | class TestDynaload < TestCase 5 | { 6 | public define test_dynaload_class_names 7 | { 8 | var t = Interpreter() 9 | 10 | assert_parse_string(t, """ 11 | try: { 12 | 1 / 0 13 | except Exception as e: 14 | var v = "{0}".format(e) 15 | if v.starts_with("") == false: { 17 | raise Exception("Interpolation failed.") 18 | } 19 | } 20 | """) 21 | } 22 | 23 | public define test_dynaload_name_clash 24 | { 25 | var t = Interpreter() 26 | 27 | assert_parse_string(t, """ 28 | define ends_with(a: String, b: String) { } 29 | 30 | var s = "".ends_with("") 31 | """) 32 | } 33 | 34 | public define test_dynaload_restores_generics 35 | { 36 | var t = Interpreter() 37 | 38 | assert_parse_string(t, """ 39 | class Test[A, B] 40 | { 41 | Some(1).is_none() 42 | 43 | public define abc(key: A): Test[A, B] 44 | { 45 | return self 46 | } 47 | } 48 | """) 49 | } 50 | 51 | public define test_dynaload_save_bytestring 52 | { 53 | var t = Interpreter() 54 | 55 | assert_parse_string(t, """ 56 | B"0".encode("") 57 | B"0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" 58 | """) 59 | } 60 | 61 | public define test_dynaload_then_property 62 | { 63 | var t = Interpreter() 64 | 65 | assert_parse_string(t, """ 66 | class Test { 67 | public var @contents = [1, 2, 3] 68 | 69 | public define check(source: String) { 70 | var f = File.open(source, "r") 71 | var lines: List[String] = [] 72 | 73 | f.each_line(|l| l.encode().unwrap() |> lines.push ) 74 | 75 | @contents.size() 76 | } 77 | } 78 | """) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /test/file_for_io.txt: -------------------------------------------------------------------------------- 1 | 12345 2 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. In efficitur lacinia tortor, ac commodo metus vulputate non. 3 | Praesent👏blandit👏purus👏sit👏amet👏urna👏faucibus👏dapibus. 4 | Nulla lacus turpis, ornare at nisl id, rhoncus venenatis urna. 5 | Suspendisse leo nulla, interdum eget erat at, vehicula gravida enim. 6 | Integer ultricies metus nisl, nec tempus nisi imperdiet eu. Suspendisse tincidunt id odio ac consequat. Proin faucibus nisi a elit egestas eleifend. Nullam at fermentum nisl, ut pharetra lorem. Sed blandit ante in sapien efficitur, sit amet accumsan enim dictum. Praesent blandit lorem nec orci interdum, eu pharetra felis tincidunt. Vivamus vestibulum risus non accumsan vestibulum. 7 | -------------------------------------------------------------------------------- /test/forward/test_forward.lily: -------------------------------------------------------------------------------- 1 | import (Interpreter, 2 | TestCase) "../t/testing" 3 | 4 | class TestForward < TestCase 5 | { 6 | public define test_method_call 7 | { 8 | var t = Interpreter() 9 | assert_parse_string(t, """ 10 | class Example(public var @x: Integer) { 11 | forward public define square(Integer): Integer { ... } 12 | 13 | public define square_x { 14 | @x = square(@x) 15 | } 16 | 17 | public define square(x: Integer): Integer { return x * x } 18 | } 19 | 20 | var v = Example(10) 21 | v.square_x() 22 | v.square_x() 23 | if v.x != 10000: { 24 | raise Exception("Failed.") 25 | } 26 | """) 27 | } 28 | 29 | public define test_optarg_vararg 30 | { 31 | var t = Interpreter() 32 | assert_parse_string(t, """ 33 | var results: List[Integer] = [] 34 | 35 | forward define varopt(*Integer, *Integer, *Integer...): Integer { ... } 36 | 37 | results = [varopt(), 38 | varopt(5), 39 | varopt(5, 10), 40 | varopt(5, 10, 20, 30)] 41 | 42 | if results != [6, 10, 18, 65]: { 43 | raise Exception("Failed.") 44 | } 45 | 46 | define varopt(x: *Integer = 1, 47 | y: *Integer = 2, 48 | extra: *Integer... = [3]): Integer 49 | { 50 | return x + y + extra.fold(0, (|a, b| a + b) ) 51 | } 52 | """) 53 | } 54 | 55 | public define test_recursion 56 | { 57 | var t = Interpreter() 58 | assert_parse_string(t, """ 59 | var v: List[Integer] = [] 60 | forward define g(Integer) { ... } 61 | 62 | define f(n: Integer) { 63 | if n: { 64 | v.push(n) 65 | g(n - 1) 66 | } 67 | } 68 | 69 | define g(n: Integer) { 70 | if n: { 71 | v.push(n) 72 | f(n - 1) 73 | } 74 | } 75 | 76 | f(10) 77 | 78 | if v != [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]: { 79 | raise Exception("Failed.") 80 | } 81 | """) 82 | } 83 | 84 | public define test_forward_class_var 85 | { 86 | var t = Interpreter() 87 | 88 | assert_parse_string(t, """ 89 | forward class Test { ... } 90 | 91 | var v: List[Test] = [] 92 | 93 | class Test {} 94 | """) 95 | } 96 | 97 | public define test_forward_class_vars 98 | { 99 | var t = Interpreter() 100 | 101 | assert_parse_string(t, """ 102 | forward class Test { ... } 103 | 104 | var v: List[Test] = [] 105 | 106 | class Test { 107 | public define f: Integer { return 10 } 108 | } 109 | 110 | var result = v.push(Test())[0].f() 111 | 112 | if result != 10: { 113 | raise Exception("Failed.") 114 | } 115 | """) 116 | 117 | t = Interpreter() 118 | 119 | assert_parse_string(t, """ 120 | forward class TestGeneric[A] { ... } 121 | 122 | var v: List[TestGeneric[Integer]] = [] 123 | 124 | class TestGeneric[A](a: A) { 125 | public var @a = a 126 | } 127 | 128 | v.push(TestGeneric(10)) 129 | 130 | var result = v[0].a 131 | 132 | if result != 10: { 133 | raise Exception("Failed.") 134 | } 135 | """) 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /test/gc/test_verify_gc.lily: -------------------------------------------------------------------------------- 1 | import (Interpreter, 2 | TestCase) "../t/testing" 3 | 4 | class TestVerifyGc < TestCase 5 | { 6 | public define test_circular_at_exit 7 | { 8 | var t = Interpreter() 9 | 10 | # circular at exit (self-reference through Option) 11 | 12 | assert_parse_string(t, """ 13 | class Example { 14 | public var @a: Option[Example] = None 15 | } 16 | 17 | var v = Example() 18 | v.a = Some(v) 19 | """) 20 | } 21 | 22 | public define test_deref 23 | { 24 | var t = Interpreter() 25 | 26 | # deref (drop Coroutine by deref) 27 | # Most Coroutine values are dropped by the gc because they have a 28 | # reference to themselves on their stack. 29 | 30 | assert_parse_string(t, """ 31 | import (Coroutine) coroutine 32 | 33 | define base_one(co: Coroutine[Integer, Integer]): Integer { return 0 } 34 | 35 | var other_co = Coroutine.build(base_one) 36 | 37 | define co_base(co: Coroutine[Integer, Integer]): Integer { 38 | co = other_co 39 | return 10 40 | } 41 | 42 | define f { 43 | var local_co = Coroutine.build(co_base) 44 | local_co.resume_with(0) 45 | } 46 | f() 47 | f() 48 | """) 49 | } 50 | 51 | public define test_gc_drop 52 | { 53 | var t = Interpreter() 54 | 55 | # gc drop (drop Function closures) 56 | 57 | assert_parse_string(t, """ 58 | define no_op {} 59 | 60 | define closure_base: Function() { 61 | var inner_fn = no_op 62 | define a { 63 | inner_fn = a 64 | } 65 | return a 66 | } 67 | 68 | define f { 69 | var lst: List[Function()] = [] 70 | 71 | for i in 0...5: { 72 | var local_fn = closure_base() 73 | local_fn() 74 | lst.push(local_fn) 75 | } 76 | 77 | for i in 0...lst.size() - 1: { 78 | lst[i] = no_op 79 | } 80 | } 81 | 82 | f() 83 | f() 84 | """) 85 | 86 | # gc drop (drop Coroutine holding derefable value) 87 | 88 | t = Interpreter() 89 | assert_parse_string(t, """ 90 | import (Coroutine) coroutine 91 | 92 | define f(co: Coroutine[Integer, List[Function(Integer)]]): Integer { 93 | co.yield(1) 94 | return 1 95 | } 96 | 97 | var co = Coroutine.build(f) 98 | co.resume_with([(|x| 10)]) 99 | """) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /test/import/enum_export_main.lily: -------------------------------------------------------------------------------- 1 | import enum_exporter 2 | import enum_export_user 3 | 4 | var v = enum_export_user.User(enum_export_user.enum_exporter.One(1), enum_exporter.Two("2")) -------------------------------------------------------------------------------- /test/import/enum_export_user.lily: -------------------------------------------------------------------------------- 1 | import enum_exporter 2 | 3 | class User(left: enum_exporter.Exporter, right: enum_exporter.Exporter) 4 | { 5 | public var @left = left 6 | public var @right = right 7 | } 8 | -------------------------------------------------------------------------------- /test/import/enum_exporter.lily: -------------------------------------------------------------------------------- 1 | enum Exporter { 2 | One(Integer), 3 | Two(String) 4 | } 5 | -------------------------------------------------------------------------------- /test/import/nest/deep_target.lily: -------------------------------------------------------------------------------- 1 | var v = 10 2 | -------------------------------------------------------------------------------- /test/import/nest/rooted_include.lily: -------------------------------------------------------------------------------- 1 | import fakepackage 2 | -------------------------------------------------------------------------------- /test/import/packages/fakepackage/src/fakepackage.lily: -------------------------------------------------------------------------------- 1 | var v = 10 2 | -------------------------------------------------------------------------------- /test/import/rooted_base.lily: -------------------------------------------------------------------------------- 1 | import "nest/rooted_include" 2 | -------------------------------------------------------------------------------- /test/method/test_boolean.lily: -------------------------------------------------------------------------------- 1 | import (Interpreter, 2 | TestCase) "../t/testing" 3 | 4 | class TestBooleanMethods < TestCase 5 | { 6 | public define test_to_i 7 | { 8 | assert_equal(true.to_i(), 1) 9 | assert_equal(false.to_i(), 0) 10 | } 11 | 12 | public define test_to_s 13 | { 14 | assert_equal(true.to_s(), "true") 15 | assert_equal(false.to_s(), "false") 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /test/method/test_byte.lily: -------------------------------------------------------------------------------- 1 | import (Interpreter, 2 | TestCase) "../t/testing" 3 | 4 | class TestByteMethods < TestCase 5 | { 6 | public define test_to_i 7 | { 8 | assert_equal(0t .to_i(), 0) 9 | assert_equal(255t.to_i(), 255) 10 | assert_equal(128t.to_i(), 128) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test/method/test_bytestring.lily: -------------------------------------------------------------------------------- 1 | import (Interpreter, 2 | TestCase) "../t/testing" 3 | 4 | class TestByteStringMethods < TestCase 5 | { 6 | public define test_each_byte 7 | { 8 | var output: List[Byte] = [] 9 | 10 | B"abc".each_byte(|b| b |> output.push ) 11 | 12 | assert_equal(output, ['a', 'b', 'c']) 13 | } 14 | 15 | public define test_encode 16 | { 17 | assert_equal(B"\195\169".encode("error").unwrap(), "é") 18 | assert_equal(B"" .encode("error").unwrap(), "") 19 | assert_equal(B"asdf" .encode("error").unwrap(), "asdf") 20 | 21 | B"0" .encode("invalidmode").is_none() |> assert_true 22 | B"\000" .encode("error") .is_none() |> assert_true 23 | B"\255\255\255".encode("error") .is_none() |> assert_true 24 | } 25 | 26 | public define test_size 27 | { 28 | assert_equal(B"abc" .size(), 3) 29 | assert_equal("abc".to_bytestring().size(), 3) 30 | } 31 | 32 | public define test_slice 33 | { 34 | assert_equal(B"abc".slice(), B"abc") 35 | assert_equal(B"abc".slice(0, -1), B"ab") 36 | assert_equal(B"abc".slice(1, 2), B"b") 37 | assert_equal(B"abc".slice(2, 1), B"") 38 | assert_equal(B"abc".slice(1, 5), B"") 39 | assert_equal(B"abc".slice(0, 3), B"abc") 40 | assert_equal(B"abc".slice(-4, 2), B"") 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /test/method/test_hash.lily: -------------------------------------------------------------------------------- 1 | import (Interpreter, 2 | TestCase) "../t/testing" 3 | 4 | class TestHashMethods < TestCase 5 | { 6 | public define test_clear 7 | { 8 | var v = [1 => 1, 2 => 2] 9 | v.clear() 10 | assert_equal(v, []) 11 | 12 | var v2 = [1 => 1] 13 | v2.clear() 14 | v2.clear() 15 | assert_equal(v2, []) 16 | 17 | assert_raises( 18 | "RuntimeError: Cannot remove key from hash during iteration.", 19 | (|| var v3 = [1 => 1] 20 | v3.each_pair(|k_, v_| v3.clear()) )) 21 | } 22 | 23 | public define test_delete 24 | { 25 | var v = [1 => 1, 2 => 2, 3 => 3] 26 | v.delete(1) 27 | v.delete(2) 28 | assert_equal(v, [3 => 3]) 29 | 30 | var v2 = [1 => 1, 2 => 2, 3 => 3] 31 | v2.delete(-1) 32 | assert_equal(v2, [1 => 1, 2 => 2, 3 => 3]) 33 | } 34 | 35 | private define each_pair_empty 36 | { 37 | var v: Hash[Integer, Integer] = [] 38 | v.each_pair(|k_, v_| 0 / 0) 39 | } 40 | 41 | private define each_pair_order 42 | { 43 | var v = [100 => 0, 25 => 1, 50 => 2] 44 | var entries = [0, 0, 0] 45 | 46 | v.each_value(|v_| entries[v_] = 1 ) 47 | assert_equal(entries, [1, 1, 1]) 48 | } 49 | 50 | private define each_value_empty 51 | { 52 | var v: Hash[Integer, Integer] = [] 53 | v.each_value(|v_| 0 / 0) 54 | } 55 | 56 | private define each_value_order 57 | { 58 | var v = [0 => 0, 1 => -1, 2 => -2] 59 | var entries = [0, 0, 0] 60 | 61 | v.each_pair(|k_, v_| entries[k_] = v_ ) 62 | assert_equal(entries, [0, -1, -2]) 63 | } 64 | 65 | public define test_each_pair 66 | { 67 | each_pair_empty() 68 | each_pair_order() 69 | } 70 | 71 | public define test_each_value 72 | { 73 | each_value_empty() 74 | each_value_order() 75 | } 76 | 77 | public define test_get 78 | { 79 | var v = [1 => 100, 2 => 200, 3 => 300].get(5) 80 | v.is_none() |> assert_true 81 | 82 | var v2 = [1 => 100, 2 => 200, 3 => 300].get(1).unwrap() 83 | assert_equal(v2, 100) 84 | } 85 | 86 | public define test_has_key 87 | { 88 | var v = [1 => 1].has_key(1) 89 | v |> assert_true 90 | 91 | var v2 = [1 => 1].has_key(2) 92 | v2 |> assert_false 93 | } 94 | 95 | public define test_keys 96 | { 97 | var v = [1 => 1, 2 => 2] 98 | var entries = [0, 0, 0] 99 | 100 | v.keys().each(|e| entries[e] = 1 ) 101 | assert_equal(entries, [0, 1, 1]) 102 | } 103 | 104 | private define map_values_empty 105 | { 106 | var v: Hash[Integer, Integer] = [] 107 | v.map_values(|v_| 0 / 0) 108 | } 109 | 110 | public define test_map_values 111 | { 112 | map_values_empty() 113 | var v = [1 => "1", 2 => "a", 3 => "2"].map_values(String.parse_i) 114 | 115 | assert_equal(v, [1 => Some(1), 2 => None, 3 => Some(2)]) 116 | } 117 | 118 | public define test_merge 119 | { 120 | var v = [1 => 1, 2 => 2] 121 | var v2 = [3 => 3] 122 | 123 | assert_equal(Hash.merge(v, v2), [1 => 1, 2 => 2, 3 => 3]) 124 | 125 | var v3 = [1 => 1, 2 => 2] 126 | var v4 = [2 => 4] 127 | 128 | assert_equal(v3.merge(v4), [1 => 1, 2 => 4]) 129 | } 130 | 131 | public define test_reject 132 | { 133 | var v = [1 => 1, 2 => 2, 3 => 3].reject(|k_, v_| (k_ % 2) == 1) 134 | assert_equal(v, [2 => 2]) 135 | } 136 | 137 | public define test_select 138 | { 139 | var v = [1 => 1, 2 => 2, 3 => 3].select(|k_, v_| true) 140 | assert_equal(v, [1 => 1, 2 => 2, 3 => 3]) 141 | } 142 | 143 | public define test_size 144 | { 145 | var v: Hash[Integer, Integer] = [] 146 | assert_equal(v.size(), 0) 147 | 148 | var v2 = [1 => 1] 149 | assert_equal(v2.size(), 1) 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /test/method/test_integer.lily: -------------------------------------------------------------------------------- 1 | import (Interpreter, 2 | TestCase) "../t/testing" 3 | 4 | class TestIntegerMethods < TestCase 5 | { 6 | public define test_to_binary 7 | { 8 | assert_equal(0.to_binary(), "0b0") 9 | assert_equal(127.to_binary(), "0b1111111") 10 | assert_equal(-127.to_binary(), "-0b1111111") 11 | assert_equal(128.to_binary(), "0b10000000") 12 | assert_equal(0x7fffffffffffffff.to_binary(), 13 | "0b111111111111111111111111111111111111111111111111111111111111111") 14 | assert_equal(-0x8000000000000000.to_binary(), 15 | "-0b1000000000000000000000000000000000000000000000000000000000000000") 16 | } 17 | 18 | public define test_to_bool 19 | { 20 | assert_equal(1 .to_bool(), true) 21 | assert_equal(0 .to_bool(), false) 22 | assert_equal(-1.to_bool(), true) 23 | } 24 | 25 | public define test_to_byte 26 | { 27 | assert_equal(0 .to_byte(), 0t) 28 | assert_equal(255.to_byte(), 255t) 29 | } 30 | 31 | public define test_to_d 32 | { 33 | assert_equal(500 .to_d(), 500.0) 34 | assert_equal(-250.to_d(), -250.0) 35 | assert_equal(0 .to_d(), 0.0) 36 | } 37 | 38 | public define test_to_hex 39 | { 40 | assert_equal(0x0.to_hex(), "0x0") 41 | assert_equal(0xff.to_hex(), "0xff") 42 | assert_equal(-0xff.to_hex(), "-0xff") 43 | assert_equal(0x7fffffffffffffff.to_hex(), 44 | "0x7fffffffffffffff") 45 | assert_equal(-0x8000000000000000.to_hex(), 46 | "-0x8000000000000000") 47 | } 48 | 49 | public define test_to_octal 50 | { 51 | assert_equal(0c0.to_octal(), "0c0") 52 | assert_equal(0c77.to_octal(), "0c77") 53 | assert_equal(-0c77.to_octal(), "-0c77") 54 | assert_equal(0x7fffffffffffffff.to_octal(), 55 | "0c777777777777777777777") 56 | assert_equal(-0x8000000000000000.to_octal(), 57 | "-0c1000000000000000000000") 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /test/method/test_option.lily: -------------------------------------------------------------------------------- 1 | import (Interpreter, 2 | TestCase) "../t/testing" 3 | 4 | class TestOptionMethods < TestCase 5 | { 6 | public define test__is_none 7 | { 8 | None.is_none() |> assert_true 9 | Some(1).is_none() |> assert_false 10 | } 11 | 12 | public define test__is_some 13 | { 14 | None .is_some() |> assert_false 15 | Some(1).is_some() |> assert_true 16 | } 17 | 18 | public define test_and 19 | { 20 | None .and(None) .is_none() |> assert_true 21 | None .and(Some(1)).is_none() |> assert_true 22 | Some(1).and(None) .is_none() |> assert_true 23 | Some(1).and(Some(2)).is_none() |> assert_false 24 | } 25 | 26 | public define test_and_then 27 | { 28 | var v = None.@(Option[Integer]).and_then(|x| Some(0 / 0)) 29 | v.is_none() |> assert_true 30 | 31 | var v2 = Some(4).and_then(|x| Some(x * x)).unwrap() 32 | assert_equal(v2, 16) 33 | } 34 | 35 | public define test_map 36 | { 37 | var v: Option[Integer] = None 38 | v.map(|x| 0 / 0).is_none() |> assert_true 39 | 40 | var v2 = Some([1, 2, 3]).map(List.size).unwrap() 41 | assert_equal(v2, 3) 42 | } 43 | 44 | public define test_or 45 | { 46 | var v = None.@(Option[Integer]).or(None) 47 | v.is_none() |> assert_true 48 | 49 | var v2 = None.@(Option[Integer]).or(Some(2)).unwrap() 50 | assert_equal(v2, 2) 51 | 52 | var v3 = Some(1).or(None).unwrap() 53 | assert_equal(v3, 1) 54 | 55 | var v4 = Some(1).or(Some(2)).unwrap() 56 | assert_equal(v4, 1) 57 | } 58 | 59 | public define test_or_else 60 | { 61 | var v = None.@(Option[Integer]).or_else(|| Some(1)).unwrap() 62 | assert_equal(v, 1) 63 | 64 | var v2 = Some(1).or_else(|| Some(0 / 0)).unwrap() 65 | assert_equal(v2, 1) 66 | } 67 | 68 | public define test_unwrap 69 | { 70 | assert_raises( 71 | "ValueError: unwrap called on None.", 72 | (|| None.@(Option[Integer]).unwrap() )) 73 | 74 | var v = Some(1).unwrap() 75 | assert_equal(v, 1) 76 | } 77 | 78 | public define test_unwrap_or 79 | { 80 | var v = None.@(Option[Integer]).unwrap_or(5) 81 | assert_equal(v, 5) 82 | 83 | assert_equal(Some(1).unwrap_or(5), 1) 84 | } 85 | 86 | public define test_unwrap_or_else 87 | { 88 | var v = None.@(Option[Integer]).unwrap_or_else(|| 5) 89 | assert_equal(v, 5) 90 | 91 | var v2 = Some(1).unwrap_or_else(|| 0 / 0) 92 | assert_equal(v2, 1) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /test/method/test_result.lily: -------------------------------------------------------------------------------- 1 | import (Interpreter, 2 | TestCase) "../t/testing" 3 | 4 | class TestResultMethods < TestCase 5 | { 6 | public define test__is_failure 7 | { 8 | Failure(1).is_failure() |> assert_true 9 | Success(2).is_failure() |> assert_false 10 | } 11 | 12 | public define test__is_success 13 | { 14 | Failure(1).is_success() |> assert_false 15 | Success(2).is_success() |> assert_true 16 | } 17 | 18 | public define test_failure 19 | { 20 | assert_equal(Failure(1).failure().unwrap(), 1) 21 | Success(2).failure().is_none() |> assert_true 22 | } 23 | 24 | public define test_success 25 | { 26 | Failure(1).success().is_none() |> assert_true 27 | assert_equal(Success(2).success().unwrap(), 2) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/prelude/test_pkg_fs.lily: -------------------------------------------------------------------------------- 1 | import (Interpreter, 2 | TestCase) "../t/testing", fs 3 | 4 | class TestPkgFs < TestCase 5 | { 6 | public define test_all 7 | { 8 | # These methods are tested together to make sure the filesystem is put 9 | # back the way it started. Also, less actions are made this way. 10 | var long_name = List.repeat(15, "qwertyuiop").join("") 11 | var original_dir = fs.current_dir() 12 | 13 | # Start with the basics. 14 | assert_not_equal(fs.current_dir(), "") 15 | 16 | var long_dir = original_dir ++ "\/" ++ long_name 17 | var t = Interpreter() 18 | 19 | # Create a long directory to check fs.current_dir too. 20 | fs.create_dir(long_name, 0c777) 21 | 22 | # Switch to a long directory. 23 | fs.change_dir(long_dir) 24 | assert_equal(fs.current_dir(), long_dir) 25 | 26 | # Get the current directory through a subinterpreter. 27 | # A subinterpreter is used to make sure fs.current_dir handles growing 28 | # the vm's buffer for long paths correctly. 29 | assert_parse_string(t, """ 30 | import fs 31 | 32 | if fs.current_dir() != "%1": { 33 | 0 / 0 34 | } 35 | """.replace("%1", long_dir) 36 | .replace("\\", "\\\\")) 37 | 38 | # Switch back out of it. 39 | fs.change_dir(original_dir) 40 | assert_equal(fs.current_dir(), original_dir) 41 | 42 | # Delete it. 43 | try: { 44 | fs.remove_dir(long_dir) 45 | except IOError: 46 | assert_true(false) 47 | } 48 | 49 | var message = "" 50 | 51 | # Make sure it's gone. 52 | try: { 53 | fs.change_dir(long_dir) 54 | except IOError as e: 55 | message = e.message 56 | } 57 | 58 | message.starts_with("Errno ") |> assert_true 59 | 60 | var dir_tail = original_dir.split("\/")[-1] 61 | var same_dir = original_dir ++ "\/..\/" ++ dir_tail 62 | 63 | # Try to create the current directory. 64 | try: { 65 | fs.create_dir(same_dir) 66 | except IOError as e: 67 | message = e.message 68 | } 69 | 70 | message.starts_with("Errno ") |> assert_true 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /test/prelude/test_pkg_math.lily: -------------------------------------------------------------------------------- 1 | import (Interpreter, 2 | TestCase) "../t/testing", math 3 | 4 | class TestPkgMath < TestCase 5 | { 6 | public define test_abs 7 | { 8 | assert_equal(math.abs(-34), 34) 9 | assert_equal(math.abs(-3000000000), 3000000000) 10 | } 11 | 12 | public define test_acos 13 | { 14 | assert_near_equal(math.acos(math.pi / 4.0), 0.6674572160283838) 15 | } 16 | 17 | public define test_asin 18 | { 19 | assert_near_equal(math.asin(math.pi / 4.0), 0.9033391107665127) 20 | } 21 | 22 | public define test_atan 23 | { 24 | assert_near_equal(math.atan(math.pi / 4.0), 0.6657737500283538) 25 | } 26 | 27 | public define test_ceil 28 | { 29 | assert_near_equal(math.ceil(23.4), 24.0) 30 | } 31 | 32 | public define test_constants 33 | { 34 | math.huge 35 | math.infinity 36 | math.nan 37 | } 38 | 39 | public define test_cos 40 | { 41 | assert_near_equal(math.cos(math.pi), -1.0) 42 | } 43 | 44 | public define test_cosh 45 | { 46 | assert_near_equal(math.cosh(math.pi), 11.591953275521519) 47 | } 48 | 49 | public define test_exp 50 | { 51 | assert_near_equal(math.exp(3.0), 20.085536923187668) 52 | } 53 | 54 | public define test_fabs 55 | { 56 | assert_near_equal(math.fabs(-3.0), 3.0) 57 | } 58 | 59 | public define test_floor 60 | { 61 | assert_near_equal(math.floor(23.4), 23.0) 62 | } 63 | 64 | public define test_fmod 65 | { 66 | assert_near_equal(math.fmod(3.0, 2.0), 1.0) 67 | } 68 | 69 | public define test_ldexp 70 | { 71 | assert_near_equal(math.ldexp(5.0, 4), 80.0) 72 | } 73 | 74 | public define test_log 75 | { 76 | assert_near_equal(math.log(math.exp(1.0)), 1.0) 77 | } 78 | 79 | public define test_log10 80 | { 81 | assert_near_equal(math.log10(10.0), 1.0) 82 | } 83 | 84 | public define test_modf 85 | { 86 | var ip_fp = math.modf(23.4) 87 | 88 | assert_near_equal(ip_fp[0], 23.0) 89 | assert_near_equal(ip_fp[1], 0.4) 90 | } 91 | 92 | public define test_pow 93 | { 94 | assert_near_equal(math.pow(2.0, 5.0), 32.0) 95 | 96 | math.pow(999999.0, 99999.0) 97 | |> math.is_infinity 98 | |> assert_true 99 | } 100 | 101 | public define test_sin 102 | { 103 | assert_near_equal(math.sin(math.pi / 2.0), 1.0) 104 | } 105 | 106 | public define test_sinh 107 | { 108 | assert_near_equal(math.sinh(math.pi / 2.0), 2.3012989023072947) 109 | } 110 | 111 | public define test_sqrt 112 | { 113 | assert_near_equal(math.sqrt(25.0), 5.0) 114 | 115 | math.sqrt(-1.0) 116 | |> math.is_nan 117 | |> assert_true 118 | } 119 | 120 | public define test_tan 121 | { 122 | assert_near_equal(math.tan(math.pi / 8.0), 0.41421356237309503) 123 | } 124 | 125 | public define test_tanh 126 | { 127 | assert_near_equal(math.tanh(math.pi / 8.0), 0.3736847479012153) 128 | } 129 | 130 | public define test_to_deg 131 | { 132 | assert_near_equal(math.to_deg(math.pi), 180.0) 133 | } 134 | 135 | public define test_to_rad 136 | { 137 | assert_near_equal(math.to_rad(180.0), math.pi) 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /test/prelude/test_pkg_random.lily: -------------------------------------------------------------------------------- 1 | import (Interpreter, 2 | TestCase) "../t/testing", random 3 | 4 | class TestPkgRandom < TestCase 5 | { 6 | private define random_ctor_no_seed 7 | { 8 | var r = random.Random() 9 | var to_check = r.between(0, 10) 10 | var ok = true 11 | for i in 0...100: { 12 | var b = r.between(0, 10) 13 | if b < 0 || b > 10: { 14 | ok = false 15 | break 16 | } 17 | } 18 | 19 | ok |> assert_true 20 | } 21 | 22 | private define random_ctor_with_seed 23 | { 24 | var r = random.Random(1234567890) 25 | var to_check = r.between(0, 10) 26 | var ok = true 27 | for i in 0...100: { 28 | var b = r.between(0, 10) 29 | if b < 0 || b > 10: { 30 | ok = false 31 | break 32 | } 33 | } 34 | 35 | ok |> assert_true 36 | } 37 | 38 | public define test_Random_ctor 39 | { 40 | random_ctor_no_seed() 41 | random_ctor_with_seed() 42 | } 43 | 44 | public define test_Random_between 45 | { 46 | assert_raises("ValueError: Interval range is empty.", 47 | (|| random.Random().between(0, 0) )) 48 | 49 | assert_raises("ValueError: Interval exceeds 32 bits in size.", 50 | (|| random.Random().between(-999999999999999, 0) )) 51 | 52 | assert_raises("ValueError: Interval exceeds 32 bits in size.", 53 | (|| random.Random().between(0, 999999999999999) )) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /test/prelude/test_pkg_subprocess.lily: -------------------------------------------------------------------------------- 1 | import (Interpreter, 2 | TestCase) "../t/testing", subprocess 3 | 4 | class TestPkgSubprocess < TestCase 5 | { 6 | public define test_popen 7 | { 8 | assert_raises("ValueError: Invalid mode 'z' given.", 9 | (|| subprocess.popen("test", "z") )) 10 | 11 | var a = subprocess.popen("test") 12 | a.close() 13 | 14 | a = subprocess.popen("test", "r") 15 | a.close() 16 | 17 | a = subprocess.popen("test", "w") 18 | a.close() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /test/prelude/test_pkg_sys.lily: -------------------------------------------------------------------------------- 1 | import (Interpreter, 2 | TestCase) "../t/testing", sys 3 | 4 | class TestPkgSys < TestCase 5 | { 6 | public define test_exit 7 | { 8 | var t = Interpreter() 9 | 10 | t.parse_string("[ctx]", """ 11 | import sys 12 | 13 | sys.exit(69) 14 | """) 15 | 16 | assert_equal(t.exit_code(), 69) 17 | } 18 | 19 | public define test_exit_failure_success 20 | { 21 | var failure_t = Interpreter() 22 | var success_t = Interpreter() 23 | 24 | success_t.parse_string("[ctx]", "var v = 1") 25 | failure_t.parse_string("[ctx]", "0 / 0") 26 | 27 | var success_exit = success_t.exit_code() 28 | var failure_exit = failure_t.exit_code() 29 | 30 | assert_not_equal(success_exit, failure_exit) 31 | 32 | success_t.parse_string("[ctx]", """ 33 | import sys 34 | 35 | sys.exit_failure() 36 | """) 37 | failure_t.parse_string("[ctx]", """ 38 | import sys 39 | 40 | sys.exit_success() 41 | """) 42 | 43 | assert_equal(success_exit, failure_t.exit_code()) 44 | assert_equal(failure_exit, success_t.exit_code()) 45 | } 46 | 47 | public define test_getenv 48 | { 49 | var home = sys.getenv("HOME") 50 | 51 | var impossible = sys.getenv("ABCDEFG") 52 | 53 | impossible.is_none() |> assert_true 54 | } 55 | 56 | public define test_recursion_limit 57 | { 58 | var t = Interpreter() 59 | 60 | # recursion limit (base case to fetch existing value) 61 | 62 | assert_parse_string(t, """ 63 | import sys 64 | 65 | sys.set_recursion_limit(5) 66 | 67 | if sys.recursion_limit() != 5: { 68 | raise Exception("Failed.") 69 | } 70 | """) 71 | } 72 | 73 | public define test_set_recursion_limit 74 | { 75 | var t = Interpreter() 76 | 77 | # set recursion limit (base case for limit) 78 | 79 | assert_parse_fails(t, """\ 80 | RuntimeError: Function call recursion limit reached.\n\ 81 | Traceback:\n \ 82 | from [test]:4: in f\n \ 83 | from [test]:4: in f\n \ 84 | from [test]:4: in f\n \ 85 | from [test]:4: in f\n \ 86 | from [test]:4: in f\n \ 87 | from [test]:5: in __main__\n\ 88 | """, 89 | """ 90 | import sys 91 | sys.set_recursion_limit(5) 92 | define f { f() } 93 | f() 94 | """) 95 | 96 | # set recursion limit (too high) 97 | 98 | t = Interpreter() 99 | assert_parse_fails(t, """\ 100 | ValueError: Limit value (9999999999999999) is not reasonable.\n\ 101 | Traceback:\n \ 102 | from [sys]: in set_recursion_limit\n \ 103 | from [test]:3: in __main__\n\ 104 | """, 105 | """ 106 | import sys 107 | sys.set_recursion_limit(9999999999999999) 108 | """) 109 | 110 | # set recursion limit (too low) 111 | 112 | t = Interpreter() 113 | assert_parse_fails(t, """\ 114 | ValueError: Limit value (1) is lower than the current recursion depth.\n\ 115 | Traceback:\n \ 116 | from [sys]: in set_recursion_limit\n \ 117 | from [test]:5: in f\n \ 118 | from [test]:8: in __main__\n\ 119 | """, 120 | """ 121 | import sys 122 | 123 | define f { 124 | sys.set_recursion_limit(1) 125 | } 126 | 127 | f() 128 | """) 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /test/prelude/test_pkg_time.lily: -------------------------------------------------------------------------------- 1 | import (Interpreter, 2 | TestCase) "../t/testing", time 3 | 4 | class TestPkgTime < TestCase 5 | { 6 | public define test_Time_clock 7 | { 8 | time.Time.clock() 9 | } 10 | 11 | public define test_Time_now 12 | { 13 | time.Time.now() 14 | } 15 | 16 | public define test_Time_since_epoch 17 | { 18 | var tm = time.Time.now() 19 | tm.since_epoch() 20 | } 21 | 22 | public define test_Time_to_s 23 | { 24 | var tm = time.Time.now() 25 | tm.to_s() 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test/t/manifest/backbone.lily: -------------------------------------------------------------------------------- 1 | import manifest 2 | 3 | library backbone 4 | 5 | class TestCaseBase 6 | { 7 | public var @pass_count: Integer 8 | public var @fail_count: Integer 9 | public var @skip_count: Integer 10 | 11 | public define run_tests 12 | } 13 | 14 | # Begin spawni section. 15 | 16 | foreign static class RawInterpreter {} 17 | 18 | class Interpreter 19 | { 20 | private var @raw: RawInterpreter 21 | private var @import_hook: Function(Interpreter, String) 22 | public define config_set_extra_info(b: Boolean): Interpreter 23 | public define error: String 24 | public define error_message: String 25 | public define exit_code: Byte 26 | public define has_exited: Boolean 27 | public define import_current_root_dir: String 28 | public define import_file(path: String): Boolean 29 | public define import_hook_reset 30 | public define import_hook_set(fn: Function(Interpreter, String)) 31 | public define import_library(path: String): Boolean 32 | public define import_string(target: String, content: String): Boolean 33 | public define import_use_local_dir(dir: String) 34 | public define import_use_package_dir(dir: String) 35 | public define parse_expr(context: String, data: String): Option[String] 36 | public define parse_file(filename: String): Boolean 37 | public define parse_manifest_file(filename: String): Boolean 38 | public define parse_manifest_string(context: String, data: String): Boolean 39 | public define parse_string(context: String, data: String): Boolean 40 | public define render_file(filename: String): Option[String] 41 | public define render_string(context: String, data: String): Option[String] 42 | public define set_hook(fn: Function(Interpreter, String)) 43 | public define validate_file(filename: String): Boolean 44 | public define validate_string(context: String, data: String): Boolean 45 | } 46 | -------------------------------------------------------------------------------- /test/t/manifest/covlib.lily: -------------------------------------------------------------------------------- 1 | import manifest 2 | 3 | library covlib 4 | 5 | import farm 6 | 7 | foreign class Foreign {} 8 | foreign static class ForeignGeneric[A, B] {} 9 | 10 | class Container(value: String) 11 | { 12 | private var @value: String 13 | public define fetch: String 14 | public define update(x: String) 15 | public define nothing: self 16 | } 17 | 18 | class C2(value: String, x: Integer) < Container(value) 19 | { 20 | public define check: Integer 21 | private var @x: Integer 22 | } 23 | 24 | enum FlatEnum { 25 | FlatOne, 26 | FlatTwo, 27 | FlatThree 28 | } 29 | 30 | scoped enum ScopedEnum { 31 | ScopedOne, 32 | ScopedTwo, 33 | ScopedThree 34 | } 35 | 36 | define cover_func_check(native: Function(Integer), foreign: Function(Integer => String)): Boolean 37 | define cover_function_bytecode(foreign: Function(String), native: Function(Integer)): Boolean 38 | define cover_id_checks[A](u: Unit, c: A, d: String): Boolean 39 | define cover_list_reserve 40 | define cover_list_sfs 41 | define cover_misc_api 42 | define cover_optional_boolean( a: *Boolean = true, 43 | :b b: *Boolean = true, 44 | :c c: *Boolean = true): Integer 45 | define cover_optional_integer( a: *Integer = 100, 46 | :b b: *Integer = 200, 47 | :c c: *Integer = 300): Integer 48 | define cover_optional_keyarg_call(f: Function(*Integer, *Integer, *Integer => Integer)): Integer 49 | define cover_optional_string( a: *String = "", 50 | :b b: *String = "", 51 | :c c: *String = ""): String 52 | define cover_push_boolean: Boolean 53 | define cover_value_as(a: Byte, 54 | b: ByteString, 55 | c: Exception, 56 | d: Double, 57 | e: File, 58 | f: Function(Integer), 59 | g: Foreign, 60 | h: Hash[Integer, Integer], 61 | i: Integer, 62 | j: String) 63 | 64 | define cover_value_group(a: Boolean, 65 | b: Byte, 66 | c: ByteString, 67 | d: Double, 68 | e: Option[Integer], 69 | f: File, 70 | g: Function(Integer), 71 | h: Hash[Integer, Integer], 72 | i: Foreign, 73 | j: Exception, 74 | k: Integer, 75 | l: List[Integer], 76 | m: String, 77 | n: Tuple[Integer], 78 | o: Unit, 79 | p: Option[Integer]): Boolean 80 | define make_flat_n(n: Integer): FlatEnum 81 | define make_scoped_n(n: Integer): ScopedEnum 82 | define raise_dbzerror 83 | define raise_keyerror 84 | define scoop_narrow(f: Function($1)) 85 | define scoop_narrow_with_args(f: Function(Integer, String, $1 => Boolean)) 86 | -------------------------------------------------------------------------------- /test/t/manifest/farm.lily: -------------------------------------------------------------------------------- 1 | import manifest 2 | 3 | library farm 4 | 5 | var carrot_count: Integer 6 | constant a: Integer 7 | constant b: Double 8 | constant c: String 9 | -------------------------------------------------------------------------------- /test/t/src/lily_farm.c: -------------------------------------------------------------------------------- 1 | #include "lily.h" 2 | #include "lily_farm_bindings.h" 3 | 4 | void lily_farm_var_carrot_count(lily_state *s) 5 | { 6 | lily_push_integer(s, 100); 7 | } 8 | 9 | void lily_farm_constant_a(lily_state *s) 10 | { 11 | lily_push_integer(s, 10); 12 | } 13 | 14 | void lily_farm_constant_b(lily_state *s) 15 | { 16 | lily_push_double(s, 5.5); 17 | } 18 | 19 | void lily_farm_constant_c(lily_state *s) 20 | { 21 | lily_push_string(s, "test"); 22 | } 23 | 24 | LILY_DECLARE_FARM_CALL_TABLE 25 | -------------------------------------------------------------------------------- /test/t/src/lily_farm_bindings.h: -------------------------------------------------------------------------------- 1 | #ifndef LILY_FARM_BINDINGS_H 2 | #define LILY_FARM_BINDINGS_H 3 | /* Generated by lily-bindgen, do not edit. */ 4 | 5 | #if defined(_WIN32) && !defined(LILY_NO_EXPORT) 6 | #define LILY_FARM_EXPORT __declspec(dllexport) 7 | #else 8 | #define LILY_FARM_EXPORT 9 | #endif 10 | 11 | LILY_FARM_EXPORT 12 | const char *lily_farm_info_table[] = { 13 | "\0\0" 14 | ,"R\0carrot_count\0Integer" 15 | ,"O\0a\0Integer" 16 | ,"O\0b\0Double" 17 | ,"O\0c\0String" 18 | ,"Z" 19 | }; 20 | #define LILY_DECLARE_FARM_CALL_TABLE \ 21 | LILY_FARM_EXPORT \ 22 | lily_call_entry_func lily_farm_call_table[] = { \ 23 | NULL, \ 24 | lily_farm_var_carrot_count, \ 25 | lily_farm_constant_a, \ 26 | lily_farm_constant_b, \ 27 | lily_farm_constant_c, \ 28 | lily_v21_plus_required, \ 29 | }; 30 | #endif 31 | -------------------------------------------------------------------------------- /test/types/test_generics.lily: -------------------------------------------------------------------------------- 1 | import (Interpreter, 2 | TestCase) "../t/testing" 3 | 4 | class TestGenerics < TestCase 5 | { 6 | public define test_bad_list_hash_unify 7 | { 8 | var t = Interpreter() 9 | 10 | # bad list hash unify (fail on a List) 11 | 12 | assert_parse_fails(t, """\ 13 | SyntaxError: List elements do not have a consistent type.\n\ 14 | Expected Type: Integer\n\ 15 | Received Type: String\n \ 16 | from [test]:1:\n\ 17 | """, 18 | """\ 19 | var v = [1, "2"] 20 | """) 21 | 22 | # bad list hash unify (fail on Hash key) 23 | 24 | assert_parse_fails(t, """\ 25 | SyntaxError: Hash keys do not have a consistent type.\n\ 26 | Expected Type: Integer\n\ 27 | Received Type: String\n \ 28 | from [test]:1:\n\ 29 | """, 30 | """\ 31 | var v = [1 => 1, "2" => 2] 32 | """) 33 | 34 | # bad list hash unify (fail on Hash value) 35 | 36 | assert_parse_fails(t, """\ 37 | SyntaxError: Hash values do not have a consistent type.\n\ 38 | Expected Type: Integer\n\ 39 | Received Type: String\n \ 40 | from [test]:1:\n\ 41 | """, 42 | """\ 43 | var v = [1 => 1, 2 => "2"] 44 | """) 45 | } 46 | 47 | public define test_close_over_generic 48 | { 49 | var t = Interpreter() 50 | 51 | # Blocked because even though 'g' is an inner function, it may have a 52 | # different A than 'f'. 53 | 54 | assert_parse_fails(t, """\ 55 | SyntaxError: Cannot close over a var of an incomplete type in this scope.\n \ 56 | from [test]:4:\n\ 57 | """, 58 | """\ 59 | define f[A](a: A) { 60 | define g { 61 | var v = a 62 | } 63 | } 64 | """) 65 | } 66 | 67 | public define test_inherit_generics 68 | { 69 | var t = Interpreter() 70 | 71 | assert_parse_string(t, """ 72 | define check_generics_inherited { 73 | define a[A] { 74 | define b(v1: A) { 75 | 76 | } 77 | } 78 | } 79 | """) 80 | } 81 | 82 | public define test_self 83 | { 84 | var t = Interpreter() 85 | 86 | # self inferenece (block resolving a generic as self) 87 | 88 | assert_parse_fails(t, """\ 89 | SyntaxError: Argument #1 to f is invalid:\n\ 90 | Expected Type: Function (Example => ?)\n\ 91 | Received Type: Function (Example => self)\n \ 92 | from [test]:7:\n\ 93 | """, 94 | """\ 95 | class Example { 96 | public define f: self { } 97 | } 98 | 99 | define f[A](g: Function(Example => A)) {} 100 | 101 | f(Example.f) 102 | """) 103 | } 104 | 105 | public define test_too_many_generics 106 | { 107 | var t = Interpreter() 108 | 109 | assert_parse_fails(t, """\ 110 | SyntaxError: Too many generics.\n \ 111 | from [test]:1:\n\ 112 | """, 113 | """\ 114 | enum Test[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, a] { 115 | Test0 116 | Test1 117 | } 118 | """) 119 | } 120 | 121 | public define test_quantification 122 | { 123 | var t = Interpreter() 124 | 125 | assert_parse_fails(t, """\ 126 | SyntaxError: Global variables cannot have a type with generics (Function (A)).\n \ 127 | from [test]:3:\n\ 128 | """, 129 | """\ 130 | define f[A](a: A) {} 131 | 132 | var v = f 133 | 134 | define g[A, B](a: A, b: Function(A)) { v = b } 135 | define h(a: String) { a.size() |> print } 136 | 137 | g("1", h) 138 | v(1) 139 | """) 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /test/types/test_narrowing.lily: -------------------------------------------------------------------------------- 1 | import (Interpreter, 2 | TestCase) "../t/testing" 3 | 4 | class TestVariance < TestCase 5 | { 6 | public define test_bad_narrow_variance 7 | { 8 | var t = Interpreter() 9 | 10 | # bad narrow invariance (invariant narrow to Unit) 11 | 12 | assert_parse_fails(t, """\ 13 | SyntaxError: Argument #2 to List.push is invalid:\n\ 14 | Expected Type: List[Function ( => Integer)]\n\ 15 | Received Type: List[Function ()]\n \ 16 | from [test]:6:\n\ 17 | """, 18 | """\ 19 | var l: List[List[Function( => Integer)]] = [] 20 | 21 | define f {} 22 | var l2 = [f] 23 | 24 | l.push(l2) 25 | """) 26 | 27 | # bad narrow invariance (covariant narrow to Unit) 28 | 29 | assert_parse_fails(t, """\ 30 | SyntaxError: Argument #1 to g is invalid:\n\ 31 | Expected Type: Function ( => Integer)\n\ 32 | Received Type: Function ()\n \ 33 | from [test]:7:\n\ 34 | """, 35 | """\ 36 | define f {} 37 | 38 | define g(h: Function( => Integer)): Integer { 39 | return h() 40 | } 41 | 42 | g(f) 43 | """) 44 | 45 | # bad narrow invariance (contravariant narrow to Unit) 46 | 47 | assert_parse_fails(t, """\ 48 | SyntaxError: Cannot assign type 'Function (Function ())' to type 'Function (Function ( => Integer))'.\n \ 49 | from [test]:3:\n\ 50 | """, 51 | """\ 52 | define f(g: Function(Function( => Integer)), 53 | h: Function(Function())) { 54 | g = h 55 | } 56 | """) 57 | } 58 | 59 | public define test_narrow_away_optargs 60 | { 61 | var t = Interpreter() 62 | 63 | assert_parse_fails(t, """\ 64 | SyntaxError: Cannot assign type 'Function (Integer)' to type 'Function (Integer, *Integer)'.\n \ 65 | from [test]:5:\n\ 66 | """, 67 | """\ 68 | define f(a: Integer) { } 69 | define g(a: Integer, b: *Integer=10) { } 70 | 71 | var h = g 72 | h = f 73 | """) 74 | } 75 | 76 | public define test_narrow_extra_optargs 77 | { 78 | var t = Interpreter() 79 | 80 | assert_parse_string(t, """ 81 | define f(a: Integer, b: *Integer=10) { } 82 | 83 | define g(a: Function(Integer)) { } 84 | 85 | g(f) 86 | """) 87 | } 88 | 89 | public define test_narrow_mismatch_optargs 90 | { 91 | var t = Interpreter() 92 | assert_parse_fails(t, """\ 93 | SyntaxError: Cannot assign type 'Function (Integer, *Integer => Integer)' to type 'Function (String => String)'.\n \ 94 | from [test]:5:\n\ 95 | """, 96 | """\ 97 | define f(a: String): String { return "10" } 98 | define g(a: Integer, b: *Integer=10): Integer { return 10 } 99 | 100 | var h = f 101 | h = g 102 | """) 103 | } 104 | 105 | public define test_equivalent_args 106 | { 107 | var t = Interpreter() 108 | 109 | assert_parse_string(t, """ 110 | define f(a: Integer) {} 111 | define g(a: *Integer=10) {} 112 | 113 | var h = f 114 | 115 | h = g 116 | """) 117 | } 118 | 119 | public define test_narrow_mismatched_returns 120 | { 121 | var t = Interpreter() 122 | assert_parse_string(t, """ 123 | define f(a: Integer): String { return "" } 124 | define g(a: Integer): Integer { return 0 } 125 | 126 | var lst = [f, g] 127 | """) 128 | } 129 | 130 | public define test_static_list_hash_narrow 131 | { 132 | var t = Interpreter() 133 | 134 | assert_parse_string(t, """ 135 | class One {} 136 | class Two < One {} 137 | class Three < Two {} 138 | 139 | var l = [Three(), Two(), One()] 140 | var h = [1 => Three(), 2 => Two(), 3 => One()] 141 | """) 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /test/types/test_quantification.lily: -------------------------------------------------------------------------------- 1 | import (Interpreter, 2 | TestCase) "../t/testing" 3 | 4 | class TestQuantification < TestCase 5 | { 6 | public define test_quantified_not_forall 7 | { 8 | var t = Interpreter() 9 | 10 | # This should fail because A is quantified by f's scope. 11 | 12 | assert_parse_fails(t, """\ 13 | SyntaxError: Argument #2 to f is invalid:\n\ 14 | Expected Type: List[Integer]\n\ 15 | Received Type: List[A]\n \ 16 | from [test]:4:\n\ 17 | """, 18 | """\ 19 | define f[A](a: List[A], b: List[A]) {} 20 | 21 | define g[A](a: List[A]) { 22 | f([1], a) 23 | } 24 | """) 25 | } 26 | 27 | public define test_quantified_parameters 28 | { 29 | var t = Interpreter() 30 | 31 | # quantification (parameters that are functions are quantified) 32 | # Inside of 'f', all generics are quantified, even those of functions 33 | # passed to it. This requires rank-2 polymorphism to work. 34 | 35 | assert_parse_fails(t, """\ 36 | SyntaxError: Argument #1 to g is invalid:\n\ 37 | Expected Type: A\n\ 38 | Received Type: B\n \ 39 | from [test]:4:\n\ 40 | """, 41 | """\ 42 | # This isn't allowed because A and B may be incompatible types. 43 | # Ex: A being an integer, B as a string. 44 | define f[A, B](g: Function(A), value: B) { 45 | g(value) 46 | } 47 | """) 48 | } 49 | 50 | public define test_quantify_once 51 | { 52 | var t = Interpreter() 53 | 54 | # quantification (solve as a generic only once, flat inference) 55 | # The call to 'f' expects and gets the following: 56 | # 57 | # one: Box for A. 58 | # two: List[g's A] for B. 59 | # three: List[Box] for B. 60 | # 61 | # This is the same as above but in a different context. 62 | 63 | assert_parse_fails(t, """\ 64 | SyntaxError: Argument #3 to f is invalid:\n\ 65 | Expected Type: List[A]\n\ 66 | Received Type: List[Box]\n \ 67 | from [test]:10:\n\ 68 | """, 69 | """\ 70 | class Box(a: Integer) {} 71 | 72 | define f[A, B](one: A, two: B, three: B) { 73 | 74 | } 75 | 76 | define g[A, B](value: A, value2: B) { 77 | var a = Box(10) 78 | 79 | f(a, [value], [a]) 80 | } 81 | """) 82 | } 83 | 84 | public define test_quantify_once_nested 85 | { 86 | var t = Interpreter() 87 | 88 | # quantification (solve as a generic only once, nested inference) 89 | # The call to 'f' expects and gets the following: 90 | # 91 | # one: List[Box] for A. 92 | # two: List[g's A] for B 93 | # three: List[List[Integer]] for B. 94 | # 95 | # This is wrong because B is not a forall A that can be narrowed down, 96 | # but instead the g's quantified A that cannot be narrowed. 97 | 98 | assert_parse_fails(t, """\ 99 | SyntaxError: Argument #3 to f is invalid:\n\ 100 | Expected Type: List[A]\n\ 101 | Received Type: List[List[Integer]]\n \ 102 | from [test]:6:\n\ 103 | """, 104 | """\ 105 | class Box(a: Integer) {} 106 | 107 | define f[A, B](one: A, two: B, three: B) { } 108 | 109 | define g[A](one: A) { 110 | f([Box(10)], [one], [[1]]) 111 | } 112 | """) 113 | } 114 | 115 | public define test_quantify_result 116 | { 117 | # This is supposed to work because Option.unwrap is generic and 118 | # unquantified in this scope. This tries to cause a syntax error after 119 | # to make sure that the solving wrote the correct resulting type. 120 | 121 | var t = Interpreter() 122 | 123 | assert_parse_fails(t, """\ 124 | SyntaxError: Argument #2 to List.push is invalid:\n\ 125 | Expected Type: Integer\n\ 126 | Received Type: String\n \ 127 | from [test]:2:\n\ 128 | """, 129 | """\ 130 | var v = [Some(1)].map(Option.unwrap) 131 | v.push("") 132 | """) 133 | } 134 | 135 | public define test_quantify_to_concrete 136 | { 137 | var t = Interpreter() 138 | 139 | # quantification (generic against concrete type) 140 | 141 | assert_parse_fails(t, """\ 142 | SyntaxError: Argument #2 to f is invalid:\n\ 143 | Expected Type: A\n\ 144 | Received Type: Integer\n \ 145 | from [test]:4:\n\ 146 | """, 147 | """\ 148 | define f[A](v1: A, v2: A) { } 149 | 150 | define g[A](v1: A) { 151 | f(v1, 10) 152 | } 153 | """) 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /test/types/test_scoop.lily: -------------------------------------------------------------------------------- 1 | import (Interpreter, 2 | TestCase) "../t/testing" 3 | 4 | class TestScoop < TestCase 5 | { 6 | public define test_scoop_as_argument 7 | { 8 | var t = Interpreter() 9 | 10 | assert_parse_fails(t, """\ 11 | SyntaxError: Argument #1 to f is invalid:\n\ 12 | Expected Type: Function (String, List[?])\n\ 13 | Received Type: Function (String, $1... => String)\n \ 14 | from [test]:5:\n\ 15 | """, 16 | """\ 17 | define f[A](a: Function(String, List[A]), b: A, c: A): List[A] { 18 | return [b, c] 19 | } 20 | 21 | f(String.format, 1, "2") |> print 22 | """) 23 | } 24 | 25 | public define test_scoop_unify 26 | { 27 | var t = Interpreter() 28 | 29 | assert_parse_fails(t, """\ 30 | SyntaxError: List elements do not have a consistent type.\n\ 31 | Expected Type: Function (String, $1... => String)\n\ 32 | Received Type: Function (String, $1... => String)\n \ 33 | from [test]:1:\n\ 34 | """, 35 | """\ 36 | var v = [String.format, String.format] 37 | """) 38 | } 39 | 40 | public define test_scoop_from_zip 41 | { 42 | var t = Interpreter() 43 | 44 | # List.zip shouldn't send scoop back if the input is empty. 45 | 46 | assert_parse_fails(t, """\ 47 | SyntaxError: Cannot assign type 'Integer' to type 'List[Tuple[Integer, Unit]]'.\n \ 48 | from [test]:3:\n\ 49 | """, 50 | """\ 51 | var v = [1, 2].zip([]) 52 | 53 | v = 1 54 | """) 55 | } 56 | 57 | public define test_scoop_init 58 | { 59 | var t = Interpreter() 60 | 61 | # scoop init (init by assign). 62 | 63 | assert_parse_fails(t, """\ 64 | SyntaxError: Global variables cannot have a type with generics (Function (List[A], List[$1]... => List[Tuple[A, $1]])).\n \ 65 | from [test]:1:\n\ 66 | """, 67 | """\ 68 | var v = List.zip 69 | """) 70 | 71 | # scoop init (init by prop assign). 72 | 73 | assert_parse_fails(t, """\ 74 | SyntaxError: Right side of assignment is incomplete type 'Function (List[A], List[$1]... => List[Tuple[A, $1]])'.\n \ 75 | from [test]:2:\n\ 76 | """, 77 | """\ 78 | class Example { 79 | public var @x = List.zip 80 | } 81 | """) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /test/types/test_variance.lily: -------------------------------------------------------------------------------- 1 | import (Interpreter, 2 | TestCase) "../t/testing" 3 | 4 | class TestVariance < TestCase 5 | { 6 | public define test_argument_covariant 7 | { 8 | var t = Interpreter() 9 | 10 | assert_parse_string(t, """ 11 | class One[A](a: A) {} 12 | class Two[A](a: A) < One(a) {} 13 | 14 | define g[A](a: One[A]) 15 | { 16 | } 17 | 18 | define f[A](a: One[A], b: Two[A]) 19 | { 20 | g(b) 21 | } 22 | """) 23 | } 24 | 25 | public define test_assign_covariant 26 | { 27 | var t = Interpreter() 28 | 29 | assert_parse_string(t, """ 30 | class One {} 31 | class Two < One {} 32 | 33 | var v = One() 34 | v = Two() 35 | 36 | var v2 = [v] 37 | v2[0] = Two() 38 | """) 39 | } 40 | 41 | public define test_function_input_contravariant 42 | { 43 | var t = Interpreter() 44 | 45 | assert_parse_fails(t, """\ 46 | SyntaxError: Cannot assign type 'Function (Two)' to type 'Function (One)'.\n \ 47 | from [test]:8:\n\ 48 | """, 49 | """\ 50 | class One {} 51 | class Two < One {} 52 | 53 | define f(a: One) {} 54 | define g(a: Two) {} 55 | 56 | var v = f 57 | v = g 58 | """) 59 | } 60 | 61 | public define test_function_result_covariant 62 | { 63 | var t = Interpreter() 64 | 65 | assert_parse_string(t, """ 66 | class One {} 67 | class Two < One {} 68 | 69 | define f(a: One) {} 70 | define g(a: Two) {} 71 | 72 | var v = g 73 | v = f 74 | """) 75 | } 76 | 77 | public define test_generic_solve_in_covariant 78 | { 79 | var t = Interpreter() 80 | 81 | assert_parse_string(t, """ 82 | class One {} 83 | class Two < One {} 84 | class Three < Two {} 85 | 86 | define f[A](input: A, g: Function(A => A)) {} 87 | 88 | # This works because when trying to solve as 'Function(A => A)', the first A 89 | # is looked at contravariantly, while the second is covariant. 90 | # Doing the reverse (taking either Three or outputting One) would be incorrect. 91 | 92 | define g(a: One): Three { return Three() } 93 | 94 | f(Two(), g) 95 | """) 96 | } 97 | 98 | public define test_list_invariant 99 | { 100 | var t = Interpreter() 101 | 102 | assert_parse_fails(t, """\ 103 | SyntaxError: Cannot assign type 'List[Two]' to type 'List[One]'.\n \ 104 | from [test]:7:\n\ 105 | """, 106 | """\ 107 | class One {} 108 | class Two < One {} 109 | 110 | var v = [[One()]] 111 | var v2 = [Two()] 112 | # Assignment is naturally covariant. However, lists are invariant. 113 | v[0] = v2 114 | """) 115 | } 116 | 117 | public define test_property_covariant 118 | { 119 | var t = Interpreter() 120 | 121 | assert_parse_string(t, """ 122 | class One {} 123 | class Two < One {} 124 | 125 | class Test { 126 | public var @a: One = Two() 127 | @a = One() 128 | } 129 | """) 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /try/coroutine_tasks.lily: -------------------------------------------------------------------------------- 1 | import (Coroutine) coroutine 2 | 3 | define factorial_co(co: Coroutine[Integer, Unit], num: Integer) { 4 | var f = 1 5 | for i in 2...num + 1: { 6 | co.yield(f) 7 | f *= i 8 | } 9 | } 10 | 11 | define create_tasks(source: List[Integer]): List[Coroutine[Integer, Unit]] 12 | { 13 | print("Creating {} tasks:".format(source.size())) 14 | 15 | for i in 0...source.size() - 1: { 16 | var s = source[i] 17 | 18 | print("Task {} will last {} rounds.".format(i, s)) 19 | } 20 | 21 | var result = source.map(|m| Coroutine.build_with_value(factorial_co, m)) 22 | 23 | return result 24 | } 25 | 26 | var task_list = [ 27 | 2, 28 | 4, 29 | 1, 30 | 3, 31 | ] |> create_tasks 32 | var total = task_list.size() 33 | var round = 1 34 | 35 | while 1: { 36 | var results = task_list.map(Coroutine.resume) 37 | var remaining = task_list.select(Coroutine.is_waiting) 38 | .size() 39 | 40 | if remaining == 0: { 41 | break 42 | } 43 | 44 | print("\nRound {} with {} tasks.".format(round, remaining)) 45 | 46 | for i in 0...results.size() - 1: { 47 | var r = results[i] 48 | 49 | match r: { 50 | case Some(s): 51 | print("Task {}: factorial({}) = {}".format(i, round, s)) 52 | case None: 53 | } 54 | } 55 | 56 | round += 1 57 | } 58 | -------------------------------------------------------------------------------- /try/fib.lily: -------------------------------------------------------------------------------- 1 | # fibonacci numbers 2 | class Fib { 3 | public var @a=0 4 | public var @b=1 5 | public define next: Integer { 6 | var c = @a + @b 7 | @a = @b 8 | @b = c 9 | return @a 10 | } 11 | } 12 | 13 | var fib = Fib() 14 | print([fib.next(), 15 | fib.next(), 16 | fib.next(), 17 | fib.next(), 18 | fib.next(), 19 | fib.next()]) 20 | -------------------------------------------------------------------------------- /try/fizzbuzz.lily: -------------------------------------------------------------------------------- 1 | define fizzbuzz(start: *Integer = 1, stop: *Integer = 100): List[String] { 2 | var result: List[String] = [] 3 | 4 | for i in start...stop: { 5 | if (i % 3) == 0: { 6 | if (i % 5) == 0: { 7 | result.push("FizzBuzz") 8 | else: 9 | result.push("Fizz") 10 | } 11 | elif (i % 5) == 0: 12 | result.push("Buzz") 13 | else: 14 | result.push(i.to_s()) 15 | } 16 | } 17 | 18 | return result 19 | } 20 | 21 | fizzbuzz(1, 100) |> print 22 | 23 | # Or you can also write... 24 | # fizzbuzz() |> print # Use defaults of 1 and 100. 25 | # print(fizzbuzz()) # Normal nested kind of calling. 26 | # fizzbuzz(6) # Specify start, but use default for stop. 27 | -------------------------------------------------------------------------------- /try/one_hundred_doors.lily: -------------------------------------------------------------------------------- 1 | var doors = List.repeat(100, false) 2 | 3 | for i in 0...99: { 4 | for j in i...99 by i + 1: { 5 | doors[j] = !doors[j] 6 | } 7 | } 8 | 9 | # The type must be specified since the list starts off empty. 10 | var open_doors: List[Integer] = [] 11 | 12 | doors.each_index(|i| 13 | if doors[i]: { 14 | open_doors.push(i + 1) 15 | } 16 | ) 17 | 18 | print("Open doors: {}".format(open_doors)) 19 | -------------------------------------------------------------------------------- /try/rpn_calculator.lily: -------------------------------------------------------------------------------- 1 | var math_ops = ["+" => (|a: Integer, b: Integer| a + b), 2 | "-" => (|a, b| a - b), 3 | "*" => (|a, b| a * b), 4 | "/" => (|a, b| a / b)] 5 | 6 | #[ 7 | This implements a reverse polish notation calculator. 8 | This calculator demonstrates the usefulness of the Result type. 9 | If the result was just Integer, then you'd probably wonder what it would do 10 | for division by zero. 11 | But since it's written as Result[String, Integer], you have a pretty strong 12 | guarantee that it won't throw exceptions, regardless. 13 | Furthermore, if it does throw, then you have a valid reason to say that it 14 | has a bug (since something that returns Result shouldn't also throw). 15 | ]# 16 | define rpn(input: String): Result[String, List[Integer]] 17 | { 18 | var values = input.split(" ") 19 | var stack: List[Integer] = [] 20 | 21 | foreach v in values: { 22 | with v.parse_i() as Some(s): { 23 | s |> stack.push 24 | else: 25 | if stack.size() < 2: { 26 | return Failure("Stack underflow") 27 | } 28 | 29 | var right = stack.pop() 30 | var left = stack.pop() 31 | try: { 32 | var op_fn = math_ops[v] 33 | var op_value = op_fn(left, right) 34 | stack.push(op_value) 35 | except KeyError: 36 | return Failure("Invalid operation {}".format(v)) 37 | except DivisionByZeroError: 38 | return Failure("Attempt to divide by zero") 39 | except Exception: 40 | return Failure("Unexpected error from op {}".format(v)) 41 | } 42 | } 43 | } 44 | 45 | return Success(stack) 46 | } 47 | 48 | var lines = [ 49 | "1 2 3 4 * + -", 50 | "2 2 2 2 * *", 51 | "*", 52 | "1 2 ?" 53 | ] 54 | 55 | foreach l in lines: { 56 | print("{}: {}".format(l, rpn(l))) 57 | } 58 | -------------------------------------------------------------------------------- /try/show_classes.lily: -------------------------------------------------------------------------------- 1 | class Point(x: Integer, y: Integer) 2 | { 3 | public var @x = x 4 | public var @y = y 5 | 6 | public define move2D(moveX: Integer, moveY: Integer) 7 | { 8 | @x += moveX 9 | @y += moveY 10 | } 11 | 12 | public define compare(other: Point): Boolean 13 | { 14 | if @x == other.x && @y == other.y: { 15 | return true 16 | else: 17 | return false 18 | } 19 | } 20 | 21 | public define to_s: String 22 | { 23 | return "Point({}, {})".format(@x, @y) 24 | } 25 | } 26 | 27 | var coordinates = [Point(5, 10), Point(40, 500), Point(11, 2)] 28 | 29 | stdout.write("Created three Points: ") 30 | coordinates.map(Point.to_s) |> print 31 | 32 | stdout.write("How many coordinates have an x < y? : ") 33 | coordinates.count(|c| c.x < c.y ) |> print 34 | 35 | stdout.write("Using Point.moveBy to move each X down by half...\n") 36 | coordinates.each(|e| e.move2D(-e.x / 2, 0) ) 37 | 38 | stdout.write("Points are now: ") 39 | coordinates.map(Point.to_s) |> print 40 | -------------------------------------------------------------------------------- /try/show_how_to_combine_functions.lily: -------------------------------------------------------------------------------- 1 | define doubleit(a: Integer): Integer { return a * 2 } 2 | 3 | # This combines two functions together into a closure that does both. 4 | define combine[A, B, C](f: Function(A => B), g: Function(B => C)): Function(A => C) 5 | { 6 | return (|x| x |> f |> g ) 7 | } 8 | 9 | # This combines three different functions together: 10 | # String.parse_i, which takes a String and produces Option[Integer] 11 | # A lambda, which takes an Option[Integer] and produces an Integer 12 | # A defined function, which takes an Integer to double it. 13 | 14 | var super_func = 15 | combine(String.parse_i, 16 | combine((|maybe_int| maybe_int.unwrap_or(0) ), 17 | doubleit)) 18 | 19 | stdout.write("This example joined three functions together.\n") 20 | stdout.write("In one call, String \"123\" becomes...") 21 | super_func("123") |> print 22 | -------------------------------------------------------------------------------- /try/show_off_enums.lily: -------------------------------------------------------------------------------- 1 | enum TreeObject { 2 | TreeValue(String), 3 | TreeList(TreeObject...) 4 | 5 | define as_string: String { 6 | match self: { 7 | case TreeValue(v): 8 | return v 9 | 10 | case TreeList(l): 11 | var result = "[" 12 | var size = l.size() - 1 13 | 14 | for i in 0...size: { 15 | result = "{}{}".format(result, l[i].as_string()) 16 | if i != size: { 17 | result = result ++ " " 18 | } 19 | } 20 | 21 | return result ++ "]" 22 | } 23 | } 24 | } 25 | 26 | stdout.write("Here's a JSON-like enum value as a string: ") 27 | print( 28 | TreeList( 29 | TreeList( 30 | TreeValue("abc"), 31 | TreeValue("def") 32 | ), 33 | TreeValue("1"), 34 | TreeValue("2"), 35 | TreeList( 36 | TreeList( 37 | TreeValue("0") 38 | ) 39 | ) 40 | ).as_string() 41 | ) 42 | -------------------------------------------------------------------------------- /try/sum_numbers_from_argv.lily: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.argv.map(String.parse_i) 3 | .reject(Option.is_none) 4 | .map(Option.unwrap) 5 | .fold(0, (|a, b| a + b)) 6 | |> print 7 | --------------------------------------------------------------------------------