code: 9ferno

Download patch

ref: 97d9da73218273da5b0f7657c86df50b8fc0d127
parent: ce1c3bf2557c737b087401365884e678e9b9b8d4
author: henesy <unknown>
date: Thu Dec 10 09:23:38 EST 2020

string(2): add replace() for string replacement

--- a/appl/lib/string.b
+++ b/appl/lib/string.b
@@ -425,3 +425,73 @@
 		return unquoted(s + "'");
 	return args;
 }
+
+# Replace up to 'max' instances of 's' with 'with' in 'in'
+replace(in, s, with: string, max: int): string {
+	if(max == 0 || s == "") {
+		# Nothing to replace
+		return in;
+	}
+	if(len in < 1) {
+		# No string given
+		return "";
+	}
+	
+	n := 0;
+	
+	# For each rune 'in' the original string
+	for(r0 := 0; r0 < len in ; r0++) {
+		# Try to build 's'	
+		r1: int;
+		
+		substring:
+		for(r1 = 0; r1 < len s; r1++) {
+			if(in[r0 + r1] == s[r1]) {
+				# Match so far
+				continue substring;
+			} else {
+				# No longer matches
+				break substring;
+			}
+		}
+		
+		# We didn't break the loop, this is a match 
+		# String will be [r0 , r0+len(s))
+		if(r1 >= len s) {
+			left, right: string;
+			
+			# Left side
+			case r0 {
+			0 =>
+				# First rune, no left side
+				left = "";
+			* =>
+				left = in[:r0];
+			}
+			
+			# Right side
+			redge := r0 + len s;
+
+			if(redge == len in - 1)
+				# Last rune
+				right = "";
+			else
+				right = in[redge:];
+			
+			# Insert
+			in = left + with + right;
+			
+			# Skip forward to end of replacement string
+			r0 = r0 + (len with - 1);
+			
+			# Tick counts
+			n++;
+			if(n == max && max > 0) {
+				# We hit max and care about the number of replacements
+				return in;
+			}
+		}
+	}
+	
+	return in;
+}
--- a/man/2/string
+++ b/man/2/string
@@ -13,6 +13,7 @@
 prefix:    fn(pre, s: string): int;
 splitl:    fn(s, cl: string): (string, string);
 splitr:    fn(s, cl: string): (string, string);
+replace:   fn(in, s, with: string, max: int): string;
 splitstrl: fn(s, t: string): (string, string);
 splitstrr: fn(s, t: string): (string, string);
 take:      fn(s, cl: string): string;
@@ -110,6 +111,30 @@
 .IR s ,
 the result is
 .RI ( nil, s ).
+
+.PP
+.B Replace
+returns the string
+.I in
+with up to
+.I max
+instances of
+.I s
+removed and replaced by
+.IR with .
+If 
+.I max
+is less than 0, there is no limit to the number of replacements.
+If 
+.I max
+is 0 or 
+.I s
+is
+.IR nil ,
+the string
+.I in
+is returned unchanged. 
+
 .PP
 .B Take
 returns the maximal prefix of string
--- a/module/string.m
+++ b/module/string.m
@@ -20,6 +20,7 @@
 	# in these, the second string is a string to match, not a class
 	splitstrl:	fn(s, t: string): (string, string);
 	splitstrr:	fn(s, t: string): (string, string);
+	replace:	fn(in, s, with: string, max: int): string;
 
 	# is first arg a prefix of second?
 	prefix:		fn(pre, s: string): int;