Browse Source

Merge branch 'master' of github.com:marcobambini/gravity

Brandon Ray 8 years ago
parent
commit
578dba6e01

+ 23 - 3
docs/types.html

@@ -101,11 +101,31 @@
 			<h4 class="section-h4">String</h4>
 			<p>Strings are an immutable sequence of characters. String literals can be surrounded in double or single quotes.</p>
 			<pre><code class="swift">
-	var a = "Hello World";	// double quotes
-	var b = 'Hello World';	// single quotes
+	var a = "Hello World";  // double quotes
+	var b = 'Hello World';  // single quotes
 	
 	// Strings have also a length property
-	var n = b.length;	// n is now 11
+	var n = b.length;       // n is now 11
+
+	// Strings also have some built in methods
+	n = b.index("Wor")      // n is now 6.
+
+	n = b.count("l")        // n is now 3.
+	n = b.count("llo")      // n is now 1.
+
+	n = "A".string_repeat(10) // n is now "AAAAAAAAAA"
+
+	n = b.upper()           // n is now "HELLO WORLD"
+	n = b.lower()           // n is now "hello world"
+
+	// You are also able to edit strings by character...
+	b[0] = "Z"              // b is now "Zello World"
+	b[1] = "abc"            // b is now "Zabco World"
+	b[-7] = "QWERTY"        // b is now "ZabcQWERTYd"
+
+	// and retrieve those characters
+	n = a[6]                // n is now "W"
+	n = a[-7]               // n is now "o"
 			</code></pre>
 			
 			<h4 class="section-h4">Bool</h4>

+ 16 - 1
src/cli/unittest.c

@@ -24,6 +24,8 @@ typedef struct {
 	int32_t			expected_col;
 } test_data;
 
+static const char *test_folder_path;
+
 static void unittest_init (const char *target_file, test_data *data) {
 	#pragma unused(target_file)
 	++data->ncount;
@@ -78,7 +80,16 @@ static void callback_error (error_type_t error_type, const char *message, error_
 
 static const char *callback_read (const char *path, size_t *size, uint32_t *fileid, void *xdata) {
 	#pragma unused(fileid,xdata)
-	return file_read(path, size);
+	if (file_exists(path)) return file_read(path, size);
+	
+	// this unittest is able to resolve path only next to main test folder (not in nested folders)
+	const char *newpath = file_buildpath(path, test_folder_path);
+	if (!newpath) return NULL;
+	
+	const char *buffer = file_read(newpath, size);
+	mem_free(newpath);
+	
+	return buffer;
 }
 
 static void test_folder (const char *folder_path, test_data *data) {
@@ -104,6 +115,9 @@ static void test_folder (const char *folder_path, test_data *data) {
 			continue;
 		}
 		
+		// test only files with a .gravity extension
+		if (strstr(full_path, ".gravity") == NULL) continue;
+		
 		// load source code
 		size_t size = 0;
 		const char *source_code = file_read(full_path, &size);
@@ -179,6 +193,7 @@ int main (int argc, const char* argv[]) {
 	
 	mem_init();
 	nanotime_t tstart = nanotime();
+	test_folder_path = argv[1];
 	test_folder(argv[1], &data);
 	nanotime_t tend = nanotime();
 	

+ 115 - 68
src/compiler/gravity_parser.c

@@ -510,6 +510,30 @@ static gnode_t *parse_identifier_expression (gravity_parser_t *parser) {
 	return gnode_identifier_expr_create(token, identifier, NULL);
 }
 
+static gnode_t *parse_identifier_or_keyword_expression (gravity_parser_t *parser) {
+	DEBUG_PARSER("parse_identifier_expression");
+	DECLARE_LEXER;
+	
+	// check if token is a keyword
+	uint32_t idx_start, idx_end;
+	token_keywords_indexes(&idx_start, &idx_end);
+	
+	gtoken_t peek = gravity_lexer_peek(lexer);
+	if ((peek >= idx_start) && (peek <= idx_end)) {
+		
+		// consume token keyword
+		gtoken_t keyword = gravity_lexer_next(lexer);
+		gtoken_s token = gravity_lexer_token(lexer);
+		
+		// convert from keyword to identifier
+		const char	*identifier = string_dup(token_name(keyword));
+		return gnode_identifier_expr_create(token, identifier, NULL);
+	}
+	
+	// default case
+	return parse_identifier_expression(parser);
+}
+
 static gnode_t *parse_number_expression (gtoken_s token) {
 	DEBUG_PARSER("parse_number_expression");
 	
@@ -655,7 +679,10 @@ static gnode_t *parse_postfix_expression (gravity_parser_t *parser, gtoken_t tok
 			parse_required(parser, TOK_OP_CLOSED_PARENTHESIS);
 			node = gnode_postfix_subexpr_create(subtoken, NODE_CALL_EXPR, NULL, args);
 		} else if (tok == TOK_OP_DOT) {
-			gnode_t *expr = parse_identifier_expression(parser);
+			// was parse_identifier_expression but we need to allow also keywords here in order
+			// to be able to supports expressions like name.repeat (repeat is a keyword but in this
+			// context it should be interpreted as an identifier)
+			gnode_t *expr = parse_identifier_or_keyword_expression(parser);
 			gtoken_s subtoken = gravity_lexer_token(lexer);
 			node = gnode_postfix_subexpr_create(subtoken, NODE_ACCESS_EXPR, expr, NULL);
 		} else {
@@ -1444,7 +1471,7 @@ loop:
 	return params;
 }
 
-// MARK: - UnitTest -
+// MARK: - Macro -
 
 typedef enum {
 	UNITTEST_NONE,
@@ -1456,7 +1483,7 @@ typedef enum {
 	UNITTEST_NOTE
 } unittest_t;
 
-static unittest_t parse_unittest_identifier(const char *identifier) {
+static unittest_t parse_unittest_identifier (const char *identifier) {
 	if (string_cmp(identifier, "name") == 0) return UNITTEST_NAME;
 	if (string_cmp(identifier, "note") == 0) return UNITTEST_NOTE;
 	if (string_cmp(identifier, "error") == 0) return UNITTEST_ERROR;
@@ -1467,9 +1494,9 @@ static unittest_t parse_unittest_identifier(const char *identifier) {
 	return UNITTEST_NONE;
 }
 
-static gnode_t *parse_unittest_declaration(gravity_parser_t *parser) {
+static gnode_t *parse_unittest_macro (gravity_parser_t *parser) {
+	DEBUG_PARSER("parse_unittest_macro");
 	DECLARE_LEXER;
-	DEBUG_PARSER("parse_unittest_declaration");
 	
 	// @unittest {
 	//		name: "Unit test name";
@@ -1632,6 +1659,65 @@ handle_error:
 	return NULL;
 }
 
+static gnode_t *parse_include_macro (gravity_parser_t *parser) {
+	DEBUG_PARSER("parse_include_macro");
+	DECLARE_LEXER;
+	
+	// process filename (can be an identifier or a literal string)
+	// only literals are supported in this version
+	gtoken_t		type;
+	gtoken_s		token;
+	const char		*module_name;
+	gravity_lexer_t	*newlexer;
+	
+loop:
+	newlexer = NULL;
+	type = gravity_lexer_next(lexer);
+	token = gravity_lexer_token(lexer);
+	
+	// check if it is a string token
+	if (type != TOK_STRING) {
+		REPORT_ERROR(token, "Expected file name but found %s.", token_name(type));
+		return NULL;
+	}
+	
+	// check pre-requisites
+	if ((!parser->delegate) || (!parser->delegate->loadfile_callback)) {
+		REPORT_ERROR(gravity_lexer_token(lexer), "%s", "Unable to load file because no loadfile callback registered in delegate.");
+		return NULL;
+	}
+	
+	// parse string
+	module_name = cstring_from_token(parser, token);
+	size_t size = 0;
+	uint32_t fileid = 0;
+	
+	// module_name is a filename and it is used by lexer to store filename into tokens
+	// tokens are then stored inside AST nodes in order to locate errors into source code
+	// AST can live a lot longer than both lexer and parser so we need a way to persistent
+	// store these chuncks of memory
+	const char *source = parser->delegate->loadfile_callback(module_name, &size, &fileid, parser->delegate->xdata);
+	if (source) newlexer = gravity_lexer_create(source, size, fileid, false);
+	
+	if (newlexer) {
+		// push new lexer into lexer stack
+		marray_push(gravity_lexer_t*, *parser->lexer, newlexer);
+	} else {
+		REPORT_ERROR(token, "Unable to load file %s.", module_name);
+	}
+	
+	// check for optional comma
+	if (gravity_lexer_peek(lexer) == TOK_OP_COMMA) {
+		gravity_lexer_next(lexer); // consume TOK_OP_COMMA
+		goto loop;
+	}
+	
+	// parse semicolon
+	parse_semicolon(parser);
+	
+	return NULL;
+}
+
 // MARK: - Statements -
 
 static gnode_t *parse_label_statement (gravity_parser_t *parser) {
@@ -1872,70 +1958,20 @@ static gnode_t *parse_declaration_statement (gravity_parser_t *parser) {
 }
 
 static gnode_t *parse_import_statement (gravity_parser_t *parser) {
+	#pragma unused(parser)
 	DEBUG_PARSER("parse_import_statement");
 	
-	DECLARE_LEXER;
-
-	// parse import keyword
-	gravity_lexer_next(lexer);
-	
-	// process filename (can be an identifier or a literal string)
-	// identifier to import modules ?
-	// only literals are supported in this version
-	gtoken_t		type;
-	gtoken_s		token;
-	const char		*module_name;
-	gravity_lexer_t	*newlexer;
-	
-loop:
-	newlexer = NULL;
-	type = gravity_lexer_next(lexer);
-	token = gravity_lexer_token(lexer);
-	
-	// check if it is a string token
-	if (type != TOK_STRING) {
-		REPORT_ERROR(token, "Expected file name but found %s.", token_name(type));
-		return NULL;
-	}
-	
-	// check pre-requisites
-	if ((!parser->delegate) || (!parser->delegate->loadfile_callback)) {
-		REPORT_ERROR(gravity_lexer_token(lexer), "%s", "Unable to load file because no loadfile callback registered in delegate.");
-		return NULL;
-	}
-	
-	// parse string
-	module_name = cstring_from_token(parser, token);
-	size_t size = 0;
-	uint32_t fileid = 0;
-	
-	// module_name is a filename and it is used by lexer to store filename into tokens
-	// tokens are then stored inside AST nodes in order to locate errors into source code
-	// AST can live a lot longer than both lexer and parser so we need a way to persistent
-	// store these chuncks of memory
-	const char *source = parser->delegate->loadfile_callback(module_name, &size, &fileid, parser->delegate->xdata);
-	if (source) newlexer = gravity_lexer_create(source, size, fileid, false);
-
-	if (newlexer) {
-		// push new lexer into lexer stack
-		marray_push(gravity_lexer_t*, *parser->lexer, newlexer);
-	} else {
-		REPORT_ERROR(token, "Unable to load file %s.", module_name);
-	}
-	
-	// check for optional comma
-	if (gravity_lexer_peek(lexer) == TOK_OP_COMMA) {
-		gravity_lexer_next(lexer); // consume TOK_OP_COMMA
-		goto loop;
-	}
-
-	// parse semicolon
-	parse_semicolon(parser);
-	
+	// import is a syntactic sugar for System.import
 	return NULL;
 }
 
 static gnode_t *parse_macro_statement (gravity_parser_t *parser) {
+	typedef enum {
+		MACRO_UNKNOWN = 0,
+		MACRO_UNITEST = 1,
+		MACRO_INCLUDE = 2
+	} builtin_macro;
+	
 	DEBUG_PARSER("parse_macro_statement");
 	DECLARE_LEXER;
 	
@@ -1944,7 +1980,7 @@ static gnode_t *parse_macro_statement (gravity_parser_t *parser) {
 	assert(type == TOK_MACRO);
 	
 	// check for #! and interpret #! shebang bash as one line comment
-	// only if found on first line (https://github.com/marcobambini/gravity/issues/86)
+	// only if found on first line
 	if (gravity_lexer_peek(lexer) == TOK_OP_NOT && gravity_lexer_lineno(lexer) == 1) {
 		// consume special ! symbol
 		type = gravity_lexer_next(lexer);
@@ -1959,12 +1995,23 @@ static gnode_t *parse_macro_statement (gravity_parser_t *parser) {
 	const char *macroid = parse_identifier(parser);
 	if (macroid == NULL) goto handle_error;
 	
-	// check #unittest macro
-	bool is_unittest = (string_cmp(macroid, "unittest") == 0);
+	// check macro
+	builtin_macro macro_type = MACRO_UNKNOWN;
+	if (string_cmp(macroid, "unittest") == 0) macro_type = MACRO_UNITEST;
+	else if (string_cmp(macroid, "include") == 0) macro_type = MACRO_INCLUDE;
 	mem_free(macroid);
 	
-	if (is_unittest) {
-		return parse_unittest_declaration(parser);
+	switch (macro_type) {
+		case MACRO_UNITEST:
+			return parse_unittest_macro(parser);
+			break;
+			
+		case MACRO_INCLUDE:
+			return parse_include_macro(parser);
+			break;
+			
+		case MACRO_UNKNOWN:
+			break;
 	}
 	
 handle_error:

+ 1 - 2
src/compiler/gravity_token.h

@@ -10,7 +10,6 @@
 #define __GRAVITY_TOKEN__
 
 #include <stdint.h>
-
 #include "debug_macros.h"
 
 //	================
@@ -66,7 +65,7 @@ typedef enum {
 	TOK_EOF	= 0, TOK_ERROR, TOK_COMMENT, TOK_STRING, TOK_NUMBER, TOK_IDENTIFIER, TOK_SPECIAL, TOK_MACRO,
 	
 	// Keywords (36)
-	// remember to keep in sync functions token_count and token_value
+	// remember to keep in sync functions token_keywords_indexes and token_name
 	TOK_KEY_FUNC, TOK_KEY_SUPER, TOK_KEY_DEFAULT, TOK_KEY_TRUE, TOK_KEY_FALSE, TOK_KEY_IF,
 	TOK_KEY_ELSE, TOK_KEY_SWITCH, TOK_KEY_BREAK, TOK_KEY_CONTINUE, TOK_KEY_RETURN, TOK_KEY_WHILE,
 	TOK_KEY_REPEAT, TOK_KEY_FOR, TOK_KEY_IN, TOK_KEY_ENUM, TOK_KEY_CLASS, TOK_KEY_STRUCT, TOK_KEY_PRIVATE,

+ 174 - 6
src/runtime/gravity_core.c

@@ -1429,6 +1429,18 @@ static bool operator_string_neg (gravity_vm *vm, gravity_value_t *args, uint16_t
 	RETURN_VALUE(VALUE_FROM_OBJECT(string), rindex);
 }
 
+static bool operator_string_cmp (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+	#pragma unused(vm, nargs)
+	
+	DECLARE_2VARIABLES(v1, v2, 0, 1);
+	INTERNAL_CONVERT_STRING(v2);
+	
+	gravity_string_t *s1 = VALUE_AS_STRING(v1);
+	gravity_string_t *s2 = VALUE_AS_STRING(v2);
+	
+	RETURN_VALUE(VALUE_FROM_INT(strcmp(s1->s, s2->s)), rindex);
+}
+
 static bool string_length (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
 	#pragma unused(vm, nargs)
 	
@@ -1438,16 +1450,165 @@ static bool string_length (gravity_vm *vm, gravity_value_t *args, uint16_t nargs
 	RETURN_VALUE(VALUE_FROM_INT(s1->len), rindex);
 }
 
-static bool operator_string_cmp (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+static bool string_index (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
 	#pragma unused(vm, nargs)
+
+	if ((nargs != 2) || (!VALUE_ISA_STRING(GET_VALUE(1)))) {
+		RETURN_ERROR("String.index() expects a string as an argument");
+	}
 	
-	DECLARE_2VARIABLES(v1, v2, 0, 1);
-	INTERNAL_CONVERT_STRING(v2);
+	gravity_string_t *main_str = VALUE_AS_STRING(GET_VALUE(0));
+	gravity_string_t *str_to_index = VALUE_AS_STRING(GET_VALUE(1));
+
+	// search for the string
+	char *ptr = strstr(main_str->s, str_to_index->s);
+
+	// if it doesn't exist, return null
+	if (ptr == NULL) {
+		RETURN_VALUE(VALUE_FROM_NULL, rindex);
+	}
+	// otherwise, return the difference, which is the index that the string starts at
+	else {
+		RETURN_VALUE(VALUE_FROM_INT(ptr-main_str->s), rindex);
+	}
+}
+
+static bool string_count (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+	#pragma unused(vm, nargs)
+
+	if ((nargs != 2) || (!VALUE_ISA_STRING(GET_VALUE(1)))) {
+		RETURN_ERROR("String.count() expects a string as an argument");
+	}
 	
-	gravity_string_t *s1 = VALUE_AS_STRING(v1);
-	gravity_string_t *s2 = VALUE_AS_STRING(v2);
+	gravity_string_t *main_str = VALUE_AS_STRING(GET_VALUE(0));
+	gravity_string_t *str_to_count = VALUE_AS_STRING(GET_VALUE(1));
+
+	int j = 0;
+	int count = 0;
+
+	// iterate through whole string
+	for (int i = 0; i < main_str->len; i++) {
+		if (main_str->s[i] == str_to_count->s[j]) {
+			// if the characters match and we are on the last character of the search
+			// string, then we have found a match
+			if (j == str_to_count->len - 1) {
+				count++;
+				j = 0;
+				continue;
+			}
+		}
+		// reset if it isn't a match
+		else {
+			j = 0;
+			continue;
+		}
+		// move forward in the search string if we found a match but we aren't
+		// finished checking all the characters of the search string yet
+		j++;
+	}
+
+	RETURN_VALUE(VALUE_FROM_INT(count), rindex);
+}
+
+
+static bool string_repeat (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+	#pragma unused(vm, nargs)
 	
-	RETURN_VALUE(VALUE_FROM_INT(strcmp(s1->s, s2->s)), rindex);
+	if ((nargs != 2) || (!VALUE_ISA_INT(GET_VALUE(1)))) {
+		RETURN_ERROR("String.repeat() expects an integer argument");
+	}
+	
+	gravity_string_t *main_str = VALUE_AS_STRING(GET_VALUE(0));
+	gravity_int_t times_to_repeat = VALUE_AS_INT(GET_VALUE(1));
+	if (times_to_repeat < 1) {
+		RETURN_ERROR("String.repeat() expects an integer >= 1");
+	}
+
+	// figure out the size of the array we need to make to hold the new string
+	uint32_t new_size = (uint32_t)(main_str->len * times_to_repeat);
+	char new_str[new_size+1];
+	
+	// this code could be much faster with a memcpy
+	strcpy(new_str, main_str->s);
+	for (int i = 0; i < times_to_repeat-1; ++i) {
+		strcat(new_str, main_str->s);
+	}
+
+	RETURN_VALUE(VALUE_FROM_CSTRING(vm, new_str), rindex);
+}
+
+static bool string_upper (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+	#pragma unused(vm, nargs)
+
+	if (nargs != 1) {
+		RETURN_ERROR("String.upper() expects no argument");
+	}
+	
+	gravity_string_t *main_str = VALUE_AS_STRING(GET_VALUE(0));
+	for (int i = 0; i < main_str->len; i++) {
+		main_str->s[i] = toupper(main_str->s[i]);
+	}
+
+	RETURN_VALUE(VALUE_FROM_STRING(vm, main_str->s, main_str->len), rindex);
+}
+
+static bool string_lower (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+	#pragma unused(vm, nargs)
+
+	if (nargs != 1) {
+		RETURN_ERROR("String.lower() expects no argument");
+	}
+
+	gravity_string_t *main_str = VALUE_AS_STRING(GET_VALUE(0));
+	for (int i = 0; i < main_str->len; i++) {
+		main_str->s[i] = tolower(main_str->s[i]);
+	}
+
+	RETURN_VALUE(VALUE_FROM_STRING(vm, main_str->s, main_str->len), rindex);
+}
+
+static bool string_loadat (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));
+	gravity_value_t value = GET_VALUE(1);
+	if (!VALUE_ISA_INT(value)) RETURN_ERROR("An integer index is required to access a string item.");
+	
+	int32_t index = (int32_t)VALUE_AS_INT(value);
+	
+	if (index < 0) index = string->len + index;
+	if ((index < 0) || ((uint32_t)index >= string->len)) RETURN_ERROR("Out of bounds error: index %d beyond bounds 0...%d", index, string->len-1);
+
+	// this code is not UTF-8 safe
+	char c[2] = "\0";
+	c[0] = string->s[index];
+	
+	RETURN_VALUE(VALUE_FROM_STRING(vm, c, 1), rindex);
+}
+
+static bool string_storeat (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+	#pragma unused(vm, nargs, rindex)
+	
+	gravity_string_t *string = VALUE_AS_STRING(GET_VALUE(0));
+	gravity_value_t idxvalue = GET_VALUE(1);
+	if (!VALUE_ISA_INT(idxvalue)) RETURN_ERROR("An integer index is required to access a string item.");
+	if (!VALUE_ISA_STRING(GET_VALUE(2))) RETURN_ERROR("A string needs to be assigned to a string index");
+
+	gravity_string_t *value = VALUE_AS_STRING(GET_VALUE(2));
+	register int32_t index = (int32_t)VALUE_AS_INT(idxvalue);
+	
+	if (index < 0) index = string->len + index;
+	if (index < 0 || index >= string->len) RETURN_ERROR("Out of bounds error: index %d beyond bounds 0...%d", index, string->len-1);
+	if (index+value->len - 1 >= string->len) RETURN_ERROR("Out of bounds error: End of inserted string exceeds the length of the initial string");
+
+	// this code is not UTF-8 safe
+	for (int i = index; i < index+value->len; ++i) {
+		string->s[i] = value->s[i-index];
+	}
+	
+	// characters inside string changed so we need to re-compute hash
+	string->hash = gravity_hash_compute_buffer((const char *)string->s, string->len);
+	
+	RETURN_NOVALUE();
 }
 
 // MARK: - Fiber Class -
@@ -1840,7 +2001,14 @@ static void gravity_core_init (void) {
 	gravity_class_bind(gravity_class_string, GRAVITY_OPERATOR_OR_NAME,  NEW_CLOSURE_VALUE(operator_string_or));
 	gravity_class_bind(gravity_class_string, GRAVITY_OPERATOR_CMP_NAME, NEW_CLOSURE_VALUE(operator_string_cmp));
 	gravity_class_bind(gravity_class_string, GRAVITY_OPERATOR_NEG_NAME, NEW_CLOSURE_VALUE(operator_string_neg));
+	gravity_class_bind(gravity_class_string, GRAVITY_INTERNAL_LOADAT_NAME, NEW_CLOSURE_VALUE(string_loadat));
+	gravity_class_bind(gravity_class_string, GRAVITY_INTERNAL_STOREAT_NAME, NEW_CLOSURE_VALUE(string_storeat));
 	gravity_class_bind(gravity_class_string, "length", VALUE_FROM_OBJECT(computed_property(NULL, NEW_FUNCTION(string_length), NULL)));
+	gravity_class_bind(gravity_class_string, "index", NEW_CLOSURE_VALUE(string_index));
+	gravity_class_bind(gravity_class_string, "count", NEW_CLOSURE_VALUE(string_count));
+	gravity_class_bind(gravity_class_string, "repeat", NEW_CLOSURE_VALUE(string_repeat));
+	gravity_class_bind(gravity_class_string, "upper", NEW_CLOSURE_VALUE(string_upper));
+	gravity_class_bind(gravity_class_string, "lower", NEW_CLOSURE_VALUE(string_lower));
 	
 	// FIBER CLASS
 	gravity_class_t *fiber_meta = gravity_class_get_meta(gravity_class_fiber);

+ 17 - 0
test/include_test.gravity

@@ -0,0 +1,17 @@
+#unittest {
+	name: "Test #include macro.";
+	error: NONE;
+	result: 90;
+};
+
+#include "test.inc"
+
+func sum2 (a, b) {
+	return a + b;
+}
+
+func main() {
+	var n1 = sum2(10, 20);
+	var n2 = sum3(10, 20, 30);
+	return n1 + n2;
+}

+ 10 - 0
test/string/count_method.gravity

@@ -0,0 +1,10 @@
+#unittest {
+	name: "count() method for string";
+	error: NONE;
+	result: true;
+};
+
+func main () {
+	var s = "Hello World"
+	return s.count("l") == 3 and s.count("World") == 1 and s.count("xyz") == 0
+}

+ 13 - 0
test/string/index_method.gravity

@@ -0,0 +1,13 @@
+#unittest {
+	name: "index() method for string";
+	error: NONE;
+	result: true;
+};
+
+func main () {
+	var s = "Hello World"
+	if (s.index("H")==s.index("Hel") and s.index("H")==0 and s.index("d")==10) {
+		return true;
+	}
+	return false;
+}

+ 9 - 0
test/string/index_method_error.gravity

@@ -0,0 +1,9 @@
+#unittest {
+	name: "index() method for string - error";
+	error: RUNTIME;
+};
+
+func main () {
+	var s = "Hello World"
+	return s.index(2)
+}

+ 10 - 0
test/string/index_method_notfound.gravity

@@ -0,0 +1,10 @@
+#unittest {
+	name: "index() method for string - not found";
+	error: NONE;
+	result: true;
+};
+
+func main () {
+	var s = "Hello World"
+	return s.index("qwerty") == null
+}

+ 13 - 0
test/string/loadat.gravity

@@ -0,0 +1,13 @@
+#unittest {
+	name: "Loadat an index for string";
+	error: NONE;
+	result: true;
+};
+
+func main () {
+	var s = "Hello World"
+	if (s[6] == s[-5] and s[6] == "W") {
+		return true
+	}
+	return false
+}

+ 9 - 0
test/string/loadat_error.gravity

@@ -0,0 +1,9 @@
+#unittest {
+	name: "Loadat an index for string - error";
+	error: RUNTIME;
+};
+
+func main () {
+	var s = "Hello World"
+	return s[11]
+}

+ 12 - 0
test/string/storeat.gravity

@@ -0,0 +1,12 @@
+#unittest {
+	name: "storeat an index for string";
+	error: NONE;
+	result: "Hzllqwertyd";
+};
+
+func main () {
+	var s = "Hello World";
+	s[1] = "z";
+	s[-7] = "qwerty"
+	return s;
+}

+ 10 - 0
test/string/storeat_error.gravity

@@ -0,0 +1,10 @@
+#unittest {
+	name: "storeat an index for string - error";
+	error: RUNTIME;
+};
+
+func main () {
+	var s = "Hello World";
+	s[11] = "z";
+	return s;
+}

+ 12 - 0
test/string/string_repeat_method.gravity

@@ -0,0 +1,12 @@
+#unittest {
+	name: "string_repeat() method for string";
+	error: NONE;
+	result: true;
+};
+
+func main () {
+	var s = "Hello World"
+	var b = s.repeat(3) == "Hello WorldHello WorldHello World"
+	var c = s.repeat(1) == s
+	return b && c
+}

+ 9 - 0
test/string/string_repeat_method_error.gravity

@@ -0,0 +1,9 @@
+#unittest {
+	name: "string_repeat() method for string - error";
+	error: RUNTIME;
+};
+
+func main () {
+	var s = "Hello World"
+	return s.string_repeat(0)
+}

+ 12 - 0
test/string/upper_lower_method.gravity

@@ -0,0 +1,12 @@
+#unittest {
+	name: "upper() and lower methods for string";
+	error: NONE;
+	result: true;
+};
+
+func main () {
+	var s = "Hello World"
+	var u = s.upper() == "HELLO WORLD"
+	var l = s.lower() == "hello world"
+	return u && l
+}

+ 5 - 0
test/test.inc

@@ -0,0 +1,5 @@
+// this is a test file to be included in include_test.gravity
+
+func sum3 (a, b, c) {
+	return a + b + c;
+}