diff --git a/crates/bytecode/src/compiler.rs b/crates/bytecode/src/compiler.rs index 16ef360fe..d62d307bd 100644 --- a/crates/bytecode/src/compiler.rs +++ b/crates/bytecode/src/compiler.rs @@ -34,7 +34,7 @@ enum ErrorKind { u16::MAX )] JumpOffsetIsTooLarge(usize), - #[error("Function has too many {property} ({amount})")] + #[error("function has too many {property} ({amount})")] FunctionPropertyLimit { property: String, amount: usize }, #[error("missing argument in for loop")] MissingArgumentInForLoop, @@ -64,7 +64,7 @@ enum ErrorKind { OutOfPositionMatchEllipsis, #[error("root chain node out of position")] OutOfPositionRootNodeInChain, - #[error("The compiled bytecode is larger than the maximum size of 4GB (size: {0} bytes)")] + #[error("the compiled bytecode is larger than the maximum size of 4GB (size: {0} bytes)")] ResultingBytecodeIsTooLarge(usize), #[error("too many targets in assignment ({0})")] TooManyAssignmentTargets(usize), @@ -79,7 +79,7 @@ enum ErrorKind { UnassignedBreakValue, #[error("unexpected Ellipsis")] UnexpectedEllipsis, - #[error("unexpected Wildcard")] + #[error("attempting to access an ignored value")] UnexpectedWildcard, #[error("expected {expected} patterns in match arm, found {unexpected}")] UnexpectedMatchPatternCount { expected: usize, unexpected: usize }, diff --git a/crates/bytecode/src/module_loader.rs b/crates/bytecode/src/module_loader.rs index 7b0477ee8..b14a29667 100644 --- a/crates/bytecode/src/module_loader.rs +++ b/crates/bytecode/src/module_loader.rs @@ -21,9 +21,9 @@ pub enum ModuleLoaderErrorKind { Compiler(#[from] CompilerError), #[error(transparent)] Io(#[from] io::Error), - #[error("Failed to get parent of path ('{0}')")] + #[error("failed to get parent of path ('{0}')")] FailedToGetPathParent(PathBuf), - #[error("Unable to find module '{0}'")] + #[error("unable to find module '{0}'")] UnableToFindModule(String), } diff --git a/crates/cli/docs/cli.md b/crates/cli/docs/cli.md index 1104ec362..22ce67b53 100644 --- a/crates/cli/docs/cli.md +++ b/crates/cli/docs/cli.md @@ -27,6 +27,7 @@ Welcome to Koto » 'hello!' ➝ hello! ``` + ## Help The [language guide][guide] and the [core library reference][core], diff --git a/crates/cli/docs/core_lib/test.md b/crates/cli/docs/core_lib/test.md index 91658179d..b9ac62aa5 100644 --- a/crates/cli/docs/core_lib/test.md +++ b/crates/cli/docs/core_lib/test.md @@ -107,20 +107,49 @@ assert_near 1 % 0.2, 0.2 |tests: Map| -> Null ``` -Runs the tests contained in the map. +Runs the `@test` functions contained in the map. + +`@pre_test` and `@post_test` functions can be implemented in the same way as when exporting +[module tests](../language_guide.md#module-tests). +`@pre_test` will be run before each `@test`, and `@post_test` will be run after. + ### Example -```koto,skip_check -my_tests = - @pre_test: || self.test_data = 1, 2, 3 - @post_test: || self.test_data = null +```koto +make_x = |n| + data: n + @+: |other| make_x self.data + other.data + @-: |other| make_x self.data - other.data + +x_tests = + @pre_test: || + self.x1 = make_x 100 + self.x2 = make_x 200 - @test data_size: || assert_eq self.test_data.size(), 3 - @test failure: || assert_eq self.test_data.size(), 0 + @post_test: || + print 'Test complete' + + @test addition: || + print 'Testing addition' + assert_eq self.x1 + self.x2, make_x 300 + + @test subtraction: || + print 'Testing subtraction' + assert_eq self.x1 - self.x2, make_x -100 + + @test failing_test: || + print 'About to fail' + assert false try - test.run_tests my_tests -catch error - print "An error occurred while running my_tests:\n {error}" + test.run_tests x_tests +catch _ + print 'A test failed' +check! Testing addition +check! Test complete +check! Testing subtraction +check! Test complete +check! About to fail +check! A test failed ``` diff --git a/crates/cli/docs/language_guide.md b/crates/cli/docs/language_guide.md index 0330dc7d8..a18a4e6db 100644 --- a/crates/cli/docs/language_guide.md +++ b/crates/cli/docs/language_guide.md @@ -150,6 +150,18 @@ print! x check! true ``` +The result of an assignment is the value that's being assigned, so chained assignments are possible. + +```koto +print! x = 1 +check! 1 + +print! a = b = 100 +check! 100 +print! a + b +check! 200 +``` + [Compound assignment][compound-assignment] operators are also available. For example, `x *= y` is a simpler way of writing `x = x * y`. @@ -384,6 +396,22 @@ print! '2 plus 3 is {2 + 3}.' check! 2 plus 3 is 5. ``` +### String Indexing + +Individual _bytes_ of a string can be accessed via indexing with `[]` braces. + +```koto +print! 'abcdef'[3] +check! d +print! 'xyz'[1..] +check! yz +``` + +Care must be taken when using indexing with strings that could contain +non-[ASCII][ascii] data. +If the indexed bytes would produce invalid UTF-8 data then an +error will be thrown. To access Unicode characters see [`string.chars`][chars]. + ### String Escape Codes Strings can contain the following escape codes to define special characters, @@ -425,7 +453,7 @@ check! This string doesn't contain newlines. ### Single or Double Quotes Both single `'` and double `"` quotes are valid for defining strings in Koto -and can be used interchangeably. +and have the same meaning. A practical reason to choose one over the other is that the alternate quote type can be used in a string without needing to use escape characters. @@ -438,22 +466,6 @@ print "This string contains unescaped 'single quotes'." check! This string contains unescaped 'single quotes'. ``` -### String Indexing - -Individual _bytes_ of a string can be accessed via indexing with `[]` braces. - -```koto -print! 'abcdef'[3] -check! d -print! 'xyz'[1..] -check! yz -``` - -Care must be taken when using indexing with strings that could contain -non-[ASCII][ascii] data. -If the indexed bytes would produce invalid UTF-8 data then an -error will be thrown. To access Unicode characters see [`string.chars`][chars]. - ### Raw Strings When a string contains a lot of special characters, it can be preferable to use @@ -540,7 +552,6 @@ of optional parentheses, `f(1, 2)` is _not the same_ as `f (1, 2)`. The former is parsed as a call to `f` with two arguments, whereas the latter is a call to `f` with a tuple as the single argument. - ### Return When the function should be exited early, the `return` keyword can be used. @@ -596,8 +607,8 @@ check! 32 ## Maps -_Maps_ in Koto are containers that contain a series of -_entries_ with _keys_ that correspond to [associated][associated] _values_. +_Maps_ in Koto are [associative containers][associated] that contain a series of +_entries_ with _keys_ that correspond to associated _values_. The `.` dot operator returns the value associated with a particular key. @@ -666,8 +677,10 @@ check! {apples: 42, pears: 123, lemons: 63} ### Shorthand Values -Koto supports a shorthand notation when creating maps with inline syntax. -If a value isn't provided for a key, then Koto will look for a value in scope +When creating maps with inline syntax, Koto supports a shorthand notation that +simplifies adding existing values to the map. + +If a value isn't provided for a key, then Koto will look for a value that matches the key's name, and if one is found then it will be used as that entry's value. @@ -796,7 +809,7 @@ available in the `help` command of the [Koto CLI][cli]. ### Prelude Koto's _prelude_ is a collection of core library items that are automatically -made available in a Koto script without the need for first calling `import`. +made available in a Koto script without the need for first calling [`import`](#import). The modules that make up the core library are all included by default in the prelude. The following functions are also added to the prelude by default: @@ -919,9 +932,12 @@ with `...` available for capturing the rest of the sequence. ```koto print! match ['a', 'b', 'c'].extend [1, 2, 3] - ('a', 'b') then "A list containing 'a' and 'b'" - (1, ...) then "Starts with '1'" - (..., 'y', last) then "Ends with 'y' followed by '{last}'" + ('a', 'b') then + "A list containing 'a' and 'b'" + (1, ...) then + "Starts with '1'" + (..., 'y', last) then + "Ends with 'y' followed by '{last}'" ('a', x, others...) then "Starts with 'a', followed by '{x}', then {size others} others" unmatched then "other: {unmatched}" @@ -1087,7 +1103,7 @@ check! null ### Iterator Generators -The [`iterator` module][iterator] contains iterator _generators_ like +The [`iterator`][iterator] module contains iterator _generators_ like [`once`][once] and [`repeat`][repeat] that generate output values [_lazily_][lazy] during iteration. @@ -1102,11 +1118,10 @@ print! i.next() check! null ``` - ### Iterator Adaptors The output of an iterator can be modified using _adaptors_ from the -[`iterator` module][iterator]. +[`iterator`][iterator] module. ```koto # Create an iterator that keeps any value above 3 @@ -1134,8 +1149,8 @@ check! d ### Iterator Chains -Iterator adaptors can be passed into other adaptors, creating _iterator chains_ -that act as data processing pipelines. +Any iterator can be passed into an adaptor, including other adaptors, +creating _iterator chains_ that act as data processing pipelines. ```koto i = (1, 2, 3, 4, 5) @@ -1172,6 +1187,10 @@ print! (1, 2, 3, 4) check! [22, 44] ``` +Iterator consumers are also available for creating [strings][to_string] and [maps][to_map], +as well as operations like [counting the number of values][iterator-count] yielded from an +iterator, or getting the [total sum][iterator-sum] of an iterator's output. + ## Value Unpacking Multiple assignments can be performed in a single expression by separating the @@ -1229,6 +1248,25 @@ check! ('foo', 42) check! ('bar', 99) ``` +#### Ignoring Unpacked Values + +`_` can be used as a placeholder for unpacked values that aren't needed elsewhere +in the code and can be ignored. + +If you would like to add a name to the ignored value as a reminder, +then the name can be appended to `_`. Ignored values (any variables starting with +`_`) can be written to, but can't be accessed. + +```koto +a, _, c = 10..20 +print! a, c +check! (10, 12) + +_first, second = 'xyz' +print! second +check! y +``` + ## Generators Generators are iterators that are made by calling _generator functions_, @@ -1413,7 +1451,7 @@ check! ('l', 'l', 'ø') ## Type Checks Koto is a primarily a dynamically typed language, however in more complex programs -you might find it beneficial to add type checks. +you might find it beneficial to add _type checks_. These checks can help in catching errors earlier, and can also act as documentation for the reader. @@ -1519,7 +1557,7 @@ check! null #### `Any` -The `Any` type will result in a successful check with any value. +The `Any` type hint will result in a successful check with any value. ```koto print! let x: Any = 'hello' @@ -1549,7 +1587,7 @@ check! 199 #### `Iterable` -The `Iterable` type is useful when any iterable value can be accepted. +The `Iterable` type hint is useful when any iterable value can be accepted. ```koto let a: Iterable, b: Iterable = [1, 2], 3..=5 @@ -1803,7 +1841,7 @@ check! 60 ### Ignoring Arguments -The wildcard `_` can be used to ignore function arguments. +As with [assignments](#ignoring-unpacked-values), `_` can be used to ignore function arguments. ```koto # A function that sums the first and third elements of a container @@ -1813,16 +1851,13 @@ print! f [100, 10, 1] check! 101 ``` -If you would like to keep the name of the ignored value as a reminder, -then `_` can be used as a prefix for an identifier. Identifiers starting with -`_` can be written to, but can't be accessed. - ```koto -my_map = {foo_a: 1, bar_a: 2, foo_b: 3, bar_b: 4} +my_map = {foo1: 1, bar1: 2, foo2: 3, bar2: 4} + print! my_map .keep |(key, _value)| key.starts_with 'foo' .to_tuple() -check! (('foo_a', 1), ('foo_b', 3)) +check! (('foo1', 1), ('foo2', 3)) ``` ## Objects and Metamaps @@ -1893,9 +1928,9 @@ check! -100 #### `@size` and `@index` -The `@size` metakey defines how the object should report its size, -while the `@index` metakey defines what values should be returned when indexing is -performed on the object. +The `@size` metakey defines how an object should report its size, +while the `@index` metakey defines which values should be returned +when indexing is performed. If `@size` is implemented, then `@index` should also be implemented. @@ -1939,9 +1974,10 @@ check! first: 10, remaining: 4 #### `@index_mut` -The `@index_mut` metakey defines how the object should behave when index-assignment is used. +The `@index_mut` metakey defines how an object should behave when index-assignment is used. -The given value should be a function that takes an index as the first argument, and the value to be assigned as the second argument. +The given value should be a function that takes an index as the first argument, +with the second argument being the value to be assigned. ```koto foo = |data| @@ -1957,7 +1993,7 @@ check! hello #### `@call` -The `@call` metakey defines how the object should behave when its called as a +The `@call` metakey defines how an object should behave when its called as a function. ```koto @@ -1976,8 +2012,9 @@ check! 8 #### `@iterator` -The `@iterator` metakey defines how iterators should be created when the object +The `@iterator` metakey defines how iterators should be created when an object is used in an iterable context. + When called, `@iterator` should return an iterable value that will then be used for iterator operations. @@ -1989,24 +2026,22 @@ foo = |n| yield n + 2 yield n + 3 -print! (foo 0).to_tuple() +print! foo(0).to_tuple() check! (1, 2, 3) -print! (foo 100).to_list() +print! foo(100).to_list() check! [101, 102, 103] ``` Note that this key will be ignored if the object also implements `@next`, which implies that the object is _already_ an iterator. - #### `@next` The `@next` metakey allows for objects to behave as iterators. Whenever the runtime needs to produce an iterator from an object, it will first -check the metamap for an implementation of `@next`, before looking for -`@iterator`. +check the metamap for an implementation of `@next`, before looking for `@iterator`. The `@next` function will be called repeatedly during iteration, with the returned value being used as the iterator's output. @@ -2030,9 +2065,8 @@ check! (10, 11, 12, 13, 14) #### `@next_back` -The `@next_back` metakey is used by -[`iterator.reversed`](./core_lib/iterator.md#reversed) when producing a reversed -iterator. +The `@next_back` metakey is used by [`iterator.reversed`][iterator-reversed] +when producing a reversed iterator. The runtime will only look for `@next_back` if `@next` is implemented. @@ -2052,7 +2086,7 @@ check! (2, 1, 0) #### `@display` -The `@display` metakey defines how the object should be represented when +The `@display` metakey defines how an object should be represented when displaying the object as a string. ```koto @@ -2070,15 +2104,16 @@ check! The value of x is 'Foo(-1)' #### `@type` -The `@type` metakey takes a string as a value which is used when checking the -value's type, e.g. with [`koto.type`](./core_lib/koto.md#type) +The `@type` metakey takes a string which is used when checking a +value's type, e.g. with [type checks](#type-checks) or [`koto.type`][koto-type]. ```koto foo = |n| data: n @type: "Foo" -print! koto.type (foo 42) +let x: Foo = foo 42 +print! koto.type x check! Foo ``` @@ -2144,7 +2179,7 @@ check! ('data') ### Sharing Metamaps Metamaps can be shared between objects by using -[`Map.with_meta`](./core_lib/map.md#with_meta), which helps to avoid inefficient +[`Map.with_meta`][map-with_meta], which helps to avoid inefficient duplication when creating a lot of objects. In the following example, behavior is overridden in a single metamap, which is @@ -2234,90 +2269,6 @@ catch other check! Throwing a String ``` -## Testing - -Koto includes a simple testing framework that help you to check that your code -is behaving as you expect through automated checks. - -### Assertions - -The core library includes a collection of _assertion_ functions in the -[`test` module](./core_lib/test.md), -which are included by default in the [prelude](#prelude). - -```koto -try - assert 1 + 1 == 3 -catch error - print 'An assertion failed' -check! An assertion failed - -try - assert_eq 'hello', 'goodbye' -catch error - print 'An assertion failed' -check! An assertion failed -``` - -### Organizing Tests - -Tests can be organized by collecting `@test` functions in a map. - -The tests can then be run manually with [`test.run_tests`](./core_lib/test.md#run_tests). - -For automatic testing, see the description of exporting `@test` functions in the -[following section](#modules). - -```koto -basic_tests = - @test add: || assert_eq 1 + 1, 2 - @test subtract: || assert_eq 1 - 1, 0 - -test.run_tests basic_tests -``` - -For setup and cleanup operations shared across tests, -`@pre_test` and `@post_test` metakeys can be implemented. -`@pre_test` will be run before each `@test`, and `@post_test` will be run after. - -```koto -make_x = |n| - data: n - @+: |other| make_x self.data + other.data - @-: |other| make_x self.data - other.data - -x_tests = - @pre_test: || - self.x1 = make_x 100 - self.x2 = make_x 200 - - @post_test: || - print 'Test complete' - - @test addition: || - print 'Testing addition' - assert_eq self.x1 + self.x2, make_x 300 - - @test subtraction: || - print 'Testing subtraction' - assert_eq self.x1 - self.x2, make_x -100 - - @test failing_test: || - print 'About to fail' - assert false - -try - test.run_tests x_tests -catch _ - print 'A test failed' -check! Testing addition -check! Test complete -check! Testing subtraction -check! Test complete -check! About to fail -check! A test failed -``` - ## Modules Koto includes a module system that helps you to organize and re-use your code @@ -2477,19 +2428,11 @@ print! koto.exports().x check! 99 ``` -### `@test` functions and `@main` +### `@main` -A module can export `@test` functions, which by default will be automatically run -after the module has been compiled and initialized. +A module can export a `@main` function, which will be called after the module has been compiled and successfully initialized. -The runtime can be configured to skip running tests, so scripts shouldn't rely on -tests being run. - -Additionally, a module can export a `@main` function. -The `@main` function will be called after the module has been compiled and -initialized, and after any exported `@test` functions have been successfully run. - -The use of `export` is optional when assigning to metakeys like `@main` and `@test`. +The use of `export` is optional when assigning to module metakeys like `@main`. ```koto,skip_run ################## @@ -2499,20 +2442,14 @@ The use of `export` is optional when assigning to metakeys like `@main` and `@te export say_hello = |name| 'Hello, {name}!' # Equivalent to `export @main = ...` -@main = || - print '`my_module` initialized' - -@test hello_world = || - print 'Testing...' - assert_eq (say_hello 'World'), 'Hello, World!' +@main = || print '`my_module` initialized' ################## # other.koto # ################## from my_module import say_hello -check! Testing... -check! Successfully initialized `my_module` +check! `my_module` initialized say_hello 'Koto' check! 'Hello, Koto!' @@ -2527,6 +2464,114 @@ E.g. When an `import foo` expression is run, then a `foo.koto` file will be looked for in the same location as the current script, and if `foo.koto` isn't found then the runtime will look for `foo/main.koto`. +## Testing + +Koto includes a simple testing framework that allows you to automatically check that your code is behaving as you would expect. + +### Assertions + +The core library includes a collection of _assertion_ functions which +throw errors if a given condition isn't met. + +The assertion functions are found in the [`test` module](./core_lib/test.md), +and are included by default in the [prelude](#prelude). + +```koto +try + assert 1 + 1 == 3 +catch error + print 'An assertion failed' +check! An assertion failed + +try + assert_eq 'hello', 'goodbye' +catch error + print 'An assertion failed' +check! An assertion failed +``` + +### Module Tests + +Tests can be added to a module by exporting `@test` functions. A test function is considered to have failed if it throws an error (e.g. from an assertion). + +By default, tests will be run after a module has been successfully initialized. If the module also exports `@main` then it will be called after all tests have run successfully. + +```koto,skip_run +################## +# my_module.koto # +################## + +export say_hello = |name| 'Hello, {name}!' + +@main = || print '`my_module` initialized' + +@test say_hello = || + print 'Running @test say_hello' + assert_eq say_hello('Test'), 'Hello, Test!' + +################## +# other.koto # +################## + +from my_module import say_hello +check! Running @test say_hello +check! `my_module` initialized +``` + +`@pre_test` and `@post_test` functions can be implemented alongside tests +for setup and cleanup operations. +`@pre_test` will be run before each `@test`, and `@post_test` will be run after. + +```koto,skip_run +################## +# my_module.koto # +################## + +export say_hello = |name| 'Hello, {name}!' + +@main = || print '`my_module` initialized' + +@pre_test = || + print 'In @pre_test' + +@post_test = || + print 'In @post_test' + +@test say_hello_1 = || + print 'Running @test say_hello_1' + assert_eq say_hello('One'), 'Hello, One!' + +@test say_hello_2 = || + print 'Running @test say_hello_2' + assert_eq say_hello('Two'), 'Hello, Two!' + +################## +# other.koto # +################## + +from my_module import say_hello +check! In @pre_test +check! Running @test say_hello_1 +check! In @post_test +check! In @pre_test +check! Running @test say_hello_2 +check! In @post_test +check! `my_module` initialized +``` + +### Running Tests Manually + +Tests can be run manually by calling [`test.run_tests`][test-run_tests] +with a map that contains `@test` functions. + +```koto +my_tests = + @test add: || assert_eq 1 + 1, 2 + @test subtract: || assert_eq 1 - 1, 0 + +test.run_tests my_tests +``` + --- [ascii]: https://en.wikipedia.org/wiki/ASCII @@ -2537,10 +2582,14 @@ and if `foo.koto` isn't found then the runtime will look for `foo/main.koto`. [core]: ./core_lib [immutable]: https://en.wikipedia.org/wiki/Immutable_object [iterator]: ./core_lib/iterator.md +[iterator-count]: ./core_lib/iterator.md#count +[iterator-reversed]: ./core_lib/iterator.md#reversed +[iterator-sum]: ./core_lib/iterator.md#sum [koto-exports]: ./core_lib/koto.md#exports [koto-type]: ./core_lib/koto.md#type [map-get]: ./core_lib/map.md#get [map-insert]: ./core_lib/map.md#insert +[map-with_meta]: ./core_lib/map.md#with_meta [lazy]: https://en.wikipedia.org/wiki/Lazy_evaluation [next]: ./core_lib/iterator.md#next [once]: ./core_lib/iterator.md#once @@ -2548,7 +2597,10 @@ and if `foo.koto` isn't found then the runtime will look for `foo/main.koto`. [operation-order]: https://en.wikipedia.org/wiki/Order_of_operations#Conventional_order [repeat]: ./core_lib/iterator.md#repeat [rust-format-options]: https://doc.rust-lang.org/std/fmt/#formatting-parameters +[test-run_tests]: ./core_lib/test.md#run_tests [to_list]: ./core_lib/iterator.md#to_list +[to_map]: ./core_lib/iterator.md#to_map +[to_string]: ./core_lib/iterator.md#to_string [to_tuple]: ./core_lib/iterator.md#to_tuple [utf-8]: https://en.wikipedia.org/wiki/UTF-8 [variadic]: https://en.wikipedia.org/wiki/Variadic_function diff --git a/crates/koto/examples/wasm/src/lib.rs b/crates/koto/examples/wasm/src/lib.rs index 145dc3cc9..f8f2673da 100644 --- a/crates/koto/examples/wasm/src/lib.rs +++ b/crates/koto/examples/wasm/src/lib.rs @@ -47,11 +47,11 @@ impl KotoFile for BlockedInput { impl KotoWrite for BlockedInput {} impl KotoRead for BlockedInput { fn read_line(&self) -> Result> { - runtime_error!("Unsupported in the browser") + runtime_error!("unsupported in the browser") } fn read_to_string(&self) -> Result { - runtime_error!("Unsupported in the browser") + runtime_error!("unsupported in the browser") } } diff --git a/crates/parser/src/error.rs b/crates/parser/src/error.rs index c879ddea1..a45e4aaec 100644 --- a/crates/parser/src/error.rs +++ b/crates/parser/src/error.rs @@ -8,25 +8,25 @@ use crate::string_format_options::StringFormatError; #[derive(Error, Clone, Debug)] #[allow(missing_docs)] pub enum InternalError { - #[error("There are more nodes in the program than the AST can support")] + #[error("there are more nodes in the program than the AST can support")] AstCapacityOverflow, - #[error("There are more constants in the program than the runtime can support")] + #[error("there are more constants in the program than the runtime can support")] ConstantPoolCapacityOverflow, - #[error("Expected ':' after map key")] + #[error("expected ':' after map key")] ExpectedMapColon, - #[error("Failed to parse ID")] + #[error("failed to parse ID")] IdParseFailure, - #[error("Failed to parse chain")] + #[error("failed to parse chain")] ChainParseFailure, - #[error("Missing assignment target")] + #[error("missing assignment target")] MissingAssignmentTarget, - #[error("Frame unavailable during parsing")] + #[error("frame unavailable during parsing")] MissingFrame, - #[error("Failed to parse number")] + #[error("failed to parse number")] NumberParseFailure, - #[error("Failed to parse raw string")] + #[error("failed to parse raw string")] RawStringParseFailure, - #[error("Unexpected token")] + #[error("unexpected token")] UnexpectedToken, } @@ -37,35 +37,35 @@ pub enum InternalError { #[derive(Error, Clone, Debug)] #[allow(missing_docs)] pub enum ExpectedIndentation { - #[error("Expected expression after assignment operator")] + #[error("expected expression after assignment operator")] AssignmentExpression, - #[error("Expected indented block for catch expression")] + #[error("expected indented block for catch expression")] CatchBody, - #[error("Expected indented block for 'else'.")] + #[error("expected indented block for 'else'.")] ElseBlock, - #[error("Expected indented block for 'else if'.")] + #[error("expected indented block for 'else if'.")] ElseIfBlock, - #[error("Expected indented block for finally expression")] + #[error("expected indented block for finally expression")] FinallyBody, - #[error("Expected indented block as for loop body")] + #[error("expected indented block as for loop body")] ForBody, - #[error("Expected function body")] + #[error("expected function body")] FunctionBody, - #[error("Expected indented block as loop body")] + #[error("expected indented block as loop body")] LoopBody, - #[error("Expected indented arm for match expression")] + #[error("expected indented arm for match expression")] MatchArm, - #[error("Expected expression after binary operator")] + #[error("expected expression after binary operator")] RhsExpression, - #[error("Expected indented arm for switch expression")] + #[error("expected indented arm for switch expression")] SwitchArm, - #[error("Error parsing if expression, expected 'then' keyword or indented block.")] + #[error("error parsing if expression, expected 'then' keyword or indented block.")] ThenKeywordOrBlock, - #[error("Expected indented block for try expression")] + #[error("expected indented block for try expression")] TryBody, - #[error("Expected indented block as until loop body")] + #[error("expected indented block as until loop body")] UntilBody, - #[error("Expected indented block as while loop body")] + #[error("expected indented block as while loop body")] WhileBody, } @@ -73,133 +73,133 @@ pub enum ExpectedIndentation { #[derive(Error, Clone, Debug)] #[allow(missing_docs)] pub enum SyntaxError { - #[error("Ascii value out of range, the maximum is \\x7f")] + #[error("ascii value out of range, the maximum is \\x7f")] AsciiEscapeCodeOutOfRange, - #[error("Expected end of arguments ')'")] + #[error("expected end of arguments ')'")] ExpectedArgsEnd, - #[error("Expected target for assignment")] + #[error("expected target for assignment")] ExpectedAssignmentTarget, - #[error("Expected '=' assignment after meta key")] + #[error("expected '=' assignment after meta key")] ExpectedAssignmentAfterMetaKey, - #[error("Expected argument for catch expression")] + #[error("expected argument for catch expression")] ExpectedCatchArgument, - #[error("Expected catch expression after try")] + #[error("expected catch expression after try")] ExpectedCatch, - #[error("Expected closing parenthesis ')'")] + #[error("expected closing parenthesis ')'")] ExpectedCloseParen, - #[error("Expected expression after 'else'.")] + #[error("expected expression after 'else'.")] ExpectedElseExpression, - #[error("Expected condition for 'else if'.")] + #[error("expected condition for 'else if'.")] ExpectedElseIfCondition, - #[error("Expected expression")] + #[error("expected expression")] ExpectedExpression, - #[error("Expected arguments in for loop")] + #[error("expected arguments in for loop")] ExpectedForArgs, - #[error("Expected 'in' keyword in for loop")] + #[error("expected 'in' keyword in for loop")] ExpectedForInKeyword, - #[error("Expected iterable in for loop")] + #[error("expected iterable in for loop")] ExpectedForIterable, - #[error("Expected format string after ':'")] + #[error("expected format string after ':'")] ExpectedFormatString, - #[error("Expected end of function arguments '|'")] + #[error("expected end of function arguments '|'")] ExpectedFunctionArgsEnd, - #[error("Expected ID in import expression")] + #[error("expected ID in import expression")] ExpectedIdInImportExpression, - #[error("Expected condition after 'if'")] + #[error("expected condition after 'if'")] ExpectedIfCondition, - #[error("Expected import after from")] + #[error("expected import after from")] ExpectedImportAfterFrom, - #[error("Expected module ID in import expression")] + #[error("expected module ID in import expression")] ExpectedImportModuleId, - #[error("Expected index end ']'")] + #[error("expected index end ']'")] ExpectedIndexEnd, - #[error("Expected index expression")] + #[error("expected index expression")] ExpectedIndexExpression, - #[error("Expected id after 'as'")] + #[error("expected id after 'as'")] ExpectedIdAfterAs, - #[error("Expected List end ']'")] + #[error("expected List end ']'")] ExpectedListEnd, - #[error("Expected ':' after map key")] + #[error("expected ':' after map key")] ExpectedMapColon, - #[error("Expected '}}' at end of map declaration")] + #[error("expected '}}' at end of map declaration")] ExpectedMapEnd, - #[error("Expected map entry")] + #[error("expected map entry")] ExpectedMapEntry, - #[error("Expected key after '.' in Map access")] + #[error("expected key after '.' in Map access")] ExpectedMapKey, - #[error("Expected value after ':' in Map")] + #[error("expected value after ':' in Map")] ExpectedMapValue, - #[error("Expected expression in match arm")] + #[error("expected expression in match arm")] ExpectedMatchArmExpression, - #[error("Expected expression after then in match arm")] + #[error("expected expression after then in match arm")] ExpectedMatchArmExpressionAfterThen, - #[error("Expected condition after if in match arm")] + #[error("expected condition after if in match arm")] ExpectedMatchCondition, - #[error("Expected expression after match")] + #[error("expected expression after match")] ExpectedMatchExpression, - #[error("Expected pattern for match arm")] + #[error("expected pattern for match arm")] ExpectedMatchPattern, - #[error("Expected id after @meta")] + #[error("expected id after @meta")] ExpectedMetaId, - #[error("Expected a module path after 'from'")] + #[error("expected a module path after 'from'")] ExpectedPathAfterFrom, - #[error("Expected a line break before starting a map block")] + #[error("expected a line break before starting a map block")] ExpectedLineBreakBeforeMapBlock, - #[error("Expected '}}' at end of string placeholder")] + #[error("expected '}}' at end of string placeholder")] ExpectedStringPlaceholderEnd, - #[error("Expected expression in switch arm")] + #[error("expected expression in switch arm")] ExpectedSwitchArmExpression, - #[error("Expected expression after 'then' in switch arm")] + #[error("expected expression after 'then' in switch arm")] ExpectedSwitchArmExpressionAfterThen, - #[error("Expected a test name")] + #[error("expected a test name")] ExpectedTestName, - #[error("Expected expression after 'then'")] + #[error("expected expression after 'then'")] ExpectedThenExpression, - #[error("Expected condition in until loop")] + #[error("expected condition in until loop")] ExpectedUntilCondition, - #[error("Expected condition in while loop")] + #[error("expected condition in while loop")] ExpectedWhileCondition, - #[error("Expected a type after ':'")] + #[error("expected a type after ':'")] ExpectedType, #[error(transparent)] FormatStringError(StringFormatError), - #[error("Non-inline if expression isn't allowed in this context")] + #[error("non-inline if expression isn't allowed in this context")] IfBlockNotAllowedInThisContext, - #[error("Ellipsis found outside of nested match patterns")] + #[error("ellipsis found outside of nested match patterns")] MatchEllipsisOutsideOfNestedPatterns, #[error("'else' can only be used in the last arm in a match expression")] MatchElseNotInLastArm, - #[error("Nested types aren't currently supported")] + #[error("nested types aren't currently supported")] NestedTypesArentSupported, - #[error("Keyword reserved for future use")] + #[error("keyword reserved for future use")] ReservedKeyword, #[error("'self' doesn't need to be declared as an argument")] SelfArg, #[error("'else' can only be used in the last arm in a switch expression")] SwitchElseNotInLastArm, - #[error("Unexpected character in numeric escape code")] + #[error("unexpected character in numeric escape code")] UnexpectedCharInNumericEscapeCode, #[error("'.' after imported item. You might want a 'from' import instead")] UnexpectedDotAfterImportItem, - #[error("Unexpected escape pattern in string")] + #[error("unexpected escape pattern in string")] UnexpectedEscapeInString, - #[error("Unexpected 'else' in match arm")] + #[error("unexpected 'else' in match arm")] UnexpectedMatchElse, - #[error("Unexpected if condition in match arm")] + #[error("unexpected if condition in match arm")] UnexpectedMatchIf, - #[error("Unexpected meta key")] + #[error("unexpected meta key")] UnexpectedMetaKey, - #[error("Unexpected 'else' in switch arm")] + #[error("unexpected 'else' in switch arm")] UnexpectedSwitchElse, - #[error("Unexpected '?'")] + #[error("unexpected '?'")] UnexpectedNullCheck, - #[error("Unexpected token")] + #[error("unexpected token")] UnexpectedToken, - #[error("Unicode value out of range, the maximum is \\u{{10ffff}}")] + #[error("unicode value out of range, the maximum is \\u{{10ffff}}")] UnicodeEscapeCodeOutOfRange, - #[error("Unterminated numeric escape code")] + #[error("unterminated numeric escape code")] UnterminatedNumericEscapeCode, - #[error("Unterminated string")] + #[error("unterminated string")] UnterminatedString, } diff --git a/crates/parser/src/string_format_options.rs b/crates/parser/src/string_format_options.rs index 595e8d87f..1aeeeb57c 100644 --- a/crates/parser/src/string_format_options.rs +++ b/crates/parser/src/string_format_options.rs @@ -135,13 +135,13 @@ pub enum StringAlignment { #[derive(Error, Clone, Debug)] #[allow(missing_docs)] pub enum StringFormatError { - #[error("Expected a number '{0}'")] + #[error("expected a number '{0}'")] ExpectedNumber(char), #[error("{0} is larger than the maximum of {}", u32::MAX)] FormatNumberIsTooLarge(u64), - #[error("An unexpected internal error occurred")] + #[error("an unexpected internal error occurred")] InternalError, - #[error("Unexpected token '{0}'")] + #[error("unexpected token '{0}'")] UnexpectedToken(char), } diff --git a/crates/runtime/src/core_lib/io.rs b/crates/runtime/src/core_lib/io.rs index f0202bf08..caed4b0ae 100644 --- a/crates/runtime/src/core_lib/io.rs +++ b/crates/runtime/src/core_lib/io.rs @@ -19,7 +19,7 @@ pub fn make_module() -> KMap { let path = Path::new(path.as_str()).to_path_buf(); match fs::File::create(&path) { Ok(file) => Ok(File::system_file(file, path)), - Err(error) => runtime_error!("Error while creating file: {error}"), + Err(error) => runtime_error!("error while creating file: {error}"), } } unexpected => unexpected_args("|String|", unexpected), @@ -66,9 +66,9 @@ pub fn make_module() -> KMap { [Str(path)] => match fs::canonicalize(path.as_str()) { Ok(path) => match fs::File::open(&path) { Ok(file) => Ok(File::system_file(file, path)), - Err(error) => runtime_error!("Error while opening path: {error}"), + Err(error) => runtime_error!("error while opening path: {error}"), }, - Err(_) => runtime_error!("Failed to canonicalize path"), + Err(_) => runtime_error!("failed to canonicalize path"), }, unexpected => unexpected_args("|String|", unexpected), } @@ -218,7 +218,7 @@ impl File { match args { [KValue::Number(n)] => { if *n < 0.0 { - return runtime_error!("Negative seek positions not allowed"); + return runtime_error!("negative seek positions not allowed"); } self.0.seek(n.into()).map(|_| KValue::Null) } diff --git a/crates/runtime/src/core_lib/list.rs b/crates/runtime/src/core_lib/list.rs index d116ee8ab..ac0e0eb3c 100644 --- a/crates/runtime/src/core_lib/list.rs +++ b/crates/runtime/src/core_lib/list.rs @@ -146,7 +146,7 @@ pub fn make_module() -> KMap { (KValue::List(l), [KValue::Number(n), value]) => { let index: usize = n.into(); if *n < 0.0 || index > l.data().len() { - return runtime_error!("Index out of bounds"); + return runtime_error!("index out of bounds"); } l.data_mut().insert(index, value.clone()); @@ -208,7 +208,7 @@ pub fn make_module() -> KMap { (KValue::List(l), [KValue::Number(n)]) => { let index: usize = n.into(); if *n < 0.0 || index >= l.data().len() { - return runtime_error!("Index out of bounds"); + return runtime_error!("index out of bounds"); } Ok(l.data_mut().remove(index)) @@ -222,7 +222,7 @@ pub fn make_module() -> KMap { match ctx.instance_and_args(is_list, expected_error)? { (_, [KValue::Number(n), ..]) if *n < 0.0 => { - runtime_error!("Expected a non-negative size") + runtime_error!("expected a non-negative size") } (KValue::List(l), [KValue::Number(n)]) => { l.data_mut().resize(n.into(), KValue::Null); @@ -242,7 +242,7 @@ pub fn make_module() -> KMap { match ctx.instance_and_args(is_list, expected_error)? { (KValue::List(l), [KValue::Number(n), f]) if f.is_callable() => { if *n < 0.0 { - return runtime_error!("Expected a non-negative size"); + return runtime_error!("expected a non-negative size"); } let new_size = usize::from(n); diff --git a/crates/runtime/src/core_lib/map.rs b/crates/runtime/src/core_lib/map.rs index 8108c4121..6a39499f9 100644 --- a/crates/runtime/src/core_lib/map.rs +++ b/crates/runtime/src/core_lib/map.rs @@ -214,7 +214,7 @@ pub fn make_module() -> KMap { Some(ordering) => ordering, None => { // This should never happen, ValueKeys can only be made with sortable values - error = Some(runtime_error!("Invalid map key encountered")); + error = Some(runtime_error!("invalid map key encountered")); Ordering::Equal } } diff --git a/crates/runtime/src/core_lib/os/command.rs b/crates/runtime/src/core_lib/os/command.rs index 65e5b1112..2b7c8ea8e 100644 --- a/crates/runtime/src/core_lib/os/command.rs +++ b/crates/runtime/src/core_lib/os/command.rs @@ -233,7 +233,7 @@ macro_rules! child_stream_fn { ($self:expr, $stream:ident, $buffer_wrapper:ident, $child_stream:ident) => {{ let mut this = $self.handle.borrow_mut(); let Some(child) = this.as_mut() else { - return runtime_error!("The process has already finished"); + return runtime_error!("the process has already finished"); }; match child.$stream.take() { @@ -267,7 +267,7 @@ impl Child { fn id(&self) -> Result { let mut this = self.handle.borrow_mut(); let Some(child) = this.as_mut() else { - return runtime_error!("The process has already finished"); + return runtime_error!("the process has already finished"); }; Ok(child.id().into()) } @@ -306,7 +306,7 @@ impl Child { let mut this = self.handle.borrow_mut(); let Some(child) = this.as_mut() else { - return runtime_error!("The process has already finished"); + return runtime_error!("the process has already finished"); }; match child.kill() { @@ -323,7 +323,7 @@ impl Child { self.close_streams(); let Some(child) = self.handle.borrow_mut().take() else { - return runtime_error!("The process has already finished"); + return runtime_error!("the process has already finished"); }; match child.wait_with_output() { @@ -338,7 +338,7 @@ impl Child { let mut this = self.handle.borrow_mut(); let Some(child) = this.as_mut() else { - return runtime_error!("The process has already finished"); + return runtime_error!("the process has already finished"); }; match child.wait() { @@ -379,7 +379,7 @@ impl KotoWrite for ChildStdin { fn write(&self, bytes: &[u8]) -> Result<()> { match self.0.borrow_mut().as_mut() { Some(stream) => stream.write_all(bytes).map_err(map_io_err), - None => runtime_error!("The stream has been closed"), + None => runtime_error!("the stream has been closed"), } } @@ -387,14 +387,14 @@ impl KotoWrite for ChildStdin { self.write(output.as_bytes())?; match self.0.borrow_mut().as_mut() { Some(stream) => stream.write_all("\n".as_bytes()).map_err(map_io_err), - None => runtime_error!("The stream has been closed"), + None => runtime_error!("the stream has been closed"), } } fn flush(&self) -> Result<()> { match self.0.borrow_mut().as_mut() { Some(stream) => stream.flush().map_err(map_io_err), - None => runtime_error!("The stream has been closed"), + None => runtime_error!("the stream has been closed"), } } } @@ -413,7 +413,7 @@ impl KotoRead for ChildStdout { let mut result = String::new(); let bytes_read = match self.0.borrow_mut().as_mut() { Some(stream) => stream.read_line(&mut result).map_err(map_io_err)?, - None => return runtime_error!("The stream has been closed"), + None => return runtime_error!("the stream has been closed"), }; if bytes_read > 0 { Ok(Some(result)) @@ -426,7 +426,7 @@ impl KotoRead for ChildStdout { let mut result = String::new(); match self.0.borrow_mut().as_mut() { Some(stream) => stream.read_to_string(&mut result).map_err(map_io_err)?, - None => return runtime_error!("The stream has been closed"), + None => return runtime_error!("the stream has been closed"), }; Ok(result) } @@ -447,7 +447,7 @@ impl KotoRead for ChildStderr { let mut result = String::new(); let bytes_read = match self.0.borrow_mut().as_mut() { Some(stream) => stream.read_line(&mut result).map_err(map_io_err)?, - None => return runtime_error!("The stream has been closed"), + None => return runtime_error!("the stream has been closed"), }; if bytes_read > 0 { Ok(Some(result)) @@ -460,7 +460,7 @@ impl KotoRead for ChildStderr { let mut result = String::new(); match self.0.borrow_mut().as_mut() { Some(stream) => stream.read_to_string(&mut result).map_err(map_io_err)?, - None => return runtime_error!("The stream has been closed"), + None => return runtime_error!("the stream has been closed"), }; Ok(result) } diff --git a/crates/runtime/src/core_lib/string.rs b/crates/runtime/src/core_lib/string.rs index 3fcaedf4c..ea33e76cd 100644 --- a/crates/runtime/src/core_lib/string.rs +++ b/crates/runtime/src/core_lib/string.rs @@ -96,7 +96,7 @@ pub fn make_module() -> KMap { match String::from_utf8(bytes) { Ok(result) => Ok(result.into()), - Err(_) => runtime_error!("Input failed UTF-8 validation"), + Err(_) => runtime_error!("input failed UTF-8 validation"), } } unexpected => unexpected_args("|Iterable|", unexpected), @@ -225,7 +225,7 @@ pub fn make_module() -> KMap { (KValue::Str(s), [KValue::Number(n)]) => { let base = n.into(); if !(2..=36).contains(&base) { - return runtime_error!("Number base must be within 2..=36"); + return runtime_error!("number base must be within 2..=36"); } if let Ok(result) = i64::from_str_radix(s, base) { diff --git a/crates/runtime/src/core_lib/test.rs b/crates/runtime/src/core_lib/test.rs index cb5f97973..17523930b 100644 --- a/crates/runtime/src/core_lib/test.rs +++ b/crates/runtime/src/core_lib/test.rs @@ -11,7 +11,7 @@ pub fn make_module() -> KMap { match value { KValue::Bool(b) => { if !b { - return runtime_error!("Assertion failed"); + return runtime_error!("assertion failed"); } } unexpected => return unexpected_type("Bool", unexpected), @@ -29,7 +29,7 @@ pub fn make_module() -> KMap { Ok(KValue::Bool(true)) => Ok(KValue::Null), Ok(KValue::Bool(false)) => { runtime_error!( - "Assertion failed, '{}' is not equal to '{}'", + "assertion failed, '{}' is not equal to '{}'", ctx.vm.value_to_string(&a)?, ctx.vm.value_to_string(&b)?, ) @@ -52,7 +52,7 @@ pub fn make_module() -> KMap { Ok(KValue::Bool(true)) => Ok(KValue::Null), Ok(KValue::Bool(false)) => { runtime_error!( - "Assertion failed, '{}' should not be equal to '{}'", + "assertion failed, '{}' should not be equal to '{}'", ctx.vm.value_to_string(&a)?, ctx.vm.value_to_string(&b)? ) @@ -92,7 +92,7 @@ fn number_near(a: KNumber, b: KNumber, allowed_diff: f64) -> Result { Ok(KValue::Null) } else { runtime_error!( - "Assertion failed, '{a}' and '{b}' are not within {allowed_diff} of each other" + "assertion failed, '{a}' and '{b}' are not within {allowed_diff} of each other" ) } } diff --git a/crates/runtime/src/error.rs b/crates/runtime/src/error.rs index 255567837..a4baa8868 100644 --- a/crates/runtime/src/error.rs +++ b/crates/runtime/src/error.rs @@ -21,9 +21,9 @@ pub enum ErrorKind { /// A VM that should be used to format the thrown value vm: KotoVm, }, - #[error("Execution timed out (the limit of {} seconds was reached)", .0.as_secs_f64())] + #[error("execution timed out (the limit of {} seconds was reached)", .0.as_secs_f64())] Timeout(Duration), - #[error("Unable to borrow an object that is already mutably borrowed")] + #[error("unable to borrow an object that is already mutably borrowed")] UnableToBorrowObject, #[error( "Unexpected arguments.\n Expected: {expected}\n Provided: |{}|", @@ -33,14 +33,14 @@ pub enum ErrorKind { expected: String, unexpected: Vec, }, - #[error("Too many arguments - expected {expected}, found {actual}")] + #[error("too many arguments - expected {expected}, found {actual}")] TooManyArguments { expected: u8, actual: u8 }, - #[error("Unexpected type - expected: '{expected}', found: '{}'", unexpected.type_as_string())] + #[error("unexpected type - expected: '{expected}', found: '{}'", unexpected.type_as_string())] UnexpectedType { expected: String, unexpected: KValue, }, - #[error("Unexpected object type - expected: '{expected}', found: '{unexpected}'")] + #[error("unexpected object type - expected: '{expected}', found: '{unexpected}'")] UnexpectedObjectType { expected: &'static str, unexpected: KString, @@ -50,7 +50,7 @@ pub enum ErrorKind { fn_name: &'static str, object_type: KString, }, - #[error("Unable to perform operation '{op}' with '{}' and '{}'", lhs.type_as_string(), rhs.type_as_string())] + #[error("unable to perform operation '{op}' with '{}' and '{}'", lhs.type_as_string(), rhs.type_as_string())] InvalidBinaryOp { lhs: KValue, rhs: KValue, @@ -58,15 +58,15 @@ pub enum ErrorKind { }, #[error(transparent)] CompileError(#[from] ModuleLoaderError), - #[error("Empty call stack")] + #[error("empty call stack")] EmptyCallStack, - #[error("Missing sequence builder")] + #[error("missing sequence builder")] MissingSequenceBuilder, - #[error("Missing string builder")] + #[error("missing string builder")] MissingStringBuilder, - #[error("This operation is unsupported on this platform")] + #[error("this operation is unsupported on this platform")] UnsupportedPlatform, - #[error("An unexpected error occurred, please report this as a bug at https://github.com/koto-lang/koto/issues")] + #[error("an unexpected error occurred, please report this as a bug at https://github.com/koto-lang/koto/issues")] UnexpectedError, } diff --git a/crates/runtime/src/types/iterator.rs b/crates/runtime/src/types/iterator.rs index 0bfbd5d3e..cc6477720 100644 --- a/crates/runtime/src/types/iterator.rs +++ b/crates/runtime/src/types/iterator.rs @@ -205,7 +205,7 @@ impl RangeIterator { if range.is_bounded() { Ok(Self { range }) } else { - runtime_error!("Unbounded ranges can't be used as iterators (range: {range})") + runtime_error!("unbounded ranges can't be used as iterators (range: {range})") } } } @@ -444,13 +444,13 @@ struct MetaIterator { impl MetaIterator { fn new(vm: KotoVm, iterator: KValue) -> Result { let KValue::Map(m) = &iterator else { - return runtime_error!("Expected Map with implementation of @next"); + return runtime_error!("expected Map with implementation of @next"); }; match m.get_meta_value(&UnaryOp::Next.into()) { Some(op) if op.is_callable() => {} Some(op) => return unexpected_type("Callable function from @next", &op), - None => return runtime_error!("Expected implementation of @next"), + None => return runtime_error!("expected implementation of @next"), }; let is_bidirectional = match m.get_meta_value(&UnaryOp::NextBack.into()) { diff --git a/crates/runtime/src/types/map.rs b/crates/runtime/src/types/map.rs index 4f7873311..0cec428e2 100644 --- a/crates/runtime/src/types/map.rs +++ b/crates/runtime/src/types/map.rs @@ -198,7 +198,7 @@ impl KMap { if self.contains_meta_key(&UnaryOp::Display.into()) { let mut vm = ctx .vm() - .ok_or_else(|| Error::from("Missing VM in map display op"))? + .ok_or_else(|| Error::from("missing VM in map display op"))? .spawn_shared_vm(); match vm.run_unary_op(UnaryOp::Display, self.clone().into())? { KValue::Str(display_result) => { diff --git a/crates/runtime/src/types/meta_map.rs b/crates/runtime/src/types/meta_map.rs index 301c09995..ddc5e54c4 100644 --- a/crates/runtime/src/types/meta_map.rs +++ b/crates/runtime/src/types/meta_map.rs @@ -241,15 +241,15 @@ pub fn meta_id_to_key(id: MetaKeyId, name: Option) -> Result { MetaKeyId::Size => MetaKey::UnaryOp(Size), MetaKeyId::Call => MetaKey::Call, MetaKeyId::Named => { - MetaKey::Named(name.ok_or_else(|| Error::from("Missing name for named meta entry"))?) + MetaKey::Named(name.ok_or_else(|| Error::from("missing name for named meta entry"))?) } - MetaKeyId::Test => MetaKey::Test(name.ok_or_else(|| Error::from("Missing name for test"))?), + MetaKeyId::Test => MetaKey::Test(name.ok_or_else(|| Error::from("missing name for test"))?), MetaKeyId::PreTest => MetaKey::PreTest, MetaKeyId::PostTest => MetaKey::PostTest, MetaKeyId::Main => MetaKey::Main, MetaKeyId::Type => MetaKey::Type, MetaKeyId::Base => MetaKey::Base, - MetaKeyId::Invalid => return runtime_error!("Invalid MetaKeyId"), + MetaKeyId::Invalid => return runtime_error!("invalid MetaKeyId"), }; Ok(result) diff --git a/crates/runtime/src/types/range.rs b/crates/runtime/src/types/range.rs index 0755d2949..f9cbe0269 100644 --- a/crates/runtime/src/types/range.rs +++ b/crates/runtime/src/types/range.rs @@ -257,7 +257,7 @@ impl KRange { } } } - _ => return runtime_error!("KRange::pop_front can only be used with bounded ranges"), + _ => return runtime_error!("expected a bounded range"), }; Ok(result) @@ -322,7 +322,7 @@ impl KRange { } } } - _ => return runtime_error!("KRange::pop_back can only be used with bounded ranges"), + _ => return runtime_error!("expected a bounded range"), }; Ok(result) diff --git a/crates/runtime/src/types/value_key.rs b/crates/runtime/src/types/value_key.rs index cde9b4519..5b45cb18b 100644 --- a/crates/runtime/src/types/value_key.rs +++ b/crates/runtime/src/types/value_key.rs @@ -26,7 +26,7 @@ impl TryFrom for ValueKey { if value.is_hashable() { Ok(Self(value)) } else { - runtime_error!("Only hashable values can be used as value keys") + runtime_error!("only hashable values can be used as value keys") } } } diff --git a/crates/runtime/src/vm.rs b/crates/runtime/src/vm.rs index 8fcecdeda..be7f65cbf 100644 --- a/crates/runtime/src/vm.rs +++ b/crates/runtime/src/vm.rs @@ -68,6 +68,8 @@ impl ModuleImportedCallback for T where T: Fn(&Path) + KotoSend + KotoSync {} /// The configurable settings that should be used by the Koto runtime pub struct KotoVmSettings { /// Whether or not tests should be run when importing modules + /// + /// Default: `true` pub run_import_tests: bool, /// An optional duration that limits how long execution is allowed to take. @@ -80,6 +82,8 @@ pub struct KotoVmSettings { /// /// The check is performed between VM instructions, so external functions will still be able to /// block execution. + /// + /// Default: `None` pub execution_limit: Option, /// An optional callback that is called whenever a module is imported by the runtime @@ -89,12 +93,18 @@ pub struct KotoVmSettings { pub module_imported_callback: Option>, /// The runtime's `stdin` + /// + /// Default: [`DefaultStdin`] pub stdin: Ptr, /// The runtime's `stdout` + /// + /// Default: [`DefaultStdout`] pub stdout: Ptr, /// The runtime's `stderr` + /// + /// Default: [`DefaultStderr`] pub stderr: Ptr, } @@ -1402,7 +1412,7 @@ impl KotoVm { // The function was temporary and has been removed from the value stack, // but the capture of `x` is still attempted. It would be cleaner for the compiler to // detect this case but for now a runtime error will have to do. - return runtime_error!("Function not found while attempting to capture a value"); + return runtime_error!("function not found while attempting to capture a value"); }; match function { @@ -1462,7 +1472,7 @@ impl KotoVm { self.set_register(result, display_context.result().into()); Ok(()) } - Err(_) => runtime_error!("Failed to get display value"), + Err(_) => runtime_error!("failed to get display value"), } } } @@ -2146,7 +2156,7 @@ impl KotoVm { Some(None) => { // If the cache contains a None placeholder entry for the module path, // then we're in a recursive import (see below). - return runtime_error!("Recursive import of module '{import_name}'"); + return runtime_error!("recursive import of module '{import_name}'"); } Some(Some(cached_exports)) if compile_result.loaded_from_cache => { self.set_register(import_register, KValue::Map(cached_exports)); @@ -2242,7 +2252,7 @@ impl KotoVm { if *index >= 0.0 && u_index < list_len { list_data[u_index] = value.clone(); } else { - return runtime_error!("Invalid index ({index})"); + return runtime_error!("invalid index ({index})"); } } Range(range) => { @@ -2301,7 +2311,7 @@ impl KotoVm { unexpected => unexpected_type("Tuple with 2 elements", unexpected), } } else { - runtime_error!("Invalid index ({index})") + runtime_error!("invalid index ({index})") } } unexpected => unexpected_type("Number", unexpected), @@ -2315,10 +2325,10 @@ impl KotoVm { let index = usize::from(n); if n < 0.0 { - return runtime_error!("Negative indices aren't allowed ('{n}')"); + return runtime_error!("negative indices aren't allowed ('{n}')"); } else if let Some(size) = size { if index >= size { - return runtime_error!("Index out of bounds - index: {n}, size: {size}"); + return runtime_error!("index out of bounds - index: {n}, size: {size}"); } } @@ -2429,7 +2439,7 @@ impl KotoVm { entries.insert(key, value); Ok(()) } else { - runtime_error!("Insertion not supported for '{}'", o.type_string()) + runtime_error!("insertion not supported for '{}'", o.type_string()) } } unexpected => unexpected_type("a value that supports insertion", unexpected), @@ -2440,7 +2450,7 @@ impl KotoVm { let value = self.clone_register(value); let meta_key = match meta_id_to_key(meta_id, None) { Ok(meta_key) => meta_key, - Err(error) => return runtime_error!("Error while preparing meta key: {error}"), + Err(error) => return runtime_error!("error while preparing meta key: {error}"), }; match self.get_register_mut(map_register) { @@ -2464,7 +2474,7 @@ impl KotoVm { let meta_key = match self.clone_register(name_register) { KValue::Str(name) => match meta_id_to_key(meta_id, Some(name)) { Ok(key) => key, - Err(error) => return runtime_error!("Error while preparing meta key: {error}"), + Err(error) => return runtime_error!("error while preparing meta key: {error}"), }, other => return unexpected_type("String", &other), }; @@ -2482,7 +2492,7 @@ impl KotoVm { let value = self.clone_register(value); let meta_key = match meta_id_to_key(meta_id, None) { Ok(meta_key) => meta_key, - Err(error) => return runtime_error!("Error while preparing meta key: {error}"), + Err(error) => return runtime_error!("error while preparing meta key: {error}"), }; self.exports.insert_meta(meta_key, value); @@ -2500,7 +2510,7 @@ impl KotoVm { let meta_key = match self.clone_register(name_register) { KValue::Str(name) => match meta_id_to_key(meta_id, Some(name)) { Ok(key) => key, - Err(error) => return runtime_error!("Error while preparing meta key: {error}"), + Err(error) => return runtime_error!("error while preparing meta key: {error}"), }, other => return unexpected_type("String", &other), }; @@ -2886,7 +2896,7 @@ impl KotoVm { if size == expected_size { Ok(()) } else { - runtime_error!("The container has a size of '{size}', expected '{expected_size}'") + runtime_error!("the container has a size of '{size}', expected '{expected_size}'") } } diff --git a/crates/runtime/tests/vm_tests.rs b/crates/runtime/tests/vm_tests.rs index dcc51a0e1..4bd420aaa 100644 --- a/crates/runtime/tests/vm_tests.rs +++ b/crates/runtime/tests/vm_tests.rs @@ -1066,7 +1066,7 @@ x match value { KValue::Bool(b) => { if !b { - return runtime_error!("Assertion failed"); + return runtime_error!("assertion failed"); } } unexpected => { diff --git a/libs/color/src/color.rs b/libs/color/src/color.rs index f2479c63f..634670ee5 100644 --- a/libs/color/src/color.rs +++ b/libs/color/src/color.rs @@ -80,7 +80,7 @@ impl Color { (Encoding::Oklch(c), 1) => c.chroma = value, (Encoding::Oklch(c), 2) => c.hue = value.into(), (_, 3) => self.alpha = value, - _ => return runtime_error!("Invalid component index ({index})"), + _ => return runtime_error!("invalid component index ({index})"), } Ok(()) diff --git a/libs/json/src/lib.rs b/libs/json/src/lib.rs index da70fd3e4..7ebb3db6f 100644 --- a/libs/json/src/lib.rs +++ b/libs/json/src/lib.rs @@ -12,7 +12,7 @@ pub fn json_value_to_koto_value(value: &serde_json::Value) -> Result { Some(n64) => KValue::Number(n64.into()), None => match n.as_f64() { Some(n64) => KValue::Number(n64.into()), - None => return runtime_error!("Number is out of range: {n}"), + None => return runtime_error!("number is out of range: {n}"), }, }, JsonValue::String(s) => KValue::Str(s.as_str().into()), diff --git a/libs/random/src/lib.rs b/libs/random/src/lib.rs index 058589fe8..8bafee924 100644 --- a/libs/random/src/lib.rs +++ b/libs/random/src/lib.rs @@ -180,7 +180,7 @@ impl ChaChaRng { match vm.run_unary_op(UnaryOp::Size, arg.clone())? { Number(size) => { if size <= 0 { - return runtime_error!("Expected a positive @size, found {}", size); + return runtime_error!("expected a positive @size, found {}", size); } for i in (1..usize::from(size)).rev() { diff --git a/libs/regex/src/lib.rs b/libs/regex/src/lib.rs index b41186029..9bfb4d2c3 100755 --- a/libs/regex/src/lib.rs +++ b/libs/regex/src/lib.rs @@ -19,7 +19,7 @@ impl Regex { pub fn new(pattern: &str) -> Result { match regex::Regex::new(pattern) { Ok(r) => Ok(Self(r.into())), - Err(e) => runtime_error!("Failed to parse regex pattern: {e}"), + Err(e) => runtime_error!("failed to parse regex pattern: {e}"), } } diff --git a/libs/toml/src/lib.rs b/libs/toml/src/lib.rs index 88141e825..6a6593fe6 100644 --- a/libs/toml/src/lib.rs +++ b/libs/toml/src/lib.rs @@ -34,9 +34,9 @@ pub fn make_module() -> KMap { [KValue::Str(s)] => match toml::from_str(s) { Ok(toml) => match toml_to_koto_value(&toml) { Ok(result) => Ok(result), - Err(e) => runtime_error!("Error while parsing input: {e}"), + Err(e) => runtime_error!("error while parsing input: {e}"), }, - Err(e) => runtime_error!("Error while parsing input: {}", e.to_string()), + Err(e) => runtime_error!("error while parsing input: {}", e.to_string()), }, unexpected => unexpected_args("|String|", unexpected), }); diff --git a/libs/yaml/src/lib.rs b/libs/yaml/src/lib.rs index 6755730c1..aa09ded57 100644 --- a/libs/yaml/src/lib.rs +++ b/libs/yaml/src/lib.rs @@ -12,7 +12,7 @@ pub fn yaml_value_to_koto_value(value: &YamlValue) -> Result { Some(n64) => KValue::Number(n64.into()), None => match n.as_f64() { Some(n64) => KValue::Number(n64.into()), - None => return runtime_error!("Number is out of range: {n}"), + None => return runtime_error!("number is out of range: {n}"), }, }, YamlValue::String(s) => KValue::Str(s.as_str().into()), @@ -63,9 +63,9 @@ pub fn make_module() -> KMap { [KValue::Str(s)] => match serde_yaml_ng::from_str(s) { Ok(value) => match yaml_value_to_koto_value(&value) { Ok(result) => Ok(result), - Err(e) => runtime_error!("Error while parsing input: {}", e), + Err(e) => runtime_error!("error while parsing input: {}", e), }, - Err(e) => runtime_error!("Error while parsing input: {}", e.to_string()), + Err(e) => runtime_error!("error while parsing input: {}", e.to_string()), }, unexpected => unexpected_args("|String|", unexpected), });