rev 448 - trunk/src

SVN User <[email protected]> Mon, 03 May 2004 11:04:29 -0400
Newsgroups gmane.comp.lang.prothon.cvs
Message-ID <[email protected]>
Author: jknapka
Date: 2004-05-03 11:04:26 -0400 (Mon, 03 May 2004)
New Revision: 448

Modified:
   trunk/src/builtins-string.c
Log:
Implemented a number of Python's string methods.


Modified: trunk/src/builtins-string.c
===================================================================
--- trunk/src/builtins-string.c	2004-05-03 03:25:30 UTC (rev 447)
+++ trunk/src/builtins-string.c	2004-05-03 15:04:26 UTC (rev 448)
@@ -53,6 +53,7 @@
 
 // builtins.c
 
+#include <ctype.h>
 #include <stdio.h>
 #include <string.h>
 
@@ -353,7 +354,7 @@
 	} else {
 		obj_str = obj_malloc(ist, OBJ(STRING_PROTO), obj, sizeof(pr_str_t)+tlen+1);
 		if(!obj_str) {
-			raise_exception(ist, OBJ(OUTOFMEMORY_EXC), "memory allocation failed for string multiplication");
+			raise_exception(ist, OBJ(OUTOFMEMORY_EXC), "memory allocation failed for string join");
 			return NULL;
 		}
 		obj_str->len = tlen;
@@ -407,7 +408,312 @@
 	return NEW_INT(sizeof(pr_str_t) + pr_strlen(self) + 1);
 }
 
+// Generic case-changer function.
+static obj_p changeCase(isp ist,obj_p self,int (*changer)(int), size_t howMany) {
+	size_t str_len = pr_strlen(self), ii = 0;
+	char* dest_ptr = 0;
+	obj_p obj=0;
+	pr_str_p obj_str=0;
 
+	obj = NEW_OBJ(String_OBJ);
+	obj_str = obj_malloc(ist, OBJ(STRING_PROTO), obj, sizeof(pr_str_t)+str_len+1);
+	if(!obj_str) {
+		raise_exception(ist, OBJ(OUTOFMEMORY_EXC),
+						"memory allocation failed for string.changeCase (used by lower(), upper(), etc.)");
+		return NULL;
+	}
+	obj_str->len = str_len;
+	dest_ptr = obj_str->str;
+
+	// Fill the copy.
+	char* src_ptr = pr_strptr(self);
+	for (ii=0; ii<str_len; ++ii) {
+		if (ii<howMany) {
+			dest_ptr[ii] = (char)(*changer)(src_ptr[ii]);
+		} else {
+			dest_ptr[ii] = src_ptr[ii];
+		}
+	}
+	dest_ptr[str_len] = 0;
+
+	return obj;
+}
+
+DEF(String, lower, NULL) {
+	return changeCase(ist,self,&tolower,pr_strlen(self));
+}
+
+DEF(String, upper, NULL) {
+	return changeCase(ist,self,&toupper,pr_strlen(self));
+}
+
+DEF(String, capitalize, NULL) {
+	return changeCase(ist,self,&toupper,1);
+}
+
+// strstr()-analog that works for strings that may contain nulls.
+// Totally dependent upon ASCII encoding; beware.
+static char* bin_strstr(char* haystack,size_t lhaystack, char* needle,size_t lneedle) {
+	char* lookfor = needle;
+	char* looking_at = haystack;
+	char* hs_end = haystack+lhaystack;
+	char* ndl_end = needle+lneedle;
+	while (looking_at < hs_end) {
+		if (*lookfor == *looking_at) {
+			// Matched a character.
+			++lookfor;
+			if (lookfor == ndl_end) {
+				// Matched the whole string. It started lneedle-1
+				// characters before the character we're looking at
+				// now.
+				return looking_at + 1 - lneedle;
+			}
+		} else {
+			// No match.
+			if (lookfor > needle) {
+				// We had recognized the beginning of a match. We must
+				// return to the character after the location at which
+				// the non-match began.
+				looking_at -= lookfor-needle;
+				lookfor = needle;
+			}
+		}
+		++looking_at;
+	}
+	return 0;
+}
+
+#define Int_value(objid)	(objid->data.i64)
+DEF(String, find, FORM_PARAM2) {
+	size_t str_len = pr_strlen(self);
+	size_t find_len = 0;
+	char* find_ptr = 0;
+	size_t start = 0;
+	obj_p result = 0;
+
+	if (!has_proto_QUES(ist, parms[1], String_OBJ)) {
+		raise_exception(ist, OBJ(TYPE_EXC), "find function parameter 1 must be a string");
+		return NULL;
+	}
+	if (parms[3]) {
+		if (!has_proto_QUES(ist, parms[3], OBJ(INT_PROTO))) {
+			raise_exception(ist, OBJ(TYPE_EXC), "find function parameter 2 must be an integer");
+			return NULL;
+		} else {
+			start = (size_t)Int_value(parms[3]);
+		}
+	}
+
+	find_ptr = pr_strptr(parms[1]);
+	find_len = pr_strlen(parms[1]);
+	if (find_len<1) {
+		return new_int_obj(ist,0);
+	}
+	char* found = bin_strstr(pr_strptr(self)+Int_value(parms[3]),str_len,find_ptr,find_len);
+	if (found) {
+		result = new_int_obj(ist,found-pr_strptr(self));
+	} else {
+		result = new_int_obj(ist,0);
+	}
+
+	return result;
+}
+
+DEF(String, replace, FORM_PARAM2) {
+	size_t str_len = pr_strlen(self);
+	size_t find_len = 0;
+	size_t repl_len = 0;
+	size_t result_len = str_len;
+	char* dest_ptr = 0;
+	obj_p obj=0;
+	pr_str_p obj_str = 0;
+	char* src_ptr = 0;
+	char* find_ptr = 0;
+	char* repl_ptr = 0;
+	size_t temp_len = 0;
+	char* a_match = 0;
+	char* where_ptr = 0;
+
+	if (!has_proto_QUES(ist, parms[1], String_OBJ)) {
+		raise_exception(ist, OBJ(TYPE_EXC), "replace function parameter 1 must be a string");
+		return NULL;
+	}
+	if (!has_proto_QUES(ist, parms[3], String_OBJ)) {
+		raise_exception(ist, OBJ(TYPE_EXC), "replace function parameter 2 must be a string");
+		return NULL;
+	}
+
+	find_len = pr_strlen(parms[1]);
+	if (find_len>str_len) return self;
+	if (find_len<1) {
+		raise_exception(ist, OBJ(TYPE_EXC), "replace: empty search string");
+		return NULL;
+	}
+	repl_len = pr_strlen(parms[3]);
+	src_ptr = pr_strptr(self);
+	find_ptr = pr_strptr(parms[1]);
+	repl_ptr = pr_strptr(parms[3]);
+
+	// This is perhaps not as efficient as it could be.
+
+	// Find the length of the result string.
+	temp_len = str_len;
+	a_match = bin_strstr(src_ptr,temp_len,find_ptr,find_len);
+	while (a_match) {
+		result_len -= find_len;
+		result_len += repl_len;
+		a_match += find_len; // Skip the matching text.
+		temp_len = str_len - (a_match - src_ptr);
+		a_match = bin_strstr(a_match,temp_len,find_ptr,find_len);
+	}
+
+	// Create the copy.
+	// Create the copy.
+	obj = NEW_OBJ(String_OBJ);
+	if (result_len < IMMEDIATE_DATA_LEN) {
+		obj->data_type = DATA_TYPE_IMMDATA;
+		obj->imm_data_len = (int) result_len;
+		dest_ptr = obj->data.str;
+	} else {
+		obj_str = obj_malloc(ist, OBJ(STRING_PROTO), obj, sizeof(pr_str_t)+result_len+1);
+		if(!obj_str) {
+			raise_exception(ist, OBJ(OUTOFMEMORY_EXC), "memory allocation failed for string.replace");
+			return NULL;
+		}
+		obj_str->len = result_len;
+		dest_ptr = obj_str->str;
+	}
+
+	// Fill the replaced copy.
+	temp_len = str_len;
+	where_ptr = src_ptr;
+	a_match = bin_strstr(where_ptr,temp_len,find_ptr,find_len);
+	while (a_match) {
+		// Copy everything from where we were to the beginning of the
+		// match.
+		int frag_len = a_match-where_ptr;
+		memcpy(dest_ptr,where_ptr,frag_len);
+		dest_ptr += frag_len;
+
+		// Copy the replacement text.
+		memcpy(dest_ptr,repl_ptr,repl_len);
+		dest_ptr += repl_len;
+
+		a_match += find_len; // Skip the matched text.
+		where_ptr = a_match;
+		temp_len = str_len - (a_match - where_ptr);
+		a_match = bin_strstr(a_match,temp_len,find_ptr,find_len);
+	}
+
+	// Copy any trailing text after the last match.
+	if (where_ptr<src_ptr+str_len) {
+		memcpy(dest_ptr,where_ptr,(src_ptr+str_len)-where_ptr);
+	}
+
+	return obj;
+}
+
+DEF(String, expandtabs, NULL) {
+	// FIX ME: should accept an int arg (default 8) telling how many
+	// spaces per tab.
+	obj_p tab = new_string_obj(ist,"\t");
+	obj_p spcs = new_string_obj(ist,"        ");
+	obj_p args[] = {NULL,tab,NULL,spcs};
+	obj_p result = Stringreplace( ist, self, 2, args, 0 );
+	del_unlock(tab);
+	del_unlock(spcs);
+	return result;
+}
+
+//===============================
+// ASSUMPTION: if we're using lstrip, rstrip, etc, then
+// we're dealing with null-terminated text strings.
+//===============================
+DEF(String, lstrip, NULL) {
+	char* cstr = pr_strptr(self);
+	while (isspace(*cstr)) {
+		++cstr;
+	}
+	
+	// Create the copy.
+	return new_string_obj(ist,cstr);
+}
+
+DEF(String, rstrip, NULL) {
+	// Count whitespace at the end.
+	char* cstr = pr_strptr(self)+pr_strlen(self)-1;
+	size_t result_len = 0;
+	while (isspace(*cstr)) {
+		--cstr;
+	}
+	result_len = cstr-pr_strptr(self)+1;
+	
+	// Create the copy.
+	return new_string_n_obj(ist,pr_strptr(self),result_len);
+}
+
+DEF(String, strip, NULL) {
+	char* cstr = pr_strptr(self);
+	while (isspace(*cstr)) {
+		++cstr;
+	}
+	
+	// Count whitespace at the end.
+	char* cstr2 = pr_strptr(self)+pr_strlen(self)-1;
+	size_t result_len = 0;
+	while (isspace(*cstr2)) {
+		--cstr2;
+	}
+	result_len = cstr2-cstr+1;
+	
+	// Create the copy.
+	return new_string_n_obj(ist,cstr,result_len);
+}
+
+DEF(String, split, FORM_RPARAM) {
+	size_t str_len = pr_strlen(self);
+	size_t find_len = 0;
+	char* src_ptr = 0;
+	char* find_ptr = 0;
+	size_t temp_len = 0;
+	char* a_match = 0;
+	char* where_ptr = 0;
+	obj_p list_obj = new_list_obj(ist,0);
+
+	if (!has_proto_QUES(ist, parms[1], String_OBJ)) {
+		raise_exception(ist, OBJ(TYPE_EXC), "split function parameter 1 must be a string");
+		return NULL;
+	}
+
+	find_len = pr_strlen(parms[1]);
+	if (find_len<1) {
+		raise_exception(ist, OBJ(TYPE_EXC), "split: empty separator");
+		return NULL;
+	}
+	src_ptr = pr_strptr(self);
+	where_ptr = src_ptr;
+	find_ptr = pr_strptr(parms[1]);
+
+	temp_len = str_len;
+	a_match = bin_strstr(src_ptr,temp_len,find_ptr,find_len);
+	while (a_match) {
+		// Make new string from where_ptr to a_match.
+		obj_p next_frag = new_string_n_obj(ist,where_ptr,a_match-where_ptr);
+		list_append(ist,list_obj,next_frag);
+
+		a_match += find_len; // Skip the matching text.
+		where_ptr = a_match;
+ 		temp_len = str_len - (a_match - src_ptr);
+		a_match = bin_strstr(a_match,temp_len,find_ptr,find_len);
+	}
+
+	// Copy any trailing text after the last match.
+	obj_p next_frag = new_string_n_obj(ist,where_ptr,str_len-(where_ptr-src_ptr));
+	list_append(ist,list_obj,next_frag);
+	
+	return list_obj;
+}
+
 //********************************* STRINGGEN MODULE **************************
 
 MODULE_START(StringGen)
@@ -435,8 +741,6 @@
         return res;
 }
 
-
-
 //********************************* MODULES INIT ******************************
 
 MAIN_MODULE_INIT(String)
@@ -461,6 +765,16 @@
 	MODULE_ADD_SYM(String, __delItem__);
 	MODULE_ADD_SYM(String, __objList__);
 	MODULE_ADD_SYM(String, __cDataLen__);
+	MODULE_ADD_SYM(String, lower);
+	MODULE_ADD_SYM(String, upper);
+	MODULE_ADD_SYM(String, replace);
+	MODULE_ADD_SYM(String, capitalize);
+	MODULE_ADD_SYM(String, expandtabs);
+	MODULE_ADD_SYM(String, lstrip);
+	MODULE_ADD_SYM(String, rstrip);
+	MODULE_ADD_SYM(String, strip);
+	MODULE_ADD_SYM(String, split);
+	MODULE_ADD_SYM(String, find);
 
 	MODULE_SUB_INIT(StringGen);
 	MODULE_ADD_SYM(StringGen, next);