2
0
Эх сурвалжийг харах

Gravity 0.7.4

Added preliminary support for Object introspection
Added optional JSON class
Improved support for autocompletion
Improved support for optional C classes
Improved List.sort() method
Fixed an issue with optional arguments in constructors
Fixed an incorrect float to string conversion
Fixed closures issues that can lead to a crash
Several other internal fixes and improvements
Marco Bambini 6 жил өмнө
parent
commit
a3dc780410

+ 6 - 0
gravity.xcodeproj/project.pbxproj

@@ -29,6 +29,7 @@
 		A9506D5D1E69AB8D009A0045 /* gravity_json.c in Sources */ = {isa = PBXBuildFile; fileRef = A9506D2C1E69AB1E009A0045 /* gravity_json.c */; };
 		A9506D5D1E69AB8D009A0045 /* gravity_json.c in Sources */ = {isa = PBXBuildFile; fileRef = A9506D2C1E69AB1E009A0045 /* gravity_json.c */; };
 		A9506D5E1E69AB8D009A0045 /* gravity_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = A9506D2E1E69AB1E009A0045 /* gravity_utils.c */; };
 		A9506D5E1E69AB8D009A0045 /* gravity_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = A9506D2E1E69AB1E009A0045 /* gravity_utils.c */; };
 		A956AADE220EBB8100C361D6 /* gravity_env.c in Sources */ = {isa = PBXBuildFile; fileRef = A956AADC220EBB8100C361D6 /* gravity_env.c */; };
 		A956AADE220EBB8100C361D6 /* gravity_env.c in Sources */ = {isa = PBXBuildFile; fileRef = A956AADC220EBB8100C361D6 /* gravity_env.c */; };
+		A989830623189DFF006F82BE /* gravity_json.c in Sources */ = {isa = PBXBuildFile; fileRef = A989830423189DFF006F82BE /* gravity_json.c */; };
 		A9BB72F01F470832002FD2D6 /* gravity_math.c in Sources */ = {isa = PBXBuildFile; fileRef = A9BB72EC1F470832002FD2D6 /* gravity_math.c */; };
 		A9BB72F01F470832002FD2D6 /* gravity_math.c in Sources */ = {isa = PBXBuildFile; fileRef = A9BB72EC1F470832002FD2D6 /* gravity_math.c */; };
 /* End PBXBuildFile section */
 /* End PBXBuildFile section */
 
 
@@ -95,6 +96,8 @@
 		A9506D2F1E69AB1E009A0045 /* gravity_utils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = gravity_utils.h; sourceTree = "<group>"; };
 		A9506D2F1E69AB1E009A0045 /* gravity_utils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = gravity_utils.h; sourceTree = "<group>"; };
 		A956AADC220EBB8100C361D6 /* gravity_env.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = gravity_env.c; sourceTree = "<group>"; };
 		A956AADC220EBB8100C361D6 /* gravity_env.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = gravity_env.c; sourceTree = "<group>"; };
 		A956AADD220EBB8100C361D6 /* gravity_env.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gravity_env.h; sourceTree = "<group>"; };
 		A956AADD220EBB8100C361D6 /* gravity_env.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gravity_env.h; sourceTree = "<group>"; };
+		A989830423189DFF006F82BE /* gravity_json.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = gravity_json.c; sourceTree = "<group>"; };
+		A989830523189DFF006F82BE /* gravity_json.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gravity_json.h; sourceTree = "<group>"; };
 		A9BB72EC1F470832002FD2D6 /* gravity_math.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = gravity_math.c; sourceTree = "<group>"; };
 		A9BB72EC1F470832002FD2D6 /* gravity_math.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = gravity_math.c; sourceTree = "<group>"; };
 		A9BB72ED1F470832002FD2D6 /* gravity_math.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gravity_math.h; sourceTree = "<group>"; };
 		A9BB72ED1F470832002FD2D6 /* gravity_math.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gravity_math.h; sourceTree = "<group>"; };
 		A9BB72EE1F470832002FD2D6 /* gravity_optionals.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gravity_optionals.h; sourceTree = "<group>"; };
 		A9BB72EE1F470832002FD2D6 /* gravity_optionals.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gravity_optionals.h; sourceTree = "<group>"; };
@@ -228,6 +231,8 @@
 			children = (
 			children = (
 				A9BB72EC1F470832002FD2D6 /* gravity_math.c */,
 				A9BB72EC1F470832002FD2D6 /* gravity_math.c */,
 				A9BB72ED1F470832002FD2D6 /* gravity_math.h */,
 				A9BB72ED1F470832002FD2D6 /* gravity_math.h */,
+				A989830423189DFF006F82BE /* gravity_json.c */,
+				A989830523189DFF006F82BE /* gravity_json.h */,
 				A956AADC220EBB8100C361D6 /* gravity_env.c */,
 				A956AADC220EBB8100C361D6 /* gravity_env.c */,
 				A956AADD220EBB8100C361D6 /* gravity_env.h */,
 				A956AADD220EBB8100C361D6 /* gravity_env.h */,
 				A9BB72EE1F470832002FD2D6 /* gravity_optionals.h */,
 				A9BB72EE1F470832002FD2D6 /* gravity_optionals.h */,
@@ -315,6 +320,7 @@
 				A9506D5E1E69AB8D009A0045 /* gravity_utils.c in Sources */,
 				A9506D5E1E69AB8D009A0045 /* gravity_utils.c in Sources */,
 				A9506D451E69AB78009A0045 /* gravity_visitor.c in Sources */,
 				A9506D451E69AB78009A0045 /* gravity_visitor.c in Sources */,
 				A9506D3C1E69AB78009A0045 /* gravity_compiler.c in Sources */,
 				A9506D3C1E69AB78009A0045 /* gravity_compiler.c in Sources */,
+				A989830623189DFF006F82BE /* gravity_json.c in Sources */,
 				A9506D521E69AB7E009A0045 /* gravity_core.c in Sources */,
 				A9506D521E69AB7E009A0045 /* gravity_core.c in Sources */,
 			);
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			runOnlyForDeploymentPostprocessing = 0;

+ 1 - 1
src/cli/gravity.c

@@ -314,7 +314,7 @@ static op_type parse_args (int argc, const char* argv[]) {
     return type;
     return type;
 }
 }
 
 
-// MARK: - Special Modes
+// MARK: - Special Modes -
 
 
 static void gravity_repl (void) {
 static void gravity_repl (void) {
 
 

+ 6 - 3
src/compiler/gravity_ast.c

@@ -139,22 +139,24 @@ gnode_t *gnode_label_stat_create (gtoken_s token, gnode_t *expr, gnode_t *stmt,
     return (gnode_t *)node;
     return (gnode_t *)node;
 }
 }
 
 
-gnode_t *gnode_flow_stat_create (gtoken_s token, gnode_t *cond, gnode_t *stmt1, gnode_t *stmt2, gnode_t *decl) {
+gnode_t *gnode_flow_stat_create (gtoken_s token, gnode_t *cond, gnode_t *stmt1, gnode_t *stmt2, gnode_t *decl, uint32_t block_length) {
     gnode_flow_stmt_t *node = (gnode_flow_stmt_t *)mem_alloc(NULL, sizeof(gnode_flow_stmt_t));
     gnode_flow_stmt_t *node = (gnode_flow_stmt_t *)mem_alloc(NULL, sizeof(gnode_flow_stmt_t));
 
 
     SETBASE(node, NODE_FLOW_STAT, token);
     SETBASE(node, NODE_FLOW_STAT, token);
     SETDECL(node, decl);
     SETDECL(node, decl);
+	node->base.block_length = block_length;
     node->cond = cond;
     node->cond = cond;
     node->stmt = stmt1;
     node->stmt = stmt1;
     node->elsestmt = stmt2;
     node->elsestmt = stmt2;
     return (gnode_t *)node;
     return (gnode_t *)node;
 }
 }
 
 
-gnode_t *gnode_loop_stat_create (gtoken_s token, gnode_t *cond, gnode_t *stmt, gnode_t *expr, gnode_t *decl) {
+gnode_t *gnode_loop_stat_create (gtoken_s token, gnode_t *cond, gnode_t *stmt, gnode_t *expr, gnode_t *decl, uint32_t block_length) {
     gnode_loop_stmt_t *node = (gnode_loop_stmt_t *)mem_alloc(NULL, sizeof(gnode_loop_stmt_t));
     gnode_loop_stmt_t *node = (gnode_loop_stmt_t *)mem_alloc(NULL, sizeof(gnode_loop_stmt_t));
 
 
     SETBASE(node, NODE_LOOP_STAT, token);
     SETBASE(node, NODE_LOOP_STAT, token);
     SETDECL(node, decl);
     SETDECL(node, decl);
+    node->base.block_length = block_length;
     node->cond = cond;
     node->cond = cond;
     node->stmt = stmt;
     node->stmt = stmt;
     node->expr = expr;
     node->expr = expr;
@@ -162,11 +164,12 @@ gnode_t *gnode_loop_stat_create (gtoken_s token, gnode_t *cond, gnode_t *stmt, g
     return (gnode_t *)node;
     return (gnode_t *)node;
 }
 }
 
 
-gnode_t *gnode_block_stat_create (gnode_n type, gtoken_s token, gnode_r *stmts, gnode_t *decl) {
+gnode_t *gnode_block_stat_create (gnode_n type, gtoken_s token, gnode_r *stmts, gnode_t *decl, uint32_t block_length) {
     gnode_compound_stmt_t *node = (gnode_compound_stmt_t *)mem_alloc(NULL, sizeof(gnode_compound_stmt_t));
     gnode_compound_stmt_t *node = (gnode_compound_stmt_t *)mem_alloc(NULL, sizeof(gnode_compound_stmt_t));
 
 
     SETBASE(node, type, token);
     SETBASE(node, type, token);
     SETDECL(node, decl);
     SETDECL(node, decl);
+	node->base.block_length = block_length;
     node->stmts = stmts;
     node->stmts = stmts;
     node->nclose = UINT32_MAX;
     node->nclose = UINT32_MAX;
     return (gnode_t *)node;
     return (gnode_t *)node;

+ 4 - 3
src/compiler/gravity_ast.h

@@ -46,6 +46,7 @@ typedef enum {
 typedef struct {
 typedef struct {
     gnode_n     tag;                        // node type from gnode_n enum
     gnode_n     tag;                        // node type from gnode_n enum
     uint32_t    refcount;                   // reference count to manage duplicated nodes
     uint32_t    refcount;                   // reference count to manage duplicated nodes
+    uint32_t    block_length;               // total length in bytes of the block (used in autocompletion)
     gtoken_s    token;                      // token type and location
     gtoken_s    token;                      // token type and location
     bool        is_assignment;              // flag to check if it is an assignment node
     bool        is_assignment;              // flag to check if it is an assignment node
     void        *decl;                      // enclosing declaration node
     void        *decl;                      // enclosing declaration node
@@ -257,9 +258,9 @@ typedef struct {
 
 
 gnode_t *gnode_jump_stat_create (gtoken_s token, gnode_t *expr, gnode_t *decl);
 gnode_t *gnode_jump_stat_create (gtoken_s token, gnode_t *expr, gnode_t *decl);
 gnode_t *gnode_label_stat_create (gtoken_s token, gnode_t *expr, gnode_t *stmt, gnode_t *decl);
 gnode_t *gnode_label_stat_create (gtoken_s token, gnode_t *expr, gnode_t *stmt, gnode_t *decl);
-gnode_t *gnode_flow_stat_create (gtoken_s token, gnode_t *cond, gnode_t *stmt1, gnode_t *stmt2, gnode_t *decl);
-gnode_t *gnode_loop_stat_create (gtoken_s token, gnode_t *cond, gnode_t *stmt, gnode_t *expr, gnode_t *decl);
-gnode_t *gnode_block_stat_create (gnode_n type, gtoken_s token, gnode_r *stmts, gnode_t *decl);
+gnode_t *gnode_flow_stat_create (gtoken_s token, gnode_t *cond, gnode_t *stmt1, gnode_t *stmt2, gnode_t *decl, uint32_t block_length);
+gnode_t *gnode_loop_stat_create (gtoken_s token, gnode_t *cond, gnode_t *stmt, gnode_t *expr, gnode_t *decl, uint32_t block_length);
+gnode_t *gnode_block_stat_create (gnode_n type, gtoken_s token, gnode_r *stmts, gnode_t *decl, uint32_t block_length);
 gnode_t *gnode_empty_stat_create (gtoken_s token, gnode_t *decl);
 gnode_t *gnode_empty_stat_create (gtoken_s token, gnode_t *decl);
 
 
 gnode_t *gnode_enum_decl_create (gtoken_s token, const char *identifier, gtoken_t access_specifier, gtoken_t storage_specifier, symboltable_t *symtable, gnode_t *decl);
 gnode_t *gnode_enum_decl_create (gtoken_s token, const char *identifier, gtoken_t access_specifier, gtoken_t storage_specifier, symboltable_t *symtable, gnode_t *decl);

+ 54 - 25
src/compiler/gravity_parser.c

@@ -410,7 +410,10 @@ static gnode_t *parse_ternary_expression (gravity_parser_t *parser) {
     gnode_t *expr2 = parse_expression(parser);
     gnode_t *expr2 = parse_expression(parser);
     CHECK_NODE(expr2);
     CHECK_NODE(expr2);
 
 
-    return gnode_flow_stat_create(token, cond, expr1, expr2, LAST_DECLARATION());
+	// read current token to extract node total length
+	gtoken_s end_token = gravity_lexer_token(lexer);
+
+    return gnode_flow_stat_create(token, cond, expr1, expr2, LAST_DECLARATION(), end_token.position + end_token.length - token.position);
 }
 }
 
 
 static gnode_t *parse_file_expression (gravity_parser_t *parser) {
 static gnode_t *parse_file_expression (gravity_parser_t *parser) {
@@ -1294,8 +1297,11 @@ static gnode_t *parse_getter_setter (gravity_parser_t *parser) {
     gnode_array_push(functions, (getter) ? getter : NULL);    // getter is at index 0
     gnode_array_push(functions, (getter) ? getter : NULL);    // getter is at index 0
     gnode_array_push(functions, (setter) ? setter : NULL);    // setter is at index 1
     gnode_array_push(functions, (setter) ? setter : NULL);    // setter is at index 1
 
 
+	// read current token to extract node total length
+	gtoken_s end_token = gravity_lexer_token(lexer);
+	
     // a compound node is used to capture getter and setter
     // a compound node is used to capture getter and setter
-    return gnode_block_stat_create(NODE_COMPOUND_STAT, token_block, functions, LAST_DECLARATION());
+    return gnode_block_stat_create(NODE_COMPOUND_STAT, token_block, functions, LAST_DECLARATION(), end_token.position + end_token.length - token_block.position);
 
 
 parse_error:
 parse_error:
     return NULL;
     return NULL;
@@ -1499,9 +1505,21 @@ static gnode_t *parse_enum_declaration (gravity_parser_t *parser, gtoken_t acces
                 }
                 }
 
 
                 if (unary->op == TOK_OP_SUB) {
                 if (unary->op == TOK_OP_SUB) {
-                    if (enum_literal->type == LITERAL_FLOAT) enum_literal->value.d = -enum_literal->value.d;
-                    else if (enum_literal->type == LITERAL_INT) enum_literal->value.n64 = -enum_literal->value.n64;
-                    else assert(0); // should never reach this point
+                    gnode_t *temp = NULL;
+                    if (enum_literal->type == LITERAL_FLOAT) {
+                        //enum_literal->value.d = -enum_literal->value.d;
+                        temp = gnode_literal_float_expr_create(enum_value->base.token, -enum_literal->value.d, LAST_DECLARATION());
+                    }
+                    else if (enum_literal->type == LITERAL_INT) {
+                        //enum_literal->value.n64 = -enum_literal->value.n64;
+                        temp = gnode_literal_int_expr_create(enum_value->base.token, -enum_literal->value.n64, LAST_DECLARATION());
+                    }
+                    
+                    if (temp) {
+                        gnode_free((gnode_t *)enum_value);
+                        enum_value = (gnode_base_t *)temp;
+                        enum_literal = (gnode_literal_expr_t *)temp;
+                    }
                 }
                 }
 
 
             } else {
             } else {
@@ -2155,7 +2173,10 @@ static gnode_t *parse_flow_statement (gravity_parser_t *parser) {
         stmt2 = parse_statement(parser);
         stmt2 = parse_statement(parser);
     }
     }
 
 
-    return gnode_flow_stat_create(token, cond, stmt1, stmt2, LAST_DECLARATION());
+	// read current token to extract node total length
+	gtoken_s end_token = gravity_lexer_token(lexer);
+	
+	return gnode_flow_stat_create(token, cond, stmt1, stmt2, LAST_DECLARATION(), end_token.position + end_token.length - token.position);
 }
 }
 
 
 static gnode_t *parse_loop_statement (gravity_parser_t *parser) {
 static gnode_t *parse_loop_statement (gravity_parser_t *parser) {
@@ -2172,6 +2193,7 @@ static gnode_t *parse_loop_statement (gravity_parser_t *parser) {
 
 
     gtoken_t type = gravity_lexer_next(lexer);
     gtoken_t type = gravity_lexer_next(lexer);
     gtoken_s token = gravity_lexer_token(lexer);
     gtoken_s token = gravity_lexer_token(lexer);
+    gtoken_s end_token;
     assert((type == TOK_KEY_WHILE) || (type == TOK_KEY_REPEAT)  || (type == TOK_KEY_FOR));
     assert((type == TOK_KEY_WHILE) || (type == TOK_KEY_REPEAT)  || (type == TOK_KEY_FOR));
 
 
     // 'while' '(' expression ')' statement
     // 'while' '(' expression ')' statement
@@ -2238,9 +2260,13 @@ static gnode_t *parse_loop_statement (gravity_parser_t *parser) {
         // parse for statement
         // parse for statement
         stmt = parse_statement(parser);
         stmt = parse_statement(parser);
     }
     }
-
+    
 return_node:
 return_node:
-    return gnode_loop_stat_create(token, cond, stmt, expr, LAST_DECLARATION());
+    // read current token to extract node total length
+    end_token = gravity_lexer_token(lexer);
+    
+    // return loop node
+    return gnode_loop_stat_create(token, cond, stmt, expr, LAST_DECLARATION(), end_token.position + end_token.length - token.position);
 }
 }
 
 
 static gnode_t *parse_jump_statement (gravity_parser_t *parser) {
 static gnode_t *parse_jump_statement (gravity_parser_t *parser) {
@@ -2288,7 +2314,10 @@ static gnode_t *parse_compound_statement (gravity_parser_t *parser) {
     // check and consume TOK_OP_CLOSED_CURLYBRACE
     // check and consume TOK_OP_CLOSED_CURLYBRACE
     parse_required(parser, TOK_OP_CLOSED_CURLYBRACE);
     parse_required(parser, TOK_OP_CLOSED_CURLYBRACE);
 
 
-    return gnode_block_stat_create(NODE_COMPOUND_STAT, token, stmts, LAST_DECLARATION());
+	// read current token to extract node total length
+	gtoken_s end_token = gravity_lexer_token(lexer);
+
+    return gnode_block_stat_create(NODE_COMPOUND_STAT, token, stmts, LAST_DECLARATION(), end_token.position + end_token.length - token.position);
 }
 }
 
 
 static gnode_t *parse_empty_statement (gravity_parser_t *parser) {
 static gnode_t *parse_empty_statement (gravity_parser_t *parser) {
@@ -2487,7 +2516,7 @@ static void parser_register_core_classes (gravity_parser_t *parser) {
     const char **list = gravity_core_identifiers();
     const char **list = gravity_core_identifiers();
 
 
     // for each core identifier create a dummy extern variable node
     // for each core identifier create a dummy extern variable node
-    gnode_r    *decls = gnode_array_create();
+    gnode_r *decls = gnode_array_create();
 
 
     uint32_t i = 0;
     uint32_t i = 0;
     while (list[i]) {
     while (list[i]) {
@@ -2504,22 +2533,22 @@ static void parser_register_core_classes (gravity_parser_t *parser) {
 
 
 static void parser_register_optional_classes (gravity_parser_t *parser) {
 static void parser_register_optional_classes (gravity_parser_t *parser) {
     // for each optional identifier create a dummy extern variable node
     // for each optional identifier create a dummy extern variable node
-    gnode_r    *decls = gnode_array_create();
+    gnode_r *decls = gnode_array_create();
 
 
-    #ifdef GRAVITY_INCLUDE_MATH
-    gnode_t *decl = gnode_variable_create(NO_TOKEN, string_dup(GRAVITY_MATH_NAME()), NULL, NULL, LAST_DECLARATION(), NULL);
-    gnode_array_push(decls, decl);
-    #endif
-    
-    #ifdef GRAVITY_INCLUDE_ENV
-    gnode_t *decl2 = gnode_variable_create(NO_TOKEN, string_dup(GRAVITY_ENV_NAME()), NULL, NULL, LAST_DECLARATION(), NULL);
-    gnode_array_push(decls, decl2);
-    #endif
+    // compile time optional classes
+    const char **list = gravity_optional_identifiers();
+    uint32_t i = 0;
+    while (list[i]) {
+        const char *identifier = list[i];
+        gnode_t *decl = gnode_variable_create(NO_TOKEN, string_dup(identifier), NULL, NULL, LAST_DECLARATION(), NULL);
+        gnode_array_push(decls, decl);
+        ++i;
+    }
 
 
-    // check if optional classes callback is registered
+    // check if optional classes callback is registered (runtime optional classes)
     if (parser->delegate && parser->delegate->optional_classes) {
     if (parser->delegate && parser->delegate->optional_classes) {
-        const char **list = parser->delegate->optional_classes();
-        uint32_t i = 0;
+        list = parser->delegate->optional_classes();
+        i = 0;
         while (list[i]) {
         while (list[i]) {
             const char *identifier = list[i];
             const char *identifier = list[i];
             gnode_t *decl_node = gnode_variable_create(NO_TOKEN, string_dup(identifier), NULL, NULL, LAST_DECLARATION(), NULL);
             gnode_t *decl_node = gnode_variable_create(NO_TOKEN, string_dup(identifier), NULL, NULL, LAST_DECLARATION(), NULL);
@@ -2564,7 +2593,7 @@ static uint32_t parser_run (gravity_parser_t *parser) {
 static void parser_cleanup (gravity_parser_t *parser) {
 static void parser_cleanup (gravity_parser_t *parser) {
     // in case of error (so AST is not returned)
     // in case of error (so AST is not returned)
     // then cleanup internal nodes
     // then cleanup internal nodes
-    gnode_t *node= gnode_block_stat_create(NODE_LIST_STAT, NO_TOKEN, parser->statements, LAST_DECLARATION());
+    gnode_t *node= gnode_block_stat_create(NODE_LIST_STAT, NO_TOKEN, parser->statements, LAST_DECLARATION(), 0);
     gnode_free(node);
     gnode_free(node);
 }
 }
 
 
@@ -2635,7 +2664,7 @@ gnode_t *gravity_parser_run (gravity_parser_t *parser, gravity_delegate_t *deleg
     if (marray_size(*parser->declarations) > 0) return NULL;
     if (marray_size(*parser->declarations) > 0) return NULL;
 
 
     // return ast
     // return ast
-    return gnode_block_stat_create(NODE_LIST_STAT, NO_TOKEN, parser->statements, NULL);
+    return gnode_block_stat_create(NODE_LIST_STAT, NO_TOKEN, parser->statements, NULL, 0);
 }
 }
 
 
 void gravity_parser_free (gravity_parser_t *parser) {
 void gravity_parser_free (gravity_parser_t *parser) {

+ 9 - 9
src/compiler/gravity_token.h

@@ -98,15 +98,15 @@ typedef enum {
 } gbuiltin_t;
 } gbuiltin_t;
 
 
 struct gtoken_s {
 struct gtoken_s {
-    gtoken_t            type;       // enum based token type
-    uint32_t            lineno;     // token line number (1-based)
-    uint32_t            colno;      // token column number (0-based) at the end of the token
-    uint32_t            position;   // offset of the first character of the token
-    uint32_t            bytes;      // token length in bytes
-    uint32_t            length;     // token length (UTF-8)
-    uint32_t            fileid;     // token file id
-    gbuiltin_t          builtin;    // builtin special identifier flag
-    const char          *value;     // token value (not null terminated)
+    gtoken_t            type;           // enum based token type
+    uint32_t            lineno;         // token line number (1-based)
+    uint32_t            colno;          // token column number (0-based) at the end of the token
+    uint32_t            position;       // offset of the first character of the token
+    uint32_t            bytes;          // token length in bytes
+    uint32_t            length;         // token length (UTF-8)
+    uint32_t            fileid;         // token file id
+    gbuiltin_t          builtin;        // builtin special identifier flag
+    const char          *value;         // token value (not null terminated)
 };
 };
 typedef struct gtoken_s         gtoken_s;
 typedef struct gtoken_s         gtoken_s;
 
 

+ 3 - 6
src/optionals/gravity_env.c

@@ -24,9 +24,6 @@
 #define unsetenv(_key)                      _putenv_s(_key, "")
 #define unsetenv(_key)                      _putenv_s(_key, "")
 #endif
 #endif
 
 
-
-#define ENV_CLASS_NAME "ENV"
-
 static gravity_class_t              *gravity_class_env = NULL;
 static gravity_class_t              *gravity_class_env = NULL;
 static uint32_t                     refcount = 0;
 static uint32_t                     refcount = 0;
 
 
@@ -102,7 +99,7 @@ static bool gravity_env_keys(gravity_vm *vm, gravity_value_t *args, uint16_t npa
 // MARK: - Internals -
 // MARK: - Internals -
 
 
 static void create_optional_class (void) {
 static void create_optional_class (void) {
-    gravity_class_env = gravity_class_new_pair(NULL, ENV_CLASS_NAME, NULL, 0, 0);
+    gravity_class_env = gravity_class_new_pair(NULL, GRAVITY_CLASS_ENV_NAME, NULL, 0, 0);
     gravity_class_t *meta = gravity_class_get_meta(gravity_class_env);
     gravity_class_t *meta = gravity_class_get_meta(gravity_class_env);
     
     
     // .get(key) and .set(key, value)
     // .get(key) and .set(key, value)
@@ -124,7 +121,7 @@ void gravity_env_register(gravity_vm *vm) {
     ++refcount;
     ++refcount;
     
     
     if (!vm || gravity_vm_ismini(vm)) return;
     if (!vm || gravity_vm_ismini(vm)) return;
-    gravity_vm_setvalue(vm, ENV_CLASS_NAME, VALUE_FROM_OBJECT(gravity_class_env));
+    gravity_vm_setvalue(vm, GRAVITY_CLASS_ENV_NAME, VALUE_FROM_OBJECT(gravity_class_env));
 }
 }
 
 
 void gravity_env_free (void) {
 void gravity_env_free (void) {
@@ -143,5 +140,5 @@ bool gravity_isenv_class (gravity_class_t *c) {
 }
 }
 
 
 const char *gravity_env_name (void) {
 const char *gravity_env_name (void) {
-    return ENV_CLASS_NAME;
+    return GRAVITY_CLASS_ENV_NAME;
 }
 }

+ 2 - 0
src/optionals/gravity_env.h

@@ -1,6 +1,8 @@
 #ifndef GRAVITY_ENV_H
 #ifndef GRAVITY_ENV_H
 #define GRAVITY_ENV_H
 #define GRAVITY_ENV_H
 
 
+#define GRAVITY_CLASS_ENV_NAME "ENV"
+
 #include "gravity_value.h"
 #include "gravity_value.h"
 
 
 void gravity_env_register (gravity_vm *vm);
 void gravity_env_register (gravity_vm *vm);

+ 200 - 0
src/optionals/gravity_json.c

@@ -0,0 +1,200 @@
+//
+//  gravity_json.c
+//  gravity
+//
+//  Created by Marco Bambini on 20/08/2019.
+//  Copyright © 2019 CreoLabs. All rights reserved.
+//
+
+#include <inttypes.h>
+#include "gravity_json.h"
+#include "gravity_vm.h"
+#include "gravity_json.h"
+#include "gravity_core.h"
+#include "gravity_hash.h"
+#include "gravity_utils.h"
+#include "gravity_macros.h"
+#include "gravity_opcodes.h"
+#include "gravity_vmmacros.h"
+
+#define GRAVITY_JSON_STRINGIFY_NAME     "stringify"
+#define GRAVITY_JSON_PARSE_NAME         "parse"
+#define STATIC_BUFFER_SIZE              8192
+
+static gravity_class_t                  *gravity_class_json = NULL;
+static uint32_t                         refcount = 0;
+
+// MARK: - Implementation -
+
+static bool JSON_stringify (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+    if (nargs < 2) RETURN_VALUE(VALUE_FROM_NULL, rindex);
+    
+    // extract value
+    gravity_value_t value = GET_VALUE(1);
+    
+    // special case for string because it can be huge (and must be quoted)
+    if (VALUE_ISA_STRING(value)) {
+        const int nchars = 5;
+        const char *v = VALUE_AS_STRING(value)->s;
+        size_t vlen = VALUE_AS_STRING(value)->len;
+
+        // string must be quoted
+        if (vlen < 4096-nchars) {
+            char vbuffer2[4096];
+            vlen = snprintf(vbuffer2, sizeof(vbuffer2), "\"%s\"", v);
+            RETURN_VALUE(VALUE_FROM_STRING(vm, vbuffer2, (uint32_t)vlen), rindex);
+        } else {
+            char *vbuffer2 = mem_alloc(NULL, vlen + nchars);
+            vlen = snprintf(vbuffer2, vlen + nchars, "\"%s\"", v);
+            RETURN_VALUE(VALUE_FROM_OBJECT(gravity_string_new(vm, vbuffer2, (uint32_t)vlen, 0)), rindex);
+        }
+    }
+    
+    // primitive cases supported by JSON (true, false, null, number)
+    char vbuffer[512];
+    const char *v = NULL;
+    
+    if (VALUE_ISA_NULL(value) || (VALUE_ISA_UNDEFINED(value))) v = "null";
+    else if (VALUE_ISA_FLOAT(value)) {snprintf(vbuffer, sizeof(vbuffer), "%f", value.f); v = vbuffer;}
+    else if (VALUE_ISA_BOOL(value)) v = (value.n) ? "true" : "false";
+    else if (VALUE_ISA_INT(value)) {
+        #if GRAVITY_ENABLE_INT64
+        snprintf(vbuffer, sizeof(vbuffer), "%" PRId64 "", value.n);
+        #else
+        snprintf(vbuffer, sizeof(vbuffer), "%d", value.n);
+        #endif
+        v = vbuffer;
+    }
+    if (v) RETURN_VALUE(VALUE_FROM_CSTRING(vm, v), rindex);
+    
+    // more complex object case (list, map, class, closure, instance/object)
+    json_t *json = json_new();
+    json_set_option(json, json_opt_no_maptype);
+    json_set_option(json, json_opt_no_undef);
+    json_set_option(json, json_opt_prettify);
+    gravity_value_serialize(NULL, value, json);
+    
+    size_t len = 0;
+    const char *jbuffer = json_buffer(json, &len);
+    const char *buffer = string_ndup(jbuffer, len);
+    
+    gravity_string_t *s = gravity_string_new(vm, (char *)buffer, (uint32_t)len, 0);
+    json_free(json);
+    
+    RETURN_VALUE(VALUE_FROM_OBJECT(s), rindex);
+}
+
+static gravity_value_t JSON_value (gravity_vm *vm, json_value *json) {
+    switch (json->type) {
+        case json_none:
+        case json_null:
+            return VALUE_FROM_NULL;
+            
+        case json_object: {
+            gravity_object_t *obj = gravity_object_deserialize(vm, json);
+            gravity_value_t objv = (obj) ? VALUE_FROM_OBJECT(obj) : VALUE_FROM_NULL;
+            return objv;
+        }
+            
+        case json_array: {
+            unsigned int length = json->u.array.length;
+            gravity_list_t *list = gravity_list_new(vm, length);
+            for (unsigned int i = 0; i < length; ++i) {
+                gravity_value_t value = JSON_value(vm, json->u.array.values[i]);
+                marray_push(gravity_value_t, list->array, value);
+            }
+            return VALUE_FROM_OBJECT(list);
+        }
+            
+        case json_integer:
+            return VALUE_FROM_INT(json->u.integer);
+            
+        case json_double:
+            return VALUE_FROM_FLOAT(json->u.dbl);
+            
+        case json_string:
+            return VALUE_FROM_STRING(vm, json->u.string.ptr, json->u.string.length);
+            
+        case json_boolean:
+            return VALUE_FROM_BOOL(json->u.boolean);
+    }
+    
+    return VALUE_FROM_NULL;
+}
+
+static bool JSON_parse (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+    if (nargs < 2) RETURN_VALUE(VALUE_FROM_NULL, rindex);
+    
+    // value to parse
+    gravity_value_t value = GET_VALUE(1);
+    if (!VALUE_ISA_STRING(value)) RETURN_VALUE(VALUE_FROM_NULL, rindex);
+    
+    gravity_string_t *string = VALUE_AS_STRING(value);
+    json_value *json = json_parse(string->s, string->len);
+    if (!json) RETURN_VALUE(VALUE_FROM_NULL, rindex);
+    
+    RETURN_VALUE(JSON_value(vm, json), rindex);
+}
+
+//static bool JSON_begin_object (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+//
+//    return false;
+//}
+//
+//static bool json_end_object (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+//
+//}
+//
+//static bool json_begin_array (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+//
+//}
+//
+//static bool json_end_array (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+//
+//}
+//
+//static bool json_add_object (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+//
+//}
+
+// MARK: - Internals -
+
+static void create_optional_class (void) {
+    gravity_class_json = gravity_class_new_pair(NULL, GRAVITY_CLASS_JSON_NAME, NULL, 0, 0);
+    gravity_class_t *json_meta = gravity_class_get_meta(gravity_class_json);
+    
+    //gravity_class_bind(json_meta, GRAVITY_INTERNAL_EXEC_NAME, NEW_CLOSURE_VALUE(JSON_exec));
+    gravity_class_bind(json_meta, GRAVITY_JSON_STRINGIFY_NAME, NEW_CLOSURE_VALUE(JSON_stringify));
+    gravity_class_bind(json_meta, GRAVITY_JSON_PARSE_NAME, NEW_CLOSURE_VALUE(JSON_parse));
+    //gravity_class_bind(gravity_class_json, "begin", NEW_CLOSURE_VALUE(JSON_begin_object));
+    
+    SETMETA_INITED(gravity_class_json);
+}
+
+// MARK: - Commons -
+
+bool gravity_isjson_class (gravity_class_t *c) {
+    return (c == gravity_class_json);
+}
+
+const char *gravity_json_name (void) {
+    return GRAVITY_CLASS_JSON_NAME;
+}
+
+void gravity_json_register (gravity_vm *vm) {
+    if (!gravity_class_json) create_optional_class();
+    ++refcount;
+    
+    if (!vm || gravity_vm_ismini(vm)) return;
+    gravity_vm_setvalue(vm, GRAVITY_CLASS_JSON_NAME, VALUE_FROM_OBJECT(gravity_class_json));
+}
+
+void gravity_json_free (void) {
+    if (!gravity_class_json) return;
+    if (--refcount) return;
+    
+    gravity_class_free_core(NULL, gravity_class_get_meta(gravity_class_json));
+    gravity_class_free_core(NULL, gravity_class_json);
+    
+    gravity_class_json = NULL;
+}

+ 21 - 0
src/optionals/gravity_json.h

@@ -0,0 +1,21 @@
+//
+//  gravity_json.h
+//  gravity
+//
+//  Created by Marco Bambini on 20/08/2019.
+//  Copyright © 2019 CreoLabs. All rights reserved.
+//
+
+#ifndef __GRAVITY_JSON__
+#define __GRAVITY_JSON__
+
+#define GRAVITY_CLASS_JSON_NAME         "JSON"
+
+#include "gravity_value.h"
+
+void gravity_json_register (gravity_vm *vm);
+void gravity_json_free (void);
+bool gravity_isjson_class (gravity_class_t *c);
+const char *gravity_json_name (void);
+
+#endif

+ 3 - 5
src/optionals/gravity_math.c

@@ -18,8 +18,6 @@
 #include "gravity_macros.h"
 #include "gravity_macros.h"
 #include "gravity_vmmacros.h"
 #include "gravity_vmmacros.h"
 
 
-#define MATH_CLASS_NAME             "Math"
-
 #if GRAVITY_ENABLE_DOUBLE
 #if GRAVITY_ENABLE_DOUBLE
 #define SIN                         sin
 #define SIN                         sin
 #define COS                         cos
 #define COS                         cos
@@ -933,7 +931,7 @@ static bool math_random (gravity_vm *vm, gravity_value_t *args, uint16_t nargs,
 // MARK: - Internals -
 // MARK: - Internals -
 
 
 static void create_optional_class (void) {
 static void create_optional_class (void) {
-    gravity_class_math = gravity_class_new_pair(NULL, MATH_CLASS_NAME, NULL, 0, 0);
+    gravity_class_math = gravity_class_new_pair(NULL, GRAVITY_CLASS_MATH_NAME, NULL, 0, 0);
     gravity_class_t *meta = gravity_class_get_meta(gravity_class_math);
     gravity_class_t *meta = gravity_class_get_meta(gravity_class_math);
 
 
     gravity_class_bind(meta, "abs", NEW_CLOSURE_VALUE(math_abs));
     gravity_class_bind(meta, "abs", NEW_CLOSURE_VALUE(math_abs));
@@ -990,7 +988,7 @@ bool gravity_ismath_class (gravity_class_t *c) {
 }
 }
 
 
 const char *gravity_math_name (void) {
 const char *gravity_math_name (void) {
-    return MATH_CLASS_NAME;
+    return GRAVITY_CLASS_MATH_NAME;
 }
 }
 
 
 void gravity_math_register (gravity_vm *vm) {
 void gravity_math_register (gravity_vm *vm) {
@@ -998,7 +996,7 @@ void gravity_math_register (gravity_vm *vm) {
     ++refcount;
     ++refcount;
 
 
     if (!vm || gravity_vm_ismini(vm)) return;
     if (!vm || gravity_vm_ismini(vm)) return;
-    gravity_vm_setvalue(vm, MATH_CLASS_NAME, VALUE_FROM_OBJECT(gravity_class_math));
+    gravity_vm_setvalue(vm, GRAVITY_CLASS_MATH_NAME, VALUE_FROM_OBJECT(gravity_class_math));
 }
 }
 
 
 void gravity_math_free (void) {
 void gravity_math_free (void) {

+ 2 - 0
src/optionals/gravity_math.h

@@ -9,6 +9,8 @@
 #ifndef __GRAVITY_MATH__
 #ifndef __GRAVITY_MATH__
 #define __GRAVITY_MATH__
 #define __GRAVITY_MATH__
 
 
+#define GRAVITY_CLASS_MATH_NAME             "Math"
+
 #include "gravity_value.h"
 #include "gravity_value.h"
 
 
 void gravity_math_register (gravity_vm *vm);
 void gravity_math_register (gravity_vm *vm);

+ 32 - 0
src/optionals/gravity_optionals.h

@@ -26,6 +26,23 @@
 #define GRAVITY_ISMATH_CLASS(_c)            false
 #define GRAVITY_ISMATH_CLASS(_c)            false
 #endif
 #endif
 
 
+#ifndef GRAVITY_INCLUDE_JSON
+#define GRAVITY_INCLUDE_JSON
+#endif
+
+#ifdef GRAVITY_INCLUDE_JSON
+#define GRAVITY_JSON_REGISTER(_vm)          gravity_json_register(_vm)
+#define GRAVITY_JSON_FREE()                 gravity_json_free()
+#define GRAVITY_JSON_NAME()                 gravity_json_name()
+#define GRAVITY_ISJSON_CLASS(_c)            gravity_isjson_class(_c)
+#include "gravity_json.h"
+#else
+#define GRAVITY_JSON_REGISTER(_vm)
+#define GRAVITY_JSON_FREE()
+#define GRAVITY_JSON_NAME()                 NULL
+#define GRAVITY_ISJSON_CLASS(_c)            false
+#endif
+
 #ifndef GRAVITY_INCLUDE_ENV
 #ifndef GRAVITY_INCLUDE_ENV
 #define GRAVITY_INCLUDE_ENV
 #define GRAVITY_INCLUDE_ENV
 #endif
 #endif
@@ -43,4 +60,19 @@
 #define GRAVITY_ISENV_CLASS(_c)             false
 #define GRAVITY_ISENV_CLASS(_c)             false
 #endif
 #endif
 
 
+inline static const char **gravity_optional_identifiers(void) {
+    static const char *list[] = {
+        #ifdef GRAVITY_INCLUDE_MATH
+        GRAVITY_CLASS_MATH_NAME,
+        #endif
+        #ifdef GRAVITY_INCLUDE_ENV
+        GRAVITY_CLASS_ENV_NAME,
+        #endif
+        #ifdef GRAVITY_INCLUDE_JSON
+        GRAVITY_CLASS_JSON_NAME,
+        #endif
+        NULL};
+    return list;
+}
+
 #endif
 #endif

+ 280 - 52
src/runtime/gravity_core.c

@@ -95,6 +95,12 @@ typedef enum {
     number_format_float
     number_format_float
 } number_format_type;
 } number_format_type;
 
 
+typedef enum {
+    introspection_info_all,
+    introspection_info_methods,
+    introspection_info_variables
+} introspection_info_type;
+
 // MARK: - Utils -
 // MARK: - Utils -
 static void map_keys_array (gravity_hash_t *hashtable, gravity_value_t key, gravity_value_t value, void *data) {
 static void map_keys_array (gravity_hash_t *hashtable, gravity_value_t key, gravity_value_t value, void *data) {
     #pragma unused (hashtable, value)
     #pragma unused (hashtable, value)
@@ -120,14 +126,16 @@ static gravity_value_t convert_string2number (gravity_string_t *string, number_f
 
 
     // check special HEX, OCT, BIN cases
     // check special HEX, OCT, BIN cases
     if ((s[0] == '0') && (len > 2)) {
     if ((s[0] == '0') && (len > 2)) {
-        int64_t n = 0;
-
         int c = toupper(s[1]);
         int c = toupper(s[1]);
-        if (c == 'B') n = number_from_bin(&s[2], len-2);
-        else if (c == 'O') n = number_from_oct(&s[2], len-2);
-        else if (c == 'X') n = number_from_hex(s, len);
-        if (sign == -1) n = -n;
-        return (number_format == number_format_float) ? VALUE_FROM_FLOAT((gravity_float_t)n) : VALUE_FROM_INT((gravity_int_t)n);
+        bool isHexBinOct = ((c == 'B') || (c == 'O') || (c == 'X'));
+        if (isHexBinOct) {
+            int64_t n = 0;
+            if (c == 'B') n = number_from_bin(&s[2], len-2);
+            else if (c == 'O') n = number_from_oct(&s[2], len-2);
+            else if (c == 'X') n = number_from_hex(s, len);
+            if (sign == -1) n = -n;
+            return (number_format == number_format_float) ? VALUE_FROM_FLOAT((gravity_float_t)n) : VALUE_FROM_INT((gravity_int_t)n);
+        }
     }
     }
 
 
     // if dot character is contained into the string than force the float_preferred flag
     // if dot character is contained into the string than force the float_preferred flag
@@ -463,7 +471,7 @@ inline gravity_value_t convert_value2string (gravity_vm *vm, gravity_value_t v)
     return VALUE_FROM_ERROR(NULL);
     return VALUE_FROM_ERROR(NULL);
 }
 }
 
 
-// MARK: - Object Class -
+// MARK: - Object Introspection -
 
 
 static bool object_class (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
 static bool object_class (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
     #pragma unused(vm, nargs)
     #pragma unused(vm, nargs)
@@ -471,6 +479,120 @@ static bool object_class (gravity_vm *vm, gravity_value_t *args, uint16_t nargs,
     RETURN_VALUE(VALUE_FROM_OBJECT(c), rindex);
     RETURN_VALUE(VALUE_FROM_OBJECT(c), rindex);
 }
 }
 
 
+static bool object_meta (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+    #pragma unused(vm, nargs)
+    gravity_class_t *c = gravity_value_getclass(GET_VALUE(0));
+    RETURN_VALUE(VALUE_FROM_OBJECT(gravity_class_get_meta(c)), rindex);
+}
+
+static bool object_respond (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+    #pragma unused(vm, nargs)
+    gravity_class_t *c = gravity_value_getclass(GET_VALUE(0));
+    gravity_value_t key = GET_VALUE(1);
+    
+    bool result = false;
+    if (VALUE_ISA_STRING(key)) result = (gravity_class_lookup(c, key) != NULL);
+    
+    RETURN_VALUE(VALUE_FROM_BOOL(result), rindex);
+}
+
+static void collect_introspection (gravity_hash_t *hashtable, gravity_value_t key, gravity_value_t value, void *data1, void *data2, void *data3) {
+    #pragma unused (hashtable, data3)
+    gravity_list_t *list = (gravity_list_t *)data1;
+    introspection_info_type mask = *((introspection_info_type *)data2);
+    
+    // EXEC_TYPE_NATIVE     = 0
+    // EXEC_TYPE_INTERNAL   = 1
+    // EXEC_TYPE_BRIDGED    = 2
+    // EXEC_TYPE_SPECIAL    = 3
+    
+    gravity_closure_t *closure = VALUE_AS_CLOSURE(value);
+    gravity_function_t *func = closure->f;
+    bool is_var = (func->tag == EXEC_TYPE_SPECIAL);
+    
+    if ((mask == introspection_info_all) ||
+        ((mask == introspection_info_variables) && (is_var)) ||
+        ((mask == introspection_info_methods) && (!is_var))) {
+            marray_push(gravity_value_t, list->array, key);
+    }
+}
+
+static void collect_introspection_extended (gravity_hash_t *hashtable, gravity_value_t key, gravity_value_t value, void *data1, void *data2, void *data3) {
+    #pragma unused (hashtable)
+    
+    if (!VALUE_ISA_CLOSURE(value)) return;
+    
+    gravity_map_t *map = (gravity_map_t *)data1;
+    introspection_info_type mask = *((introspection_info_type *)data2);
+    gravity_vm *vm = (gravity_vm *)data3;
+    
+    gravity_closure_t *closure = VALUE_AS_CLOSURE(value);
+    gravity_function_t *func = closure->f;
+    bool is_var = (func->tag == EXEC_TYPE_SPECIAL);
+    
+    // create info map
+    gravity_map_t *info = gravity_map_new(vm, 16);
+    
+    // name
+    // description?
+    // isvar
+        // index
+        // read-only
+        // type?
+    // parameters (array of maps)
+        // name
+        // type?
+        // index
+        // value?
+    
+    gravity_map_insert(vm, info, VALUE_FROM_CSTRING(vm, "name"), VALUE_FROM_CSTRING(vm, VALUE_AS_STRING(key)->s));
+    gravity_map_insert(vm, info, VALUE_FROM_CSTRING(vm, "isvar"), VALUE_FROM_BOOL(is_var));
+    if (is_var) {
+        if (func->index < GRAVITY_COMPUTED_INDEX) gravity_map_insert(vm, info, VALUE_FROM_CSTRING(vm, "index"), VALUE_FROM_INT(func->index));
+        gravity_map_insert(vm, info, VALUE_FROM_CSTRING(vm, "readonly"), VALUE_FROM_BOOL(func->special[0] != NULL && func->special[1] == NULL));
+    } else {
+        gravity_list_t *params = gravity_function_params_get(vm, func);
+        if (params) gravity_map_insert(vm, info, VALUE_FROM_CSTRING(vm, "params"), VALUE_FROM_OBJECT(params));
+    }
+    
+    if ((mask == introspection_info_all) ||
+        ((mask == introspection_info_variables) && (is_var)) ||
+        ((mask == introspection_info_methods) && (!is_var))) {
+        gravity_map_insert(vm, map, key, VALUE_FROM_OBJECT(info));
+    }
+}
+
+static bool object_real_introspection (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex, introspection_info_type mask) {
+    bool extended = ((nargs >= 2) && (VALUE_ISA_BOOL(args[1]) && (VALUE_AS_BOOL(args[1]) == true)));
+    bool nosuper = ((nargs >= 3) && (VALUE_ISA_BOOL(args[2]) && (VALUE_AS_BOOL(args[2]) == true)));
+    
+    gravity_hash_iterate3_fn callback = (extended) ? collect_introspection_extended : collect_introspection;
+    gravity_object_t *data = (extended) ? (gravity_object_t *)gravity_map_new(vm, 256) : (gravity_object_t *)gravity_list_new(vm, 256);
+    
+    gravity_class_t *c = gravity_value_getclass(GET_VALUE(0));
+    while (c) {
+        gravity_hash_t *htable = c->htable;
+        gravity_hash_iterate3(htable, callback, (void *)data, (void *)&mask, (void *)vm);
+        c = (nosuper) ? NULL : c->superclass;
+    }
+    
+    RETURN_VALUE(VALUE_FROM_OBJECT(data), rindex);
+}
+
+static bool object_methods (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+    return object_real_introspection(vm, args, nargs, rindex, introspection_info_methods);
+}
+
+static bool object_properties (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+    return object_real_introspection(vm, args, nargs, rindex, introspection_info_variables);
+}
+
+static bool object_introspection (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+    return object_real_introspection(vm, args, nargs, rindex, introspection_info_all);
+}
+
+// MARK: - Object Class -
+
 static bool object_internal_size (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
 static bool object_internal_size (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
     #pragma unused(vm, nargs)
     #pragma unused(vm, nargs)
     gravity_int_t size = gravity_value_size(vm, GET_VALUE(0));
     gravity_int_t size = gravity_value_size(vm, GET_VALUE(0));
@@ -478,10 +600,10 @@ static bool object_internal_size (gravity_vm *vm, gravity_value_t *args, uint16_
     RETURN_VALUE(VALUE_FROM_INT(size), rindex);
     RETURN_VALUE(VALUE_FROM_INT(size), rindex);
 }
 }
 
 
-static bool object_isa (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+static bool object_is (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
     #pragma unused(vm, nargs)
     #pragma unused(vm, nargs)
 
 
-    gravity_class_t    *c1 = gravity_value_getclass(GET_VALUE(0));
+    gravity_class_t *c1 = gravity_value_getclass(GET_VALUE(0));
     gravity_class_t *c2 = VALUE_AS_CLASS(GET_VALUE(1));
     gravity_class_t *c2 = VALUE_AS_CLASS(GET_VALUE(1));
 
 
     while (c1 != c2 && c1->superclass != NULL) {
     while (c1 != c2 && c1->superclass != NULL) {
@@ -1022,7 +1144,21 @@ static bool list_reversed (gravity_vm *vm, gravity_value_t *args, uint16_t nargs
     RETURN_VALUE(VALUE_FROM_OBJECT(newlist), rindex);
     RETURN_VALUE(VALUE_FROM_OBJECT(newlist), rindex);
 }
 }
 
 
-static bool compare_values(gravity_vm *vm, gravity_value_t selfvalue, gravity_value_t val1, gravity_value_t val2, gravity_closure_t *predicate) {
+typedef bool (list_comparison_callback) (gravity_vm *vm, gravity_value_t val1, gravity_value_t val2);
+
+static bool list_default_number_compare (gravity_vm *vm, gravity_value_t val1, gravity_value_t val2) {
+    gravity_float_t n1 = convert_value2float(vm, val1).f;
+    gravity_float_t n2 = convert_value2float(vm, val2).f;
+    return n1 > n2;
+}
+
+static bool list_default_string_compare (gravity_vm *vm, gravity_value_t val1, gravity_value_t val2) {
+    gravity_string_t *s1 = VALUE_AS_STRING(convert_value2string(vm, val1));
+    gravity_string_t *s2 = VALUE_AS_STRING(convert_value2string(vm, val2));
+    return (strcmp(s1->s, s2->s) > 0);
+}
+
+static bool compare_values (gravity_vm *vm, gravity_value_t selfvalue, gravity_value_t val1, gravity_value_t val2, gravity_closure_t *predicate) {
     gravity_value_t params[2] = {val1, val2};
     gravity_value_t params[2] = {val1, val2};
     if (!gravity_vm_runclosure(vm, predicate, selfvalue, params, 2)) return false;
     if (!gravity_vm_runclosure(vm, predicate, selfvalue, params, 2)) return false;
     gravity_value_t result = gravity_vm_result(vm);
     gravity_value_t result = gravity_vm_result(vm);
@@ -1034,12 +1170,12 @@ static bool compare_values(gravity_vm *vm, gravity_value_t selfvalue, gravity_va
     return truthy_value.n;
     return truthy_value.n;
 }
 }
 
 
-static uint32_t partition(gravity_vm *vm, gravity_value_t *array, int32_t low, int32_t high, gravity_value_t selfvalue, gravity_closure_t *predicate) {
+static uint32_t partition (gravity_vm *vm, gravity_value_t *array, int32_t low, int32_t high, gravity_value_t selfvalue, gravity_closure_t *predicate, list_comparison_callback *callback) {
     gravity_value_t pivot = array[high];
     gravity_value_t pivot = array[high];
     int32_t i = low - 1;
     int32_t i = low - 1;
     
     
     for (int32_t j = low; j <= high - 1; j++) {
     for (int32_t j = low; j <= high - 1; j++) {
-        if (!compare_values(vm, selfvalue, array[j], pivot, predicate)) {
+        if ((predicate && !compare_values(vm, selfvalue, array[j], pivot, predicate)) || (callback && !callback(vm, array[j], pivot))) {
             ++i;
             ++i;
             gravity_value_t temp = array[i]; //swap a[i], a[j]
             gravity_value_t temp = array[i]; //swap a[i], a[j]
             array[i] = array[j];
             array[i] = array[j];
@@ -1054,39 +1190,48 @@ static uint32_t partition(gravity_vm *vm, gravity_value_t *array, int32_t low, i
     return i + 1;
     return i + 1;
 }
 }
 
 
-static void quicksort(gravity_vm *vm, gravity_value_t *array, int32_t low, int32_t high, gravity_value_t selfvalue, gravity_closure_t *predicate) {
+static void quicksort (gravity_vm *vm, gravity_value_t *array, int32_t low, int32_t high, gravity_value_t selfvalue, gravity_closure_t *predicate, list_comparison_callback *callback) {
     if (gravity_vm_isaborted(vm)) return;
     if (gravity_vm_isaborted(vm)) return;
     
     
     if (low < high) {
     if (low < high) {
-        int32_t pi = partition(vm, array, low, high, selfvalue, predicate);
-        quicksort(vm, array, low, pi - 1, selfvalue, predicate);
-        quicksort(vm, array, pi + 1, high, selfvalue, predicate);
+        int32_t pi = partition(vm, array, low, high, selfvalue, predicate, callback);
+        quicksort(vm, array, low, pi - 1, selfvalue, predicate, callback);
+        quicksort(vm, array, pi + 1, high, selfvalue, predicate, callback);
     }
     }
 }
 }
 
 
 static bool list_sort (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
 static bool list_sort (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
-  if (nargs != 2) RETURN_ERROR("One argument is needed by the sort function.");
-  if (!VALUE_ISA_CLOSURE(GET_VALUE(1))) RETURN_ERROR("Argument must be a Closure.");
-  gravity_value_t selfvalue = GET_VALUE(0);                            // self parameter
+    //the predicate is the comparison function passed to list.sort() if any
+    gravity_closure_t *predicate = NULL;
+    list_comparison_callback *callback = NULL;
+    if (nargs >=2 && VALUE_ISA_CLOSURE(GET_VALUE(1))) predicate = VALUE_AS_CLOSURE(GET_VALUE(1));
     
     
-  //the predicate is the comparison function, passed to list.sort()
-  gravity_closure_t *predicate = VALUE_AS_CLOSURE(GET_VALUE(1));
-  gravity_list_t *list = VALUE_AS_LIST(selfvalue);
-  int32_t count = (int32_t)marray_size(list->array);
+    gravity_value_t selfvalue = GET_VALUE(0); // self parameter
+    gravity_list_t *list = VALUE_AS_LIST(selfvalue);
+    
+    int32_t count = (int32_t)marray_size(list->array);
+    if (count > 1) {
+        if (predicate == NULL) {
+            gravity_value_t first_value = marray_get(list->array, 0);
+            if (VALUE_ISA_INT(first_value) || (VALUE_ISA_FLOAT(first_value))) callback = list_default_number_compare;
+            else callback = list_default_string_compare;
+        }
+        quicksort(vm, list->array.p, 0, count-1, selfvalue, predicate, callback);
+    }
     
     
-  quicksort(vm, list->array.p, 0, count-1, selfvalue, predicate);
-  RETURN_VALUE(VALUE_FROM_OBJECT((gravity_object_t *)list), rindex);
+    RETURN_VALUE(VALUE_FROM_OBJECT((gravity_object_t *)list), rindex);
 }
 }
 
 
 static bool list_sorted (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
 static bool list_sorted (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
-    if (nargs != 2) RETURN_ERROR("One argument is needed by the sort function.");
-    if (!VALUE_ISA_CLOSURE(GET_VALUE(1))) RETURN_ERROR("Argument must be a Closure.");
-    gravity_value_t selfvalue = GET_VALUE(0);   // self parameter
-
-    // the predicate is the comparison function, passed to list.sort()
-    gravity_closure_t *predicate = VALUE_AS_CLOSURE(GET_VALUE(1));
+    //the predicate is the comparison function passed to list.sort() if any
+    gravity_closure_t *predicate = NULL;
+    list_comparison_callback *callback = NULL;
+    if (nargs >=2 && VALUE_ISA_CLOSURE(GET_VALUE(1))) predicate = VALUE_AS_CLOSURE(GET_VALUE(1));
+    
+    gravity_value_t selfvalue = GET_VALUE(0); // self parameter
     gravity_list_t *list = VALUE_AS_LIST(selfvalue);
     gravity_list_t *list = VALUE_AS_LIST(selfvalue);
-    size_t count = marray_size(list->array);
+    
+    int32_t count = (int32_t)marray_size(list->array);
     
     
     // do not transfer newlist to GC because it could be freed during predicate closure execution
     // do not transfer newlist to GC because it could be freed during predicate closure execution
     // (because newlist is not yet in any stack)
     // (because newlist is not yet in any stack)
@@ -1096,7 +1241,14 @@ static bool list_sorted (gravity_vm *vm, gravity_value_t *args, uint16_t nargs,
     memcpy(newlist->array.p, list->array.p, sizeof(gravity_value_t)*count);
     memcpy(newlist->array.p, list->array.p, sizeof(gravity_value_t)*count);
     newlist->array.m = list->array.m;
     newlist->array.m = list->array.m;
     newlist->array.n = list->array.n;
     newlist->array.n = list->array.n;
-    quicksort(vm, newlist->array.p, 0, (int32_t)count-1, selfvalue, predicate);
+    if (count > 1) {
+        if (predicate == NULL) {
+            gravity_value_t first_value = marray_get(list->array, 0);
+            if (VALUE_ISA_INT(first_value) || (VALUE_ISA_FLOAT(first_value))) callback = list_default_number_compare;
+            else callback = list_default_string_compare;
+        }
+        quicksort(vm, newlist->array.p, 0, (int32_t)count-1, selfvalue, predicate, callback);
+    }
 
 
     gravity_vm_transfer(vm, (gravity_object_t*) newlist);
     gravity_vm_transfer(vm, (gravity_object_t*) newlist);
     RETURN_VALUE(VALUE_FROM_OBJECT(newlist), rindex);
     RETURN_VALUE(VALUE_FROM_OBJECT(newlist), rindex);
@@ -1126,7 +1278,7 @@ static bool list_map (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uin
     RETURN_VALUE(VALUE_FROM_OBJECT(newlist), rindex);
     RETURN_VALUE(VALUE_FROM_OBJECT(newlist), rindex);
 }
 }
 
 
-static bool list_filter(gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+static bool list_filter (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
     if (nargs != 2) RETURN_ERROR("One argument is needed by the filter function.");
     if (nargs != 2) RETURN_ERROR("One argument is needed by the filter function.");
     if (!VALUE_ISA_CLOSURE(GET_VALUE(1))) RETURN_ERROR("Argument must be a Closure.");
     if (!VALUE_ISA_CLOSURE(GET_VALUE(1))) RETURN_ERROR("Argument must be a Closure.");
     
     
@@ -1151,23 +1303,23 @@ static bool list_filter(gravity_vm *vm, gravity_value_t *args, uint16_t nargs, u
     RETURN_VALUE(VALUE_FROM_OBJECT(newlist), rindex);
     RETURN_VALUE(VALUE_FROM_OBJECT(newlist), rindex);
 }
 }
 
 
-static bool list_reduce(gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
-  if (nargs != 3) RETURN_ERROR("Two arguments are needed by the reduce function.");
-  if (!VALUE_ISA_CLOSURE(GET_VALUE(2))) RETURN_ERROR("Argument 2 must be a Closure.");
+static bool list_reduce (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+    if (nargs != 3) RETURN_ERROR("Two arguments are needed by the reduce function.");
+    if (!VALUE_ISA_CLOSURE(GET_VALUE(2))) RETURN_ERROR("Argument 2 must be a Closure.");
     
     
-  gravity_value_t selfvalue = GET_VALUE(0); // self parameter
-  gravity_value_t start = GET_VALUE(1);     // start parameter
-  gravity_closure_t *predicate = VALUE_AS_CLOSURE(GET_VALUE(2));
+    gravity_value_t selfvalue = GET_VALUE(0); // self parameter
+    gravity_value_t start = GET_VALUE(1);     // start parameter
+    gravity_closure_t *predicate = VALUE_AS_CLOSURE(GET_VALUE(2));
     
     
-  gravity_list_t *list = VALUE_AS_LIST(selfvalue);
-  size_t count = marray_size(list->array);
-  for (uint32_t i = 0; i < count; i++) {
-    gravity_value_t params[2] = {start, marray_get(list->array, i)};
-    if (!gravity_vm_runclosure(vm, predicate, selfvalue, params, 2)) return false;
-    start = gravity_vm_result(vm);
-  }
+    gravity_list_t *list = VALUE_AS_LIST(selfvalue);
+    size_t count = marray_size(list->array);
+    for (uint32_t i = 0; i < count; i++) {
+        gravity_value_t params[2] = {start, marray_get(list->array, i)};
+        if (!gravity_vm_runclosure(vm, predicate, selfvalue, params, 2)) return false;
+        start = gravity_vm_result(vm);
+    }
     
     
-  RETURN_VALUE(start, rindex);
+    RETURN_VALUE(start, rindex);
 }
 }
 
 
 static bool list_join (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
 static bool list_join (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
@@ -1541,6 +1693,15 @@ static bool class_exec (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, u
 
 
     // if constructor found in this class then executes it
     // if constructor found in this class then executes it
     if (closure) {
     if (closure) {
+        // as with func call even in constructor if less arguments are passed then fill the holes with UNDEFINED values
+        if (nargs < closure->f->nparams) {
+            uint16_t rargs = nargs;
+            while (rargs < closure->f->nparams) {
+                args[rargs] = VALUE_FROM_UNDEFINED;
+                ++rargs;
+            }
+        }
+        
         gravity_gc_setenabled(vm, true);
         gravity_gc_setenabled(vm, true);
         RETURN_CLOSURE(VALUE_FROM_OBJECT(closure), rindex);
         RETURN_CLOSURE(VALUE_FROM_OBJECT(closure), rindex);
     }
     }
@@ -1603,6 +1764,36 @@ static bool closure_bind (gravity_vm *vm, gravity_value_t *args, uint16_t nargs,
     RETURN_NOVALUE();
     RETURN_NOVALUE();
 }
 }
 
 
+// MARK: - Function Class -
+
+static bool function_closure (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+    #pragma unused(nargs)
+    
+    gravity_function_t *func = VALUE_AS_FUNCTION(GET_VALUE(0));
+    gravity_closure_t *closure = gravity_closure_new(vm, func);
+    RETURN_VALUE(VALUE_FROM_OBJECT(closure), rindex);
+}
+
+static bool function_exec (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+    if (!VALUE_ISA_FUNCTION(GET_VALUE(0))) RETURN_ERROR("Unable to convert Object to closure");
+    
+    gravity_function_t *func = VALUE_AS_FUNCTION(GET_VALUE(0));
+    gravity_closure_t *closure = gravity_closure_new(vm, func);
+    
+    // fix the default arg values case
+    // to be really correct and safe, stack size should be checked here but I know in advance that a minimum DEFAULT_MINSTACK_SIZE
+    // is guaratee before calling a function, so just make sure that you do not use more than DEFAULT_MINSTACK_SIZE arguments
+    // DEFAULT_MINSTACK_SIZE is 256 and in case of a 256 function arguments a maximum registers error would be returned
+    // so I can assume to be always safe here
+    while (nargs < func->nparams) {
+        uint32_t index = (func->nparams - nargs);
+        args[index] = VALUE_FROM_UNDEFINED;
+        ++nargs;
+    }
+    
+    RETURN_CLOSURE(VALUE_FROM_OBJECT(closure), rindex);
+}
+
 // MARK: - Float Class -
 // MARK: - Float Class -
 
 
 static bool operator_float_add (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
 static bool operator_float_add (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
@@ -2613,6 +2804,28 @@ static bool string_trim (gravity_vm *vm, gravity_value_t *args, uint16_t nargs,
     RETURN_VALUE(value, rindex);
     RETURN_VALUE(value, rindex);
 }
 }
 
 
+static bool string_raw (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+    #pragma unused(vm, nargs)
+    gravity_string_t *string = VALUE_AS_STRING(GET_VALUE(0));
+    
+    uint32_t ascii = 0;
+    uint32_t n = utf8_charbytes(string->s, 0);
+    for (uint32_t i=0; i<n; ++i) {
+        // if (n > 1) {printf("%u (%d)\n", (uint8_t)string->s[i], (uint32_t)pow(10, n-(i+1)));}
+        ascii += ((uint8_t)string->s[i] * pow(10, n-(i+1)));
+    }
+    
+    RETURN_VALUE(VALUE_FROM_INT(ascii), rindex);
+}
+
+static bool string_toclass (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+    #pragma unused(vm, nargs)
+    gravity_value_t result = gravity_vm_lookup(vm, GET_VALUE(0));
+    
+    if (VALUE_ISA_CLASS(result)) RETURN_VALUE(result, rindex);
+    RETURN_VALUE(VALUE_FROM_NULL, rindex);
+}
+
 // MARK: - Fiber Class -
 // MARK: - Fiber Class -
 
 
 static bool fiber_create (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
 static bool fiber_create (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
@@ -3033,10 +3246,10 @@ void gravity_core_init (void) {
     gravity_class_list = gravity_class_new_pair(NULL, GRAVITY_CLASS_LIST_NAME, NULL, 0, 0);
     gravity_class_list = gravity_class_new_pair(NULL, GRAVITY_CLASS_LIST_NAME, NULL, 0, 0);
     gravity_class_map = gravity_class_new_pair(NULL, GRAVITY_CLASS_MAP_NAME, NULL, 0, 0);
     gravity_class_map = gravity_class_new_pair(NULL, GRAVITY_CLASS_MAP_NAME, NULL, 0, 0);
     gravity_class_range = gravity_class_new_pair(NULL, GRAVITY_CLASS_RANGE_NAME, NULL, 0, 0);
     gravity_class_range = gravity_class_new_pair(NULL, GRAVITY_CLASS_RANGE_NAME, NULL, 0, 0);
-
+    
     // OBJECT CLASS
     // OBJECT CLASS
     gravity_class_bind(gravity_class_object, GRAVITY_CLASS_CLASS_NAME, NEW_CLOSURE_VALUE(object_class));
     gravity_class_bind(gravity_class_object, GRAVITY_CLASS_CLASS_NAME, NEW_CLOSURE_VALUE(object_class));
-    gravity_class_bind(gravity_class_object, GRAVITY_OPERATOR_ISA_NAME, NEW_CLOSURE_VALUE(object_isa));
+    gravity_class_bind(gravity_class_object, GRAVITY_OPERATOR_IS_NAME, NEW_CLOSURE_VALUE(object_is));
     gravity_class_bind(gravity_class_object, GRAVITY_OPERATOR_CMP_NAME, NEW_CLOSURE_VALUE(object_cmp));
     gravity_class_bind(gravity_class_object, GRAVITY_OPERATOR_CMP_NAME, NEW_CLOSURE_VALUE(object_cmp));
     gravity_class_bind(gravity_class_object, GRAVITY_OPERATOR_EQQ_NAME, NEW_CLOSURE_VALUE(object_eqq));
     gravity_class_bind(gravity_class_object, GRAVITY_OPERATOR_EQQ_NAME, NEW_CLOSURE_VALUE(object_eqq));
     gravity_class_bind(gravity_class_object, GRAVITY_OPERATOR_NEQQ_NAME, NEW_CLOSURE_VALUE(object_neqq));
     gravity_class_bind(gravity_class_object, GRAVITY_OPERATOR_NEQQ_NAME, NEW_CLOSURE_VALUE(object_neqq));
@@ -3055,6 +3268,14 @@ void gravity_core_init (void) {
     gravity_class_bind(gravity_class_object, GRAVITY_INTERNAL_EXEC_NAME, NEW_CLOSURE_VALUE(object_exec));
     gravity_class_bind(gravity_class_object, GRAVITY_INTERNAL_EXEC_NAME, NEW_CLOSURE_VALUE(object_exec));
     gravity_class_bind(gravity_class_object, "clone", NEW_CLOSURE_VALUE(object_clone));
     gravity_class_bind(gravity_class_object, "clone", NEW_CLOSURE_VALUE(object_clone));
 
 
+    // INTROSPECTION support added to OBJECT CLASS
+    gravity_class_bind(gravity_class_object, "class", VALUE_FROM_OBJECT(computed_property_create(NULL, NEW_FUNCTION(object_class), NULL)));
+    gravity_class_bind(gravity_class_object, "meta", VALUE_FROM_OBJECT(computed_property_create(NULL, NEW_FUNCTION(object_meta), NULL)));
+    gravity_class_bind(gravity_class_object, "respondTo", NEW_CLOSURE_VALUE(object_respond));
+    gravity_class_bind(gravity_class_object, "methods", NEW_CLOSURE_VALUE(object_methods));
+    gravity_class_bind(gravity_class_object, "properties", NEW_CLOSURE_VALUE(object_properties));
+    gravity_class_bind(gravity_class_object, "introspection", NEW_CLOSURE_VALUE(object_introspection));
+    
     // NULL CLASS
     // NULL CLASS
     // Meta
     // Meta
     gravity_class_t *null_meta = gravity_class_get_meta(gravity_class_null);
     gravity_class_t *null_meta = gravity_class_get_meta(gravity_class_null);
@@ -3069,6 +3290,10 @@ void gravity_core_init (void) {
     gravity_class_bind(gravity_class_closure, "apply", NEW_CLOSURE_VALUE(closure_apply));
     gravity_class_bind(gravity_class_closure, "apply", NEW_CLOSURE_VALUE(closure_apply));
     gravity_class_bind(gravity_class_closure, "bind", NEW_CLOSURE_VALUE(closure_bind));
     gravity_class_bind(gravity_class_closure, "bind", NEW_CLOSURE_VALUE(closure_bind));
 
 
+    // FUNCTION CLASS
+    gravity_class_bind(gravity_class_function, GRAVITY_INTERNAL_EXEC_NAME, NEW_CLOSURE_VALUE(function_exec));
+    gravity_class_bind(gravity_class_function, "closure", NEW_CLOSURE_VALUE(function_closure));
+    
     // LIST CLASS
     // LIST CLASS
     gravity_closure_t *closure = computed_property_create(NULL, NEW_FUNCTION(list_count), NULL);
     gravity_closure_t *closure = computed_property_create(NULL, NEW_FUNCTION(list_count), NULL);
     gravity_class_bind(gravity_class_list, "count", VALUE_FROM_OBJECT(closure));
     gravity_class_bind(gravity_class_list, "count", VALUE_FROM_OBJECT(closure));
@@ -3217,6 +3442,9 @@ void gravity_core_init (void) {
     gravity_class_bind(gravity_class_string, ITERATOR_NEXT_FUNCTION, NEW_CLOSURE_VALUE(string_iterator_next));
     gravity_class_bind(gravity_class_string, ITERATOR_NEXT_FUNCTION, NEW_CLOSURE_VALUE(string_iterator_next));
     gravity_class_bind(gravity_class_string, "number", NEW_CLOSURE_VALUE(string_number));
     gravity_class_bind(gravity_class_string, "number", NEW_CLOSURE_VALUE(string_number));
     gravity_class_bind(gravity_class_string, "trim", NEW_CLOSURE_VALUE(string_trim));
     gravity_class_bind(gravity_class_string, "trim", NEW_CLOSURE_VALUE(string_trim));
+    gravity_class_bind(gravity_class_string, "raw", NEW_CLOSURE_VALUE(string_raw));
+    gravity_class_bind(gravity_class_string, GRAVITY_TOCLASS_NAME, NEW_CLOSURE_VALUE(string_toclass));
+    
     // Meta
     // Meta
     gravity_class_t *string_meta = gravity_class_get_meta(gravity_class_string);
     gravity_class_t *string_meta = gravity_class_get_meta(gravity_class_string);
     gravity_class_bind(string_meta, GRAVITY_INTERNAL_EXEC_NAME, NEW_CLOSURE_VALUE(string_exec));
     gravity_class_bind(string_meta, GRAVITY_INTERNAL_EXEC_NAME, NEW_CLOSURE_VALUE(string_exec));
@@ -3270,7 +3498,7 @@ void gravity_core_init (void) {
     gravity_class_bind(system_meta, GRAVITY_VM_MAXCALLS, value);
     gravity_class_bind(system_meta, GRAVITY_VM_MAXCALLS, value);
     gravity_class_bind(system_meta, GRAVITY_VM_MAXBLOCK, value);
     gravity_class_bind(system_meta, GRAVITY_VM_MAXBLOCK, value);
     gravity_class_bind(system_meta, GRAVITY_VM_MAXRECURSION, value);
     gravity_class_bind(system_meta, GRAVITY_VM_MAXRECURSION, value);
-
+    
     // INIT META
     // INIT META
     SETMETA_INITED(gravity_class_int);
     SETMETA_INITED(gravity_class_int);
     SETMETA_INITED(gravity_class_float);
     SETMETA_INITED(gravity_class_float);

+ 15 - 3
src/runtime/gravity_vm.c

@@ -180,7 +180,7 @@ static void gravity_cache_setup (void) {
     cache[GRAVITY_OR_INDEX] = VALUE_FROM_CSTRING(NULL, GRAVITY_OPERATOR_OR_NAME);
     cache[GRAVITY_OR_INDEX] = VALUE_FROM_CSTRING(NULL, GRAVITY_OPERATOR_OR_NAME);
     cache[GRAVITY_CMP_INDEX] = VALUE_FROM_CSTRING(NULL, GRAVITY_OPERATOR_CMP_NAME);
     cache[GRAVITY_CMP_INDEX] = VALUE_FROM_CSTRING(NULL, GRAVITY_OPERATOR_CMP_NAME);
     cache[GRAVITY_EQQ_INDEX] = VALUE_FROM_CSTRING(NULL, GRAVITY_OPERATOR_EQQ_NAME);
     cache[GRAVITY_EQQ_INDEX] = VALUE_FROM_CSTRING(NULL, GRAVITY_OPERATOR_EQQ_NAME);
-    cache[GRAVITY_ISA_INDEX] = VALUE_FROM_CSTRING(NULL, GRAVITY_OPERATOR_ISA_NAME);
+    cache[GRAVITY_IS_INDEX] = VALUE_FROM_CSTRING(NULL, GRAVITY_OPERATOR_IS_NAME);
     cache[GRAVITY_MATCH_INDEX] = VALUE_FROM_CSTRING(NULL, GRAVITY_OPERATOR_MATCH_NAME);
     cache[GRAVITY_MATCH_INDEX] = VALUE_FROM_CSTRING(NULL, GRAVITY_OPERATOR_MATCH_NAME);
     cache[GRAVITY_NEG_INDEX] = VALUE_FROM_CSTRING(NULL, GRAVITY_OPERATOR_NEG_NAME);
     cache[GRAVITY_NEG_INDEX] = VALUE_FROM_CSTRING(NULL, GRAVITY_OPERATOR_NEG_NAME);
     cache[GRAVITY_NOT_INDEX] = VALUE_FROM_CSTRING(NULL, GRAVITY_OPERATOR_NOT_NAME);
     cache[GRAVITY_NOT_INDEX] = VALUE_FROM_CSTRING(NULL, GRAVITY_OPERATOR_NOT_NAME);
@@ -357,15 +357,17 @@ static void gravity_vm_loadclass (gravity_vm *vm, gravity_class_t *c) {
 void gravity_opt_register (gravity_vm *vm) {
 void gravity_opt_register (gravity_vm *vm) {
     GRAVITY_MATH_REGISTER(vm);
     GRAVITY_MATH_REGISTER(vm);
     GRAVITY_ENV_REGISTER(vm);
     GRAVITY_ENV_REGISTER(vm);
+    GRAVITY_JSON_REGISTER(vm);
 }
 }
 
 
 void gravity_opt_free() {
 void gravity_opt_free() {
     GRAVITY_MATH_FREE();
     GRAVITY_MATH_FREE();
     GRAVITY_ENV_FREE();
     GRAVITY_ENV_FREE();
+    GRAVITY_JSON_FREE();
 }
 }
 
 
 bool gravity_isopt_class (gravity_class_t *c) {
 bool gravity_isopt_class (gravity_class_t *c) {
-    return (GRAVITY_ISMATH_CLASS(c)) || (GRAVITY_ISENV_CLASS(c));
+    return (GRAVITY_ISMATH_CLASS(c)) || (GRAVITY_ISENV_CLASS(c) || (GRAVITY_ISJSON_CLASS(c)));
 }
 }
 
 
 // MARK: - MAIN EXECUTION -
 // MARK: - MAIN EXECUTION -
@@ -692,7 +694,7 @@ static bool gravity_vm_exec (gravity_vm *vm) {
                 DEFINE_STACK_VARIABLE(v3,r3);
                 DEFINE_STACK_VARIABLE(v3,r3);
 
 
                 // prepare function call for binary operation
                 // prepare function call for binary operation
-                PREPARE_FUNC_CALL2(closure, v2, v3, (op == ISA) ? GRAVITY_ISA_INDEX : GRAVITY_MATCH_INDEX, rwin);
+                PREPARE_FUNC_CALL2(closure, v2, v3, (op == ISA) ? GRAVITY_IS_INDEX : GRAVITY_MATCH_INDEX, rwin);
 
 
                 // call function f
                 // call function f
                 CALL_FUNC(ISA, closure, r1, 2, rwin);
                 CALL_FUNC(ISA, closure, r1, 2, rwin);
@@ -2331,3 +2333,13 @@ void gravity_gc_temppush (gravity_vm *vm, gravity_object_t *obj) {
 void gravity_gc_temppop (gravity_vm *vm) {
 void gravity_gc_temppop (gravity_vm *vm) {
     marray_pop(vm->gctemp);
     marray_pop(vm->gctemp);
 }
 }
+
+void gravity_gc_tempnull (gravity_vm *vm, gravity_object_t *obj) {
+    for (uint32_t i=0; i<marray_size(vm->gctemp); ++i) {
+        gravity_object_t *tobj = marray_get(vm->gctemp, i);
+        if (tobj == obj) {
+            marray_setnull(vm->gctemp, i);
+            break;
+        }
+    }
+}

+ 1 - 0
src/runtime/gravity_vm.h

@@ -58,6 +58,7 @@ GRAVITY_API void                gravity_gc_start (gravity_vm* vm);
 GRAVITY_API void                gravity_gc_setenabled (gravity_vm* vm, bool enabled);
 GRAVITY_API void                gravity_gc_setenabled (gravity_vm* vm, bool enabled);
 GRAVITY_API void                gravity_gc_temppush (gravity_vm *vm, gravity_object_t *obj);
 GRAVITY_API void                gravity_gc_temppush (gravity_vm *vm, gravity_object_t *obj);
 GRAVITY_API void                gravity_gc_temppop (gravity_vm *vm);
 GRAVITY_API void                gravity_gc_temppop (gravity_vm *vm);
+GRAVITY_API void                gravity_gc_tempnull (gravity_vm *vm, gravity_object_t *obj);
 GRAVITY_API void                gravity_gc_setvalues (gravity_vm *vm, gravity_int_t threshold, gravity_int_t minthreshold, gravity_float_t ratio);
 GRAVITY_API void                gravity_gc_setvalues (gravity_vm *vm, gravity_int_t threshold, gravity_int_t minthreshold, gravity_float_t ratio);
     
     
 GRAVITY_API void                gravity_vm_transfer (gravity_vm* vm, gravity_object_t *obj);
 GRAVITY_API void                gravity_vm_transfer (gravity_vm* vm, gravity_object_t *obj);

+ 1 - 0
src/shared/gravity_array.h

@@ -19,6 +19,7 @@
 #define marray_decl_init(_t,_v)     _t _v; marray_init(_v)
 #define marray_decl_init(_t,_v)     _t _v; marray_init(_v)
 #define marray_destroy(v)           if ((v).p) free((v).p)
 #define marray_destroy(v)           if ((v).p) free((v).p)
 #define marray_get(v, i)            ((v).p[(i)])
 #define marray_get(v, i)            ((v).p[(i)])
+#define marray_setnull(v, i)        ((v).p[(i)] = NULL)
 #define marray_pop(v)               ((v).p[--(v).n])
 #define marray_pop(v)               ((v).p[--(v).n])
 #define marray_last(v)              ((v).p[(v).n-1])
 #define marray_last(v)              ((v).p[(v).n-1])
 #define marray_size(v)              ((v).n)
 #define marray_size(v)              ((v).n)

+ 14 - 0
src/shared/gravity_hash.c

@@ -389,6 +389,20 @@ void gravity_hash_iterate2 (gravity_hash_t *hashtable, gravity_hash_iterate2_fn
     }
     }
 }
 }
 
 
+void gravity_hash_iterate3 (gravity_hash_t *hashtable, gravity_hash_iterate3_fn iterate, void *data1, void *data2, void *data3) {
+    if ((!hashtable) || (!iterate)) return;
+    
+    for (uint32_t i=0; i<hashtable->size; ++i) {
+        hash_node_t *node = hashtable->nodes[i];
+        if (!node) continue;
+        
+        while (node) {
+            iterate(hashtable, node->key, node->value, data1, data2, data3);
+            node = node->next;
+        }
+    }
+}
+
 void gravity_hash_dump (gravity_hash_t *hashtable) {
 void gravity_hash_dump (gravity_hash_t *hashtable) {
     gravity_hash_iterate(hashtable, table_dump, NULL);
     gravity_hash_iterate(hashtable, table_dump, NULL);
 }
 }

+ 2 - 0
src/shared/gravity_hash.h

@@ -34,6 +34,7 @@ typedef uint32_t    (*gravity_hash_compute_fn) (gravity_value_t key);
 typedef bool        (*gravity_hash_isequal_fn) (gravity_value_t v1, gravity_value_t v2);
 typedef bool        (*gravity_hash_isequal_fn) (gravity_value_t v1, gravity_value_t v2);
 typedef void        (*gravity_hash_iterate_fn) (gravity_hash_t *hashtable, gravity_value_t key, gravity_value_t value, void *data);
 typedef void        (*gravity_hash_iterate_fn) (gravity_hash_t *hashtable, gravity_value_t key, gravity_value_t value, void *data);
 typedef void        (*gravity_hash_iterate2_fn) (gravity_hash_t *hashtable, gravity_value_t key, gravity_value_t value, void *data1, void *data2);
 typedef void        (*gravity_hash_iterate2_fn) (gravity_hash_t *hashtable, gravity_value_t key, gravity_value_t value, void *data1, void *data2);
+typedef void        (*gravity_hash_iterate3_fn) (gravity_hash_t *hashtable, gravity_value_t key, gravity_value_t value, void *data1, void *data2, void *data3);
 typedef void        (*gravity_hash_transform_fn) (gravity_hash_t *hashtable, gravity_value_t key, gravity_value_t *value, void *data);
 typedef void        (*gravity_hash_transform_fn) (gravity_hash_t *hashtable, gravity_value_t key, gravity_value_t *value, void *data);
 typedef bool        (*gravity_hash_compare_fn) (gravity_value_t value1, gravity_value_t value2, void *data);
 typedef bool        (*gravity_hash_compare_fn) (gravity_value_t value1, gravity_value_t value2, void *data);
 
 
@@ -54,6 +55,7 @@ GRAVITY_API uint32_t        gravity_hash_compute_float (gravity_float_t f);
 GRAVITY_API void            gravity_hash_stat (gravity_hash_t *hashtable);
 GRAVITY_API void            gravity_hash_stat (gravity_hash_t *hashtable);
 GRAVITY_API void            gravity_hash_iterate (gravity_hash_t *hashtable, gravity_hash_iterate_fn iterate, void *data);
 GRAVITY_API void            gravity_hash_iterate (gravity_hash_t *hashtable, gravity_hash_iterate_fn iterate, void *data);
 GRAVITY_API void            gravity_hash_iterate2 (gravity_hash_t *hashtable, gravity_hash_iterate2_fn iterate, void *data1, void *data2);
 GRAVITY_API void            gravity_hash_iterate2 (gravity_hash_t *hashtable, gravity_hash_iterate2_fn iterate, void *data1, void *data2);
+GRAVITY_API void            gravity_hash_iterate3 (gravity_hash_t *hashtable, gravity_hash_iterate3_fn iterate, void *data1, void *data2, void *data3);
 GRAVITY_API void            gravity_hash_transform (gravity_hash_t *hashtable, gravity_hash_transform_fn iterate, void *data);
 GRAVITY_API void            gravity_hash_transform (gravity_hash_t *hashtable, gravity_hash_transform_fn iterate, void *data);
 GRAVITY_API void            gravity_hash_dump (gravity_hash_t *hashtable);
 GRAVITY_API void            gravity_hash_dump (gravity_hash_t *hashtable);
 GRAVITY_API void            gravity_hash_append (gravity_hash_t *hashtable1, gravity_hash_t *hashtable2);
 GRAVITY_API void            gravity_hash_append (gravity_hash_t *hashtable1, gravity_hash_t *hashtable2);

+ 5 - 0
src/shared/gravity_macros.h

@@ -99,6 +99,8 @@
 // MARK: -
 // MARK: -
 #define GRAVITY_JSON_FUNCTION               "function"
 #define GRAVITY_JSON_FUNCTION               "function"
 #define GRAVITY_JSON_CLASS                  "class"
 #define GRAVITY_JSON_CLASS                  "class"
+#define GRAVITY_JSON_RANGE                  "range"
+#define GRAVITY_JSON_INSTANCE               "instance"
 #define GRAVITY_JSON_ENUM                   "enum"
 #define GRAVITY_JSON_ENUM                   "enum"
 #define GRAVITY_JSON_MAP                    "map"
 #define GRAVITY_JSON_MAP                    "map"
 #define GRAVITY_JSON_VAR                    "var"
 #define GRAVITY_JSON_VAR                    "var"
@@ -132,6 +134,9 @@
 #define GRAVITY_JSON_LABELSTATIC            "static"
 #define GRAVITY_JSON_LABELSTATIC            "static"
 #define GRAVITY_JSON_LABELPARAMS            "params"
 #define GRAVITY_JSON_LABELPARAMS            "params"
 #define GRAVITY_JSON_LABELSTRUCT            "struct"
 #define GRAVITY_JSON_LABELSTRUCT            "struct"
+#define GRAVITY_JSON_LABELFROM              "from"
+#define GRAVITY_JSON_LABELTO                "to"
+#define GRAVITY_JSON_LABELIVAR              "ivar"
 
 
 #define GRAVITY_VM_ANONYMOUS_PREFIX         "$$"
 #define GRAVITY_VM_ANONYMOUS_PREFIX         "$$"
 
 

+ 1 - 1
src/shared/gravity_memory.c

@@ -16,7 +16,7 @@ void *gravity_calloc(gravity_vm *vm, size_t count, size_t size) {
         gravity_vm_seterror(vm, "Maximum memory allocation block size reached (req: %d, max: %lld).", (count * size), (int64_t)gravity_vm_maxmemblock(vm));
         gravity_vm_seterror(vm, "Maximum memory allocation block size reached (req: %d, max: %lld).", (count * size), (int64_t)gravity_vm_maxmemblock(vm));
         return NULL;
         return NULL;
     }
     }
-    return calloc(count, size);;
+    return calloc(count, size);
 }
 }
 
 
 void *gravity_realloc(gravity_vm *vm, void *ptr, size_t new_size) {
 void *gravity_realloc(gravity_vm *vm, void *ptr, size_t new_size) {

+ 8 - 2
src/shared/gravity_opcodes.h

@@ -150,7 +150,7 @@ typedef enum {
     GRAVITY_OR_INDEX,
     GRAVITY_OR_INDEX,
     GRAVITY_CMP_INDEX,
     GRAVITY_CMP_INDEX,
     GRAVITY_EQQ_INDEX,
     GRAVITY_EQQ_INDEX,
-    GRAVITY_ISA_INDEX,
+    GRAVITY_IS_INDEX,
     GRAVITY_MATCH_INDEX,
     GRAVITY_MATCH_INDEX,
     GRAVITY_NEG_INDEX,
     GRAVITY_NEG_INDEX,
     GRAVITY_NOT_INDEX,
     GRAVITY_NOT_INDEX,
@@ -183,7 +183,7 @@ typedef enum {
 #define GRAVITY_OPERATOR_CMP_NAME       "=="
 #define GRAVITY_OPERATOR_CMP_NAME       "=="
 #define GRAVITY_OPERATOR_EQQ_NAME       "==="
 #define GRAVITY_OPERATOR_EQQ_NAME       "==="
 #define GRAVITY_OPERATOR_NEQQ_NAME      "!=="
 #define GRAVITY_OPERATOR_NEQQ_NAME      "!=="
-#define GRAVITY_OPERATOR_ISA_NAME       "is"
+#define GRAVITY_OPERATOR_IS_NAME        "is"
 #define GRAVITY_OPERATOR_MATCH_NAME     "=~"
 #define GRAVITY_OPERATOR_MATCH_NAME     "=~"
 #define GRAVITY_OPERATOR_NEG_NAME       "neg"
 #define GRAVITY_OPERATOR_NEG_NAME       "neg"
 #define GRAVITY_OPERATOR_NOT_NAME        "!"
 #define GRAVITY_OPERATOR_NOT_NAME        "!"
@@ -223,4 +223,10 @@ typedef enum {
 #define GRAVITY_SYSTEM_PUT_NAME         "put"
 #define GRAVITY_SYSTEM_PUT_NAME         "put"
 #define GRAVITY_SYSTEM_NANOTIME_NAME    "nanotime"
 #define GRAVITY_SYSTEM_NANOTIME_NAME    "nanotime"
 
 
+#define GRAVITY_TOCLASS_NAME            "toClass"
+#define GRAVITY_TOSTRING_NAME           "toString"
+#define GRAVITY_TOINT_NAME              "toInt"
+#define GRAVITY_TOFLOAT_NAME            "toFloat"
+#define GRAVITY_TOBOOL_NAME             "toBool"
+
 #endif
 #endif

+ 184 - 59
src/shared/gravity_value.c

@@ -27,6 +27,8 @@ static gravity_map_t *gravity_map_deserialize (gravity_vm *vm, json_value *json)
 static void gravity_hash_serialize (gravity_hash_t *table, gravity_value_t key, gravity_value_t value, void *data) {
 static void gravity_hash_serialize (gravity_hash_t *table, gravity_value_t key, gravity_value_t value, void *data) {
     #pragma unused(table)
     #pragma unused(table)
     json_t *json = (json_t *)data;
     json_t *json = (json_t *)data;
+    
+    if (VALUE_ISA_CLOSURE(value)) value = VALUE_FROM_OBJECT(VALUE_AS_CLOSURE(value)->f);
 
 
     if (VALUE_ISA_FUNCTION(value)) {
     if (VALUE_ISA_FUNCTION(value)) {
         gravity_function_t *f = VALUE_AS_FUNCTION(value);
         gravity_function_t *f = VALUE_AS_FUNCTION(value);
@@ -252,7 +254,9 @@ void gravity_class_setxdata (gravity_class_t *c, void *xdata) {
 }
 }
 
 
 void gravity_class_serialize (gravity_class_t *c, json_t *json) {
 void gravity_class_serialize (gravity_class_t *c, json_t *json) {
-    json_begin_object(json, c->identifier);
+    const char *label = json_get_label(json, c->identifier);
+    json_begin_object(json, label);
+    
     json_add_cstring(json, GRAVITY_JSON_LABELTYPE, GRAVITY_JSON_CLASS);     // MANDATORY 1st FIELD
     json_add_cstring(json, GRAVITY_JSON_LABELTYPE, GRAVITY_JSON_CLASS);     // MANDATORY 1st FIELD
     json_add_cstring(json, GRAVITY_JSON_LABELIDENTIFIER, c->identifier);    // MANDATORY 2nd FIELD
     json_add_cstring(json, GRAVITY_JSON_LABELIDENTIFIER, c->identifier);    // MANDATORY 2nd FIELD
     
     
@@ -565,6 +569,21 @@ gravity_value_t gravity_function_cpool_get (gravity_function_t *f, uint16_t i) {
     return marray_get(f->cpool, i);
     return marray_get(f->cpool, i);
 }
 }
 
 
+gravity_list_t *gravity_function_params_get (gravity_vm *vm, gravity_function_t *f) {
+    #pragma unused(vm)
+    gravity_list_t *list = NULL;
+    
+    if (f->tag == EXEC_TYPE_NATIVE) {
+        // written by user in Gravity
+    } else if (f->tag == EXEC_TYPE_BRIDGED && f->xdata) {
+        // ask bridge
+    } else if (f->tag == EXEC_TYPE_INTERNAL) {
+        // native C function
+    }
+    
+    return list;
+}
+
 void gravity_function_setxdata (gravity_function_t *f, void *xdata) {
 void gravity_function_setxdata (gravity_function_t *f, void *xdata) {
     f->xdata = xdata;
     f->xdata = xdata;
 }
 }
@@ -575,7 +594,7 @@ static void gravity_function_array_serialize (gravity_function_t *f, json_t *jso
 
 
     for (size_t i=0; i<n; i++) {
     for (size_t i=0; i<n; i++) {
         gravity_value_t v = marray_get(r, i);
         gravity_value_t v = marray_get(r, i);
-        gravity_value_serialize(v, json);
+        gravity_value_serialize(NULL, v, json);
     }
     }
 }
 }
 
 
@@ -755,7 +774,8 @@ void gravity_function_dump (gravity_function_t *f, code_dump_function codef) {
 }
 }
 
 
 void gravity_function_special_serialize (gravity_function_t *f, const char *key, json_t *json) {
 void gravity_function_special_serialize (gravity_function_t *f, const char *key, json_t *json) {
-    json_begin_object(json, key);
+    const char *label = json_get_label(json, key);
+    json_begin_object(json, label);
 
 
     json_add_cstring(json, GRAVITY_JSON_LABELTYPE, GRAVITY_JSON_FUNCTION);    // MANDATORY 1st FIELD
     json_add_cstring(json, GRAVITY_JSON_LABELTYPE, GRAVITY_JSON_FUNCTION);    // MANDATORY 1st FIELD
     json_add_cstring(json, GRAVITY_JSON_LABELIDENTIFIER, key);                // MANDATORY 2nd FIELD
     json_add_cstring(json, GRAVITY_JSON_LABELIDENTIFIER, key);                // MANDATORY 2nd FIELD
@@ -794,9 +814,11 @@ void gravity_function_serialize (gravity_function_t *f, json_t *json) {
     char temp[256];
     char temp[256];
     if (!identifier) {snprintf(temp, sizeof(temp), "$anon_%p", f); identifier = temp;}
     if (!identifier) {snprintf(temp, sizeof(temp), "$anon_%p", f); identifier = temp;}
 
 
-    if (identifier) json_begin_object(json, identifier);
-    json_add_cstring(json, GRAVITY_JSON_LABELTYPE, GRAVITY_JSON_FUNCTION);                    // MANDATORY 1st FIELD
-    if (identifier) json_add_cstring(json, GRAVITY_JSON_LABELIDENTIFIER, identifier);        // MANDATORY 2nd FIELD (not for getter/setter)
+    const char *label = json_get_label(json, identifier);
+    json_begin_object(json, label);
+    
+    json_add_cstring(json, GRAVITY_JSON_LABELTYPE, GRAVITY_JSON_FUNCTION);  // MANDATORY 1st FIELD
+    json_add_cstring(json, GRAVITY_JSON_LABELIDENTIFIER, identifier);       // MANDATORY 2nd FIELD (not for getter/setter)
     json_add_int(json, GRAVITY_JSON_LABELTAG, f->tag);
     json_add_int(json, GRAVITY_JSON_LABELTAG, f->tag);
 
 
     // common fields
     // common fields
@@ -833,7 +855,7 @@ void gravity_function_serialize (gravity_function_t *f, json_t *json) {
         }
         }
     }
     }
     
     
-    if (identifier) json_end_object(json);
+    json_end_object(json);
 }
 }
 
 
 gravity_function_t *gravity_function_deserialize (gravity_vm *vm, json_value *json) {
 gravity_function_t *gravity_function_deserialize (gravity_vm *vm, json_value *json) {
@@ -1207,9 +1229,9 @@ gravity_closure_t *gravity_closure_new (gravity_vm *vm, gravity_function_t *f) {
 
 
 void gravity_closure_free (gravity_vm *vm, gravity_closure_t *closure) {
 void gravity_closure_free (gravity_vm *vm, gravity_closure_t *closure) {
     #pragma unused(vm)
     #pragma unused(vm)
-
+    if (closure->refcount > 0) return;
+    
     DEBUG_FREE("FREE %s", gravity_object_debug((gravity_object_t *)closure, true));
     DEBUG_FREE("FREE %s", gravity_object_debug((gravity_object_t *)closure, true));
-
     if (closure->upvalue) mem_free(closure->upvalue);
     if (closure->upvalue) mem_free(closure->upvalue);
     mem_free(closure);
     mem_free(closure);
 }
 }
@@ -1229,6 +1251,16 @@ uint32_t gravity_closure_size (gravity_vm *vm, gravity_closure_t *closure) {
     return closure_size;
     return closure_size;
 }
 }
 
 
+void gravity_closure_inc_refcount (gravity_vm *vm, gravity_closure_t *closure) {
+    if (closure->refcount == 0) gravity_gc_temppush(vm, (gravity_object_t *)closure);
+    ++closure->refcount;
+}
+
+void gravity_closure_dec_refcount (gravity_vm *vm, gravity_closure_t *closure) {
+    if (closure->refcount == 1) gravity_gc_tempnull(vm, (gravity_object_t *)closure);
+    if (closure->refcount >= 1) --closure->refcount;
+}
+
 void gravity_closure_blacken (gravity_vm *vm, gravity_closure_t *closure) {
 void gravity_closure_blacken (gravity_vm *vm, gravity_closure_t *closure) {
     gravity_vm_memupdate(vm, gravity_closure_size(vm, closure));
     gravity_vm_memupdate(vm, gravity_closure_size(vm, closure));
 
 
@@ -1415,7 +1447,7 @@ void gravity_object_serialize (gravity_object_t *obj, json_t *json) {
 
 
 gravity_object_t *gravity_object_deserialize (gravity_vm *vm, json_value *entry) {
 gravity_object_t *gravity_object_deserialize (gravity_vm *vm, json_value *entry) {
     // this function is able to deserialize ONLY objects with a type label
     // this function is able to deserialize ONLY objects with a type label
-
+    
     // sanity check
     // sanity check
     if (entry->type != json_object) return NULL;
     if (entry->type != json_object) return NULL;
     if (entry->u.object.length == 0) return NULL;
     if (entry->u.object.length == 0) return NULL;
@@ -1423,8 +1455,14 @@ gravity_object_t *gravity_object_deserialize (gravity_vm *vm, json_value *entry)
     // the first entry value must specify gravity object type
     // the first entry value must specify gravity object type
     const char *label = entry->u.object.values[0].name;
     const char *label = entry->u.object.values[0].name;
     json_value *value = entry->u.object.values[0].value;
     json_value *value = entry->u.object.values[0].value;
-
-    if (string_casencmp(label, GRAVITY_JSON_LABELTYPE, 4) != 0) return NULL;
+    
+    if (string_casencmp(label, GRAVITY_JSON_LABELTYPE, 4) != 0) {
+        // if no label type found then assume it is a map object
+        gravity_map_t *m = gravity_map_deserialize(vm, entry);
+        return (gravity_object_t *)m;
+    }
+    
+    // sanity check
     if (value->type != json_string) return NULL;
     if (value->type != json_string) return NULL;
 
 
     // FUNCTION case
     // FUNCTION case
@@ -1445,6 +1483,12 @@ gravity_object_t *gravity_object_deserialize (gravity_vm *vm, json_value *entry)
         gravity_map_t *m = gravity_map_deserialize(vm, entry);
         gravity_map_t *m = gravity_map_deserialize(vm, entry);
         return (gravity_object_t *)m;
         return (gravity_object_t *)m;
     }
     }
+    
+    // RANGE case
+    if (string_casencmp(value->u.string.ptr, GRAVITY_JSON_RANGE, value->u.string.length) == 0) {
+        gravity_range_t *range = gravity_range_deserialize(vm, entry);
+        return (gravity_object_t *)range;
+    }
 
 
     // unhandled case
     // unhandled case
     DEBUG_DESERIALIZE("gravity_object_deserialize unknown type");
     DEBUG_DESERIALIZE("gravity_object_deserialize unknown type");
@@ -1682,6 +1726,26 @@ void gravity_instance_blacken (gravity_vm *vm, gravity_instance_t *i) {
     }
     }
 }
 }
 
 
+void gravity_instance_serialize (gravity_instance_t *instance, json_t *json) {
+    gravity_class_t *c = instance->objclass;
+    
+    const char *label = json_get_label(json, NULL);
+    json_begin_object(json, label);
+    
+    json_add_cstring(json, GRAVITY_JSON_LABELTYPE, GRAVITY_JSON_INSTANCE);
+    json_add_cstring(json, GRAVITY_JSON_CLASS, c->identifier);
+    
+    if (c->nivars) {
+        json_begin_array(json, GRAVITY_JSON_LABELIVAR);
+        for (uint32_t i=0; i<c->nivars; ++i) {
+            gravity_value_serialize(NULL, instance->ivars[i], json);
+        }
+        json_end_array(json);
+    }
+    
+    json_end_object(json);
+}
+
 // MARK: -
 // MARK: -
 static bool hash_value_compare_cb (gravity_value_t v1, gravity_value_t v2, void *data) {
 static bool hash_value_compare_cb (gravity_value_t v1, gravity_value_t v2, void *data) {
     #pragma unused (data)
     #pragma unused (data)
@@ -1793,75 +1857,62 @@ static void gravity_map_serialize_iterator (gravity_hash_t *hashtable, gravity_v
     json_t *json = (json_t *)data;
     json_t *json = (json_t *)data;
     const char *key_value = VALUE_AS_STRING(key)->s;
     const char *key_value = VALUE_AS_STRING(key)->s;
 
 
-    // BOOL
-    if (VALUE_ISA_BOOL(v)) {
-        json_add_bool(json, key_value, (v.n == 0) ? false : true);
-        return;
-    }
-
-    // INT
-    if (VALUE_ISA_INT(v)) {
-        json_add_int(json, key_value, (int64_t)v.n);
-        return;
-    }
-
-    // FLOAT
-    if (VALUE_ISA_FLOAT(v)) {
-        json_add_double(json, key_value, (double)v.f);
-        return;
-    }
-
-    // STRING
-    if (VALUE_ISA_STRING(v)) {
-        gravity_string_t *value = VALUE_AS_STRING(v);
-        json_add_string(json, key_value, value->s, value->len);
-        return;
-    }
-
-    // should never reach this point
-    assert(0);
+    gravity_value_serialize(key_value, v, json);
 }
 }
 
 
-void gravity_value_serialize (gravity_value_t v, json_t *json) {
+void gravity_value_serialize (const char *key, gravity_value_t v, json_t *json) {
     // NULL
     // NULL
     if (VALUE_ISA_NULL(v)) {
     if (VALUE_ISA_NULL(v)) {
-        json_add_null(json, NULL);
+        json_add_null(json, key);
         return;
         return;
     }
     }
     
     
     // UNDEFINED (convention used to represent an UNDEFINED value)
     // UNDEFINED (convention used to represent an UNDEFINED value)
     if (VALUE_ISA_UNDEFINED(v)) {
     if (VALUE_ISA_UNDEFINED(v)) {
-        json_begin_object(json, NULL);
-        json_end_object(json);
+        if (json_option_isset(json, json_opt_no_undef)) {
+            json_add_null(json, key);
+        } else {
+            json_begin_object(json, key);
+            json_end_object(json);
+        }
         return;
         return;
     }
     }
 
 
     // BOOL
     // BOOL
     if (VALUE_ISA_BOOL(v)) {
     if (VALUE_ISA_BOOL(v)) {
-        json_add_bool(json, NULL, (v.n == 0) ? false : true);
+        json_add_bool(json, key, (v.n == 0) ? false : true);
         return;
         return;
     }
     }
 
 
     // INT
     // INT
     if (VALUE_ISA_INT(v)) {
     if (VALUE_ISA_INT(v)) {
-        json_add_int(json, NULL, (int64_t)v.n);
+        json_add_int(json, key, (int64_t)v.n);
         return;
         return;
     }
     }
 
 
     // FLOAT
     // FLOAT
     if (VALUE_ISA_FLOAT(v)) {
     if (VALUE_ISA_FLOAT(v)) {
-        json_add_double(json, NULL, (double)v.f);
+        json_add_double(json, key, (double)v.f);
         return;
         return;
     }
     }
 
 
     // FUNCTION
     // FUNCTION
     if (VALUE_ISA_FUNCTION(v)) {
     if (VALUE_ISA_FUNCTION(v)) {
+        json_set_label(json, key);
         gravity_function_serialize(VALUE_AS_FUNCTION(v), json);
         gravity_function_serialize(VALUE_AS_FUNCTION(v), json);
         return;
         return;
     }
     }
+    
+    // CLOSURE
+    if (VALUE_ISA_CLOSURE(v)) {
+        json_set_label(json, key);
+        gravity_function_serialize(VALUE_AS_CLOSURE(v)->f, json);
+        return;
+    }
 
 
     // CLASS
     // CLASS
     if (VALUE_ISA_CLASS(v)) {
     if (VALUE_ISA_CLASS(v)) {
+        json_set_label(json, key);
         gravity_class_serialize(VALUE_AS_CLASS(v), json);
         gravity_class_serialize(VALUE_AS_CLASS(v), json);
         return;
         return;
     }
     }
@@ -1869,19 +1920,19 @@ void gravity_value_serialize (gravity_value_t v, json_t *json) {
     // STRING
     // STRING
     if (VALUE_ISA_STRING(v)) {
     if (VALUE_ISA_STRING(v)) {
         gravity_string_t *value = VALUE_AS_STRING(v);
         gravity_string_t *value = VALUE_AS_STRING(v);
-        json_add_string(json, NULL, value->s, value->len);
+        json_add_string(json, key, value->s, value->len);
         return;
         return;
     }
     }
 
 
     // LIST (ARRAY)
     // LIST (ARRAY)
     if (VALUE_ISA_LIST(v)) {
     if (VALUE_ISA_LIST(v)) {
         gravity_list_t *value = VALUE_AS_LIST(v);
         gravity_list_t *value = VALUE_AS_LIST(v);
-        json_begin_array(json, NULL);
+        json_begin_array(json, key);
         size_t count = marray_size(value->array);
         size_t count = marray_size(value->array);
         for (size_t j=0; j<count; j++) {
         for (size_t j=0; j<count; j++) {
             gravity_value_t item = marray_get(value->array, j);
             gravity_value_t item = marray_get(value->array, j);
             // here I am sure that value is a literal value
             // here I am sure that value is a literal value
-            gravity_value_serialize(item, json);
+            gravity_value_serialize(NULL, item, json);
         }
         }
         json_end_array(json);
         json_end_array(json);
         return;
         return;
@@ -1891,13 +1942,32 @@ void gravity_value_serialize (gravity_value_t v, json_t *json) {
     // a map is serialized only if it contains only literals, otherwise it is computed at runtime
     // a map is serialized only if it contains only literals, otherwise it is computed at runtime
     if (VALUE_ISA_MAP(v)) {
     if (VALUE_ISA_MAP(v)) {
         gravity_map_t *value = VALUE_AS_MAP(v);
         gravity_map_t *value = VALUE_AS_MAP(v);
-        json_begin_object(json, NULL);
-        json_add_cstring(json, GRAVITY_JSON_LABELTYPE, GRAVITY_JSON_MAP);
+        json_begin_object(json, key);
+        if (!json_option_isset(json, json_opt_no_maptype)) json_add_cstring(json, GRAVITY_JSON_LABELTYPE, GRAVITY_JSON_MAP);
         gravity_hash_iterate(value->hash, gravity_map_serialize_iterator, json);
         gravity_hash_iterate(value->hash, gravity_map_serialize_iterator, json);
         json_end_object(json);
         json_end_object(json);
         return;
         return;
     }
     }
+    
+    // RANGE
+    if (VALUE_ISA_RANGE(v)) {
+        json_set_label(json, key);
+        gravity_range_serialize(VALUE_AS_RANGE(v), json);
+        return;
+    }
 
 
+    // INSTANCE
+    if (VALUE_ISA_INSTANCE(v)) {
+        json_set_label(json, key);
+        gravity_instance_serialize(VALUE_AS_INSTANCE(v), json);
+        return;
+    }
+    
+    // FIBER
+    if (VALUE_ISA_FIBER(v)) {
+        return;
+    }
+    
     // should never reach this point
     // should never reach this point
     assert(0);
     assert(0);
 }
 }
@@ -2145,19 +2215,34 @@ static gravity_map_t *gravity_map_deserialize (gravity_vm *vm, json_value *json)
 
 
     DEBUG_DESERIALIZE("DESERIALIZE MAP: %p\n", map);
     DEBUG_DESERIALIZE("DESERIALIZE MAP: %p\n", map);
 
 
-    for (uint32_t i=1; i<n; ++i) { // from 1 to skip type
+    for (uint32_t i=0; i<n; ++i) { // from 1 to skip type
         const char *label = json->u.object.values[i].name;
         const char *label = json->u.object.values[i].name;
         json_value *jsonv = json->u.object.values[i].value;
         json_value *jsonv = json->u.object.values[i].value;
 
 
-        gravity_value_t    key = VALUE_FROM_CSTRING(vm, label);
-        gravity_value_t    value;
+        gravity_value_t key = VALUE_FROM_CSTRING(vm, label);
+        gravity_value_t value;
 
 
         switch (jsonv->type) {
         switch (jsonv->type) {
-            case json_integer: value = VALUE_FROM_INT((gravity_int_t)jsonv->u.integer); break;
-            case json_double: value = VALUE_FROM_FLOAT((gravity_float_t)jsonv->u.dbl); break;
-            case json_boolean: value = VALUE_FROM_BOOL(jsonv->u.boolean); break;
-            case json_string: value = VALUE_FROM_STRING(vm, jsonv->u.string.ptr, jsonv->u.string.length); break;
-            default: goto abort_load;
+            case json_integer:
+                value = VALUE_FROM_INT((gravity_int_t)jsonv->u.integer); break;
+            case json_double:
+                value = VALUE_FROM_FLOAT((gravity_float_t)jsonv->u.dbl); break;
+            case json_boolean:
+                value = VALUE_FROM_BOOL(jsonv->u.boolean); break;
+            case json_string:
+                value = VALUE_FROM_STRING(vm, jsonv->u.string.ptr, jsonv->u.string.length); break;
+            case json_null:
+                value = VALUE_FROM_NULL; break;
+            case json_object: {
+                gravity_object_t *obj = gravity_object_deserialize(vm, jsonv);
+                value = (obj) ? VALUE_FROM_OBJECT(obj) : VALUE_FROM_NULL;
+                break;
+            }
+            case json_array: {
+                
+            }
+            default:
+                goto abort_load;
         }
         }
 
 
         gravity_map_insert(NULL, map, key, value);
         gravity_map_insert(NULL, map, key, value);
@@ -2216,6 +2301,46 @@ uint32_t gravity_range_size (gravity_vm *vm, gravity_range_t *range) {
     return range_size;
     return range_size;
 }
 }
 
 
+void gravity_range_serialize (gravity_range_t *r, json_t *json) {
+    const char *label = json_get_label(json, NULL);
+    json_begin_object(json, label);
+    json_add_cstring(json, GRAVITY_JSON_LABELTYPE, GRAVITY_JSON_RANGE);                    // MANDATORY 1st FIELD
+    json_add_int(json, GRAVITY_JSON_LABELFROM, r->from);
+    json_add_int(json, GRAVITY_JSON_LABELTO, r->to);
+    json_end_object(json);
+}
+
+gravity_range_t *gravity_range_deserialize (gravity_vm *vm, json_value *json) {
+    json_int_t from = 0;
+    json_int_t to = 0;
+    
+    uint32_t n = json->u.object.length;
+    for (uint32_t i=1; i<n; ++i) { // from 1 to skip type
+        const char *label = json->u.object.values[i].name;
+        json_value *value = json->u.object.values[i].value;
+        size_t label_size = strlen(label);
+        
+        // from
+        if (string_casencmp(label, GRAVITY_JSON_LABELFROM, label_size) == 0) {
+            if (value->type != json_integer) goto abort_load;
+            from = value->u.integer;
+            continue;
+        }
+        
+        // to
+        if (string_casencmp(label, GRAVITY_JSON_LABELTO, label_size) == 0) {
+            if (value->type != json_integer) goto abort_load;
+            to = value->u.integer;
+            continue;
+        }
+    }
+    
+    return gravity_range_new(vm, (gravity_int_t)from, (gravity_int_t)to, true);
+    
+abort_load:
+    return NULL;
+}
+
 void gravity_range_blacken (gravity_vm *vm, gravity_range_t *range) {
 void gravity_range_blacken (gravity_vm *vm, gravity_range_t *range) {
     gravity_vm_memupdate(vm, gravity_range_size(vm, range));
     gravity_vm_memupdate(vm, gravity_range_size(vm, range));
 }
 }

+ 11 - 4
src/shared/gravity_value.h

@@ -66,8 +66,8 @@
 extern "C" {
 extern "C" {
 #endif
 #endif
 
 
-#define GRAVITY_VERSION						"0.7.0"     // git tag 0.7.0
-#define GRAVITY_VERSION_NUMBER				0x000700    // git push --tags
+#define GRAVITY_VERSION						"0.7.4"     // git tag 0.7.4
+#define GRAVITY_VERSION_NUMBER				0x000704    // git push --tags
 #define GRAVITY_BUILD_DATE                  __DATE__
 #define GRAVITY_BUILD_DATE                  __DATE__
 
 
 #ifndef GRAVITY_ENABLE_DOUBLE
 #ifndef GRAVITY_ENABLE_DOUBLE
@@ -265,7 +265,7 @@ typedef struct {
             gravity_value_r pname;          // param names
             gravity_value_r pname;          // param names
             uint32_t        ninsts;         // number of instructions in the bytecode
             uint32_t        ninsts;         // number of instructions in the bytecode
             uint32_t        *bytecode;      // bytecode as array of 32bit values
             uint32_t        *bytecode;      // bytecode as array of 32bit values
-            uint32_t        *lineno;            // debug: line number <-> current instruction relation
+            uint32_t        *lineno;        // debug: line number <-> current instruction relation
             float           purity;         // experimental value
             float           purity;         // experimental value
             bool            useargs;        // flag set by the compiler to optimize the creation of the arguments array only if needed
             bool            useargs;        // flag set by the compiler to optimize the creation of the arguments array only if needed
         };
         };
@@ -297,6 +297,7 @@ typedef struct gravity_closure_s {
     gravity_function_t      *f;             // function prototype
     gravity_function_t      *f;             // function prototype
     gravity_object_t        *context;       // context where the closure has been created (or object bound by the user)
     gravity_object_t        *context;       // context where the closure has been created (or object bound by the user)
     gravity_upvalue_t       **upvalue;      // upvalue array
     gravity_upvalue_t       **upvalue;      // upvalue array
+    uint32_t                refcount;       // bridge language sometimes needs to protect closures from GC
 } gravity_closure_t;
 } gravity_closure_t;
 
 
 typedef struct {
 typedef struct {
@@ -433,6 +434,7 @@ GRAVITY_API gravity_value_t     gravity_function_cpool_get (gravity_function_t *
 GRAVITY_API void                gravity_function_dump (gravity_function_t *f, code_dump_function codef);
 GRAVITY_API void                gravity_function_dump (gravity_function_t *f, code_dump_function codef);
 GRAVITY_API void                gravity_function_setouter (gravity_function_t *f, gravity_object_t *outer);
 GRAVITY_API void                gravity_function_setouter (gravity_function_t *f, gravity_object_t *outer);
 GRAVITY_API void                gravity_function_setxdata (gravity_function_t *f, void *xdata);
 GRAVITY_API void                gravity_function_setxdata (gravity_function_t *f, void *xdata);
+GRAVITY_API gravity_list_t      *gravity_function_params_get (gravity_vm *vm, gravity_function_t *f);
 GRAVITY_API void                gravity_function_serialize (gravity_function_t *f, json_t *json);
 GRAVITY_API void                gravity_function_serialize (gravity_function_t *f, json_t *json);
 GRAVITY_API uint32_t            *gravity_bytecode_deserialize (const char *buffer, size_t len, uint32_t *ninst);
 GRAVITY_API uint32_t            *gravity_bytecode_deserialize (const char *buffer, size_t len, uint32_t *ninst);
 GRAVITY_API gravity_function_t  *gravity_function_deserialize (gravity_vm *vm, json_value *json);
 GRAVITY_API gravity_function_t  *gravity_function_deserialize (gravity_vm *vm, json_value *json);
@@ -444,6 +446,8 @@ GRAVITY_API uint32_t            gravity_function_size (gravity_vm *vm, gravity_f
 GRAVITY_API gravity_closure_t   *gravity_closure_new (gravity_vm *vm, gravity_function_t *f);
 GRAVITY_API gravity_closure_t   *gravity_closure_new (gravity_vm *vm, gravity_function_t *f);
 GRAVITY_API void                gravity_closure_free (gravity_vm *vm, gravity_closure_t *closure);
 GRAVITY_API void                gravity_closure_free (gravity_vm *vm, gravity_closure_t *closure);
 GRAVITY_API uint32_t            gravity_closure_size (gravity_vm *vm, gravity_closure_t *closure);
 GRAVITY_API uint32_t            gravity_closure_size (gravity_vm *vm, gravity_closure_t *closure);
+GRAVITY_API void                gravity_closure_inc_refcount (gravity_vm *vm, gravity_closure_t *closure);
+GRAVITY_API void                gravity_closure_dec_refcount (gravity_vm *vm, gravity_closure_t *closure);
 GRAVITY_API void                gravity_closure_blacken (gravity_vm *vm, gravity_closure_t *closure);
 GRAVITY_API void                gravity_closure_blacken (gravity_vm *vm, gravity_closure_t *closure);
 
 
 // MARK: - UPVALUE -
 // MARK: - UPVALUE -
@@ -495,6 +499,7 @@ GRAVITY_API void                gravity_instance_free (gravity_vm *vm, gravity_i
 GRAVITY_API gravity_closure_t   *gravity_instance_lookup_event (gravity_instance_t *i, const char *name);
 GRAVITY_API gravity_closure_t   *gravity_instance_lookup_event (gravity_instance_t *i, const char *name);
 GRAVITY_API void                gravity_instance_blacken (gravity_vm *vm, gravity_instance_t *i);
 GRAVITY_API void                gravity_instance_blacken (gravity_vm *vm, gravity_instance_t *i);
 GRAVITY_API uint32_t            gravity_instance_size (gravity_vm *vm, gravity_instance_t *i);
 GRAVITY_API uint32_t            gravity_instance_size (gravity_vm *vm, gravity_instance_t *i);
+GRAVITY_API void                gravity_instance_serialize (gravity_instance_t *i, json_t *json);
 
 
 // MARK: - VALUE -
 // MARK: - VALUE -
 GRAVITY_API bool                gravity_value_equals (gravity_value_t v1, gravity_value_t v2);
 GRAVITY_API bool                gravity_value_equals (gravity_value_t v1, gravity_value_t v2);
@@ -503,7 +508,7 @@ GRAVITY_API uint32_t            gravity_value_hash (gravity_value_t value);
 GRAVITY_API gravity_class_t     *gravity_value_getclass (gravity_value_t v);
 GRAVITY_API gravity_class_t     *gravity_value_getclass (gravity_value_t v);
 GRAVITY_API gravity_class_t     *gravity_value_getsuper (gravity_value_t v);
 GRAVITY_API gravity_class_t     *gravity_value_getsuper (gravity_value_t v);
 GRAVITY_API void                gravity_value_free (gravity_vm *vm, gravity_value_t v);
 GRAVITY_API void                gravity_value_free (gravity_vm *vm, gravity_value_t v);
-GRAVITY_API void                gravity_value_serialize (gravity_value_t v, json_t *json);
+GRAVITY_API void                gravity_value_serialize (const char *key, gravity_value_t v, json_t *json);
 GRAVITY_API void                gravity_value_dump (gravity_vm *vm, gravity_value_t v, char *buffer, uint16_t len);
 GRAVITY_API void                gravity_value_dump (gravity_vm *vm, gravity_value_t v, char *buffer, uint16_t len);
 GRAVITY_API bool                gravity_value_isobject (gravity_value_t v);
 GRAVITY_API bool                gravity_value_isobject (gravity_value_t v);
 GRAVITY_API void                *gravity_value_xdata (gravity_value_t value);
 GRAVITY_API void                *gravity_value_xdata (gravity_value_t value);
@@ -540,6 +545,8 @@ GRAVITY_API gravity_range_t     *gravity_range_new (gravity_vm *vm, gravity_int_
 GRAVITY_API void                gravity_range_free (gravity_vm *vm, gravity_range_t *range);
 GRAVITY_API void                gravity_range_free (gravity_vm *vm, gravity_range_t *range);
 GRAVITY_API void                gravity_range_blacken (gravity_vm *vm, gravity_range_t *range);
 GRAVITY_API void                gravity_range_blacken (gravity_vm *vm, gravity_range_t *range);
 GRAVITY_API uint32_t            gravity_range_size (gravity_vm *vm, gravity_range_t *range);
 GRAVITY_API uint32_t            gravity_range_size (gravity_vm *vm, gravity_range_t *range);
+GRAVITY_API void                gravity_range_serialize (gravity_range_t *r, json_t *json);
+GRAVITY_API gravity_range_t     *gravity_range_deserialize (gravity_vm *vm, json_value *json);
 
 
 /// MARK: - STRING -
 /// MARK: - STRING -
 GRAVITY_API gravity_value_t     gravity_string_to_value (gravity_vm *vm, const char *s, uint32_t len);
 GRAVITY_API gravity_value_t     gravity_string_to_value (gravity_vm *vm, const char *s, uint32_t len);

+ 142 - 90
src/utils/gravity_json.c

@@ -30,6 +30,7 @@
 #include "gravity_json.h"
 #include "gravity_json.h"
 #include "gravity_utils.h"
 #include "gravity_utils.h"
 #include "gravity_memory.h"
 #include "gravity_memory.h"
+#include "gravity_array.h"
 
 
 #include <inttypes.h>
 #include <inttypes.h>
 #include <math.h>
 #include <math.h>
@@ -49,37 +50,41 @@
 // MARK: - JSON Serializer -
 // MARK: - JSON Serializer -
 // Written by Marco Bambini
 // Written by Marco Bambini
 
 
-#define JSON_MINSIZE        4096
-#define JSON_NEWLINE        "\n"
-#define JSON_NEWLINE_CHAR    '\n'
-#define JSON_PRETTYLINE        "    "
-#define JSON_PRETTYSIZE        4
-#define JSON_WRITE_SEP        json_write_raw(json, " : ", 3, false, false)
-#define JSON_TERM_FIELD        json_write_raw(json, ",", 1, false, false); json_write_raw(json, JSON_NEWLINE, 1, false, false)
-#define JSON_MAX_NESTED        1024
-#define JSON_POP_CTX(j)        --j->ctxidx
-#define JSON_PUSH_CTX(j,x)    if(j->ctxidx<JSON_MAX_NESTED-1) j->ctx[j->ctxidx++] = x
-#define JSON_CURR_XTX(j)    j->ctx[j->ctxidx-1]
-#define JSON_ESCAPE(c)        do {        \
-                                new_buffer[j] = '\\';        \
-                                new_buffer[j+1] = (c);        \
-                                j+=2;                        \
-                            } while(0);                        \
+#define JSON_MINSIZE            4096
+#define JSON_NEWLINE            "\n"
+#define JSON_NEWLINE_CHAR       '\n'
+#define JSON_PRETTYLINE         "    "
+#define JSON_PRETTYSIZE         4
+#define JSON_WRITE_COLUMN       json_write_raw(json, ":", 1, false, false)
+#define JSON_WRITE_COMMA        json_write_raw(json, ",", 1, false, false);
 
 
+#define JSON_POP_CTX(j)         marray_pop(j->context)
+#define JSON_PUSH_CTX(j,x)      marray_push(JSON_CONTEXT, j->context, x);
+#define JSON_CURR_CTX(j)        marray_last(j->context)
+
+#define JSON_ESCAPE(c)          do {        \
+                                    new_buffer[j] = '\\';     \
+                                    new_buffer[j+1] = (c);    \
+                                    j+=2;                     \
+                                } while(0);                   \
 
 
 typedef enum {
 typedef enum {
-    json_ctx_object,
-    json_ctx_array
-} json_ctx_t;
+    JSON_CONTEXT_ROOT = 0,
+    JSON_CONTEXT_OBJECT = 1,
+    JSON_CONTEXT_ARRAY = 2
+} JSON_CONTEXT;
+typedef marray_t(JSON_CONTEXT)    JSON_CONTEXT_R;
 
 
-struct json_t {
-    char        *buffer;
-    size_t        blen;
-    size_t        bused;
-    uint32_t    ident;
+// MARK: -
 
 
-    json_ctx_t    ctx[JSON_MAX_NESTED];
-    size_t        ctxidx;
+struct json_t {
+    char            *buffer;
+    size_t          blen;
+    size_t          bused;
+    
+    const char      *label;
+    uint32_t        options;
+    JSON_CONTEXT_R  context;
 };
 };
 
 
 json_t *json_new (void) {
 json_t *json_new (void) {
@@ -88,17 +93,34 @@ json_t *json_new (void) {
 
 
     json->buffer = mem_alloc(NULL, JSON_MINSIZE);
     json->buffer = mem_alloc(NULL, JSON_MINSIZE);
     assert(json->buffer);
     assert(json->buffer);
-
+    
     json->blen = JSON_MINSIZE;
     json->blen = JSON_MINSIZE;
     json->bused = 0;
     json->bused = 0;
-    json->ident = 0;
-    json->ctxidx = 0;
-
+    json->options = json_opt_none;
+    marray_init(json->context);
+    marray_push(JSON_CONTEXT, json->context, JSON_CONTEXT_ROOT);
+    
     return json;
     return json;
 }
 }
 
 
+void json_free (json_t *json) {
+    JSON_CONTEXT context = marray_pop(json->context);
+    if (context != JSON_CONTEXT_ROOT) assert(0);
+    
+    marray_destroy(json->context);
+    mem_free(json->buffer);
+    mem_free(json);
+}
+
+// MARK: - Private
+
 static void json_write_raw (json_t *json, const char *buffer, size_t len, bool escape, bool is_pretty) {
 static void json_write_raw (json_t *json, const char *buffer, size_t len, bool escape, bool is_pretty) {
-    size_t    prettylen = (is_pretty) ? (json->ident * JSON_PRETTYSIZE)+1 : 0;
+    // pretty output disabled in this version
+    is_pretty = false;
+    
+    bool      pretty_mask = json_option_isset(json, json_opt_prettify);
+    uint32_t  ident_count = ((uint32_t) marray_size(json->context)) - 1;
+    size_t    prettylen = (is_pretty && pretty_mask) ? (ident_count * JSON_PRETTYSIZE) : 0;
     size_t    escapelen = (escape) ? 2 : 0;
     size_t    escapelen = (escape) ? 2 : 0;
 
 
     // check buffer reallocation
     // check buffer reallocation
@@ -109,8 +131,8 @@ static void json_write_raw (json_t *json, const char *buffer, size_t len, bool e
         json->blen += reqlen;
         json->blen += reqlen;
     }
     }
 
 
-    if (is_pretty) {
-        for (uint32_t i=0; i<json->ident; ++i) {
+    if (is_pretty && pretty_mask) {
+        for (uint32_t i=0; i<ident_count; ++i) {
             memcpy(json->buffer+json->bused, JSON_PRETTYLINE, JSON_PRETTYSIZE);
             memcpy(json->buffer+json->bused, JSON_PRETTYLINE, JSON_PRETTYSIZE);
             json->bused += JSON_PRETTYSIZE;
             json->bused += JSON_PRETTYSIZE;
         }
         }
@@ -136,14 +158,14 @@ static void json_write_escaped (json_t *json, const char *buffer, size_t len, bo
         return;
         return;
     }
     }
 
 
-    char    *new_buffer = mem_alloc(NULL, len*2);
-    size_t    j = 0;
+    char *new_buffer = mem_alloc(NULL, len*2);
+    size_t j = 0;
     assert(new_buffer);
     assert(new_buffer);
 
 
     for (size_t i=0; i<len; ++i) {
     for (size_t i=0; i<len; ++i) {
         char c = buffer[i];
         char c = buffer[i];
         switch (c) {
         switch (c) {
-            case '"' : JSON_ESCAPE ('\"');    continue;
+            case '"' : JSON_ESCAPE ('\"');  continue;
             case '\\': JSON_ESCAPE ('\\');  continue;
             case '\\': JSON_ESCAPE ('\\');  continue;
             case '\b': JSON_ESCAPE ('b');   continue;
             case '\b': JSON_ESCAPE ('b');   continue;
             case '\f': JSON_ESCAPE ('f');   continue;
             case '\f': JSON_ESCAPE ('f');   continue;
@@ -159,75 +181,71 @@ static void json_write_escaped (json_t *json, const char *buffer, size_t len, bo
     mem_free(new_buffer);
     mem_free(new_buffer);
 }
 }
 
 
+static void json_check_comma (json_t *json) {
+    if (json_option_isset(json, json_opt_need_comma)) {
+        JSON_WRITE_COMMA;
+    } else {
+        json_set_option(json, json_opt_need_comma);
+    }
+}
+
+// MARK: - Public
 
 
 void json_begin_object (json_t *json, const char *key) {
 void json_begin_object (json_t *json, const char *key) {
+    json_check_comma(json);
+    
     // ignore given key if not inside an object
     // ignore given key if not inside an object
-    if (JSON_CURR_XTX(json) != json_ctx_object) {
+    if (JSON_CURR_CTX(json) != JSON_CONTEXT_OBJECT) {
         key = NULL;
         key = NULL;
     }
     }
 
 
     if (key) {
     if (key) {
         json_write_raw (json, key, strlen(key), true, true);
         json_write_raw (json, key, strlen(key), true, true);
-        JSON_WRITE_SEP;
+        JSON_WRITE_COLUMN;
     }
     }
-
-    JSON_PUSH_CTX(json, json_ctx_object);
+    
+    JSON_PUSH_CTX(json, JSON_CONTEXT_OBJECT);
     json_write_raw(json, "{", 1, false, (key == NULL));
     json_write_raw(json, "{", 1, false, (key == NULL));
-    json_write_raw(json, JSON_NEWLINE, 1, false, false);
-
-    ++json->ident;
+    
+    json_clear_option(json, json_opt_need_comma);
 }
 }
 
 
 void json_end_object (json_t *json) {
 void json_end_object (json_t *json) {
-    --json->ident;
     JSON_POP_CTX(json);
     JSON_POP_CTX(json);
-
-    // check latest 2 characters
-    if ((json->buffer[json->bused-1] == JSON_NEWLINE_CHAR) && (json->buffer[json->bused-2] == ',')) {
-        json->buffer[json->bused-2] = JSON_NEWLINE_CHAR;
-        json->buffer[json->bused-1] = 0;
-        --json->bused;
-    }
-
+    
+    json_set_option(json, json_opt_need_comma);
     json_write_raw(json, "}", 1, false, true);
     json_write_raw(json, "}", 1, false, true);
-    if (json->ident) {JSON_TERM_FIELD;}
-    else json_write_raw(json, JSON_NEWLINE, 1, false, false);
 }
 }
 
 
 void json_begin_array (json_t *json, const char *key) {
 void json_begin_array (json_t *json, const char *key) {
+    json_check_comma(json);
+    
     // ignore given key if not inside an object
     // ignore given key if not inside an object
-    if (JSON_CURR_XTX(json) != json_ctx_object) {
+    if (JSON_CURR_CTX(json) != JSON_CONTEXT_OBJECT) {
         key = NULL;
         key = NULL;
     }
     }
-
+    
     if (key) {
     if (key) {
         json_write_raw (json, key, strlen(key), true, true);
         json_write_raw (json, key, strlen(key), true, true);
-        JSON_WRITE_SEP;
+        JSON_WRITE_COLUMN;
     }
     }
 
 
-    JSON_PUSH_CTX(json, json_ctx_array);
+    JSON_PUSH_CTX(json, JSON_CONTEXT_ARRAY);
     json_write_raw(json, "[", 1, false, (key == NULL));
     json_write_raw(json, "[", 1, false, (key == NULL));
-    json_write_raw(json, JSON_NEWLINE, 1, false, false);
-
-    ++json->ident;
+    
+    json_clear_option(json, json_opt_need_comma);
 }
 }
 
 
 void json_end_array (json_t *json) {
 void json_end_array (json_t *json) {
-    --json->ident;
     JSON_POP_CTX(json);
     JSON_POP_CTX(json);
-
-    // check latest 2 characters
-    if ((json->buffer[json->bused-1] == JSON_NEWLINE_CHAR) && (json->buffer[json->bused-2] == ',')) {
-        json->buffer[json->bused-2] = JSON_NEWLINE_CHAR;
-        json->buffer[json->bused-1] = 0;
-        --json->bused;
-    }
-
+    
+    json_set_option(json, json_opt_need_comma);
     json_write_raw(json, "]", 1, false, true);
     json_write_raw(json, "]", 1, false, true);
-    JSON_TERM_FIELD;
 }
 }
 
 
 void json_add_string (json_t *json, const char *key, const char *value, size_t len) {
 void json_add_string (json_t *json, const char *key, const char *value, size_t len) {
+    json_check_comma(json);
+    
     if (!value) {
     if (!value) {
         json_add_null(json, key);
         json_add_null(json, key);
         return;
         return;
@@ -235,7 +253,7 @@ void json_add_string (json_t *json, const char *key, const char *value, size_t l
 
 
     if (key) {
     if (key) {
         json_write_raw (json, key, strlen(key), true, true);
         json_write_raw (json, key, strlen(key), true, true);
-        JSON_WRITE_SEP;
+        JSON_WRITE_COLUMN;
     }
     }
 
 
     // check if string value needs to be escaped
     // check if string value needs to be escaped
@@ -243,14 +261,12 @@ void json_add_string (json_t *json, const char *key, const char *value, size_t l
     for (size_t i=0; i<len; ++i) {
     for (size_t i=0; i<len; ++i) {
         if (value[i] == '"') {write_escaped = true; break;}
         if (value[i] == '"') {write_escaped = true; break;}
     }
     }
-    if (len == 0)
-        write_escaped = true;
+    if (len == 0) write_escaped = true;
 
 
     if (write_escaped)
     if (write_escaped)
         json_write_escaped(json, value, len, true, (key == NULL));
         json_write_escaped(json, value, len, true, (key == NULL));
     else
     else
         json_write_raw(json, value, len, true, (key == NULL));
         json_write_raw(json, value, len, true, (key == NULL));
-    JSON_TERM_FIELD;
 }
 }
 
 
 void json_add_cstring (json_t *json, const char *key, const char *value) {
 void json_add_cstring (json_t *json, const char *key, const char *value) {
@@ -258,56 +274,78 @@ void json_add_cstring (json_t *json, const char *key, const char *value) {
 }
 }
 
 
 void json_add_int (json_t *json, const char *key, int64_t value) {
 void json_add_int (json_t *json, const char *key, int64_t value) {
+    json_check_comma(json);
+    
     char buffer[512];
     char buffer[512];
     size_t len = snprintf(buffer, sizeof(buffer), "%" PRId64, value);
     size_t len = snprintf(buffer, sizeof(buffer), "%" PRId64, value);
 
 
     if (key) {
     if (key) {
         json_write_raw (json, key, strlen(key), true, true);
         json_write_raw (json, key, strlen(key), true, true);
-        JSON_WRITE_SEP;
+        JSON_WRITE_COLUMN;
     }
     }
+    
     json_write_raw(json, buffer, len, false, (key == NULL));
     json_write_raw(json, buffer, len, false, (key == NULL));
-    JSON_TERM_FIELD;
-
 }
 }
 
 
 void json_add_double (json_t *json, const char *key, double value) {
 void json_add_double (json_t *json, const char *key, double value) {
+    json_check_comma(json);
+    
     char buffer[512];
     char buffer[512];
     size_t len = snprintf(buffer, sizeof(buffer), "%f", value);
     size_t len = snprintf(buffer, sizeof(buffer), "%f", value);
 
 
     if (key) {
     if (key) {
         json_write_raw (json, key, strlen(key), true, true);
         json_write_raw (json, key, strlen(key), true, true);
-        JSON_WRITE_SEP;
+        JSON_WRITE_COLUMN;
     }
     }
+    
     json_write_raw(json, buffer, len, false, (key == NULL));
     json_write_raw(json, buffer, len, false, (key == NULL));
-    JSON_TERM_FIELD;
 }
 }
 
 
 void json_add_bool (json_t *json, const char *key, bool bvalue) {
 void json_add_bool (json_t *json, const char *key, bool bvalue) {
+    json_check_comma(json);
+    
     const char *value = (bvalue) ? "true" : "false";
     const char *value = (bvalue) ? "true" : "false";
 
 
     if (key) {
     if (key) {
         json_write_raw (json, key, strlen(key), true, true);
         json_write_raw (json, key, strlen(key), true, true);
-        JSON_WRITE_SEP;
+        JSON_WRITE_COLUMN;
     }
     }
+    
     json_write_raw(json, value, strlen(value), false, (key == NULL));
     json_write_raw(json, value, strlen(value), false, (key == NULL));
-    JSON_TERM_FIELD;
 }
 }
 
 
 void json_add_null (json_t *json, const char *key) {
 void json_add_null (json_t *json, const char *key) {
+    json_check_comma (json);
+    
     if (key) {
     if (key) {
         json_write_raw (json, key, strlen(key), true, true);
         json_write_raw (json, key, strlen(key), true, true);
-        JSON_WRITE_SEP;
+        JSON_WRITE_COLUMN;
     }
     }
+    
     json_write_raw(json, "null", 4, false, (key == NULL));
     json_write_raw(json, "null", 4, false, (key == NULL));
-    JSON_TERM_FIELD;
 }
 }
 
 
-void json_free (json_t *json) {
-    mem_free(json->buffer);
-    mem_free(json);
+void json_set_label (json_t *json, const char *key) {
+    if (JSON_CURR_CTX(json) != JSON_CONTEXT_OBJECT) return;
+    json->label = key;
 }
 }
 
 
-const char *json_buffer (json_t *json, size_t *len) {
+const char *json_get_label (json_t *json, const char *key) {
+    if (JSON_CURR_CTX(json) != JSON_CONTEXT_OBJECT) return NULL;
+    
+    if (json->label) {
+        const char *result = json->label;
+        json->label = NULL;
+        return result;
+    }
+    
+    if (key) return key;
+    assert(0);
+}
+
+// MARK: - Buffer
+
+char *json_buffer (json_t *json, size_t *len) {
     assert(json->buffer);
     assert(json->buffer);
     if (len) *len = json->bused;
     if (len) *len = json->bused;
     return json->buffer;
     return json->buffer;
@@ -317,8 +355,22 @@ bool json_write_file (json_t *json, const char *path) {
     return file_write(path, json->buffer, json->bused);
     return file_write(path, json->buffer, json->bused);
 }
 }
 
 
-void json_pop (json_t *json, uint32_t n) {
-    json->bused -= n;
+// MARK: - Options
+
+uint32_t json_get_options (json_t *json) {
+    return json->options;
+}
+
+void json_set_option (json_t *json, json_opt_mask option_value) {
+    json->options |= option_value;
+}
+
+bool json_option_isset (json_t *json, json_opt_mask option_value) {
+    return (json->options & option_value);
+}
+
+void json_clear_option (json_t *json, json_opt_mask option_value) {
+    json->options &= ~option_value;
 }
 }
 
 
 #undef JSON_MINSIZE
 #undef JSON_MINSIZE

+ 25 - 3
src/utils/gravity_json.h

@@ -7,8 +7,24 @@
 
 
 // MARK: JSON serializer -
 // MARK: JSON serializer -
 
 
+typedef enum
+{
+    json_opt_none           =   0x00,
+    json_opt_need_comma     =   0x01,
+    json_opt_prettify       =   0x02,
+    json_opt_no_maptype     =   0x04,
+    json_opt_no_undef       =   0x08,
+    json_opt_unused_1       =   0x10,
+    json_opt_unused_2       =   0x20,
+    json_opt_unused_3       =   0x40,
+    json_opt_unused_4       =   0x80,
+    json_opt_unused_5       =   0x100,
+} json_opt_mask;
+
 typedef struct json_t    json_t;
 typedef struct json_t    json_t;
 json_t      *json_new (void);
 json_t      *json_new (void);
+void        json_free (json_t *json);
+
 void        json_begin_object (json_t *json, const char *key);
 void        json_begin_object (json_t *json, const char *key);
 void        json_end_object (json_t *json);
 void        json_end_object (json_t *json);
 void        json_begin_array (json_t *json, const char *key);
 void        json_begin_array (json_t *json, const char *key);
@@ -19,10 +35,16 @@ void        json_add_int (json_t *json, const char *key, int64_t value);
 void        json_add_double (json_t *json, const char *key, double value);
 void        json_add_double (json_t *json, const char *key, double value);
 void        json_add_bool (json_t *json, const char *key, bool value);
 void        json_add_bool (json_t *json, const char *key, bool value);
 void        json_add_null (json_t *json, const char *key);
 void        json_add_null (json_t *json, const char *key);
-void        json_free (json_t *json);
-const char  *json_buffer (json_t *json, size_t *len);
+void        json_set_label (json_t *json, const char *key);
+const char  *json_get_label (json_t *json, const char *key);
+
+char        *json_buffer (json_t *json, size_t *len);
 bool        json_write_file (json_t *json, const char *path);
 bool        json_write_file (json_t *json, const char *path);
-void        json_pop (json_t *json, uint32_t n);
+
+uint32_t    json_get_options (json_t *json);
+void        json_set_option (json_t *json, json_opt_mask option_value);
+void        json_clear_option (json_t *json, json_opt_mask option_value);
+bool        json_option_isset (json_t *json, json_opt_mask option_value);
 
 
 #endif
 #endif
 
 

+ 3 - 0
test/disabled/README.txt

@@ -0,0 +1,3 @@
+- loop1.gravity has been disabled because it is not possible to detect such infinite loop. For more information https://www.quora.com/Is-it-possible-to-detect-and-stop-an-infinite-loop-when-writing-a-program
+
+- heap.gravity needs further investigation but I suspect it cannot be detected too

+ 0 - 0
test/fuzzy/heap.gravity → test/disabled/heap.gravity


+ 0 - 0
test/infiniteloop/loop1.gravity → test/disabled/loop1.gravity


+ 29 - 0
test/unittest/init_optionals.gravity

@@ -0,0 +1,29 @@
+#unittest {
+    name: "Optional arguments in constructor";
+    result: 22200;
+};
+
+class Foo {
+    var v1;
+    var v2;
+
+    func init (p1, p2) {
+        if (p1) v1 = p1;
+        else v1 = 100;
+        
+        if (p2) v2 = p2;
+        else v2 = 200
+    }
+    
+    func tot () {
+        return v1 * v2;
+    }
+}
+
+func main() {
+    var f1 = Foo();         // 100 * 200 = 20000
+    var f2 = Foo(10);       // 10  * 200 =  2000
+    var f3 = Foo(10, 20);   // 10  *  20 =   200
+    
+    return f1.tot() + f2.tot() + f3.tot();
+}

+ 12 - 0
test/unittest/negative_enum.gravity

@@ -0,0 +1,12 @@
+#unittest {
+    name: "Negative enum";
+    result: -12.0;
+};
+
+enum test {
+    one = -12.0
+}
+
+func main() {
+    return test.one;
+}