From bf4bbc80c9cf88d8f5d89069df97522a2cb6cd08 Mon Sep 17 00:00:00 2001 From: octaspire Date: Sun, 17 Feb 2019 15:20:59 +0200 Subject: [PATCH] Add/modify functions to implement 'character', "apply", 'generate' and "map" * "apply" of other Lisps can now be done using Dern's 'eval'. 'eval' can now handle both use cases (regular evaluation and "apply"). * "map/mapcar" of other Lisps can now be done using Dern's 'generate'. It can also be used to generate a collection of values (somewhat like std::generate_n in C++). * 'character' creates a character from a number of the codepoint (for example {D+97}), or from a string containing a unicode character name (for example [greek small letter lambda]). --- .builds/netbsd_latest.yml | 27 + GNUmakefile | 2 +- dev/doc/book/dern-manual.htm | 31 +- .../examples/dern/applyEvalAndGenerate.dern | 22 + dev/external/vera/scripts/rules/L006.tcl | 2 +- .../octaspire/dern/octaspire_dern_config.h | 2 +- .../octaspire/dern/octaspire_dern_stdlib.h | 10 + .../octaspire/dern/octaspire_dern_value.h | 89 + .../octaspire/dern/octaspire_dern_vm.h | 8 +- dev/src/octaspire_dern_stdlib.c | 1081 ++++++++++- dev/src/octaspire_dern_value.c | 408 ++++ dev/src/octaspire_dern_vm.c | 41 +- dev/test/test_dern_vm.c | 90 +- release/documentation/dern-manual.html | 59 +- release/octaspire-dern-amalgamated.c | 1651 ++++++++++++++++- 15 files changed, 3399 insertions(+), 124 deletions(-) create mode 100644 .builds/netbsd_latest.yml create mode 100644 dev/doc/book/examples/dern/applyEvalAndGenerate.dern diff --git a/.builds/netbsd_latest.yml b/.builds/netbsd_latest.yml new file mode 100644 index 0000000..f082972 --- /dev/null +++ b/.builds/netbsd_latest.yml @@ -0,0 +1,27 @@ +image: netbsd/latest +packages: + - devel/gmake + - ncursesw + - SDL2 + - SDL2_image + - SDL2_mixer + - SDL2_ttf +sources: + - https://git.sr.ht/~octaspire/dern +tasks: + - setup: | + cd dern + gmake submodules-init + - build-devel: | + cd dern + gmake all + - test-devel: | + cd dern + ./octaspire-dern-unit-test-runner --write-test-files + gmake test + - build-release: | + cd dern/release + sh how-to-build/NetBSD.sh + - test-release: | + cd dern/release + ./octaspire-dern-unit-test-runner --write-test-files diff --git a/GNUmakefile b/GNUmakefile index a97df66..a4f57df 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -16,7 +16,7 @@ RELDOCDIR=$(RELDIR)documentation/ AMALGAMATION=$(RELDIR)octaspire-dern-amalgamated.c PLUGINS := $(wildcard $(PLUGINDIR)*.c) UNAME=$(shell uname -s) -CFLAGS=-std=c99 -Wall -Wextra -g -O2 -DOCTASPIRE_DERN_CONFIG_BINARY_PLUGINS +CFLAGS=-std=c99 -Wall -Wextra -g -Og -DOCTASPIRE_DERN_CONFIG_BINARY_PLUGINS SQLITE3_CFLAGS=-std=c99 -Wall TAGS_C_FILES := $(SRCDIR)*.c \ diff --git a/dev/doc/book/dern-manual.htm b/dev/doc/book/dern-manual.htm index b8fcb37..9c139fa 100644 --- a/dev/doc/book/dern-manual.htm +++ b/dev/doc/book/dern-manual.htm @@ -954,13 +954,15 @@ .INCLUDE: dev/doc/book/examples/dern/returnWithoutArguments.dern -

Evaluating values

+

Evaluating, applying and generating values

- Special eval can be used to evaluate a given value. It can - be called with one or two arguments. The second argument, if present, - must be an environment that is used while evaluating. If no environment - is given, the global environment is used instead. + Special eval can be used to evaluate a given value or to + do the same thing that some other Lisps use function apply + for. When used for evaluating values, it can be called with either one + or two arguments. The second argument, if present, must be an environment + that is used while evaluating. If no environment is given, the global + environment is used instead.

@@ -970,6 +972,25 @@ .INCLUDE: dev/doc/book/examples/dern/eval.dern +

+ The other use for eval is to apply a function + to one or multiple collections of values. This is like + calling the function in a form, but some or all of the arguments + can be inside one or more collections like vector. Some other + Lisps have a separate function apply for doing this; + In Dern eval can handle both of these use cases. +

+ +

+ Dern has also function generate. It has also two + uses; it can be used for generating a collection of values + (somewhat like std::generate_n in C++), or it + can be used to do the same thing that other Lisps might have + function map or mapcar for. +

+ + .INCLUDE: dev/doc/book/examples/dern/applyEvalAndGenerate.dern +

Input and output ports

diff --git a/dev/doc/book/examples/dern/applyEvalAndGenerate.dern b/dev/doc/book/examples/dern/applyEvalAndGenerate.dern new file mode 100644 index 0000000..81975ab --- /dev/null +++ b/dev/doc/book/examples/dern/applyEvalAndGenerate.dern @@ -0,0 +1,22 @@ +; These two do the same thing: +(eval (println [Hello])) +(eval (println [Hello]) (env-current)) + +; These two do the same thing, when run in global scope: +(eval (println [Hello])) +(eval (println [Hello]) (env-global)) + +; "Apply" of other Lisps can be done like this: +(eval + (env-global) '(|a| |b| |c|) |d| |e| |f|) ; [abcdef] + +; Generate collection of values +(generate 'vector of {D+10} |a|) ; (|a| |a| |a| |a| |a| |a| |a| |a| |a| |a|) +(generate 'string of {D+10} |a|) ; [aaaaaaaaaa] + +; ({D+0} {D+1} {D+2} {D+3} {D+4} {D+5} {D+6} {D+7} {D+8} {D+9}) +(generate 'vector of {D+10} (fn (container index) index)) + +; "map" or "mapcar" of other Lisps can be done like this: +(generate 'vector mapping to-integer on '(|a| |b| |c|)) ; ({D+97} {D+98} {D+99}) +(generate 'vector mapping * on '({D+1} {D+2}) '({D+3} {D+4})) ; ({D+3} {D+8}) + diff --git a/dev/external/vera/scripts/rules/L006.tcl b/dev/external/vera/scripts/rules/L006.tcl index 72433bc..4a16175 100755 --- a/dev/external/vera/scripts/rules/L006.tcl +++ b/dev/external/vera/scripts/rules/L006.tcl @@ -1,7 +1,7 @@ #!/usr/bin/tclsh # Source file should not be too long -set maxLines [getParameter "max-file-length" 12000] +set maxLines [getParameter "max-file-length" 14000] foreach f [getSourceFileNames] { set length [getLineCount $f] diff --git a/dev/include/octaspire/dern/octaspire_dern_config.h b/dev/include/octaspire/dern/octaspire_dern_config.h index b0d7650..91bbc5b 100644 --- a/dev/include/octaspire/dern/octaspire_dern_config.h +++ b/dev/include/octaspire/dern/octaspire_dern_config.h @@ -18,7 +18,7 @@ limitations under the License. #define OCTASPIRE_DERN_CONFIG_H #define OCTASPIRE_DERN_CONFIG_VERSION_MAJOR "0" -#define OCTASPIRE_DERN_CONFIG_VERSION_MINOR "487" +#define OCTASPIRE_DERN_CONFIG_VERSION_MINOR "488" #define OCTASPIRE_DERN_CONFIG_VERSION_PATCH "0" #define OCTASPIRE_DERN_CONFIG_VERSION_STR "Octaspire Dern version " \ diff --git a/dev/include/octaspire/dern/octaspire_dern_stdlib.h b/dev/include/octaspire/dern/octaspire_dern_stdlib.h index 22ec89a..6f4165e 100644 --- a/dev/include/octaspire/dern/octaspire_dern_stdlib.h +++ b/dev/include/octaspire/dern/octaspire_dern_stdlib.h @@ -347,6 +347,11 @@ octaspire_dern_value_t *octaspire_dern_vm_special_eval( octaspire_dern_value_t *arguments, octaspire_dern_value_t *environment); +octaspire_dern_value_t *octaspire_dern_vm_special_generate( + octaspire_dern_vm_t *vm, + octaspire_dern_value_t *arguments, + octaspire_dern_value_t *environment); + octaspire_dern_value_t *octaspire_dern_vm_special_quote( octaspire_dern_vm_t *vm, octaspire_dern_value_t *arguments, @@ -479,6 +484,11 @@ octaspire_dern_value_t *octaspire_dern_vm_builtin_boolean_question_mark( octaspire_dern_value_t *arguments, octaspire_dern_value_t *environment); +octaspire_dern_value_t *octaspire_dern_vm_builtin_character( + octaspire_dern_vm_t *vm, + octaspire_dern_value_t *arguments, + octaspire_dern_value_t *environment); + octaspire_dern_value_t *octaspire_dern_vm_builtin_character_question_mark( octaspire_dern_vm_t *vm, octaspire_dern_value_t *arguments, diff --git a/dev/include/octaspire/dern/octaspire_dern_value.h b/dev/include/octaspire/dern/octaspire_dern_value.h index b2558ac..3c72e3a 100644 --- a/dev/include/octaspire/dern/octaspire_dern_value.h +++ b/dev/include/octaspire/dern/octaspire_dern_value.h @@ -371,6 +371,9 @@ struct octaspire_dern_environment_t *octaspire_dern_value_as_environment_get_val struct octaspire_dern_environment_t const *octaspire_dern_value_as_environment_get_value_const( octaspire_dern_value_t const * const self); +bool octaspire_dern_value_is_callable( + octaspire_dern_value_t const * const self); + bool octaspire_dern_value_is_function( octaspire_dern_value_t const * const self); @@ -621,6 +624,14 @@ bool octaspire_dern_value_as_vector_push_back_element( octaspire_dern_value_t *self, void const *element); +bool octaspire_dern_value_as_collection_push_back_element( + octaspire_dern_value_t *self, + void const *element); + +bool octaspire_dern_value_as_text_push_back_character( + octaspire_dern_value_t *self, + void const *element); + bool octaspire_dern_value_as_vector_remove_element_at( octaspire_dern_value_t *self, ptrdiff_t const possiblyNegativeIndex); @@ -640,6 +651,11 @@ octaspire_dern_value_t const *octaspire_dern_value_as_vector_get_element_at_cons octaspire_dern_value_t const * const self, ptrdiff_t const possiblyNegativeIndex); +octaspire_dern_value_t *octaspire_dern_value_as_vector_get_element_at_evaluated( + octaspire_dern_value_t * const self, + ptrdiff_t const possiblyNegativeIndex, + octaspire_dern_value_t * const environment); + typedef struct octaspire_dern_c_data_or_unpushed_error_t { void * cData; @@ -712,6 +728,59 @@ typedef struct octaspire_dern_string_or_unpushed_error_t } octaspire_dern_string_or_unpushed_error_t; +typedef struct octaspire_dern_vector_or_unpushed_error_t +{ + octaspire_dern_value_t * value; + octaspire_dern_value_t * unpushedError; +} +octaspire_dern_vector_or_unpushed_error_t; + +octaspire_dern_vector_or_unpushed_error_t +octaspire_dern_value_as_vector_get_element_at_as_vector_or_unpushed_error( + octaspire_dern_value_t * const self, + ptrdiff_t const possiblyNegativeIndex, + char const * const dernFuncName); + +octaspire_dern_vector_or_unpushed_error_t +octaspire_dern_value_as_vector_get_and_eval_element_at_as_vector_or_unpushed_error( + octaspire_dern_value_t * const self, + ptrdiff_t const possiblyNegativeIndex, + char const * const dernFuncName, + octaspire_dern_value_t * const environment); + +typedef struct octaspire_dern_function_or_unpushed_error_t +{ + octaspire_dern_value_t * value; + octaspire_dern_value_t * unpushedError; +} +octaspire_dern_function_or_unpushed_error_t; + +octaspire_dern_function_or_unpushed_error_t +octaspire_dern_value_as_vector_get_element_at_as_function_or_unpushed_error( + octaspire_dern_value_t * const self, + ptrdiff_t const possiblyNegativeIndex, + char const * const dernFuncName); + +octaspire_dern_function_or_unpushed_error_t +octaspire_dern_value_as_vector_get_and_eval_element_at_as_callable_or_unpushed_error( + octaspire_dern_value_t * const self, + ptrdiff_t const possiblyNegativeIndex, + char const * const dernFuncName, + octaspire_dern_value_t * const environment); + +typedef struct octaspire_dern_environment_or_unpushed_error_t +{ + octaspire_dern_value_t * value; + octaspire_dern_value_t * unpushedError; +} +octaspire_dern_environment_or_unpushed_error_t; + +octaspire_dern_environment_or_unpushed_error_t +octaspire_dern_value_as_vector_get_element_at_as_environment_or_unpushed_error( + octaspire_dern_value_t * const self, + ptrdiff_t const possiblyNegativeIndex, + char const * const dernFuncName); + typedef struct octaspire_dern_symbol_or_unpushed_error_t { octaspire_string_t const * symbol; @@ -760,6 +829,13 @@ octaspire_dern_value_as_vector_get_element_at_as_number_or_unpushed_error_const( ptrdiff_t const possiblyNegativeIndex, char const * const dernFuncName); +octaspire_dern_number_or_unpushed_error_t +octaspire_dern_value_as_vector_get_and_eval_element_at_as_number_or_unpushed_error( + octaspire_dern_value_t * const self, + ptrdiff_t const possiblyNegativeIndex, + char const * const dernFuncName, + octaspire_dern_value_t * const environment); + octaspire_dern_boolean_or_unpushed_error_t octaspire_dern_value_as_vector_get_element_at_as_boolean_or_unpushed_error( octaspire_dern_value_t * const self, @@ -796,6 +872,13 @@ octaspire_dern_value_as_vector_get_element_at_as_symbol_or_unpushed_error( ptrdiff_t const possiblyNegativeIndex, char const * const dernFuncName); +octaspire_dern_symbol_or_unpushed_error_t +octaspire_dern_value_as_vector_get_element_at_as_this_symbol_or_unpushed_error( + octaspire_dern_value_t * const self, + ptrdiff_t const possiblyNegativeIndex, + char const * const dernFuncName, + char const * const str); + octaspire_dern_one_of_texts_or_unpushed_error_const_t octaspire_dern_value_as_vector_get_element_at_as_one_of_texts_or_unpushed_error_const( octaspire_dern_value_t const * const self, @@ -865,6 +948,12 @@ int octaspire_dern_value_compare( bool octaspire_dern_value_is_atom(octaspire_dern_value_t const * const self); +struct octaspire_dern_vm_t * octaspire_dern_value_get_vm( + octaspire_dern_value_t * const self); + +struct octaspire_dern_vm_t const * octaspire_dern_value_get_vm_const( + octaspire_dern_value_t const * const self); + #ifdef __cplusplus /* extern "C" */ } #endif diff --git a/dev/include/octaspire/dern/octaspire_dern_vm.h b/dev/include/octaspire/dern/octaspire_dern_vm.h index b721676..e77d072 100644 --- a/dev/include/octaspire/dern/octaspire_dern_vm.h +++ b/dev/include/octaspire/dern/octaspire_dern_vm.h @@ -224,11 +224,13 @@ struct octaspire_dern_value_t *octaspire_dern_vm_create_new_value_c_data_from_ex octaspire_dern_vm_t * const self, octaspire_dern_c_data_t * const cData); -bool octaspire_dern_vm_push_value(octaspire_dern_vm_t *self, struct octaspire_dern_value_t *value); +bool octaspire_dern_vm_push_value( + octaspire_dern_vm_t * const self, + struct octaspire_dern_value_t * const value); bool octaspire_dern_vm_pop_value( - octaspire_dern_vm_t *self, - struct octaspire_dern_value_t *valueForBalanceCheck); + octaspire_dern_vm_t * const self, + struct octaspire_dern_value_t * const valueForBalanceCheck); void const * octaspire_dern_vm_get_top_value(octaspire_dern_vm_t const * const self); diff --git a/dev/src/octaspire_dern_stdlib.c b/dev/src/octaspire_dern_stdlib.c index d9e5442..90d70bf 100644 --- a/dev/src/octaspire_dern_stdlib.c +++ b/dev/src/octaspire_dern_stdlib.c @@ -1221,6 +1221,290 @@ octaspire_dern_value_t *octaspire_dern_vm_special_define( } } +static octaspire_dern_value_t *octaspire_dern_vm_private_special_eval_helper_apply( + octaspire_dern_vm_t *vm, + octaspire_dern_value_t *arguments, + octaspire_dern_value_t *environment) +{ + size_t const stackLength = octaspire_dern_vm_get_stack_length(vm); + + octaspire_helpers_verify_true( + arguments->typeTag == OCTASPIRE_DERN_VALUE_TAG_VECTOR); + + octaspire_helpers_verify_true( + environment->typeTag == OCTASPIRE_DERN_VALUE_TAG_ENVIRONMENT); + + char const * const dernFuncName = "eval"; + size_t const numArgs = octaspire_dern_value_get_length(arguments); + + if (numArgs < 3) + { + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return octaspire_dern_vm_create_new_value_error_format( + vm, + "Special '%s' expects at least three arguments. " + "%zu arguments were given.", + dernFuncName, + octaspire_dern_value_get_length(arguments)); + } + + octaspire_dern_value_t * const funcValue = + octaspire_dern_value_as_vector_get_element_at(arguments, 0); + + octaspire_helpers_verify_not_null(funcValue); + + octaspire_dern_value_t * const funcValueEvaluated = + octaspire_dern_vm_eval(vm, funcValue, environment); + + octaspire_helpers_verify_not_null(funcValueEvaluated); + + octaspire_helpers_verify_true( + octaspire_dern_vm_push_value(vm, funcValueEvaluated)); + + if (!octaspire_dern_value_is_callable(funcValueEvaluated)) + { + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, funcValueEvaluated)); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return octaspire_dern_vm_create_new_value_error_format( + vm, + "Special '%s' expects callable value as the first argument " + "when used as 'apply'. Type '%s' was given.", + dernFuncName, + octaspire_dern_value_helper_get_type_as_c_string( + octaspire_dern_value_get_type(funcValueEvaluated))); + } + + octaspire_dern_value_t * const envValue = + octaspire_dern_value_as_vector_get_element_at(arguments, 1); + + octaspire_helpers_verify_not_null(envValue); + + octaspire_dern_value_t * const envValueEvaluated = + octaspire_dern_vm_eval(vm, envValue, environment); + + octaspire_helpers_verify_not_null(envValueEvaluated); + + octaspire_helpers_verify_true( + octaspire_dern_vm_push_value(vm, envValueEvaluated)); + + if (!octaspire_dern_value_is_environment(envValueEvaluated)) + { + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, envValueEvaluated)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, funcValueEvaluated)); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return octaspire_dern_vm_create_new_value_error_format( + vm, + "Special '%s' expects environment value as the second argument " + "when used as 'apply'. Type '%s' was given.", + dernFuncName, + octaspire_dern_value_helper_get_type_as_c_string( + octaspire_dern_value_get_type(envValueEvaluated))); + } + + octaspire_dern_value_t *valueToBeEvaluated = + octaspire_dern_vm_create_new_value_vector(vm); + + octaspire_helpers_verify_not_null(valueToBeEvaluated); + + octaspire_helpers_verify_true( + octaspire_dern_vm_push_value(vm, valueToBeEvaluated)); + + octaspire_helpers_verify_true( + octaspire_dern_value_as_vector_push_back_element( + valueToBeEvaluated, + &funcValueEvaluated)); + + for (size_t i = 2; i < numArgs; ++i) + { + octaspire_dern_value_t * const tmpVal = + octaspire_dern_value_as_vector_get_element_at_evaluated( + arguments, + i, + environment); + + if (!tmpVal) + { + // NOP + } + else if (octaspire_dern_value_is_vector(tmpVal)) + { + for (size_t j = 0; + j < octaspire_dern_value_as_vector_get_length(tmpVal); + ++j) + { + octaspire_dern_value_t * const tmpElemVal = + octaspire_dern_value_as_vector_get_element_at(tmpVal, j); + + octaspire_helpers_verify_true( + octaspire_dern_value_as_vector_push_back_element( + valueToBeEvaluated, + &tmpElemVal)); + } + } + else + { + octaspire_helpers_verify_true( + octaspire_dern_value_as_vector_push_back_element( + valueToBeEvaluated, + &tmpVal)); + } + } + + octaspire_dern_value_t *result = 0; + + result = octaspire_dern_vm_eval( + vm, + valueToBeEvaluated, + envValueEvaluated); + + octaspire_helpers_verify_not_null(result); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, valueToBeEvaluated)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, envValueEvaluated)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, funcValueEvaluated)); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return result; +} + +static octaspire_dern_value_t *octaspire_dern_vm_private_special_eval_helper_eval( + octaspire_dern_vm_t *vm, + octaspire_dern_value_t *arguments, + octaspire_dern_value_t *environment) +{ + size_t const stackLength = octaspire_dern_vm_get_stack_length(vm); + + octaspire_helpers_verify_true( + arguments->typeTag == OCTASPIRE_DERN_VALUE_TAG_VECTOR); + + octaspire_helpers_verify_true( + environment->typeTag == OCTASPIRE_DERN_VALUE_TAG_ENVIRONMENT); + + char const * const dernFuncName = "eval"; + size_t const numArgs = octaspire_dern_value_get_length(arguments); + + if (numArgs < 1 || numArgs > 2) + { + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return octaspire_dern_vm_create_new_value_error_format( + vm, + "Special '%s' expects one or two arguments. " + "%zu arguments were given.", + dernFuncName, + octaspire_dern_value_get_length(arguments)); + } + + octaspire_dern_value_t *valueToBeEvaluated = + octaspire_dern_value_as_vector_get_element_at(arguments, 0); + + octaspire_helpers_verify_not_null(valueToBeEvaluated); + + octaspire_dern_vm_push_value(vm, arguments); + + octaspire_dern_value_t *result = 0; + + if (numArgs == 2) + { + octaspire_dern_value_t *envVal = + octaspire_dern_value_as_vector_get_element_at(arguments, 1); + + octaspire_helpers_verify_not_null(envVal); + + if (octaspire_dern_value_is_environment(envVal)) + { + result = octaspire_dern_vm_eval(vm, valueToBeEvaluated, envVal); + octaspire_dern_vm_push_value(vm, result); + octaspire_dern_value_t *tmpResult = octaspire_dern_vm_eval(vm, result, envVal); + octaspire_dern_vm_pop_value(vm, result); + result = tmpResult; + } + else + { + envVal = octaspire_dern_vm_eval(vm, envVal, environment); + + octaspire_helpers_verify_not_null(envVal); + + if (octaspire_dern_value_is_error(envVal)) + { + octaspire_dern_vm_pop_value(vm, arguments); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return envVal; + } + + if (!octaspire_dern_value_is_environment(envVal)) + { + octaspire_dern_vm_pop_value(vm, arguments); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return octaspire_dern_vm_create_new_value_error_format( + vm, + "Second argument to special '%s' must evaluate into " + "environment value.\nNow it evaluated into type %s.", + dernFuncName, + octaspire_dern_value_helper_get_type_as_c_string( + envVal->typeTag)); + } + + octaspire_dern_vm_push_value(vm, envVal); + result = octaspire_dern_vm_eval(vm, valueToBeEvaluated, envVal); + octaspire_dern_vm_push_value(vm, result); + + octaspire_dern_value_t *tmpResult = + octaspire_dern_vm_eval(vm, result, envVal); + + octaspire_dern_vm_pop_value(vm, result); + octaspire_dern_vm_pop_value(vm, envVal); + result = tmpResult; + } + } + else + { + result = octaspire_dern_vm_eval(vm, valueToBeEvaluated, environment); + octaspire_dern_vm_push_value(vm, result); + + octaspire_dern_value_t *tmpResult = + octaspire_dern_vm_eval(vm, result, environment); + + octaspire_dern_vm_pop_value(vm, result); + result = tmpResult; + } + + octaspire_helpers_verify_not_null(result); + + octaspire_dern_vm_pop_value(vm, arguments); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return result; +} + octaspire_dern_value_t *octaspire_dern_vm_special_eval( octaspire_dern_vm_t *vm, octaspire_dern_value_t *arguments, @@ -1228,94 +1512,670 @@ octaspire_dern_value_t *octaspire_dern_vm_special_eval( { size_t const stackLength = octaspire_dern_vm_get_stack_length(vm); - octaspire_helpers_verify_true(arguments->typeTag == OCTASPIRE_DERN_VALUE_TAG_VECTOR); - octaspire_helpers_verify_true(environment->typeTag == OCTASPIRE_DERN_VALUE_TAG_ENVIRONMENT); + octaspire_helpers_verify_true( + arguments->typeTag == OCTASPIRE_DERN_VALUE_TAG_VECTOR); + + octaspire_helpers_verify_true( + environment->typeTag == OCTASPIRE_DERN_VALUE_TAG_ENVIRONMENT); + + char const * const dernFuncName = "eval"; + size_t const numArgs = octaspire_dern_value_get_length(arguments); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + if (numArgs < 1) + { + return octaspire_dern_vm_create_new_value_error_format( + vm, + "Special '%s' expects at least one arguments. " + "%zu arguments were given.", + dernFuncName, + octaspire_dern_value_get_length(arguments)); + } + + if (numArgs == 1 || numArgs == 2) + { + return octaspire_dern_vm_private_special_eval_helper_eval( + vm, + arguments, + environment); + } + + return octaspire_dern_vm_private_special_eval_helper_apply( + vm, + arguments, + environment); +} + +static octaspire_dern_value_t *octaspire_dern_vm_private_special_generate_helper_generate( + octaspire_dern_vm_t *vm, + octaspire_dern_value_t *arguments, + octaspire_dern_value_t *environment) +{ + size_t const stackLength = octaspire_dern_vm_get_stack_length(vm); + + octaspire_helpers_verify_true( + arguments->typeTag == OCTASPIRE_DERN_VALUE_TAG_VECTOR); + + octaspire_helpers_verify_true( + environment->typeTag == OCTASPIRE_DERN_VALUE_TAG_ENVIRONMENT); + + char const * const dernFuncName = "generate"; + size_t const numArgs = octaspire_dern_value_get_length(arguments); + + if (numArgs != 4) + { + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return octaspire_dern_vm_create_new_value_error_format( + vm, + "Special '%s' expects at four arguments. " + "%zu arguments were given.", + dernFuncName, + octaspire_dern_value_get_length(arguments)); + } + + octaspire_dern_value_t * const typeValue = + octaspire_dern_value_as_vector_get_element_at(arguments, 0); + + octaspire_helpers_verify_not_null(typeValue); + + octaspire_dern_value_t * const typeValueEvaluated = + octaspire_dern_vm_eval(vm, typeValue, environment); + + octaspire_helpers_verify_not_null(typeValueEvaluated); + + octaspire_helpers_verify_true( + octaspire_dern_vm_push_value(vm, typeValueEvaluated)); + + if (!octaspire_dern_value_is_symbol(typeValueEvaluated)) + { + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, typeValueEvaluated)); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return octaspire_dern_vm_create_new_value_error_format( + vm, + "Special '%s' expects symbol as the first argument " + "when used to generate values. Type '%s' was given.", + dernFuncName, + octaspire_dern_value_helper_get_type_as_c_string( + octaspire_dern_value_get_type(typeValueEvaluated))); + } + + octaspire_dern_value_t * resultValue = 0; + + // TODO add support for generating more types. + if (octaspire_dern_value_as_text_is_equal_to_c_string( + typeValueEvaluated, + "vector")) + { + resultValue = octaspire_dern_vm_create_new_value_vector(vm); + } + else if (octaspire_dern_value_as_text_is_equal_to_c_string( + typeValueEvaluated, + "string")) + { + resultValue = octaspire_dern_vm_create_new_value_string_from_c_string( + vm, + ""); + } + else + { + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, typeValueEvaluated)); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return octaspire_dern_vm_create_new_value_error_format( + vm, + "Special '%s' expects symbol 'vector' or 'string'" + "as the first argument when used to generate values. " + "Symbol '%s' was given.", + dernFuncName, + octaspire_dern_value_as_text_get_c_string(typeValueEvaluated)); + } + + octaspire_helpers_verify_not_null(resultValue); + + octaspire_helpers_verify_true( + octaspire_dern_vm_push_value(vm, resultValue)); + + // of + + octaspire_dern_symbol_or_unpushed_error_t const symbolOfOrError = + octaspire_dern_value_as_vector_get_element_at_as_this_symbol_or_unpushed_error( + arguments, + 1, + dernFuncName, + "of"); + + if (symbolOfOrError.unpushedError) + { + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, resultValue)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, typeValueEvaluated)); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return symbolOfOrError.unpushedError; + } + + // Number + + octaspire_dern_number_or_unpushed_error_t numberOrError = + octaspire_dern_value_as_vector_get_and_eval_element_at_as_number_or_unpushed_error( + arguments, + 2, + dernFuncName, + environment); + + if (numberOrError.unpushedError) + { + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, resultValue)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, typeValueEvaluated)); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return numberOrError.unpushedError; + } + + octaspire_helpers_verify_true( + octaspire_dern_vm_push_value(vm, numberOrError.value)); + + if (numberOrError.number < 0) + { + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, numberOrError.value)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, resultValue)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, typeValueEvaluated)); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return octaspire_dern_vm_create_new_value_error_format( + vm, + "Special '%s' expects non negative number " + "as the third argument when used to generate values. ", + dernFuncName); + } + + size_t number = (size_t)(numberOrError.number); + + // Any literal value or generator function + + octaspire_dern_value_t * const generatorValue = + octaspire_dern_value_as_vector_get_element_at(arguments, 3); + + octaspire_helpers_verify_not_null(generatorValue); + + octaspire_dern_value_t * const generatorValueEvaluated = + octaspire_dern_vm_eval(vm, generatorValue, environment); + + octaspire_helpers_verify_not_null(generatorValueEvaluated); + + octaspire_helpers_verify_true( + octaspire_dern_vm_push_value(vm, generatorValueEvaluated)); + + if (octaspire_dern_value_is_callable(generatorValueEvaluated)) + { + for (size_t i = 0; i < number; ++i) + { + octaspire_dern_value_t * const indexValue = + octaspire_dern_vm_create_new_value_integer( + vm, + i); + + octaspire_helpers_verify_not_null(indexValue); + + octaspire_helpers_verify_true( + octaspire_dern_vm_push_value(vm, indexValue)); + + octaspire_dern_value_t * const quoteValue = + octaspire_dern_vm_create_new_value_vector(vm); + + octaspire_helpers_verify_not_null(quoteValue); + + octaspire_helpers_verify_true( + octaspire_dern_vm_push_value(vm, quoteValue)); + + octaspire_dern_value_t * const quoteSymbolValue = + octaspire_dern_vm_create_new_value_symbol_from_c_string( + vm, + "quote"); + + octaspire_helpers_verify_not_null(quoteSymbolValue); + + octaspire_helpers_verify_true( + octaspire_dern_value_as_vector_push_back_element( + quoteValue, + "eSymbolValue)); + + octaspire_helpers_verify_true( + octaspire_dern_value_as_vector_push_back_element( + quoteValue, + &resultValue)); + + octaspire_dern_value_t * const formValue = + octaspire_dern_vm_create_new_value_vector_from_values( + vm, + 3, + //generatorValueEvaluated, + generatorValue, + quoteValue, + indexValue); + + octaspire_helpers_verify_not_null(formValue); + + octaspire_helpers_verify_true( + octaspire_dern_vm_push_value(vm, formValue)); + + octaspire_dern_value_t * const elemValue = octaspire_dern_vm_eval( + vm, + formValue, + environment); + + octaspire_helpers_verify_not_null(elemValue); + + octaspire_helpers_verify_true( + octaspire_dern_value_as_collection_push_back_element( + resultValue, + elemValue)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, formValue)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, quoteValue)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, indexValue)); + } + } + else + { + for (size_t i = 0; i < number; ++i) + { + octaspire_helpers_verify_true( + octaspire_dern_value_as_collection_push_back_element( + resultValue, + generatorValueEvaluated)); + } + } + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, generatorValueEvaluated)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, numberOrError.value)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, resultValue)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, typeValueEvaluated)); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return resultValue; +} + +static octaspire_dern_value_t *octaspire_dern_vm_private_special_generate_helper_map( + octaspire_dern_vm_t *vm, + octaspire_dern_value_t *arguments, + octaspire_dern_value_t *environment) +{ + size_t const stackLength = octaspire_dern_vm_get_stack_length(vm); + + octaspire_helpers_verify_true( + arguments->typeTag == OCTASPIRE_DERN_VALUE_TAG_VECTOR); + + octaspire_helpers_verify_true( + environment->typeTag == OCTASPIRE_DERN_VALUE_TAG_ENVIRONMENT); + + char const * const dernFuncName = "generate"; + size_t const numArgs = octaspire_dern_value_get_length(arguments); + + if (numArgs < 5) + { + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return octaspire_dern_vm_create_new_value_error_format( + vm, + "Special '%s' expects at least five arguments " + "when used as 'map'; %zu arguments were given.", + dernFuncName, + octaspire_dern_value_get_length(arguments)); + } + + octaspire_dern_value_t * const typeValue = + octaspire_dern_value_as_vector_get_element_at(arguments, 0); + + octaspire_helpers_verify_not_null(typeValue); + + octaspire_dern_value_t * const typeValueEvaluated = + octaspire_dern_vm_eval(vm, typeValue, environment); + + octaspire_helpers_verify_not_null(typeValueEvaluated); + + octaspire_helpers_verify_true( + octaspire_dern_vm_push_value(vm, typeValueEvaluated)); + + if (!octaspire_dern_value_is_symbol(typeValueEvaluated)) + { + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, typeValueEvaluated)); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return octaspire_dern_vm_create_new_value_error_format( + vm, + "Special '%s' expects symbol as the first argument " + "when used to map values. Type '%s' was given.", + dernFuncName, + octaspire_dern_value_helper_get_type_as_c_string( + octaspire_dern_value_get_type(typeValueEvaluated))); + } + + octaspire_dern_value_t * resultValue = 0; + + // TODO add support for generating more types. + if (octaspire_dern_value_as_text_is_equal_to_c_string( + typeValueEvaluated, + "vector")) + { + resultValue = octaspire_dern_vm_create_new_value_vector(vm); + } + else if (octaspire_dern_value_as_text_is_equal_to_c_string( + typeValueEvaluated, + "string")) + { + resultValue = octaspire_dern_vm_create_new_value_string_from_c_string( + vm, + ""); + } + else + { + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, typeValueEvaluated)); - octaspire_vector_t * const vec = arguments->value.vector; + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); - if (octaspire_vector_get_length(vec) != 1 && - octaspire_vector_get_length(vec) != 2) - { - octaspire_helpers_verify_true(stackLength == octaspire_dern_vm_get_stack_length(vm)); return octaspire_dern_vm_create_new_value_error_format( vm, - "Special 'eval' expects one or two arguments. %zu arguments were given.", - octaspire_vector_get_length(vec)); + "Special '%s' expects symbol 'vector' or 'string'" + "as the first argument when used to generate values. " + "Symbol '%s' was given.", + dernFuncName, + octaspire_dern_value_as_text_get_c_string(typeValueEvaluated)); } - octaspire_dern_value_t *valueToBeEvaluated = octaspire_vector_get_element_at(vec, 0); - octaspire_helpers_verify_not_null(valueToBeEvaluated); + octaspire_helpers_verify_not_null(resultValue); - octaspire_dern_vm_push_value(vm, arguments); + octaspire_helpers_verify_true( + octaspire_dern_vm_push_value(vm, resultValue)); - octaspire_dern_value_t *result = 0; + // mapping + + octaspire_dern_symbol_or_unpushed_error_t const symbolOfOrError = + octaspire_dern_value_as_vector_get_element_at_as_this_symbol_or_unpushed_error( + arguments, + 1, + dernFuncName, + "mapping"); - if (octaspire_vector_get_length(vec) == 2) + if (symbolOfOrError.unpushedError) { - octaspire_dern_value_t *envVal = octaspire_vector_get_element_at(vec, 1); - octaspire_helpers_verify_not_null(envVal); + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, resultValue)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, typeValueEvaluated)); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return symbolOfOrError.unpushedError; + } + + // Callable function + + octaspire_dern_function_or_unpushed_error_t const callableOrError = + octaspire_dern_value_as_vector_get_and_eval_element_at_as_callable_or_unpushed_error( + arguments, + 2, + dernFuncName, + environment); + + if (callableOrError.unpushedError) + { + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, resultValue)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, typeValueEvaluated)); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return callableOrError.unpushedError; + } + + octaspire_helpers_verify_true( + octaspire_dern_vm_push_value(vm, callableOrError.value)); + + // on + + octaspire_dern_symbol_or_unpushed_error_t const symbolOnOrError = + octaspire_dern_value_as_vector_get_element_at_as_this_symbol_or_unpushed_error( + arguments, + 3, + dernFuncName, + "on"); + + if (symbolOnOrError.unpushedError) + { + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, callableOrError.value)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, resultValue)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, typeValueEvaluated)); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return symbolOnOrError.unpushedError; + } + + // Generate form (callable arg1 arg2 argN) to be called and call it + // correct number of times storing the results into the result collection. + // Number of arguments depends on the number of vectors after symbol 'on'. + // One argument is picked from each of those vectors. + // Shortest of those vectors tells how many calls are made. + + octaspire_dern_value_t * const argVectorsVal = + octaspire_dern_vm_create_new_value_vector(vm); + + octaspire_helpers_verify_not_null(argVectorsVal); + + octaspire_helpers_verify_true( + octaspire_dern_vm_push_value(vm, argVectorsVal)); + + size_t minVecLen = 0; + + for (size_t i = 4; i < octaspire_dern_value_as_vector_get_length(arguments); ++i) + { + octaspire_dern_vector_or_unpushed_error_t const vectorOrError = + octaspire_dern_value_as_vector_get_and_eval_element_at_as_vector_or_unpushed_error( + arguments, + i, + dernFuncName, + environment); - if (envVal->typeTag == OCTASPIRE_DERN_VALUE_TAG_ENVIRONMENT) + if (vectorOrError.unpushedError) { - result = octaspire_dern_vm_eval(vm, valueToBeEvaluated, envVal); - octaspire_dern_vm_push_value(vm, result); - octaspire_dern_value_t *tmpResult = octaspire_dern_vm_eval(vm, result, envVal); - octaspire_dern_vm_pop_value(vm, result); - result = tmpResult; + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, argVectorsVal)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, callableOrError.value)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, resultValue)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, typeValueEvaluated)); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return vectorOrError.unpushedError; } - else + + octaspire_helpers_verify_true( + octaspire_dern_value_as_vector_push_back_element( + argVectorsVal, + &(vectorOrError.value))); + + if (octaspire_dern_value_as_vector_get_length(vectorOrError.value) > + minVecLen) { - envVal = octaspire_dern_vm_eval(vm, envVal, environment); + minVecLen = octaspire_dern_value_as_vector_get_length( + vectorOrError.value); + } + } - octaspire_helpers_verify_not_null(envVal); + for (size_t i = 0; i < minVecLen; ++i) + { + octaspire_dern_value_t * const formValue = + octaspire_dern_vm_create_new_value_vector(vm); - if (envVal->typeTag == OCTASPIRE_DERN_VALUE_TAG_ERROR) - { - octaspire_dern_vm_pop_value(vm, arguments); + octaspire_helpers_verify_not_null(formValue); - octaspire_helpers_verify_true( - stackLength == octaspire_dern_vm_get_stack_length(vm)); + octaspire_helpers_verify_true( + octaspire_dern_vm_push_value(vm, formValue)); - return envVal; - } + octaspire_helpers_verify_true( + octaspire_dern_value_as_vector_push_back_element( + formValue, + &(callableOrError.value))); - if (envVal->typeTag != OCTASPIRE_DERN_VALUE_TAG_ENVIRONMENT) - { - octaspire_dern_vm_pop_value(vm, arguments); + for (size_t j = 0; + j < octaspire_dern_value_as_vector_get_length(argVectorsVal); + ++j) + { + octaspire_dern_value_t * const vecVal = + octaspire_dern_value_as_vector_get_element_at(argVectorsVal, j); - octaspire_helpers_verify_true( - stackLength == octaspire_dern_vm_get_stack_length(vm)); + octaspire_helpers_verify_not_null(vecVal); - return octaspire_dern_vm_create_new_value_error_format( - vm, - "Second argument to special 'eval' must evaluate into environment value.\n" - "Now it evaluated into type %s.", - octaspire_dern_value_helper_get_type_as_c_string(envVal->typeTag)); - } + octaspire_dern_value_t * argVal = + octaspire_dern_value_as_vector_get_element_at(vecVal, i); - octaspire_dern_vm_push_value(vm, envVal); - result = octaspire_dern_vm_eval(vm, valueToBeEvaluated, envVal); - octaspire_dern_vm_push_value(vm, result); - octaspire_dern_value_t *tmpResult = octaspire_dern_vm_eval(vm, result, envVal); - octaspire_dern_vm_pop_value(vm, result); - octaspire_dern_vm_pop_value(vm, envVal); - result = tmpResult; + octaspire_helpers_verify_not_null(argVal); + + octaspire_helpers_verify_true( + octaspire_dern_value_as_vector_push_back_element( + formValue, + &argVal)); } + + octaspire_dern_value_t * const resultElemVal = + octaspire_dern_vm_eval(vm, formValue, environment); + + octaspire_helpers_verify_not_null(resultElemVal); + + octaspire_helpers_verify_true( + octaspire_dern_value_as_vector_push_back_element( + resultValue, + &resultElemVal)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, formValue)); } - else + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, argVectorsVal)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, callableOrError.value)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, resultValue)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, typeValueEvaluated)); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return resultValue; +} + +octaspire_dern_value_t *octaspire_dern_vm_special_generate( + octaspire_dern_vm_t *vm, + octaspire_dern_value_t *arguments, + octaspire_dern_value_t *environment) +{ + size_t const stackLength = octaspire_dern_vm_get_stack_length(vm); + + octaspire_helpers_verify_true( + arguments->typeTag == OCTASPIRE_DERN_VALUE_TAG_VECTOR); + + octaspire_helpers_verify_true( + environment->typeTag == OCTASPIRE_DERN_VALUE_TAG_ENVIRONMENT); + + char const * const dernFuncName = "generate"; + size_t const numArgs = octaspire_dern_value_get_length(arguments); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + if (numArgs < 4) { - result = octaspire_dern_vm_eval(vm, valueToBeEvaluated, environment); - octaspire_dern_vm_push_value(vm, result); - octaspire_dern_value_t *tmpResult = octaspire_dern_vm_eval(vm, result, environment); - octaspire_dern_vm_pop_value(vm, result); - result = tmpResult; + return octaspire_dern_vm_create_new_value_error_format( + vm, + "Special '%s' expects at least four arguments. " + "%zu arguments were given.", + dernFuncName, + octaspire_dern_value_get_length(arguments)); } - octaspire_helpers_verify_not_null(result); + if (numArgs == 4) + { + return octaspire_dern_vm_private_special_generate_helper_generate( + vm, + arguments, + environment); + } - octaspire_dern_vm_pop_value(vm, arguments); - octaspire_helpers_verify_true(stackLength == octaspire_dern_vm_get_stack_length(vm)); - return result; + return octaspire_dern_vm_private_special_generate_helper_map( + vm, + arguments, + environment); } octaspire_dern_value_t *octaspire_dern_vm_special_quote( @@ -10995,6 +11855,107 @@ octaspire_dern_value_t *octaspire_dern_vm_builtin_boolean_question_mark( return octaspire_dern_vm_create_new_value_boolean(vm, octaspire_dern_value_is_boolean(value)); } +octaspire_dern_value_t *octaspire_dern_vm_builtin_character( + octaspire_dern_vm_t *vm, + octaspire_dern_value_t *arguments, + octaspire_dern_value_t *environment) +{ + size_t const stackLength = octaspire_dern_vm_get_stack_length(vm); + + octaspire_helpers_verify_true( + arguments->typeTag == OCTASPIRE_DERN_VALUE_TAG_VECTOR); + + octaspire_helpers_verify_true( + environment->typeTag == OCTASPIRE_DERN_VALUE_TAG_ENVIRONMENT); + + char const * const dernFuncName = "character"; + size_t const numArgs = octaspire_dern_value_get_length(arguments); + + if (numArgs != 1) + { + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return octaspire_dern_vm_create_new_value_error_format( + vm, + "Builtin '%s' expects at one argument. " + "%zu arguments were given.", + dernFuncName, + numArgs); + } + + octaspire_dern_value_t const * const value = + octaspire_dern_value_as_vector_get_element_at(arguments, 0); + + if (octaspire_dern_value_is_number(value)) + { + int32_t const number = octaspire_dern_value_as_number_get_value(value); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + if (number < 0) + { + return octaspire_dern_vm_create_new_value_error_format( + vm, + "Builtin '%s' expects non-negative number as the first argument. " + "Negative number %" PRId32 " was given.", + dernFuncName, + number); + } + + return octaspire_dern_vm_create_new_value_character_from_uint32t( + vm, + (uint32_t)number); + } + else if (octaspire_dern_value_is_text(value)) + { + // TODO add support for more character names. + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + if (octaspire_dern_value_as_text_is_equal_to_c_string( + value, + "copyright sign")) + { + return octaspire_dern_vm_create_new_value_character_from_uint32t( + vm, + (uint32_t)0xA9); + } + if (octaspire_dern_value_as_text_is_equal_to_c_string( + value, + "greek small letter lambda")) + { + return octaspire_dern_vm_create_new_value_character_from_uint32t( + vm, + (uint32_t)0x3BB); + } + else + { + return octaspire_dern_vm_create_new_value_error_format( + vm, + "Builtin '%s' does not support character name " + "'%s' that was given as the first argument.", + dernFuncName, + octaspire_dern_value_as_text_get_c_string(value)); + } + } + else + { + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return octaspire_dern_vm_create_new_value_error_format( + vm, + "Builtin '%s' expects number of text as the first argument. " + "Type '%s' was given.", + dernFuncName, + octaspire_dern_value_helper_get_type_as_c_string( + octaspire_dern_value_get_type(value))); + } +} + octaspire_dern_value_t *octaspire_dern_vm_builtin_character_question_mark( octaspire_dern_vm_t *vm, octaspire_dern_value_t *arguments, diff --git a/dev/src/octaspire_dern_value.c b/dev/src/octaspire_dern_value.c index 3456894..e44fb86 100644 --- a/dev/src/octaspire_dern_value.c +++ b/dev/src/octaspire_dern_value.c @@ -1814,6 +1814,32 @@ octaspire_dern_environment_t const *octaspire_dern_value_as_environment_get_valu return self->value.environment; } +bool octaspire_dern_value_is_callable( + octaspire_dern_value_t const * const self) +{ + if (octaspire_dern_value_is_function(self)) + { + return true; + } + + if (octaspire_dern_value_is_macro(self)) + { + return true; + } + + if (octaspire_dern_value_is_builtin(self)) + { + return true; + } + + if (octaspire_dern_value_is_special(self)) + { + return true; + } + + return false; +} + bool octaspire_dern_value_is_function( octaspire_dern_value_t const * const self) { @@ -3569,6 +3595,61 @@ bool octaspire_dern_value_as_vector_push_back_element( return octaspire_vector_push_back_element(self->value.vector, element); } +bool octaspire_dern_value_as_collection_push_back_element( + octaspire_dern_value_t *self, + void const *element) +{ + // TODO add support for more collection types. + + octaspire_helpers_verify_not_null(self); + octaspire_helpers_verify_not_null(element); + + if (octaspire_dern_value_is_text(self)) + { + if (!octaspire_dern_value_is_character(element)) + { + return false; + } + + return octaspire_dern_value_as_text_push_back_character(self, element); + } + else if (octaspire_dern_value_is_vector(self)) + { + return octaspire_dern_value_as_vector_push_back_element(self, element); + } + + return false; +} + +bool octaspire_dern_value_as_text_push_back_character( + octaspire_dern_value_t *self, + void const *element) +{ + octaspire_helpers_verify_not_null(self); + octaspire_helpers_verify_not_null(element); + octaspire_helpers_verify_true(octaspire_dern_value_is_character(element)); + + octaspire_dern_value_t * const charVal = + (octaspire_dern_value_t * const)element; + + if (octaspire_dern_value_is_string(self)) + { + return octaspire_string_push_back_ucs_character( + self->value.string, + octaspire_string_get_ucs_character_at_index( + charVal->value.character, 0)); + } + else if (octaspire_dern_value_is_symbol(self)) + { + return octaspire_string_push_back_ucs_character( + self->value.symbol, + octaspire_string_get_ucs_character_at_index( + charVal->value.character, 0)); + } + + return false; +} + bool octaspire_dern_value_as_vector_remove_element_at( octaspire_dern_value_t *self, ptrdiff_t const possiblyNegativeIndex) @@ -3625,6 +3706,25 @@ octaspire_dern_value_t const *octaspire_dern_value_as_vector_get_element_at_cons possiblyNegativeIndex); } +octaspire_dern_value_t *octaspire_dern_value_as_vector_get_element_at_evaluated( + octaspire_dern_value_t * const self, + ptrdiff_t const possiblyNegativeIndex, + octaspire_dern_value_t * const environment) +{ + octaspire_helpers_verify_true( + octaspire_dern_value_is_vector(self)); + + octaspire_dern_value_t * const tmpVal = + octaspire_vector_get_element_at( + self->value.vector, + possiblyNegativeIndex); + + return octaspire_dern_vm_eval( + octaspire_dern_value_get_vm(self), + tmpVal, + environment); +} + octaspire_dern_c_data_or_unpushed_error_const_t octaspire_dern_value_as_vector_get_element_at_as_c_data_or_unpushed_error_const( octaspire_dern_value_t const * const self, @@ -3761,6 +3861,50 @@ octaspire_dern_value_as_vector_get_element_at_as_number_or_unpushed_error_const( return result; } +octaspire_dern_number_or_unpushed_error_t +octaspire_dern_value_as_vector_get_and_eval_element_at_as_number_or_unpushed_error( + octaspire_dern_value_t * const self, + ptrdiff_t const possiblyNegativeIndex, + char const * const dernFuncName, + octaspire_dern_value_t * const environment) +{ + octaspire_helpers_verify_not_null(self); + + octaspire_dern_vm_t * const vm = self->vm; + + octaspire_helpers_verify_not_null(vm); + + size_t const stackLength = octaspire_dern_vm_get_stack_length(vm); + octaspire_dern_number_or_unpushed_error_t result = {0, 0, 0}; + + octaspire_dern_value_t * arg = + octaspire_dern_value_as_vector_get_element_at(self, possiblyNegativeIndex); + + octaspire_helpers_verify_not_null(arg); + + arg = octaspire_dern_vm_eval(vm, arg, environment); + + if (!octaspire_dern_value_is_number(arg)) + { + result.unpushedError = octaspire_dern_vm_create_new_value_error_format( + vm, + "Builtin '%s' expects number as " + "%d. argument. Type '%s' was given.", + dernFuncName, + possiblyNegativeIndex, + octaspire_dern_value_helper_get_type_as_c_string(arg->typeTag)); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return result; + } + + result.value = arg; + result.number = octaspire_dern_value_as_number_get_value(arg); + return result; +} + octaspire_dern_boolean_or_unpushed_error_t octaspire_dern_value_as_vector_get_element_at_as_boolean_or_unpushed_error( octaspire_dern_value_t * const self, @@ -3925,6 +4069,197 @@ octaspire_dern_value_as_vector_get_element_at_as_string_or_unpushed_error( return result; } +octaspire_dern_function_or_unpushed_error_t +octaspire_dern_value_as_vector_get_element_at_as_function_or_unpushed_error( + octaspire_dern_value_t * const self, + ptrdiff_t const possiblyNegativeIndex, + char const * const dernFuncName) +{ + octaspire_helpers_verify_not_null(self); + + octaspire_dern_vm_t * const vm = self->vm; + + octaspire_helpers_verify_not_null(vm); + + size_t const stackLength = octaspire_dern_vm_get_stack_length(vm); + octaspire_dern_function_or_unpushed_error_t result = {0, 0}; + + octaspire_dern_value_t * const arg = + octaspire_dern_value_as_vector_get_element_at(self, possiblyNegativeIndex); + + octaspire_helpers_verify_not_null(arg); + + if (!octaspire_dern_value_is_function(arg)) + { + result.unpushedError = octaspire_dern_vm_create_new_value_error_format( + vm, + "Function '%s' expects function as " + "%d. argument. Type '%s' was given.", + dernFuncName, + possiblyNegativeIndex, + octaspire_dern_value_helper_get_type_as_c_string(arg->typeTag)); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return result; + } + + result.value = arg; + return result; +} + +octaspire_dern_vector_or_unpushed_error_t +octaspire_dern_value_as_vector_get_element_at_as_vector_or_unpushed_error( + octaspire_dern_value_t * const self, + ptrdiff_t const possiblyNegativeIndex, + char const * const dernFuncName) +{ + octaspire_helpers_verify_not_null(self); + + octaspire_dern_vm_t * const vm = self->vm; + + octaspire_helpers_verify_not_null(vm); + + size_t const stackLength = octaspire_dern_vm_get_stack_length(vm); + octaspire_dern_vector_or_unpushed_error_t result = {0, 0}; + + octaspire_dern_value_t * const arg = + octaspire_dern_value_as_vector_get_element_at(self, possiblyNegativeIndex); + + octaspire_helpers_verify_not_null(arg); + + if (!octaspire_dern_value_is_vector(arg)) + { + result.unpushedError = octaspire_dern_vm_create_new_value_error_format( + vm, + "Function '%s' expects vector as " + "%d. argument. Type '%s' was given.", + dernFuncName, + possiblyNegativeIndex, + octaspire_dern_value_helper_get_type_as_c_string(arg->typeTag)); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return result; + } + + result.value = arg; + return result; +} + +octaspire_dern_vector_or_unpushed_error_t +octaspire_dern_value_as_vector_get_and_eval_element_at_as_vector_or_unpushed_error( + octaspire_dern_value_t * const self, + ptrdiff_t const possiblyNegativeIndex, + char const * const dernFuncName, + octaspire_dern_value_t * const environment) +{ + octaspire_dern_vector_or_unpushed_error_t result = + octaspire_dern_value_as_vector_get_element_at_as_vector_or_unpushed_error( + self, + possiblyNegativeIndex, + dernFuncName); + + if (result.unpushedError) + { + return result; + } + + result.value = octaspire_dern_vm_eval( + self->vm, + result.value, + environment); + + return result; +} + +octaspire_dern_function_or_unpushed_error_t +octaspire_dern_value_as_vector_get_and_eval_element_at_as_callable_or_unpushed_error( + octaspire_dern_value_t * const self, + ptrdiff_t const possiblyNegativeIndex, + char const * const dernFuncName, + octaspire_dern_value_t * const environment) +{ + octaspire_helpers_verify_not_null(self); + + octaspire_dern_vm_t * const vm = self->vm; + + octaspire_helpers_verify_not_null(vm); + + size_t const stackLength = octaspire_dern_vm_get_stack_length(vm); + octaspire_dern_function_or_unpushed_error_t result = {0, 0}; + + octaspire_dern_value_t * arg = + octaspire_dern_value_as_vector_get_element_at(self, possiblyNegativeIndex); + + octaspire_helpers_verify_not_null(arg); + + arg = octaspire_dern_vm_eval(vm, arg, environment); + + octaspire_helpers_verify_not_null(arg); + + if (!octaspire_dern_value_is_callable(arg)) + { + result.unpushedError = octaspire_dern_vm_create_new_value_error_format( + vm, + "Function '%s' expects callable as " + "%d. argument. Type '%s' was given.", + dernFuncName, + possiblyNegativeIndex, + octaspire_dern_value_helper_get_type_as_c_string(arg->typeTag)); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return result; + } + + result.value = arg; + return result; +} + +octaspire_dern_environment_or_unpushed_error_t +octaspire_dern_value_as_vector_get_element_at_as_environment_or_unpushed_error( + octaspire_dern_value_t * const self, + ptrdiff_t const possiblyNegativeIndex, + char const * const dernFuncName) +{ + octaspire_helpers_verify_not_null(self); + + octaspire_dern_vm_t * const vm = self->vm; + + octaspire_helpers_verify_not_null(vm); + + size_t const stackLength = octaspire_dern_vm_get_stack_length(vm); + octaspire_dern_environment_or_unpushed_error_t result = {0, 0}; + + octaspire_dern_value_t * const arg = + octaspire_dern_value_as_vector_get_element_at(self, possiblyNegativeIndex); + + octaspire_helpers_verify_not_null(arg); + + if (!octaspire_dern_value_is_environment(arg)) + { + result.unpushedError = octaspire_dern_vm_create_new_value_error_format( + vm, + "Function '%s' expects environment as " + "%d. argument. Type '%s' was given.", + dernFuncName, + possiblyNegativeIndex, + octaspire_dern_value_helper_get_type_as_c_string(arg->typeTag)); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return result; + } + + result.value = arg; + return result; +} + octaspire_dern_symbol_or_unpushed_error_t octaspire_dern_value_as_vector_get_element_at_as_symbol_or_unpushed_error( octaspire_dern_value_t * const self, @@ -3966,6 +4301,66 @@ octaspire_dern_value_as_vector_get_element_at_as_symbol_or_unpushed_error( return result; } +octaspire_dern_symbol_or_unpushed_error_t +octaspire_dern_value_as_vector_get_element_at_as_this_symbol_or_unpushed_error( + octaspire_dern_value_t * const self, + ptrdiff_t const possiblyNegativeIndex, + char const * const dernFuncName, + char const * const str) +{ + octaspire_helpers_verify_not_null(self); + + octaspire_dern_vm_t * const vm = self->vm; + + octaspire_helpers_verify_not_null(vm); + + size_t const stackLength = octaspire_dern_vm_get_stack_length(vm); + octaspire_dern_symbol_or_unpushed_error_t result = {0, 0, 0}; + + octaspire_dern_value_t * const arg = + octaspire_dern_value_as_vector_get_element_at(self, possiblyNegativeIndex); + + octaspire_helpers_verify_not_null(arg); + + if (!octaspire_dern_value_is_symbol(arg)) + { + result.unpushedError = octaspire_dern_vm_create_new_value_error_format( + vm, + "Builtin '%s' expects symbol '%s' as " + "%d. argument. Type '%s' was given.", + dernFuncName, + str, + possiblyNegativeIndex, + octaspire_dern_value_helper_get_type_as_c_string(arg->typeTag)); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return result; + } + + if (!octaspire_dern_value_as_symbol_is_equal_to_c_string(arg, str)) + { + result.unpushedError = octaspire_dern_vm_create_new_value_error_format( + vm, + "Builtin '%s' expects symbol '%s' as " + "%d. argument. Symbol '%s' was given.", + dernFuncName, + str, + possiblyNegativeIndex, + octaspire_dern_value_as_symbol_get_c_string(arg)); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return result; + } + + result.value = arg; + result.symbol = octaspire_dern_value_as_text_get_string_const(arg); + return result; +} + octaspire_dern_one_of_texts_or_unpushed_error_const_t octaspire_dern_value_as_vector_get_element_at_as_one_of_texts_or_unpushed_error_const( octaspire_dern_value_t const * const self, @@ -4763,3 +5158,16 @@ bool octaspire_dern_value_is_atom(octaspire_dern_value_t const * const self) return false; } +struct octaspire_dern_vm_t * octaspire_dern_value_get_vm( + octaspire_dern_value_t * const self) +{ + octaspire_helpers_verify_not_null(self); + return self->vm; +} + +struct octaspire_dern_vm_t const * octaspire_dern_value_get_vm_const( + octaspire_dern_value_t const * const self) +{ + octaspire_helpers_verify_not_null(self); + return self->vm; +} diff --git a/dev/src/octaspire_dern_vm.c b/dev/src/octaspire_dern_vm.c index 747f054..6ac1a2a 100644 --- a/dev/src/octaspire_dern_vm.c +++ b/dev/src/octaspire_dern_vm.c @@ -1266,8 +1266,24 @@ octaspire_dern_vm_t *octaspire_dern_vm_new_with_config( "eval", octaspire_dern_vm_special_eval, 1, - "Evaluate a value (first argument) in global environment or, if given, in then " - "given environment", + "Either (1) evaluate a value (first argument) in global environment\n" + "or, if given, in the given environment; or (2) apply function given\n" + "as the first argument in the environment given as the second argument\n" + "as a form with the rest of the arguments spread as the arguments to the call.", + true, + env)) + { + abort(); + } + + // generate + if (!octaspire_dern_vm_create_and_register_new_special( + self, + "generate", + octaspire_dern_vm_special_generate, + 4, + "Either (1) generate a collection of values, or (2) do the same\n" + "thing that other Lisps use 'map' or 'mapcar' for.", true, env)) { @@ -1561,6 +1577,19 @@ octaspire_dern_vm_t *octaspire_dern_vm_new_with_config( abort(); } + // character + if (!octaspire_dern_vm_create_and_register_new_builtin( + self, + "character", + octaspire_dern_vm_builtin_character, + 1, + "Create a character value from the given number or description", + true, + env)) + { + abort(); + } + // character? if (!octaspire_dern_vm_create_and_register_new_builtin( self, @@ -1672,14 +1701,16 @@ void octaspire_dern_vm_release(octaspire_dern_vm_t *self) octaspire_allocator_free(self->allocator, self); } -bool octaspire_dern_vm_push_value(octaspire_dern_vm_t *self, octaspire_dern_value_t *value) +bool octaspire_dern_vm_push_value( + octaspire_dern_vm_t * const self, + octaspire_dern_value_t * const value) { return octaspire_vector_push_back_element(self->stack, &value); } bool octaspire_dern_vm_pop_value( - octaspire_dern_vm_t *self, - octaspire_dern_value_t *valueForBalanceCheck) + octaspire_dern_vm_t * const self, + octaspire_dern_value_t * const valueForBalanceCheck) { if (octaspire_vector_peek_back_element(self->stack) != valueForBalanceCheck) { diff --git a/dev/test/test_dern_vm.c b/dev/test/test_dern_vm.c index df86ee9..cdf8200 100644 --- a/dev/test/test_dern_vm.c +++ b/dev/test/test_dern_vm.c @@ -1002,7 +1002,7 @@ TEST octaspire_dern_vm_special_define_called_with_four_arguments_error_at_first_ ASSERT_STR_EQ( "Cannot evaluate operator of type 'error' (: Unbound symbol " - "'noSuchFuNcTion'. Did you mean 'character?'?)\n" + "'noSuchFuNcTion'. Did you mean 'character' or 'character?'?)\n" "\tAt form: >>>>>>>>>>(define x as (noSuchFuNcTion) [x])<<<<<<<<<<\n", octaspire_string_get_c_string(evaluatedValue->value.error->message)); @@ -1099,7 +1099,8 @@ TEST octaspire_dern_vm_special_define_called_with_eight_arguments_error_in_envir ASSERT_STR_EQ( "Special 'define' expects environment as the seventh argument in this " "context. Value ': Cannot evaluate operator of type 'error' (: " - "Unbound symbol 'noSuchFuNcTion'. Did you mean 'character?'?)' was given.\n" + "Unbound symbol 'noSuchFuNcTion'. Did you mean 'character' or " + "'character?'?)' was given.\n" "\tAt form: >>>>>>>>>>(define f as (fn () (quote x)) [f] (quote ()) in (noSuchFuNcTion) howto-ok)<<<<<<<<<<\n", octaspire_string_get_c_string(evaluatedValue->value.error->message)); @@ -8481,7 +8482,7 @@ TEST octaspire_dern_vm_error_in_function_body_is_reported_test(void) // TODO type of 'error' or 'vector'? ASSERT_STR_EQ( "Cannot evaluate operator of type 'error' (: Unbound symbol " - "'NoSuchFunction'. Did you mean 'character?'?)\n" + "'NoSuchFunction'. Did you mean 'character' or 'character?'?)\n" "\tAt form: >>>>>>>>>>(f {D+1})<<<<<<<<<<\n", octaspire_string_get_c_string(evaluatedValue->value.error->message)); @@ -9287,7 +9288,7 @@ TEST octaspire_dern_vm_special_do_error_stops_evaluation_and_is_reported_test(vo ASSERT_STR_EQ( "Cannot evaluate operator of type 'error' (: Unbound symbol " - "'NoSuchFunction'. Did you mean 'counter' or 'character?'?)\n" + "'NoSuchFunction'. Did you mean 'character', 'counter' or 'character?'?)\n" "\tAt form: >>>>>>>>>>(NoSuchFunction)<<<<<<<<<<\n\n" "\tAt form: >>>>>>>>>>(do (++ counter) (NoSuchFunction) (++ counter))<<<<<<<<<<\n", octaspire_string_get_c_string(evaluatedValue->value.error->message)); @@ -12794,7 +12795,7 @@ TEST octaspire_dern_vm_error_during_user_function_call_test(void) ASSERT_EQ(OCTASPIRE_DERN_VALUE_TAG_ERROR, evaluatedValue->typeTag); ASSERT_STR_EQ( "Cannot evaluate operator of type 'error' (: Unbound symbol " - "'NoSuchFunction'. Did you mean 'character?'?)\n" + "'NoSuchFunction'. Did you mean 'character' or 'character?'?)\n" "\tAt form: >>>>>>>>>>(f)<<<<<<<<<<\n", octaspire_string_get_c_string(evaluatedValue->value.error->message)); @@ -12897,6 +12898,79 @@ TEST octaspire_dern_vm_special_eval_plus_1_2_test(void) PASS(); } +TEST octaspire_dern_vm_special_eval_used_as_apply_1_test(void) +{ + octaspire_dern_vm_t *vm = octaspire_dern_vm_new( + octaspireDernVmTestAllocator, + octaspireDernVmTestStdio); + + octaspire_dern_value_t *evaluatedValue = + octaspire_dern_vm_read_from_c_string_and_eval_in_global_environment( + vm, + "(eval + (env-global) '(|a| |b| |c|) |d| |e| |f|)"); + + ASSERT(evaluatedValue); + ASSERT_EQ(OCTASPIRE_DERN_VALUE_TAG_STRING, evaluatedValue->typeTag); + + ASSERT_STR_EQ( + "abcdef", + octaspire_dern_value_as_string_get_c_string(evaluatedValue)); + + octaspire_dern_vm_release(vm); + vm = 0; + + PASS(); +} + +TEST octaspire_dern_vm_special_generate_string_of_10_a_test(void) +{ + octaspire_dern_vm_t *vm = octaspire_dern_vm_new( + octaspireDernVmTestAllocator, + octaspireDernVmTestStdio); + + octaspire_dern_value_t *evaluatedValue = + octaspire_dern_vm_read_from_c_string_and_eval_in_global_environment( + vm, + "(generate 'string of {D+10} |a|)"); + + ASSERT(evaluatedValue); + ASSERT_EQ(OCTASPIRE_DERN_VALUE_TAG_STRING, evaluatedValue->typeTag); + + ASSERT_STR_EQ( + "aaaaaaaaaa", + octaspire_dern_value_as_string_get_c_string(evaluatedValue)); + + octaspire_dern_vm_release(vm); + vm = 0; + + PASS(); +} + +TEST octaspire_dern_vm_special_generate_string_of_10_fn_97plusindex_test(void) +{ + octaspire_dern_vm_t *vm = octaspire_dern_vm_new( + octaspireDernVmTestAllocator, + octaspireDernVmTestStdio); + + octaspire_dern_value_t *evaluatedValue = + octaspire_dern_vm_read_from_c_string_and_eval_in_global_environment( + vm, + "(generate 'string of {D+10} " + " (fn (container index) (character (+ {D+97} index))))"); + + ASSERT(evaluatedValue); + ASSERT_EQ(OCTASPIRE_DERN_VALUE_TAG_STRING, evaluatedValue->typeTag); + + ASSERT_STR_EQ( + "abcdefghij", + octaspire_dern_value_as_string_get_c_string(evaluatedValue)); + + octaspire_dern_vm_release(vm); + vm = 0; + + PASS(); +} + TEST octaspire_dern_vm_special_eval_plus_1_2_in_given_global_env_test(void) { octaspire_dern_vm_t *vm = octaspire_dern_vm_new(octaspireDernVmTestAllocator, octaspireDernVmTestStdio); @@ -13043,7 +13117,8 @@ TEST octaspire_dern_vm_special_eval_called_with_three_arguments_failure_test(voi ASSERT_EQ(OCTASPIRE_DERN_VALUE_TAG_ERROR, evaluatedValue->typeTag); ASSERT_STR_EQ( - "Special 'eval' expects one or two arguments. 3 arguments were given.\n" + "Special 'eval' expects callable value as the first argument when used " + "as 'apply'. Type 'integer' was given.\n" "\tAt form: >>>>>>>>>>(eval (+ {D+1} {D+1}) (env-global) {D+10})<<<<<<<<<<\n", octaspire_string_get_c_string(evaluatedValue->value.error->message)); @@ -18066,6 +18141,9 @@ GREATEST_SUITE(octaspire_dern_vm_suite) RUN_TEST(octaspire_dern_vm_error_during_special_call_test); RUN_TEST(octaspire_dern_vm_error_during_special_call_during_user_function_call_test); RUN_TEST(octaspire_dern_vm_special_eval_plus_1_2_test); + RUN_TEST(octaspire_dern_vm_special_eval_used_as_apply_1_test); + RUN_TEST(octaspire_dern_vm_special_generate_string_of_10_a_test); + RUN_TEST(octaspire_dern_vm_special_generate_string_of_10_fn_97plusindex_test); RUN_TEST(octaspire_dern_vm_special_eval_plus_1_2_in_given_global_env_test); RUN_TEST(octaspire_dern_vm_special_eval_value_from_given_local_env_test); RUN_TEST(octaspire_dern_vm_special_eval_eval_eval_f_1_2_test); diff --git a/release/documentation/dern-manual.html b/release/documentation/dern-manual.html index d00863a..35adb6f 100644 --- a/release/documentation/dern-manual.html +++ b/release/documentation/dern-manual.html @@ -376,7 +376,7 @@ Environments
Getting help with howto
Returning from functions early
-Evaluating values
+Evaluating, applying and generating values
Input and output ports
Converting between types
Searching and indexing
@@ -1591,13 +1591,15 @@ http://www.gnu.org/software/src-highlite -->

((fn () (return nil)))   ; Evaluates into 'nil'.
 ((fn () (return)))       ; Evaluates into 'nil'.
-

22. Evaluating values

+

22. Evaluating, applying and generating values

- Special eval can be used to evaluate a given value. It can - be called with one or two arguments. The second argument, if present, - must be an environment that is used while evaluating. If no environment - is given, the global environment is used instead. + Special eval can be used to evaluate a given value or to + do the same thing that some other Lisps use function apply + for. When used for evaluating values, it can be called with either one + or two arguments. The second argument, if present, must be an environment + that is used while evaluating. If no environment is given, the global + environment is used instead.

@@ -1623,6 +1625,51 @@ http://www.gnu.org/software/src-highlite --> (eval ((eval name-of-fn-to-call)))) [next level] '() howto-no) +

+ The other use for eval is to apply a function + to one or multiple collections of values. This is like + calling the function in a form, but some or all of the arguments + can be inside one or more collections like vector. Some other + Lisps have a separate function apply for doing this; + In Dern eval can handle both of these use cases. +

+ +

+ Dern has also function generate. It has also two + uses; it can be used for generating a collection of values + (somewhat like std::generate_n in C++), or it + can be used to do the same thing that other Lisps might have + function map or mapcar for. +

+ + +
; These two do the same thing:
+(eval (println [Hello]))
+(eval (println [Hello]) (env-current))
+
+; These two do the same thing, when run in global scope:
+(eval (println [Hello]))
+(eval (println [Hello]) (env-global))
+
+; "Apply" of other Lisps can be done like this:
+(eval + (env-global) '(|a| |b| |c|) |d| |e| |f|) ; [abcdef]
+
+; Generate collection of values
+(generate 'vector of {D+10} |a|)                 ; (|a| |a| |a| |a| |a| |a| |a| |a| |a| |a|)
+(generate 'string of {D+10} |a|)                 ; [aaaaaaaaaa]
+
+; ({D+0} {D+1} {D+2} {D+3} {D+4} {D+5} {D+6} {D+7} {D+8} {D+9})
+(generate 'vector of {D+10} (fn (container index) index))
+
+; "map" or "mapcar" of other Lisps can be done like this:
+(generate 'vector mapping to-integer on '(|a| |b| |c|))        ; ({D+97} {D+98} {D+99})
+(generate 'vector mapping * on '({D+1} {D+2}) '({D+3} {D+4}))  ; ({D+3} {D+8})
+
+
+

23. Input and output ports

diff --git a/release/octaspire-dern-amalgamated.c b/release/octaspire-dern-amalgamated.c index 63a0939..89486e1 100644 --- a/release/octaspire-dern-amalgamated.c +++ b/release/octaspire-dern-amalgamated.c @@ -26447,7 +26447,7 @@ limitations under the License. #define OCTASPIRE_DERN_CONFIG_H #define OCTASPIRE_DERN_CONFIG_VERSION_MAJOR "0" -#define OCTASPIRE_DERN_CONFIG_VERSION_MINOR "487" +#define OCTASPIRE_DERN_CONFIG_VERSION_MINOR "488" #define OCTASPIRE_DERN_CONFIG_VERSION_PATCH "0" #define OCTASPIRE_DERN_CONFIG_VERSION_STR "Octaspire Dern version " \ @@ -27182,6 +27182,9 @@ struct octaspire_dern_environment_t *octaspire_dern_value_as_environment_get_val struct octaspire_dern_environment_t const *octaspire_dern_value_as_environment_get_value_const( octaspire_dern_value_t const * const self); +bool octaspire_dern_value_is_callable( + octaspire_dern_value_t const * const self); + bool octaspire_dern_value_is_function( octaspire_dern_value_t const * const self); @@ -27432,6 +27435,14 @@ bool octaspire_dern_value_as_vector_push_back_element( octaspire_dern_value_t *self, void const *element); +bool octaspire_dern_value_as_collection_push_back_element( + octaspire_dern_value_t *self, + void const *element); + +bool octaspire_dern_value_as_text_push_back_character( + octaspire_dern_value_t *self, + void const *element); + bool octaspire_dern_value_as_vector_remove_element_at( octaspire_dern_value_t *self, ptrdiff_t const possiblyNegativeIndex); @@ -27451,6 +27462,11 @@ octaspire_dern_value_t const *octaspire_dern_value_as_vector_get_element_at_cons octaspire_dern_value_t const * const self, ptrdiff_t const possiblyNegativeIndex); +octaspire_dern_value_t *octaspire_dern_value_as_vector_get_element_at_evaluated( + octaspire_dern_value_t * const self, + ptrdiff_t const possiblyNegativeIndex, + octaspire_dern_value_t * const environment); + typedef struct octaspire_dern_c_data_or_unpushed_error_t { void * cData; @@ -27523,6 +27539,59 @@ typedef struct octaspire_dern_string_or_unpushed_error_t } octaspire_dern_string_or_unpushed_error_t; +typedef struct octaspire_dern_vector_or_unpushed_error_t +{ + octaspire_dern_value_t * value; + octaspire_dern_value_t * unpushedError; +} +octaspire_dern_vector_or_unpushed_error_t; + +octaspire_dern_vector_or_unpushed_error_t +octaspire_dern_value_as_vector_get_element_at_as_vector_or_unpushed_error( + octaspire_dern_value_t * const self, + ptrdiff_t const possiblyNegativeIndex, + char const * const dernFuncName); + +octaspire_dern_vector_or_unpushed_error_t +octaspire_dern_value_as_vector_get_and_eval_element_at_as_vector_or_unpushed_error( + octaspire_dern_value_t * const self, + ptrdiff_t const possiblyNegativeIndex, + char const * const dernFuncName, + octaspire_dern_value_t * const environment); + +typedef struct octaspire_dern_function_or_unpushed_error_t +{ + octaspire_dern_value_t * value; + octaspire_dern_value_t * unpushedError; +} +octaspire_dern_function_or_unpushed_error_t; + +octaspire_dern_function_or_unpushed_error_t +octaspire_dern_value_as_vector_get_element_at_as_function_or_unpushed_error( + octaspire_dern_value_t * const self, + ptrdiff_t const possiblyNegativeIndex, + char const * const dernFuncName); + +octaspire_dern_function_or_unpushed_error_t +octaspire_dern_value_as_vector_get_and_eval_element_at_as_callable_or_unpushed_error( + octaspire_dern_value_t * const self, + ptrdiff_t const possiblyNegativeIndex, + char const * const dernFuncName, + octaspire_dern_value_t * const environment); + +typedef struct octaspire_dern_environment_or_unpushed_error_t +{ + octaspire_dern_value_t * value; + octaspire_dern_value_t * unpushedError; +} +octaspire_dern_environment_or_unpushed_error_t; + +octaspire_dern_environment_or_unpushed_error_t +octaspire_dern_value_as_vector_get_element_at_as_environment_or_unpushed_error( + octaspire_dern_value_t * const self, + ptrdiff_t const possiblyNegativeIndex, + char const * const dernFuncName); + typedef struct octaspire_dern_symbol_or_unpushed_error_t { octaspire_string_t const * symbol; @@ -27571,6 +27640,13 @@ octaspire_dern_value_as_vector_get_element_at_as_number_or_unpushed_error_const( ptrdiff_t const possiblyNegativeIndex, char const * const dernFuncName); +octaspire_dern_number_or_unpushed_error_t +octaspire_dern_value_as_vector_get_and_eval_element_at_as_number_or_unpushed_error( + octaspire_dern_value_t * const self, + ptrdiff_t const possiblyNegativeIndex, + char const * const dernFuncName, + octaspire_dern_value_t * const environment); + octaspire_dern_boolean_or_unpushed_error_t octaspire_dern_value_as_vector_get_element_at_as_boolean_or_unpushed_error( octaspire_dern_value_t * const self, @@ -27607,6 +27683,13 @@ octaspire_dern_value_as_vector_get_element_at_as_symbol_or_unpushed_error( ptrdiff_t const possiblyNegativeIndex, char const * const dernFuncName); +octaspire_dern_symbol_or_unpushed_error_t +octaspire_dern_value_as_vector_get_element_at_as_this_symbol_or_unpushed_error( + octaspire_dern_value_t * const self, + ptrdiff_t const possiblyNegativeIndex, + char const * const dernFuncName, + char const * const str); + octaspire_dern_one_of_texts_or_unpushed_error_const_t octaspire_dern_value_as_vector_get_element_at_as_one_of_texts_or_unpushed_error_const( octaspire_dern_value_t const * const self, @@ -27676,6 +27759,12 @@ int octaspire_dern_value_compare( bool octaspire_dern_value_is_atom(octaspire_dern_value_t const * const self); +struct octaspire_dern_vm_t * octaspire_dern_value_get_vm( + octaspire_dern_value_t * const self); + +struct octaspire_dern_vm_t const * octaspire_dern_value_get_vm_const( + octaspire_dern_value_t const * const self); + #ifdef __cplusplus /* extern "C" */ } #endif @@ -28147,11 +28236,13 @@ struct octaspire_dern_value_t *octaspire_dern_vm_create_new_value_c_data_from_ex octaspire_dern_vm_t * const self, octaspire_dern_c_data_t * const cData); -bool octaspire_dern_vm_push_value(octaspire_dern_vm_t *self, struct octaspire_dern_value_t *value); +bool octaspire_dern_vm_push_value( + octaspire_dern_vm_t * const self, + struct octaspire_dern_value_t * const value); bool octaspire_dern_vm_pop_value( - octaspire_dern_vm_t *self, - struct octaspire_dern_value_t *valueForBalanceCheck); + octaspire_dern_vm_t * const self, + struct octaspire_dern_value_t * const valueForBalanceCheck); void const * octaspire_dern_vm_get_top_value(octaspire_dern_vm_t const * const self); @@ -28698,6 +28789,11 @@ octaspire_dern_value_t *octaspire_dern_vm_special_eval( octaspire_dern_value_t *arguments, octaspire_dern_value_t *environment); +octaspire_dern_value_t *octaspire_dern_vm_special_generate( + octaspire_dern_vm_t *vm, + octaspire_dern_value_t *arguments, + octaspire_dern_value_t *environment); + octaspire_dern_value_t *octaspire_dern_vm_special_quote( octaspire_dern_vm_t *vm, octaspire_dern_value_t *arguments, @@ -28830,6 +28926,11 @@ octaspire_dern_value_t *octaspire_dern_vm_builtin_boolean_question_mark( octaspire_dern_value_t *arguments, octaspire_dern_value_t *environment); +octaspire_dern_value_t *octaspire_dern_vm_builtin_character( + octaspire_dern_vm_t *vm, + octaspire_dern_value_t *arguments, + octaspire_dern_value_t *environment); + octaspire_dern_value_t *octaspire_dern_vm_builtin_character_question_mark( octaspire_dern_vm_t *vm, octaspire_dern_value_t *arguments, @@ -35506,41 +35607,217 @@ octaspire_dern_value_t *octaspire_dern_vm_special_define( } } -octaspire_dern_value_t *octaspire_dern_vm_special_eval( +static octaspire_dern_value_t *octaspire_dern_vm_private_special_eval_helper_apply( octaspire_dern_vm_t *vm, octaspire_dern_value_t *arguments, octaspire_dern_value_t *environment) { size_t const stackLength = octaspire_dern_vm_get_stack_length(vm); - octaspire_helpers_verify_true(arguments->typeTag == OCTASPIRE_DERN_VALUE_TAG_VECTOR); - octaspire_helpers_verify_true(environment->typeTag == OCTASPIRE_DERN_VALUE_TAG_ENVIRONMENT); + octaspire_helpers_verify_true( + arguments->typeTag == OCTASPIRE_DERN_VALUE_TAG_VECTOR); - octaspire_vector_t * const vec = arguments->value.vector; + octaspire_helpers_verify_true( + environment->typeTag == OCTASPIRE_DERN_VALUE_TAG_ENVIRONMENT); - if (octaspire_vector_get_length(vec) != 1 && - octaspire_vector_get_length(vec) != 2) + char const * const dernFuncName = "eval"; + size_t const numArgs = octaspire_dern_value_get_length(arguments); + + if (numArgs < 3) { - octaspire_helpers_verify_true(stackLength == octaspire_dern_vm_get_stack_length(vm)); + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + return octaspire_dern_vm_create_new_value_error_format( vm, - "Special 'eval' expects one or two arguments. %zu arguments were given.", - octaspire_vector_get_length(vec)); + "Special '%s' expects at least three arguments. " + "%zu arguments were given.", + dernFuncName, + octaspire_dern_value_get_length(arguments)); } - octaspire_dern_value_t *valueToBeEvaluated = octaspire_vector_get_element_at(vec, 0); + octaspire_dern_value_t * const funcValue = + octaspire_dern_value_as_vector_get_element_at(arguments, 0); + + octaspire_helpers_verify_not_null(funcValue); + + octaspire_dern_value_t * const funcValueEvaluated = + octaspire_dern_vm_eval(vm, funcValue, environment); + + octaspire_helpers_verify_not_null(funcValueEvaluated); + + octaspire_helpers_verify_true( + octaspire_dern_vm_push_value(vm, funcValueEvaluated)); + + if (!octaspire_dern_value_is_callable(funcValueEvaluated)) + { + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, funcValueEvaluated)); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return octaspire_dern_vm_create_new_value_error_format( + vm, + "Special '%s' expects callable value as the first argument " + "when used as 'apply'. Type '%s' was given.", + dernFuncName, + octaspire_dern_value_helper_get_type_as_c_string( + octaspire_dern_value_get_type(funcValueEvaluated))); + } + + octaspire_dern_value_t * const envValue = + octaspire_dern_value_as_vector_get_element_at(arguments, 1); + + octaspire_helpers_verify_not_null(envValue); + + octaspire_dern_value_t * const envValueEvaluated = + octaspire_dern_vm_eval(vm, envValue, environment); + + octaspire_helpers_verify_not_null(envValueEvaluated); + + octaspire_helpers_verify_true( + octaspire_dern_vm_push_value(vm, envValueEvaluated)); + + if (!octaspire_dern_value_is_environment(envValueEvaluated)) + { + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, envValueEvaluated)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, funcValueEvaluated)); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return octaspire_dern_vm_create_new_value_error_format( + vm, + "Special '%s' expects environment value as the second argument " + "when used as 'apply'. Type '%s' was given.", + dernFuncName, + octaspire_dern_value_helper_get_type_as_c_string( + octaspire_dern_value_get_type(envValueEvaluated))); + } + + octaspire_dern_value_t *valueToBeEvaluated = + octaspire_dern_vm_create_new_value_vector(vm); + + octaspire_helpers_verify_not_null(valueToBeEvaluated); + + octaspire_helpers_verify_true( + octaspire_dern_vm_push_value(vm, valueToBeEvaluated)); + + octaspire_helpers_verify_true( + octaspire_dern_value_as_vector_push_back_element( + valueToBeEvaluated, + &funcValueEvaluated)); + + for (size_t i = 2; i < numArgs; ++i) + { + octaspire_dern_value_t * const tmpVal = + octaspire_dern_value_as_vector_get_element_at_evaluated( + arguments, + i, + environment); + + if (!tmpVal) + { + // NOP + } + else if (octaspire_dern_value_is_vector(tmpVal)) + { + for (size_t j = 0; + j < octaspire_dern_value_as_vector_get_length(tmpVal); + ++j) + { + octaspire_dern_value_t * const tmpElemVal = + octaspire_dern_value_as_vector_get_element_at(tmpVal, j); + + octaspire_helpers_verify_true( + octaspire_dern_value_as_vector_push_back_element( + valueToBeEvaluated, + &tmpElemVal)); + } + } + else + { + octaspire_helpers_verify_true( + octaspire_dern_value_as_vector_push_back_element( + valueToBeEvaluated, + &tmpVal)); + } + } + + octaspire_dern_value_t *result = 0; + + result = octaspire_dern_vm_eval( + vm, + valueToBeEvaluated, + envValueEvaluated); + + octaspire_helpers_verify_not_null(result); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, valueToBeEvaluated)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, envValueEvaluated)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, funcValueEvaluated)); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return result; +} + +static octaspire_dern_value_t *octaspire_dern_vm_private_special_eval_helper_eval( + octaspire_dern_vm_t *vm, + octaspire_dern_value_t *arguments, + octaspire_dern_value_t *environment) +{ + size_t const stackLength = octaspire_dern_vm_get_stack_length(vm); + + octaspire_helpers_verify_true( + arguments->typeTag == OCTASPIRE_DERN_VALUE_TAG_VECTOR); + + octaspire_helpers_verify_true( + environment->typeTag == OCTASPIRE_DERN_VALUE_TAG_ENVIRONMENT); + + char const * const dernFuncName = "eval"; + size_t const numArgs = octaspire_dern_value_get_length(arguments); + + if (numArgs < 1 || numArgs > 2) + { + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return octaspire_dern_vm_create_new_value_error_format( + vm, + "Special '%s' expects one or two arguments. " + "%zu arguments were given.", + dernFuncName, + octaspire_dern_value_get_length(arguments)); + } + + octaspire_dern_value_t *valueToBeEvaluated = + octaspire_dern_value_as_vector_get_element_at(arguments, 0); + octaspire_helpers_verify_not_null(valueToBeEvaluated); octaspire_dern_vm_push_value(vm, arguments); octaspire_dern_value_t *result = 0; - if (octaspire_vector_get_length(vec) == 2) + if (numArgs == 2) { - octaspire_dern_value_t *envVal = octaspire_vector_get_element_at(vec, 1); + octaspire_dern_value_t *envVal = + octaspire_dern_value_as_vector_get_element_at(arguments, 1); + octaspire_helpers_verify_not_null(envVal); - if (envVal->typeTag == OCTASPIRE_DERN_VALUE_TAG_ENVIRONMENT) + if (octaspire_dern_value_is_environment(envVal)) { result = octaspire_dern_vm_eval(vm, valueToBeEvaluated, envVal); octaspire_dern_vm_push_value(vm, result); @@ -35554,7 +35831,7 @@ octaspire_dern_value_t *octaspire_dern_vm_special_eval( octaspire_helpers_verify_not_null(envVal); - if (envVal->typeTag == OCTASPIRE_DERN_VALUE_TAG_ERROR) + if (octaspire_dern_value_is_error(envVal)) { octaspire_dern_vm_pop_value(vm, arguments); @@ -35564,7 +35841,7 @@ octaspire_dern_value_t *octaspire_dern_vm_special_eval( return envVal; } - if (envVal->typeTag != OCTASPIRE_DERN_VALUE_TAG_ENVIRONMENT) + if (!octaspire_dern_value_is_environment(envVal)) { octaspire_dern_vm_pop_value(vm, arguments); @@ -35573,15 +35850,20 @@ octaspire_dern_value_t *octaspire_dern_vm_special_eval( return octaspire_dern_vm_create_new_value_error_format( vm, - "Second argument to special 'eval' must evaluate into environment value.\n" - "Now it evaluated into type %s.", - octaspire_dern_value_helper_get_type_as_c_string(envVal->typeTag)); + "Second argument to special '%s' must evaluate into " + "environment value.\nNow it evaluated into type %s.", + dernFuncName, + octaspire_dern_value_helper_get_type_as_c_string( + envVal->typeTag)); } octaspire_dern_vm_push_value(vm, envVal); result = octaspire_dern_vm_eval(vm, valueToBeEvaluated, envVal); octaspire_dern_vm_push_value(vm, result); - octaspire_dern_value_t *tmpResult = octaspire_dern_vm_eval(vm, result, envVal); + + octaspire_dern_value_t *tmpResult = + octaspire_dern_vm_eval(vm, result, envVal); + octaspire_dern_vm_pop_value(vm, result); octaspire_dern_vm_pop_value(vm, envVal); result = tmpResult; @@ -35591,7 +35873,10 @@ octaspire_dern_value_t *octaspire_dern_vm_special_eval( { result = octaspire_dern_vm_eval(vm, valueToBeEvaluated, environment); octaspire_dern_vm_push_value(vm, result); - octaspire_dern_value_t *tmpResult = octaspire_dern_vm_eval(vm, result, environment); + + octaspire_dern_value_t *tmpResult = + octaspire_dern_vm_eval(vm, result, environment); + octaspire_dern_vm_pop_value(vm, result); result = tmpResult; } @@ -35599,10 +35884,686 @@ octaspire_dern_value_t *octaspire_dern_vm_special_eval( octaspire_helpers_verify_not_null(result); octaspire_dern_vm_pop_value(vm, arguments); - octaspire_helpers_verify_true(stackLength == octaspire_dern_vm_get_stack_length(vm)); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + return result; } +octaspire_dern_value_t *octaspire_dern_vm_special_eval( + octaspire_dern_vm_t *vm, + octaspire_dern_value_t *arguments, + octaspire_dern_value_t *environment) +{ + size_t const stackLength = octaspire_dern_vm_get_stack_length(vm); + + octaspire_helpers_verify_true( + arguments->typeTag == OCTASPIRE_DERN_VALUE_TAG_VECTOR); + + octaspire_helpers_verify_true( + environment->typeTag == OCTASPIRE_DERN_VALUE_TAG_ENVIRONMENT); + + char const * const dernFuncName = "eval"; + size_t const numArgs = octaspire_dern_value_get_length(arguments); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + if (numArgs < 1) + { + return octaspire_dern_vm_create_new_value_error_format( + vm, + "Special '%s' expects at least one arguments. " + "%zu arguments were given.", + dernFuncName, + octaspire_dern_value_get_length(arguments)); + } + + if (numArgs == 1 || numArgs == 2) + { + return octaspire_dern_vm_private_special_eval_helper_eval( + vm, + arguments, + environment); + } + + return octaspire_dern_vm_private_special_eval_helper_apply( + vm, + arguments, + environment); +} + +static octaspire_dern_value_t *octaspire_dern_vm_private_special_generate_helper_generate( + octaspire_dern_vm_t *vm, + octaspire_dern_value_t *arguments, + octaspire_dern_value_t *environment) +{ + size_t const stackLength = octaspire_dern_vm_get_stack_length(vm); + + octaspire_helpers_verify_true( + arguments->typeTag == OCTASPIRE_DERN_VALUE_TAG_VECTOR); + + octaspire_helpers_verify_true( + environment->typeTag == OCTASPIRE_DERN_VALUE_TAG_ENVIRONMENT); + + char const * const dernFuncName = "generate"; + size_t const numArgs = octaspire_dern_value_get_length(arguments); + + if (numArgs != 4) + { + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return octaspire_dern_vm_create_new_value_error_format( + vm, + "Special '%s' expects at four arguments. " + "%zu arguments were given.", + dernFuncName, + octaspire_dern_value_get_length(arguments)); + } + + octaspire_dern_value_t * const typeValue = + octaspire_dern_value_as_vector_get_element_at(arguments, 0); + + octaspire_helpers_verify_not_null(typeValue); + + octaspire_dern_value_t * const typeValueEvaluated = + octaspire_dern_vm_eval(vm, typeValue, environment); + + octaspire_helpers_verify_not_null(typeValueEvaluated); + + octaspire_helpers_verify_true( + octaspire_dern_vm_push_value(vm, typeValueEvaluated)); + + if (!octaspire_dern_value_is_symbol(typeValueEvaluated)) + { + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, typeValueEvaluated)); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return octaspire_dern_vm_create_new_value_error_format( + vm, + "Special '%s' expects symbol as the first argument " + "when used to generate values. Type '%s' was given.", + dernFuncName, + octaspire_dern_value_helper_get_type_as_c_string( + octaspire_dern_value_get_type(typeValueEvaluated))); + } + + octaspire_dern_value_t * resultValue = 0; + + // TODO add support for generating more types. + if (octaspire_dern_value_as_text_is_equal_to_c_string( + typeValueEvaluated, + "vector")) + { + resultValue = octaspire_dern_vm_create_new_value_vector(vm); + } + else if (octaspire_dern_value_as_text_is_equal_to_c_string( + typeValueEvaluated, + "string")) + { + resultValue = octaspire_dern_vm_create_new_value_string_from_c_string( + vm, + ""); + } + else + { + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, typeValueEvaluated)); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return octaspire_dern_vm_create_new_value_error_format( + vm, + "Special '%s' expects symbol 'vector' or 'string'" + "as the first argument when used to generate values. " + "Symbol '%s' was given.", + dernFuncName, + octaspire_dern_value_as_text_get_c_string(typeValueEvaluated)); + } + + octaspire_helpers_verify_not_null(resultValue); + + octaspire_helpers_verify_true( + octaspire_dern_vm_push_value(vm, resultValue)); + + // of + + octaspire_dern_symbol_or_unpushed_error_t const symbolOfOrError = + octaspire_dern_value_as_vector_get_element_at_as_this_symbol_or_unpushed_error( + arguments, + 1, + dernFuncName, + "of"); + + if (symbolOfOrError.unpushedError) + { + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, resultValue)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, typeValueEvaluated)); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return symbolOfOrError.unpushedError; + } + + // Number + + octaspire_dern_number_or_unpushed_error_t numberOrError = + octaspire_dern_value_as_vector_get_and_eval_element_at_as_number_or_unpushed_error( + arguments, + 2, + dernFuncName, + environment); + + if (numberOrError.unpushedError) + { + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, resultValue)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, typeValueEvaluated)); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return numberOrError.unpushedError; + } + + octaspire_helpers_verify_true( + octaspire_dern_vm_push_value(vm, numberOrError.value)); + + if (numberOrError.number < 0) + { + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, numberOrError.value)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, resultValue)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, typeValueEvaluated)); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return octaspire_dern_vm_create_new_value_error_format( + vm, + "Special '%s' expects non negative number " + "as the third argument when used to generate values. ", + dernFuncName); + } + + size_t number = (size_t)(numberOrError.number); + + // Any literal value or generator function + + octaspire_dern_value_t * const generatorValue = + octaspire_dern_value_as_vector_get_element_at(arguments, 3); + + octaspire_helpers_verify_not_null(generatorValue); + + octaspire_dern_value_t * const generatorValueEvaluated = + octaspire_dern_vm_eval(vm, generatorValue, environment); + + octaspire_helpers_verify_not_null(generatorValueEvaluated); + + octaspire_helpers_verify_true( + octaspire_dern_vm_push_value(vm, generatorValueEvaluated)); + + if (octaspire_dern_value_is_callable(generatorValueEvaluated)) + { + for (size_t i = 0; i < number; ++i) + { + octaspire_dern_value_t * const indexValue = + octaspire_dern_vm_create_new_value_integer( + vm, + i); + + octaspire_helpers_verify_not_null(indexValue); + + octaspire_helpers_verify_true( + octaspire_dern_vm_push_value(vm, indexValue)); + + octaspire_dern_value_t * const quoteValue = + octaspire_dern_vm_create_new_value_vector(vm); + + octaspire_helpers_verify_not_null(quoteValue); + + octaspire_helpers_verify_true( + octaspire_dern_vm_push_value(vm, quoteValue)); + + octaspire_dern_value_t * const quoteSymbolValue = + octaspire_dern_vm_create_new_value_symbol_from_c_string( + vm, + "quote"); + + octaspire_helpers_verify_not_null(quoteSymbolValue); + + octaspire_helpers_verify_true( + octaspire_dern_value_as_vector_push_back_element( + quoteValue, + "eSymbolValue)); + + octaspire_helpers_verify_true( + octaspire_dern_value_as_vector_push_back_element( + quoteValue, + &resultValue)); + + octaspire_dern_value_t * const formValue = + octaspire_dern_vm_create_new_value_vector_from_values( + vm, + 3, + //generatorValueEvaluated, + generatorValue, + quoteValue, + indexValue); + + octaspire_helpers_verify_not_null(formValue); + + octaspire_helpers_verify_true( + octaspire_dern_vm_push_value(vm, formValue)); + + octaspire_dern_value_t * const elemValue = octaspire_dern_vm_eval( + vm, + formValue, + environment); + + octaspire_helpers_verify_not_null(elemValue); + + octaspire_helpers_verify_true( + octaspire_dern_value_as_collection_push_back_element( + resultValue, + elemValue)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, formValue)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, quoteValue)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, indexValue)); + } + } + else + { + for (size_t i = 0; i < number; ++i) + { + octaspire_helpers_verify_true( + octaspire_dern_value_as_collection_push_back_element( + resultValue, + generatorValueEvaluated)); + } + } + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, generatorValueEvaluated)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, numberOrError.value)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, resultValue)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, typeValueEvaluated)); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return resultValue; +} + +static octaspire_dern_value_t *octaspire_dern_vm_private_special_generate_helper_map( + octaspire_dern_vm_t *vm, + octaspire_dern_value_t *arguments, + octaspire_dern_value_t *environment) +{ + size_t const stackLength = octaspire_dern_vm_get_stack_length(vm); + + octaspire_helpers_verify_true( + arguments->typeTag == OCTASPIRE_DERN_VALUE_TAG_VECTOR); + + octaspire_helpers_verify_true( + environment->typeTag == OCTASPIRE_DERN_VALUE_TAG_ENVIRONMENT); + + char const * const dernFuncName = "generate"; + size_t const numArgs = octaspire_dern_value_get_length(arguments); + + if (numArgs < 5) + { + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return octaspire_dern_vm_create_new_value_error_format( + vm, + "Special '%s' expects at least five arguments " + "when used as 'map'; %zu arguments were given.", + dernFuncName, + octaspire_dern_value_get_length(arguments)); + } + + octaspire_dern_value_t * const typeValue = + octaspire_dern_value_as_vector_get_element_at(arguments, 0); + + octaspire_helpers_verify_not_null(typeValue); + + octaspire_dern_value_t * const typeValueEvaluated = + octaspire_dern_vm_eval(vm, typeValue, environment); + + octaspire_helpers_verify_not_null(typeValueEvaluated); + + octaspire_helpers_verify_true( + octaspire_dern_vm_push_value(vm, typeValueEvaluated)); + + if (!octaspire_dern_value_is_symbol(typeValueEvaluated)) + { + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, typeValueEvaluated)); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return octaspire_dern_vm_create_new_value_error_format( + vm, + "Special '%s' expects symbol as the first argument " + "when used to map values. Type '%s' was given.", + dernFuncName, + octaspire_dern_value_helper_get_type_as_c_string( + octaspire_dern_value_get_type(typeValueEvaluated))); + } + + octaspire_dern_value_t * resultValue = 0; + + // TODO add support for generating more types. + if (octaspire_dern_value_as_text_is_equal_to_c_string( + typeValueEvaluated, + "vector")) + { + resultValue = octaspire_dern_vm_create_new_value_vector(vm); + } + else if (octaspire_dern_value_as_text_is_equal_to_c_string( + typeValueEvaluated, + "string")) + { + resultValue = octaspire_dern_vm_create_new_value_string_from_c_string( + vm, + ""); + } + else + { + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, typeValueEvaluated)); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return octaspire_dern_vm_create_new_value_error_format( + vm, + "Special '%s' expects symbol 'vector' or 'string'" + "as the first argument when used to generate values. " + "Symbol '%s' was given.", + dernFuncName, + octaspire_dern_value_as_text_get_c_string(typeValueEvaluated)); + } + + octaspire_helpers_verify_not_null(resultValue); + + octaspire_helpers_verify_true( + octaspire_dern_vm_push_value(vm, resultValue)); + + // mapping + + octaspire_dern_symbol_or_unpushed_error_t const symbolOfOrError = + octaspire_dern_value_as_vector_get_element_at_as_this_symbol_or_unpushed_error( + arguments, + 1, + dernFuncName, + "mapping"); + + if (symbolOfOrError.unpushedError) + { + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, resultValue)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, typeValueEvaluated)); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return symbolOfOrError.unpushedError; + } + + // Callable function + + octaspire_dern_function_or_unpushed_error_t const callableOrError = + octaspire_dern_value_as_vector_get_and_eval_element_at_as_callable_or_unpushed_error( + arguments, + 2, + dernFuncName, + environment); + + if (callableOrError.unpushedError) + { + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, resultValue)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, typeValueEvaluated)); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return callableOrError.unpushedError; + } + + octaspire_helpers_verify_true( + octaspire_dern_vm_push_value(vm, callableOrError.value)); + + // on + + octaspire_dern_symbol_or_unpushed_error_t const symbolOnOrError = + octaspire_dern_value_as_vector_get_element_at_as_this_symbol_or_unpushed_error( + arguments, + 3, + dernFuncName, + "on"); + + if (symbolOnOrError.unpushedError) + { + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, callableOrError.value)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, resultValue)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, typeValueEvaluated)); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return symbolOnOrError.unpushedError; + } + + // Generate form (callable arg1 arg2 argN) to be called and call it + // correct number of times storing the results into the result collection. + // Number of arguments depends on the number of vectors after symbol 'on'. + // One argument is picked from each of those vectors. + // Shortest of those vectors tells how many calls are made. + + octaspire_dern_value_t * const argVectorsVal = + octaspire_dern_vm_create_new_value_vector(vm); + + octaspire_helpers_verify_not_null(argVectorsVal); + + octaspire_helpers_verify_true( + octaspire_dern_vm_push_value(vm, argVectorsVal)); + + size_t minVecLen = 0; + + for (size_t i = 4; i < octaspire_dern_value_as_vector_get_length(arguments); ++i) + { + octaspire_dern_vector_or_unpushed_error_t const vectorOrError = + octaspire_dern_value_as_vector_get_and_eval_element_at_as_vector_or_unpushed_error( + arguments, + i, + dernFuncName, + environment); + + if (vectorOrError.unpushedError) + { + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, argVectorsVal)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, callableOrError.value)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, resultValue)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, typeValueEvaluated)); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return vectorOrError.unpushedError; + } + + octaspire_helpers_verify_true( + octaspire_dern_value_as_vector_push_back_element( + argVectorsVal, + &(vectorOrError.value))); + + if (octaspire_dern_value_as_vector_get_length(vectorOrError.value) > + minVecLen) + { + minVecLen = octaspire_dern_value_as_vector_get_length( + vectorOrError.value); + } + } + + for (size_t i = 0; i < minVecLen; ++i) + { + octaspire_dern_value_t * const formValue = + octaspire_dern_vm_create_new_value_vector(vm); + + octaspire_helpers_verify_not_null(formValue); + + octaspire_helpers_verify_true( + octaspire_dern_vm_push_value(vm, formValue)); + + octaspire_helpers_verify_true( + octaspire_dern_value_as_vector_push_back_element( + formValue, + &(callableOrError.value))); + + for (size_t j = 0; + j < octaspire_dern_value_as_vector_get_length(argVectorsVal); + ++j) + { + octaspire_dern_value_t * const vecVal = + octaspire_dern_value_as_vector_get_element_at(argVectorsVal, j); + + octaspire_helpers_verify_not_null(vecVal); + + octaspire_dern_value_t * argVal = + octaspire_dern_value_as_vector_get_element_at(vecVal, i); + + octaspire_helpers_verify_not_null(argVal); + + octaspire_helpers_verify_true( + octaspire_dern_value_as_vector_push_back_element( + formValue, + &argVal)); + } + + octaspire_dern_value_t * const resultElemVal = + octaspire_dern_vm_eval(vm, formValue, environment); + + octaspire_helpers_verify_not_null(resultElemVal); + + octaspire_helpers_verify_true( + octaspire_dern_value_as_vector_push_back_element( + resultValue, + &resultElemVal)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, formValue)); + } + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, argVectorsVal)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, callableOrError.value)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, resultValue)); + + octaspire_helpers_verify_true( + octaspire_dern_vm_pop_value(vm, typeValueEvaluated)); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return resultValue; +} + +octaspire_dern_value_t *octaspire_dern_vm_special_generate( + octaspire_dern_vm_t *vm, + octaspire_dern_value_t *arguments, + octaspire_dern_value_t *environment) +{ + size_t const stackLength = octaspire_dern_vm_get_stack_length(vm); + + octaspire_helpers_verify_true( + arguments->typeTag == OCTASPIRE_DERN_VALUE_TAG_VECTOR); + + octaspire_helpers_verify_true( + environment->typeTag == OCTASPIRE_DERN_VALUE_TAG_ENVIRONMENT); + + char const * const dernFuncName = "generate"; + size_t const numArgs = octaspire_dern_value_get_length(arguments); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + if (numArgs < 4) + { + return octaspire_dern_vm_create_new_value_error_format( + vm, + "Special '%s' expects at least four arguments. " + "%zu arguments were given.", + dernFuncName, + octaspire_dern_value_get_length(arguments)); + } + + if (numArgs == 4) + { + return octaspire_dern_vm_private_special_generate_helper_generate( + vm, + arguments, + environment); + } + + return octaspire_dern_vm_private_special_generate_helper_map( + vm, + arguments, + environment); +} + octaspire_dern_value_t *octaspire_dern_vm_special_quote( octaspire_dern_vm_t *vm, octaspire_dern_value_t *arguments, @@ -45280,6 +46241,107 @@ octaspire_dern_value_t *octaspire_dern_vm_builtin_boolean_question_mark( return octaspire_dern_vm_create_new_value_boolean(vm, octaspire_dern_value_is_boolean(value)); } +octaspire_dern_value_t *octaspire_dern_vm_builtin_character( + octaspire_dern_vm_t *vm, + octaspire_dern_value_t *arguments, + octaspire_dern_value_t *environment) +{ + size_t const stackLength = octaspire_dern_vm_get_stack_length(vm); + + octaspire_helpers_verify_true( + arguments->typeTag == OCTASPIRE_DERN_VALUE_TAG_VECTOR); + + octaspire_helpers_verify_true( + environment->typeTag == OCTASPIRE_DERN_VALUE_TAG_ENVIRONMENT); + + char const * const dernFuncName = "character"; + size_t const numArgs = octaspire_dern_value_get_length(arguments); + + if (numArgs != 1) + { + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return octaspire_dern_vm_create_new_value_error_format( + vm, + "Builtin '%s' expects at one argument. " + "%zu arguments were given.", + dernFuncName, + numArgs); + } + + octaspire_dern_value_t const * const value = + octaspire_dern_value_as_vector_get_element_at(arguments, 0); + + if (octaspire_dern_value_is_number(value)) + { + int32_t const number = octaspire_dern_value_as_number_get_value(value); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + if (number < 0) + { + return octaspire_dern_vm_create_new_value_error_format( + vm, + "Builtin '%s' expects non-negative number as the first argument. " + "Negative number %" PRId32 " was given.", + dernFuncName, + number); + } + + return octaspire_dern_vm_create_new_value_character_from_uint32t( + vm, + (uint32_t)number); + } + else if (octaspire_dern_value_is_text(value)) + { + // TODO add support for more character names. + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + if (octaspire_dern_value_as_text_is_equal_to_c_string( + value, + "copyright sign")) + { + return octaspire_dern_vm_create_new_value_character_from_uint32t( + vm, + (uint32_t)0xA9); + } + if (octaspire_dern_value_as_text_is_equal_to_c_string( + value, + "greek small letter lambda")) + { + return octaspire_dern_vm_create_new_value_character_from_uint32t( + vm, + (uint32_t)0x3BB); + } + else + { + return octaspire_dern_vm_create_new_value_error_format( + vm, + "Builtin '%s' does not support character name " + "'%s' that was given as the first argument.", + dernFuncName, + octaspire_dern_value_as_text_get_c_string(value)); + } + } + else + { + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return octaspire_dern_vm_create_new_value_error_format( + vm, + "Builtin '%s' expects number of text as the first argument. " + "Type '%s' was given.", + dernFuncName, + octaspire_dern_value_helper_get_type_as_c_string( + octaspire_dern_value_get_type(value))); + } +} + octaspire_dern_value_t *octaspire_dern_vm_builtin_character_question_mark( octaspire_dern_vm_t *vm, octaspire_dern_value_t *arguments, @@ -47867,6 +48929,32 @@ octaspire_dern_environment_t const *octaspire_dern_value_as_environment_get_valu return self->value.environment; } +bool octaspire_dern_value_is_callable( + octaspire_dern_value_t const * const self) +{ + if (octaspire_dern_value_is_function(self)) + { + return true; + } + + if (octaspire_dern_value_is_macro(self)) + { + return true; + } + + if (octaspire_dern_value_is_builtin(self)) + { + return true; + } + + if (octaspire_dern_value_is_special(self)) + { + return true; + } + + return false; +} + bool octaspire_dern_value_is_function( octaspire_dern_value_t const * const self) { @@ -49622,6 +50710,61 @@ bool octaspire_dern_value_as_vector_push_back_element( return octaspire_vector_push_back_element(self->value.vector, element); } +bool octaspire_dern_value_as_collection_push_back_element( + octaspire_dern_value_t *self, + void const *element) +{ + // TODO add support for more collection types. + + octaspire_helpers_verify_not_null(self); + octaspire_helpers_verify_not_null(element); + + if (octaspire_dern_value_is_text(self)) + { + if (!octaspire_dern_value_is_character(element)) + { + return false; + } + + return octaspire_dern_value_as_text_push_back_character(self, element); + } + else if (octaspire_dern_value_is_vector(self)) + { + return octaspire_dern_value_as_vector_push_back_element(self, element); + } + + return false; +} + +bool octaspire_dern_value_as_text_push_back_character( + octaspire_dern_value_t *self, + void const *element) +{ + octaspire_helpers_verify_not_null(self); + octaspire_helpers_verify_not_null(element); + octaspire_helpers_verify_true(octaspire_dern_value_is_character(element)); + + octaspire_dern_value_t * const charVal = + (octaspire_dern_value_t * const)element; + + if (octaspire_dern_value_is_string(self)) + { + return octaspire_string_push_back_ucs_character( + self->value.string, + octaspire_string_get_ucs_character_at_index( + charVal->value.character, 0)); + } + else if (octaspire_dern_value_is_symbol(self)) + { + return octaspire_string_push_back_ucs_character( + self->value.symbol, + octaspire_string_get_ucs_character_at_index( + charVal->value.character, 0)); + } + + return false; +} + bool octaspire_dern_value_as_vector_remove_element_at( octaspire_dern_value_t *self, ptrdiff_t const possiblyNegativeIndex) @@ -49678,6 +50821,25 @@ octaspire_dern_value_t const *octaspire_dern_value_as_vector_get_element_at_cons possiblyNegativeIndex); } +octaspire_dern_value_t *octaspire_dern_value_as_vector_get_element_at_evaluated( + octaspire_dern_value_t * const self, + ptrdiff_t const possiblyNegativeIndex, + octaspire_dern_value_t * const environment) +{ + octaspire_helpers_verify_true( + octaspire_dern_value_is_vector(self)); + + octaspire_dern_value_t * const tmpVal = + octaspire_vector_get_element_at( + self->value.vector, + possiblyNegativeIndex); + + return octaspire_dern_vm_eval( + octaspire_dern_value_get_vm(self), + tmpVal, + environment); +} + octaspire_dern_c_data_or_unpushed_error_const_t octaspire_dern_value_as_vector_get_element_at_as_c_data_or_unpushed_error_const( octaspire_dern_value_t const * const self, @@ -49814,6 +50976,50 @@ octaspire_dern_value_as_vector_get_element_at_as_number_or_unpushed_error_const( return result; } +octaspire_dern_number_or_unpushed_error_t +octaspire_dern_value_as_vector_get_and_eval_element_at_as_number_or_unpushed_error( + octaspire_dern_value_t * const self, + ptrdiff_t const possiblyNegativeIndex, + char const * const dernFuncName, + octaspire_dern_value_t * const environment) +{ + octaspire_helpers_verify_not_null(self); + + octaspire_dern_vm_t * const vm = self->vm; + + octaspire_helpers_verify_not_null(vm); + + size_t const stackLength = octaspire_dern_vm_get_stack_length(vm); + octaspire_dern_number_or_unpushed_error_t result = {0, 0, 0}; + + octaspire_dern_value_t * arg = + octaspire_dern_value_as_vector_get_element_at(self, possiblyNegativeIndex); + + octaspire_helpers_verify_not_null(arg); + + arg = octaspire_dern_vm_eval(vm, arg, environment); + + if (!octaspire_dern_value_is_number(arg)) + { + result.unpushedError = octaspire_dern_vm_create_new_value_error_format( + vm, + "Builtin '%s' expects number as " + "%d. argument. Type '%s' was given.", + dernFuncName, + possiblyNegativeIndex, + octaspire_dern_value_helper_get_type_as_c_string(arg->typeTag)); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return result; + } + + result.value = arg; + result.number = octaspire_dern_value_as_number_get_value(arg); + return result; +} + octaspire_dern_boolean_or_unpushed_error_t octaspire_dern_value_as_vector_get_element_at_as_boolean_or_unpushed_error( octaspire_dern_value_t * const self, @@ -49978,6 +51184,197 @@ octaspire_dern_value_as_vector_get_element_at_as_string_or_unpushed_error( return result; } +octaspire_dern_function_or_unpushed_error_t +octaspire_dern_value_as_vector_get_element_at_as_function_or_unpushed_error( + octaspire_dern_value_t * const self, + ptrdiff_t const possiblyNegativeIndex, + char const * const dernFuncName) +{ + octaspire_helpers_verify_not_null(self); + + octaspire_dern_vm_t * const vm = self->vm; + + octaspire_helpers_verify_not_null(vm); + + size_t const stackLength = octaspire_dern_vm_get_stack_length(vm); + octaspire_dern_function_or_unpushed_error_t result = {0, 0}; + + octaspire_dern_value_t * const arg = + octaspire_dern_value_as_vector_get_element_at(self, possiblyNegativeIndex); + + octaspire_helpers_verify_not_null(arg); + + if (!octaspire_dern_value_is_function(arg)) + { + result.unpushedError = octaspire_dern_vm_create_new_value_error_format( + vm, + "Function '%s' expects function as " + "%d. argument. Type '%s' was given.", + dernFuncName, + possiblyNegativeIndex, + octaspire_dern_value_helper_get_type_as_c_string(arg->typeTag)); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return result; + } + + result.value = arg; + return result; +} + +octaspire_dern_vector_or_unpushed_error_t +octaspire_dern_value_as_vector_get_element_at_as_vector_or_unpushed_error( + octaspire_dern_value_t * const self, + ptrdiff_t const possiblyNegativeIndex, + char const * const dernFuncName) +{ + octaspire_helpers_verify_not_null(self); + + octaspire_dern_vm_t * const vm = self->vm; + + octaspire_helpers_verify_not_null(vm); + + size_t const stackLength = octaspire_dern_vm_get_stack_length(vm); + octaspire_dern_vector_or_unpushed_error_t result = {0, 0}; + + octaspire_dern_value_t * const arg = + octaspire_dern_value_as_vector_get_element_at(self, possiblyNegativeIndex); + + octaspire_helpers_verify_not_null(arg); + + if (!octaspire_dern_value_is_vector(arg)) + { + result.unpushedError = octaspire_dern_vm_create_new_value_error_format( + vm, + "Function '%s' expects vector as " + "%d. argument. Type '%s' was given.", + dernFuncName, + possiblyNegativeIndex, + octaspire_dern_value_helper_get_type_as_c_string(arg->typeTag)); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return result; + } + + result.value = arg; + return result; +} + +octaspire_dern_vector_or_unpushed_error_t +octaspire_dern_value_as_vector_get_and_eval_element_at_as_vector_or_unpushed_error( + octaspire_dern_value_t * const self, + ptrdiff_t const possiblyNegativeIndex, + char const * const dernFuncName, + octaspire_dern_value_t * const environment) +{ + octaspire_dern_vector_or_unpushed_error_t result = + octaspire_dern_value_as_vector_get_element_at_as_vector_or_unpushed_error( + self, + possiblyNegativeIndex, + dernFuncName); + + if (result.unpushedError) + { + return result; + } + + result.value = octaspire_dern_vm_eval( + self->vm, + result.value, + environment); + + return result; +} + +octaspire_dern_function_or_unpushed_error_t +octaspire_dern_value_as_vector_get_and_eval_element_at_as_callable_or_unpushed_error( + octaspire_dern_value_t * const self, + ptrdiff_t const possiblyNegativeIndex, + char const * const dernFuncName, + octaspire_dern_value_t * const environment) +{ + octaspire_helpers_verify_not_null(self); + + octaspire_dern_vm_t * const vm = self->vm; + + octaspire_helpers_verify_not_null(vm); + + size_t const stackLength = octaspire_dern_vm_get_stack_length(vm); + octaspire_dern_function_or_unpushed_error_t result = {0, 0}; + + octaspire_dern_value_t * arg = + octaspire_dern_value_as_vector_get_element_at(self, possiblyNegativeIndex); + + octaspire_helpers_verify_not_null(arg); + + arg = octaspire_dern_vm_eval(vm, arg, environment); + + octaspire_helpers_verify_not_null(arg); + + if (!octaspire_dern_value_is_callable(arg)) + { + result.unpushedError = octaspire_dern_vm_create_new_value_error_format( + vm, + "Function '%s' expects callable as " + "%d. argument. Type '%s' was given.", + dernFuncName, + possiblyNegativeIndex, + octaspire_dern_value_helper_get_type_as_c_string(arg->typeTag)); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return result; + } + + result.value = arg; + return result; +} + +octaspire_dern_environment_or_unpushed_error_t +octaspire_dern_value_as_vector_get_element_at_as_environment_or_unpushed_error( + octaspire_dern_value_t * const self, + ptrdiff_t const possiblyNegativeIndex, + char const * const dernFuncName) +{ + octaspire_helpers_verify_not_null(self); + + octaspire_dern_vm_t * const vm = self->vm; + + octaspire_helpers_verify_not_null(vm); + + size_t const stackLength = octaspire_dern_vm_get_stack_length(vm); + octaspire_dern_environment_or_unpushed_error_t result = {0, 0}; + + octaspire_dern_value_t * const arg = + octaspire_dern_value_as_vector_get_element_at(self, possiblyNegativeIndex); + + octaspire_helpers_verify_not_null(arg); + + if (!octaspire_dern_value_is_environment(arg)) + { + result.unpushedError = octaspire_dern_vm_create_new_value_error_format( + vm, + "Function '%s' expects environment as " + "%d. argument. Type '%s' was given.", + dernFuncName, + possiblyNegativeIndex, + octaspire_dern_value_helper_get_type_as_c_string(arg->typeTag)); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return result; + } + + result.value = arg; + return result; +} + octaspire_dern_symbol_or_unpushed_error_t octaspire_dern_value_as_vector_get_element_at_as_symbol_or_unpushed_error( octaspire_dern_value_t * const self, @@ -50019,6 +51416,66 @@ octaspire_dern_value_as_vector_get_element_at_as_symbol_or_unpushed_error( return result; } +octaspire_dern_symbol_or_unpushed_error_t +octaspire_dern_value_as_vector_get_element_at_as_this_symbol_or_unpushed_error( + octaspire_dern_value_t * const self, + ptrdiff_t const possiblyNegativeIndex, + char const * const dernFuncName, + char const * const str) +{ + octaspire_helpers_verify_not_null(self); + + octaspire_dern_vm_t * const vm = self->vm; + + octaspire_helpers_verify_not_null(vm); + + size_t const stackLength = octaspire_dern_vm_get_stack_length(vm); + octaspire_dern_symbol_or_unpushed_error_t result = {0, 0, 0}; + + octaspire_dern_value_t * const arg = + octaspire_dern_value_as_vector_get_element_at(self, possiblyNegativeIndex); + + octaspire_helpers_verify_not_null(arg); + + if (!octaspire_dern_value_is_symbol(arg)) + { + result.unpushedError = octaspire_dern_vm_create_new_value_error_format( + vm, + "Builtin '%s' expects symbol '%s' as " + "%d. argument. Type '%s' was given.", + dernFuncName, + str, + possiblyNegativeIndex, + octaspire_dern_value_helper_get_type_as_c_string(arg->typeTag)); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return result; + } + + if (!octaspire_dern_value_as_symbol_is_equal_to_c_string(arg, str)) + { + result.unpushedError = octaspire_dern_vm_create_new_value_error_format( + vm, + "Builtin '%s' expects symbol '%s' as " + "%d. argument. Symbol '%s' was given.", + dernFuncName, + str, + possiblyNegativeIndex, + octaspire_dern_value_as_symbol_get_c_string(arg)); + + octaspire_helpers_verify_true( + stackLength == octaspire_dern_vm_get_stack_length(vm)); + + return result; + } + + result.value = arg; + result.symbol = octaspire_dern_value_as_text_get_string_const(arg); + return result; +} + octaspire_dern_one_of_texts_or_unpushed_error_const_t octaspire_dern_value_as_vector_get_element_at_as_one_of_texts_or_unpushed_error_const( octaspire_dern_value_t const * const self, @@ -50816,6 +52273,19 @@ bool octaspire_dern_value_is_atom(octaspire_dern_value_t const * const self) return false; } +struct octaspire_dern_vm_t * octaspire_dern_value_get_vm( + octaspire_dern_value_t * const self) +{ + octaspire_helpers_verify_not_null(self); + return self->vm; +} + +struct octaspire_dern_vm_t const * octaspire_dern_value_get_vm_const( + octaspire_dern_value_t const * const self) +{ + octaspire_helpers_verify_not_null(self); + return self->vm; +} ////////////////////////////////////////////////////////////////////////////////////////////////// // END OF dev/src/octaspire_dern_value.c ////////////////////////////////////////////////////////////////////////////////////////////////// @@ -52076,8 +53546,24 @@ octaspire_dern_vm_t *octaspire_dern_vm_new_with_config( "eval", octaspire_dern_vm_special_eval, 1, - "Evaluate a value (first argument) in global environment or, if given, in then " - "given environment", + "Either (1) evaluate a value (first argument) in global environment\n" + "or, if given, in the given environment; or (2) apply function given\n" + "as the first argument in the environment given as the second argument\n" + "as a form with the rest of the arguments spread as the arguments to the call.", + true, + env)) + { + abort(); + } + + // generate + if (!octaspire_dern_vm_create_and_register_new_special( + self, + "generate", + octaspire_dern_vm_special_generate, + 4, + "Either (1) generate a collection of values, or (2) do the same\n" + "thing that other Lisps use 'map' or 'mapcar' for.", true, env)) { @@ -52371,6 +53857,19 @@ octaspire_dern_vm_t *octaspire_dern_vm_new_with_config( abort(); } + // character + if (!octaspire_dern_vm_create_and_register_new_builtin( + self, + "character", + octaspire_dern_vm_builtin_character, + 1, + "Create a character value from the given number or description", + true, + env)) + { + abort(); + } + // character? if (!octaspire_dern_vm_create_and_register_new_builtin( self, @@ -52482,14 +53981,16 @@ void octaspire_dern_vm_release(octaspire_dern_vm_t *self) octaspire_allocator_free(self->allocator, self); } -bool octaspire_dern_vm_push_value(octaspire_dern_vm_t *self, octaspire_dern_value_t *value) +bool octaspire_dern_vm_push_value( + octaspire_dern_vm_t * const self, + octaspire_dern_value_t * const value) { return octaspire_vector_push_back_element(self->stack, &value); } bool octaspire_dern_vm_pop_value( - octaspire_dern_vm_t *self, - octaspire_dern_value_t *valueForBalanceCheck) + octaspire_dern_vm_t * const self, + octaspire_dern_value_t * const valueForBalanceCheck) { if (octaspire_vector_peek_back_element(self->stack) != valueForBalanceCheck) { @@ -62257,7 +63758,7 @@ TEST octaspire_dern_vm_special_define_called_with_four_arguments_error_at_first_ ASSERT_STR_EQ( "Cannot evaluate operator of type 'error' (: Unbound symbol " - "'noSuchFuNcTion'. Did you mean 'character?'?)\n" + "'noSuchFuNcTion'. Did you mean 'character' or 'character?'?)\n" "\tAt form: >>>>>>>>>>(define x as (noSuchFuNcTion) [x])<<<<<<<<<<\n", octaspire_string_get_c_string(evaluatedValue->value.error->message)); @@ -62354,7 +63855,8 @@ TEST octaspire_dern_vm_special_define_called_with_eight_arguments_error_in_envir ASSERT_STR_EQ( "Special 'define' expects environment as the seventh argument in this " "context. Value ': Cannot evaluate operator of type 'error' (: " - "Unbound symbol 'noSuchFuNcTion'. Did you mean 'character?'?)' was given.\n" + "Unbound symbol 'noSuchFuNcTion'. Did you mean 'character' or " + "'character?'?)' was given.\n" "\tAt form: >>>>>>>>>>(define f as (fn () (quote x)) [f] (quote ()) in (noSuchFuNcTion) howto-ok)<<<<<<<<<<\n", octaspire_string_get_c_string(evaluatedValue->value.error->message)); @@ -69736,7 +71238,7 @@ TEST octaspire_dern_vm_error_in_function_body_is_reported_test(void) // TODO type of 'error' or 'vector'? ASSERT_STR_EQ( "Cannot evaluate operator of type 'error' (: Unbound symbol " - "'NoSuchFunction'. Did you mean 'character?'?)\n" + "'NoSuchFunction'. Did you mean 'character' or 'character?'?)\n" "\tAt form: >>>>>>>>>>(f {D+1})<<<<<<<<<<\n", octaspire_string_get_c_string(evaluatedValue->value.error->message)); @@ -70542,7 +72044,7 @@ TEST octaspire_dern_vm_special_do_error_stops_evaluation_and_is_reported_test(vo ASSERT_STR_EQ( "Cannot evaluate operator of type 'error' (: Unbound symbol " - "'NoSuchFunction'. Did you mean 'counter' or 'character?'?)\n" + "'NoSuchFunction'. Did you mean 'character', 'counter' or 'character?'?)\n" "\tAt form: >>>>>>>>>>(NoSuchFunction)<<<<<<<<<<\n\n" "\tAt form: >>>>>>>>>>(do (++ counter) (NoSuchFunction) (++ counter))<<<<<<<<<<\n", octaspire_string_get_c_string(evaluatedValue->value.error->message)); @@ -74049,7 +75551,7 @@ TEST octaspire_dern_vm_error_during_user_function_call_test(void) ASSERT_EQ(OCTASPIRE_DERN_VALUE_TAG_ERROR, evaluatedValue->typeTag); ASSERT_STR_EQ( "Cannot evaluate operator of type 'error' (: Unbound symbol " - "'NoSuchFunction'. Did you mean 'character?'?)\n" + "'NoSuchFunction'. Did you mean 'character' or 'character?'?)\n" "\tAt form: >>>>>>>>>>(f)<<<<<<<<<<\n", octaspire_string_get_c_string(evaluatedValue->value.error->message)); @@ -74152,6 +75654,79 @@ TEST octaspire_dern_vm_special_eval_plus_1_2_test(void) PASS(); } +TEST octaspire_dern_vm_special_eval_used_as_apply_1_test(void) +{ + octaspire_dern_vm_t *vm = octaspire_dern_vm_new( + octaspireDernVmTestAllocator, + octaspireDernVmTestStdio); + + octaspire_dern_value_t *evaluatedValue = + octaspire_dern_vm_read_from_c_string_and_eval_in_global_environment( + vm, + "(eval + (env-global) '(|a| |b| |c|) |d| |e| |f|)"); + + ASSERT(evaluatedValue); + ASSERT_EQ(OCTASPIRE_DERN_VALUE_TAG_STRING, evaluatedValue->typeTag); + + ASSERT_STR_EQ( + "abcdef", + octaspire_dern_value_as_string_get_c_string(evaluatedValue)); + + octaspire_dern_vm_release(vm); + vm = 0; + + PASS(); +} + +TEST octaspire_dern_vm_special_generate_string_of_10_a_test(void) +{ + octaspire_dern_vm_t *vm = octaspire_dern_vm_new( + octaspireDernVmTestAllocator, + octaspireDernVmTestStdio); + + octaspire_dern_value_t *evaluatedValue = + octaspire_dern_vm_read_from_c_string_and_eval_in_global_environment( + vm, + "(generate 'string of {D+10} |a|)"); + + ASSERT(evaluatedValue); + ASSERT_EQ(OCTASPIRE_DERN_VALUE_TAG_STRING, evaluatedValue->typeTag); + + ASSERT_STR_EQ( + "aaaaaaaaaa", + octaspire_dern_value_as_string_get_c_string(evaluatedValue)); + + octaspire_dern_vm_release(vm); + vm = 0; + + PASS(); +} + +TEST octaspire_dern_vm_special_generate_string_of_10_fn_97plusindex_test(void) +{ + octaspire_dern_vm_t *vm = octaspire_dern_vm_new( + octaspireDernVmTestAllocator, + octaspireDernVmTestStdio); + + octaspire_dern_value_t *evaluatedValue = + octaspire_dern_vm_read_from_c_string_and_eval_in_global_environment( + vm, + "(generate 'string of {D+10} " + " (fn (container index) (character (+ {D+97} index))))"); + + ASSERT(evaluatedValue); + ASSERT_EQ(OCTASPIRE_DERN_VALUE_TAG_STRING, evaluatedValue->typeTag); + + ASSERT_STR_EQ( + "abcdefghij", + octaspire_dern_value_as_string_get_c_string(evaluatedValue)); + + octaspire_dern_vm_release(vm); + vm = 0; + + PASS(); +} + TEST octaspire_dern_vm_special_eval_plus_1_2_in_given_global_env_test(void) { octaspire_dern_vm_t *vm = octaspire_dern_vm_new(octaspireDernVmTestAllocator, octaspireDernVmTestStdio); @@ -74298,7 +75873,8 @@ TEST octaspire_dern_vm_special_eval_called_with_three_arguments_failure_test(voi ASSERT_EQ(OCTASPIRE_DERN_VALUE_TAG_ERROR, evaluatedValue->typeTag); ASSERT_STR_EQ( - "Special 'eval' expects one or two arguments. 3 arguments were given.\n" + "Special 'eval' expects callable value as the first argument when used " + "as 'apply'. Type 'integer' was given.\n" "\tAt form: >>>>>>>>>>(eval (+ {D+1} {D+1}) (env-global) {D+10})<<<<<<<<<<\n", octaspire_string_get_c_string(evaluatedValue->value.error->message)); @@ -79321,6 +80897,9 @@ GREATEST_SUITE(octaspire_dern_vm_suite) RUN_TEST(octaspire_dern_vm_error_during_special_call_test); RUN_TEST(octaspire_dern_vm_error_during_special_call_during_user_function_call_test); RUN_TEST(octaspire_dern_vm_special_eval_plus_1_2_test); + RUN_TEST(octaspire_dern_vm_special_eval_used_as_apply_1_test); + RUN_TEST(octaspire_dern_vm_special_generate_string_of_10_a_test); + RUN_TEST(octaspire_dern_vm_special_generate_string_of_10_fn_97plusindex_test); RUN_TEST(octaspire_dern_vm_special_eval_plus_1_2_in_given_global_env_test); RUN_TEST(octaspire_dern_vm_special_eval_value_from_given_local_env_test); RUN_TEST(octaspire_dern_vm_special_eval_eval_eval_f_1_2_test); -- 2.45.2