diff --git a/apps/nyssa/app/commands/bundle.b b/apps/nyssa/app/commands/bundle.b index a82797f2..7e54b262 100644 --- a/apps/nyssa/app/commands/bundle.b +++ b/apps/nyssa/app/commands/bundle.b @@ -79,7 +79,7 @@ def get_file_list(root_dir, main_root) { if os.dir_exists(root_dir) { return os.read_dir(root_dir).filter(@(f) { - return !f.starts_with('.') + return !f.starts_with('.git') and f != '.' and f != '..' }).reduce(@(list, file) { var full_path = os.join_paths(root_dir, file) if os.dir_exists(full_path) { @@ -95,10 +95,7 @@ def get_file_list(root_dir, main_root) { } def copy_directory(src_dir, dest_dir) { - var fls = get_file_list(src_dir).filter(@(x) { - return x != '.' and x != '..' and - !x.match('/(\\/|\\\\)[.]git\\1/') - }) + var fls = get_file_list(src_dir) for fl in fls { var src = os.join_paths(src_dir, fl) diff --git a/apps/nyssa/app/index.b b/apps/nyssa/app/index.b index 28f8bab8..05673840 100644 --- a/apps/nyssa/app/index.b +++ b/apps/nyssa/app/index.b @@ -7,9 +7,8 @@ import date import .setup # initialize storage directory -var storage_dir = os.join_paths(setup.NYSSA_DIR, setup.STORAGE_DIR) -if !os.dir_exists(storage_dir) - os.create_dir(storage_dir) +if !os.dir_exists(setup.STORAGE_DIR) + os.create_dir(setup.STORAGE_DIR) # ensure config file exists... var config_file = os.join_paths(setup.NYSSA_DIR, setup.CONFIG_FILE) @@ -79,7 +78,7 @@ for o in options { } def success(msg, info) { - log.info(cmsg) + log.info(msg) if info { log.info(info) diff --git a/libs/args.b b/libs/args.b index 0a9aa125..542032f8 100644 --- a/libs/args.b +++ b/libs/args.b @@ -769,6 +769,10 @@ class Parser < _Optionable { if i < cli_args.length() - 1 { i++ var value = cli_args[i] + if command.type == LIST { + value = [value] + } + var v = _get_real_value(command, value) if command.type != CHOICE or !command.choices or command.choices.contains(value) { diff --git a/libs/ast/expr.b b/libs/ast/expr.b index f96dd3f1..ad090fce 100644 --- a/libs/ast/expr.b +++ b/libs/ast/expr.b @@ -247,16 +247,19 @@ class SetExpr < Expr { class IndexExpr < Expr { /** + * @param {Expr|any|nil} expr * @param {Expr|any|nil} args * @constructor */ - IndexExpr(args) { + IndexExpr(expr, args) { + self.expr = expr self.args = args } @to_json() { return { type: 'IndexExpr', + expr: self.expr, args: self.args, } } diff --git a/libs/ast/parser.b b/libs/ast/parser.b index 9295dbff..af02ad03 100644 --- a/libs/ast/parser.b +++ b/libs/ast/parser.b @@ -238,7 +238,7 @@ class Parser { self._ignore_newline() self._consume(TokenType.RBRACKET, "']' expected at end of indexer") - return IndexExpr(args) + return IndexExpr(callee, args) } /** @@ -734,7 +734,7 @@ class Parser { */ _using() { var expr = self._expression() - var cases = {} + var cases = [] var default_case self._consume(TokenType.LBRACE, "'{' expected after using expression") @@ -749,17 +749,16 @@ class Parser { if [TokenType.DOC, TokenType.COMMENT, TokenType.NEWLINE].contains(self._previous().type) {} else if self._previous().type == TokenType.WHEN { - var tmp_cases = [] + var conditions = [] + do { self._ignore_newline() - - tmp_cases.append(self._expression()) + conditions.append(self._expression()) } while self._match(TokenType.COMMA) + var stmt = self._statement() + cases.append(CaseStmt(conditions, stmt)) - for tmp in tmp_cases { - cases[tmp] = stmt - } } else { state = 1 default_case = self._statement() diff --git a/libs/ast/scanner.b b/libs/ast/scanner.b index 74c184e2..71d0b14c 100644 --- a/libs/ast/scanner.b +++ b/libs/ast/scanner.b @@ -263,14 +263,14 @@ class Scanner { */ _string(c) { while self._peek() != c and !self._is_at_end() { - if self._peek() == '$' and self._next() == '{' and - self._previous() != '\\' { # interpolation started + # interpolation expression + if self._peek() == '$' and self._next() == '{' and self._previous() != '\\' { + self._current++ + self._current++ if self._interpolating.length() < self._max_interpolation_nest { self._interpolating.append(c) - self._current++ self._add_token(TokenType.INTERPOLATION) - self._current++ return } @@ -287,7 +287,7 @@ class Scanner { raise Exception('unterminated string on line ${self._line}') self._match(c) - self._add_token(TokenType.LITERAL, self.source[self._start + 1, self._current - 1]) + self._add_token(TokenType.LITERAL, self.source[self._start, self._current]) } /** diff --git a/libs/ast/stmt.b b/libs/ast/stmt.b index 0ac13281..595f9e45 100644 --- a/libs/ast/stmt.b +++ b/libs/ast/stmt.b @@ -322,6 +322,32 @@ class UsingStmt < Stmt { } } +/** + * Case Stmt representation. + * + * @serializable + */ +class CaseStmt < Stmt { + + /** + * @param {Stmt|any|nil} conditions + * @param {Stmt|any|nil} statement + * @constructor + */ + CaseStmt(conditions, statement) { + self.conditions = conditions + self.statement = statement + } + + @to_json() { + return { + type: 'CaseStmt', + conditions: self.conditions, + statement: self.statement, + } + } +} + /** * Import Stmt representation. * diff --git a/libs/http/server.b b/libs/http/server.b index 9da8d99e..fc3e44c7 100644 --- a/libs/http/server.b +++ b/libs/http/server.b @@ -497,10 +497,14 @@ class HttpServer { client.close() if e { - # call the error listeners. - self._error_listeners.each(@(fn) { - fn(e, client) - }) + if self._error_listeners.length() == 0 { + raise e + } else { + # call the error listeners. + self._error_listeners.each(@(fn) { + fn(e, client) + }) + } } } } diff --git a/libs/reflect.b b/libs/reflect.b index cf1e3324..2dfb4921 100644 --- a/libs/reflect.b +++ b/libs/reflect.b @@ -331,7 +331,7 @@ def set_ptr(pointer, value) { } /** - * Returns a the address of the pointer to the value in memory. + * Returns the address of the pointer to the value in memory. * * @param any value * @returns ptr diff --git a/libs/thread.b b/libs/thread.b index 9fe1b5fc..953ad746 100644 --- a/libs/thread.b +++ b/libs/thread.b @@ -561,3 +561,37 @@ def start(delegate, args) { thread.start_from_list(args) return thread } + +/** + * Awaits all threads in the given list and returns when the last + * element exits. + * + * @param list threads + */ +def await_list(threads) { + if !is_list(threads) + raise ArgumentError('list of threads expected') + + for thread in threads { + if !instance_of(thread, Thread) + raise ValueError('list of threads expected') + + thread.await() + } +} + +/** + * Awaits all threads passed as an argument. This function is the + * same as [[thread.await_list()]], but allows passing the items as + * known arguments. + * + * @param [[thread.Thread]]... threads + */ +def await_all(...) { + for thread in __args__ { + if !instance_of(thread, Thread) + raise ValueError('list of threads expected') + + thread.await() + } +} diff --git a/packages/clib/clib.c b/packages/clib/clib.c index f8f0f544..1f0af0f3 100644 --- a/packages/clib/clib.c +++ b/packages/clib/clib.c @@ -644,9 +644,9 @@ b_value get_blade_value(b_vm *vm, int type, size_t size, void *data, size_t *rea } DECLARE_MODULE_METHOD(clib_get) { - ENFORCE_ARG_COUNT(new, 2); - ENFORCE_ARG_TYPE(new, 0, IS_PTR); - ENFORCE_ARG_TYPES(new, 1, IS_BYTES, IS_PTR); + ENFORCE_ARG_COUNT(get, 2); + ENFORCE_ARG_TYPE(get, 0, IS_PTR); + ENFORCE_ARG_TYPES(get, 1, IS_BYTES, IS_PTR); b_ffi_type *type = (b_ffi_type *)AS_PTR(args[0])->pointer; if(type->as_ffi->elements == NULL) { @@ -687,6 +687,35 @@ DECLARE_MODULE_METHOD(clib_get) { RETURN_OBJ(list); } +DECLARE_MODULE_METHOD(clib_get_value) { + ENFORCE_ARG_COUNT(get_value, 2); + ENFORCE_ARG_TYPE(get_value, 0, IS_PTR); + ENFORCE_ARG_TYPES(get_value, 1, IS_BYTES, IS_PTR); + + b_ffi_type *type = (b_ffi_type *)AS_PTR(args[0])->pointer; + if(type->as_ffi->elements != NULL) { + RETURN_ARGUMENT_ERROR("get_value can only be used on non-derived types such as struct, union and arrays."); + } + + unsigned char *data; + if(IS_PTR(args[1])) { + data = (unsigned char *)AS_PTR(args[1])->pointer; + } else { + data = AS_BYTES(args[1])->bytes.bytes; + } + + b_obj_list *list = (b_obj_list *)GC(new_list(vm)); + + size_t read_len = 0; + RETURN_VALUE(get_blade_value( + vm, + type->as_int, + type->as_ffi->size, + data, + &read_len + )); +} + DECLARE_MODULE_METHOD(clib_define) { ENFORCE_ARG_COUNT(define, 4); ENFORCE_ARG_TYPE(define, 0, IS_PTR); @@ -882,6 +911,7 @@ CREATE_MODULE_LOADER(clib) { static b_func_reg module_functions[] = { {"new", true, GET_MODULE_METHOD(clib_new)}, {"get", true, GET_MODULE_METHOD(clib_get)}, + {"get_value", true, GET_MODULE_METHOD(clib_get_value)}, {"load", true, GET_MODULE_METHOD(clib_load_library)}, {"function", true, GET_MODULE_METHOD(clib_get_function)}, {"close", true, GET_MODULE_METHOD(clib_close_library)}, diff --git a/packages/clib/clib/index.b b/packages/clib/clib/index.b index ff3f5ffd..98305ebd 100644 --- a/packages/clib/clib/index.b +++ b/packages/clib/clib/index.b @@ -90,12 +90,20 @@ class Clib { load(name) { if !is_string(name) raise TypeError('string expected in argument 1 (name)') - if !name.ends_with(_EXT) and os.platform != 'linux' - name += _EXT - + + var library_name = !name.ends_with(_EXT) and os.platform != 'linux' ? + name + _EXT : name + if self._ptr self.close() - self._ptr = _clib.load(name) + + catch { + self._ptr = _clib.load(name) + } as error + + if error { + self._ptr = _clib.load(library_name) + } } /** @@ -205,7 +213,7 @@ def new(type, ...) { } /** - * Returns the data contained in a C type _type_ encoded in the data. + * Returns the data contained in a C derived type _type_ encoded in the data. * The data should either be an output of `clib.new()` or a call to a * function returning one of struct, union or array. * @@ -220,12 +228,32 @@ def new(type, ...) { def get(type, data) { # Ensure a valid and non void clib pointer. if !(reflect.is_ptr(type) and to_string(type).match('/clib/')) and type != void - raise ValueError('invalid type for new') + raise ValueError('invalid type for get') if is_string(data) data = data.to_bytes() return _clib.get(type, data) } +/** + * Returns the data contained in a C non-derived type _type_ encoded in the data. + * The data should NOT either be an output of `clib.new()` or a call to any + * function returning one of struct, union or array. + * + * This is the opposite of [[clib.get]] + * + * @param clib_type type + * @param string|bytes data + * @returns any + */ +def get_value(type, data) { + # Ensure a valid and non void clib pointer. + if !(reflect.is_ptr(type) and to_string(type).match('/clib/')) and type != void + raise ValueError('invalid type for get_value') + if is_string(data) data = data.to_bytes() + + return _clib.get_value(type, data) +} + /** * get_ptr_index(pointer: ptr, type: clib_type, index: number) * diff --git a/packages/clib/clib/types.b b/packages/clib/clib/types.b index ad806863..83c928c9 100644 --- a/packages/clib/clib/types.b +++ b/packages/clib/clib/types.b @@ -191,12 +191,12 @@ var function = _clib.closure /** - * Returns a type that can be used to declare structs. - * To create or read value for the struct you need to use the `new()` + * Returns a type that can be used to declare structs. + * To create or read value for the struct you need to use the `new()` * and `get()` functions respectively. - * Alternatively, you may use the `pack()` and `unpack()` + * Alternatively, you may use the `pack()` and `unpack()` * function in the `struct` module respectively. - * + * * @note This function can also be used to define a C union or array. * @param any... type * @returns type @@ -215,16 +215,40 @@ def struct(...) { } /** - * Returns a type that can be used to declare structs based on the named - * types. The function works well with the `get()` function because it - * automatically assigns the name of the struct elements when getting the - * value. - * - * To create or read value for the struct you need to use the `new()` + * Returns a struct type with named fields. The function works well with the `get()` + * function because it automatically assigns the name of the struct elements when + * getting the value. + * + * To create or read value for the struct you need to use the `new()` * and `get()` functions respectively. - * Alternatively, you may use the `pack()` and `unpack()` + * Alternatively, you may use the `pack()` and `unpack()` * function in the `struct` module respectively. - * + * + * For example, let's say you have the following C struct: + * ```c + * typedef struct { + * char* message; + * int status; + * } custom_error; + * ``` + * + * This is how you'd create a named struct for it: + * ```blade + * import clib + * + * var lib = clib.load('./custom-library.so') + * + * var custom_error = clib.named_struct({ + * 'message': clib.char_ptr, + * 'status': clib.int + * }) + * + * var myfunction = lib.define('custom_error_function', custom_error) + * echo myfunction() # {message: oh no!, status: 1} + * + * lib.close() + * ``` + * * @note This function can also be used to define a C union or array. * @param dictionary types * @returns type diff --git a/packages/imagine/imagine/index.b b/packages/imagine/imagine/index.b index c8115677..0752ad01 100644 --- a/packages/imagine/imagine/index.b +++ b/packages/imagine/imagine/index.b @@ -163,10 +163,10 @@ def true_color(r, g, b, a) { * @returns dict */ def decompose(color) { - var r = (c & 0xFF0000) >> 16 - var g = (c & 0x00FF00) >> 8 - var b = (c & 0x0000FF) - var a = (c & 0x7F000000) >> 24 + var r = (color & 0xFF0000) >> 16 + var g = (color & 0x00FF00) >> 8 + var b = (color & 0x0000FF) + var a = (color & 0x7F000000) >> 24 return { r, g, b, a} } diff --git a/packages/json/json/encoder.b b/packages/json/json/encoder.b index c11045dc..1c60dc03 100644 --- a/packages/json/json/encoder.b +++ b/packages/json/json/encoder.b @@ -87,7 +87,13 @@ class Encoder { using typeof(value) { when 'nil' return 'null' when 'boolean' return to_string(value) - when 'number' return to_string(value) + when 'number' { + var encoded = to_string(value) + if encoded == 'inf' or encoded == 'nan' { + encoded = _get_string(encoded == 'nan' ? 'NaN' : 'Infinity') + } + return encoded + } when 'bytes' return _get_string(value.to_string()) when 'string' return _get_string(value) when 'list' { diff --git a/scripts/ast.b b/scripts/ast.b index 3d73688c..e014f1c4 100644 --- a/scripts/ast.b +++ b/scripts/ast.b @@ -30,7 +30,7 @@ var asts = { Call: ['callee', 'args'], Get: ['expr', 'name'], Set: ['expr', 'name', 'value'], - Index: ['args'], + Index: ['expr', 'args'], List: ['items'], Dict: ['keys', 'values'], Interpolation: ['data'] @@ -53,6 +53,7 @@ var asts = { Return: ['value'], Assert: ['expr', 'message'], Using: ['expr', 'cases', 'default_case'], + Case: ['conditions', 'statement'], Import: ['path', 'elements'], Catch: ['body', 'var_name'], Comment: ['data'], diff --git a/src/compiler.h b/src/compiler.h index 2f639c6b..b2cbd93b 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -18,11 +18,11 @@ typedef enum { PREC_BIT_XOR, // ^ PREC_BIT_AND, // & PREC_SHIFT, // <<, >> - PREC_RANGE, // .. PREC_TERM, // +, - PREC_FACTOR, // *, /, %, **, // PREC_UNARY, // !, -, ~, (++, -- this two will now be treated as statements) PREC_CALL, // ., () + PREC_RANGE, // .. PREC_PRIMARY } b_precedence; diff --git a/src/range.c b/src/range.c index 037f4bf8..1e97e957 100644 --- a/src/range.c +++ b/src/range.c @@ -39,7 +39,7 @@ DECLARE_RANGE_METHOD(__iter__) { if (index >= 0 && index < range->range) { if(index == 0) RETURN_NUMBER(range->lower); - RETURN_NUMBER(range->lower > range->upper ? --range->lower : ++range->lower); + RETURN_NUMBER(range->lower > range->upper ? range->lower - index : range->lower + index); } RETURN_NIL; diff --git a/src/vm.c b/src/vm.c index d94d97c4..294c9595 100644 --- a/src/vm.c +++ b/src/vm.c @@ -209,8 +209,8 @@ static void initialize_exceptions(b_vm *vm, b_obj_module *module) { vm->exception_class = klass; // Create other global exceptions - new_execption_class(vm, klass, "TypeError", 10); - new_execption_class(vm, klass, "ValueError", 11); + new_execption_class(vm, klass, "TypeError", 9); + new_execption_class(vm, klass, "ValueError", 10); new_execption_class(vm, klass, "NumericError", 12); new_execption_class(vm, klass, "ArgumentError", 13); new_execption_class(vm, klass, "NotImplementedError", 19);