Doing a FindAllStringSubmatch regex match including named groups for different date formats. I am having trouble looping results. The commented out conditional makes it work, but not cleanly and it breaks as I add additional matches. I feel like I am approaching it wrong and would like some redirection. thx/
package main
import (
"fmt"
"regexp"
"strings"
)
func main() {
text := "12/31/1956 31/11/1960"
reg := []string{`(?P<month>1[12])/(?P<day>\d\d)/(?P<year>19\d\d)`, `(?P<day>\d\d)/(?P<month>1[12])/(?P<year>19\d\d)`}
// Combine them, case ignored
regall := "(?i)" + strings.Join(reg, "|")
myExp := regexp.MustCompile(regall)
match := myExp.FindAllStringSubmatch(text, -1)
fmt.Println("Match", match, len(match))
fmt.Println("Names", myExp.SubexpNames(), len(myExp.SubexpNames()))
for i := 0; i < len(match); i++ {
result := make(map[string]string)
for j, name := range myExp.SubexpNames() {
result[name] = match[i][j]
//if (result["month"] != "" && result["day"] != "" && result["year"] != "") {
fmt.Println(match[i][0],i,j,result["month"] + "/" + result["day"] + "/" + result["year"])
//}
}
}
}
Results in:
Match [[12/31/1956 12 31 1956 ] [31/11/1960 31 11 1960]] 2
Names [ month day year day month year] 7
12/31/1956 0 0 //
12/31/1956 0 1 12//
12/31/1956 0 2 12/31/
12/31/1956 0 3 12/31/1956
12/31/1956 0 4 12//1956
12/31/1956 0 5 //1956
12/31/1956 0 6 //
31/11/1960 1 0 //
31/11/1960 1 1 //
31/11/1960 1 2 //
31/11/1960 1 3 //
31/11/1960 1 4 /31/
31/11/1960 1 5 11/31/
31/11/1960 1 6 11/31/1960
After some rethinking and help from above, with aid of the above answer I came up looping the regex's separately to better handle overlapping named capture groups I came up with this: (posted for benefit of others and any critique is welcome)
package main
import (
"fmt"
"os"
"regexp"
"strconv"
"strings"
)
func main() {
month_d := `(?P<month>1[012]|0?[1-9])`
month_t := `(?P<month>jan(?:uary|.)?|feb(?:ruary|.)?|mar(?:ch|.)?|apr(?:il|.)?|may(.)|jun(?:e|.)?|jul(?:y|.)?|aug(?:ust|.)?|sep(?:tember|t|t.|.)?|oct(?:ober|.)?|nov(?:ember|.)?|dec(?:ember|.)?)`
day := `(?P<day>3[01]|[12][0-9]|[0]?[1-9])(?:\s)?(?:th|rd|nd|st)?`
year := `(?P<year>(?:19|20)?\d\d)`
sep_d := `[?:/.-]`
sep_t := `[?:,\s]`
text := "fedskjnkvdsj February 6 2004 sdffd Jan 12th 56 1/12/2000 2013/12/1 2099/12/5 1/12/1999 dsfjhfdhj"
regs := []string{
"(" + month_d + sep_d + day + sep_d + year + ")",
"(" + year + sep_d + month_d + sep_d + day + ")",
"(" + day + sep_d + month_d + sep_d + year + ")",
"(" + month_t + sep_t + day + sep_t + year + ")",
"(" + day + sep_t + month_t + sep_t + year + ")",
}
for i := 0; i < len(regs); i++ {
myExp, err := regexp.Compile("(?i)" + regs[i])
if err != nil {
fmt.Printf("There is a problem with your regexp.\n")
return
}
match := myExp.FindAllStringSubmatch(text, -1)
//fmt.Println("Match", match, len(match))
//fmt.Println("Names", myExp.SubexpNames(), len(myExp.SubexpNames()))
for j := 0; j < len(match); j++ {
result := make(map[string]string)
for k, name := range myExp.SubexpNames() {
result[name] = match[j][k]
}
// Fix Month
themonth := strings.ToLower(result["month"])
if len(themonth) == 1 {
themonth = "0" + themonth
}
if len(themonth) >= 3 {
themonth = themonth[0:3]
switch themonth {
case "jan":
themonth = "01"
case "feb":
themonth = "02"
case "mar":
themonth = "03"
case "apr":
themonth = "04"
case "may":
themonth = "05"
case "jun":
themonth = "06"
case "jul":
themonth = "07"
case "aug":
themonth = "08"
case "sep":
themonth = "09"
case "oct":
themonth = "10"
case "nov":
themonth = "11"
case "dec":
themonth = "12"
default:
fmt.Println("Month Error: " + themonth)
os.Exit(2)
}
}
// Day
theday := result["day"]
if len(theday) == 1 {
theday = "0" + theday
}
// Fix Year
theyear := result["year"]
if len(theyear) == 2 {
val_year, err := strconv.ParseInt(theyear,10,0)
if err != nil {
// handle error
fmt.Println(err)
os.Exit(2)
}
if val_year > 50 {
theyear = "19" + theyear
} else {
theyear = "20" + theyear
}
}
date := themonth + "/" + theday + "/" + theyear
fmt.Println(date)
}
}
}
Related
I had been working on a QoL macro to convert selected text dates into a desired format. For that purpose I was trying to utilize the DateParse function that is referenced from the AHK archive forum. After going through all 7 pages of that thread none of the implementations seem to be working for me. It appears that regardless of my date string sent into the first RegEx expression a blank value is returned. Beyond that its still not clear to me why it would be returning today's date. The FormateTime function would return today's date but that function is not used in DateParse.
Any help to achieve the desired output of converting selected text to YYYY-MM-DD from an unknown date format would be appreciated.
Some inputs I have tested so far are:
20220202
2022/02/02
May 1, 2022
All return the current date from the DateParse function. I am using AHK version 1.1.30.2.
Here is the code that I am testing on as its own standalone script:
/*
Function: DateParse
Converts almost any date format to a YYYYMMDDHH24MISS value.
Parameters:
str - a date/time stamp as a string
Returns:
A valid YYYYMMDDHH24MISS value which can be used by FormatTime, EnvAdd and other timecommands.
Example:
> time := DateParse("2:35 PM, 27 November, 2007")
License:
- Version 1.05 <https://ahknet.autoh...ene/#dateparse>
- Dedicated to the public domain (CC0 1.0) <http://creativecommo...main/zero/1.0/>
*/
DateParse(str) {
static e2 = "i)(? :(\d{1,2}+)[\s\.\-\/,]+)?(\d{1,2}|
(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\w*)[\s\.\-\/,]+(\d{2,4})"
str := RegExReplace(str, "((?:" . SubStr(e2, 42, 47) . ")\w*)(\s*)(\d{1,2})\b", "$3$2$1", "", 1)
If RegExMatch(str, "i)^\s*(? :(\d{4})([\s\-:\/])(\d{1,2})\2(\d{1,2}))?"
. "(?:\s*[T\s](\d{1,2})([\s\-:\/])(\d{1,2})(?:\6(\d{1,2})\s*(?:(Z)|(\+|\-)?"
. "(\d{1,2})\6(\d{1,2})(?:\6(\d{1,2}))?)?)?)?\s*$", i)
d3 := i1, d2 := i3, d1 := i4, t1 := i5, t2 := i7, t3 := i8
Else If !RegExMatch(str, "^\W*(\d{1,2}+)(\d{2})\W*$", t)
RegExMatch(str, "i)(\d{1,2})\s*:\s*(\d{1,2})(?:\s*(\d{1,2}))?(?:\s*([ap]m))?", t)
, RegExMatch(str, e2, d)
f = %A_FormatFloat%
SetFormat, Float, 02.0
d := (d3 ? (StrLen(d3) = 2 ? 20 : "") . d3 : A_YYYY)
. ((d2 := d2 + 0 ? d2 : (InStr(e2, SubStr(d2, 1, 3)) - 40) // 4 + 1.0) > 0
? d2 + 0.0 : A_MM) . ((d1 += 0.0) ? d1 : A_DD) . t1
+ (t1 = 12 ? t4 = "am" ? -12.0 : 0.0 : t4 = "am" ? 0.0 : 12.0) . t2 + 0.0 . t3 + 0.0
SetFormat, Float, %f%
Return, d
}
^!B::Reload
^`;::
clipSave := Clipboard
Clipboard = ; Empty the clipboard so that ClipWait has something to detect
SendInput, ^c ; copy selected text
ClipWait
StringReplace, Clipboard, Clipboard, `r`n, `n, All ; Fix for SendInput sending Windows linebreaks
msgbox Input is %Clipboard%
msgbox % "Result is " vDate := DateParse(Clipboard)
;vDate := Clipboard
;Msgbox % vDate
;vDate := DateParse("2022-02-02")
;Msgbox % vDate
;FormatTime, vDate, %test%, d
;Msgbox % vDate
;FormatTime, %myDate, ahk_now, yyyy-MM-dd
;send %myDate%
;Len:= Strlen(Clipboard) ;Set number of characters
;SendInput %Clipboard% ;Send new string
;SendInput +{left %Len%} ;Re-select text
VarSetCapacity(OutputText, 0) ;free memory
Clipboard := clipSave ;Restore previous clipboard
return
Answer
Going off the suggestion from T_lube I searched for a more direct conversion of certain formats and found another code sample (Albeiro & Mikeyww) that got me very close to the final result.
ConvDate(dateString, dateType := "", locale := "Month first")
{ ; Version .: 19-09-2020
; https://www.autohotkey.com/boards/viewtopic.php?f=76&t=80896
; From #mikeyww (modified Albireo)
;
; dateString - The date in some dateformat
;
; Convertformat .:
; dateType = 1 - handle .:
; - Tue Aug 11 13:59:27 2020 to (yyyymmdd - 20200811)
; locale := "Month first" Assume that the month is placed first in the string (default)
; eg.
; ConvDate(dt, TypeDate, locale)
; ConvDate("Tue Aug 11 13:59:27 2020", "1") => 20200811
; ConvDate("13.10.20") => 20201013
; ConvDate("20.07.03") => 20200703
; ConvDate("7.5.2020") => 20200705
; ConvDate("7.5.2020",,"Day first") => 20200507
; ConvDate("01192020") => 20200119
;
; ConvDate("19-8-2020") => 20200819 - Not affected by "Month first" or "Day first"
; ConvDate("2020.05.07") => 20200507 - Not affected by "Month first" or "Day first"
; ConvDate("19012020") => 20200119 - Not affected by "Month first" or "Day first"
; ConvDate("200928") => 20200928 - Not affected by "Month first" or "Day first"
; ConvDate("2020/10/05") => 20201005 - Not affected by "Month first" or "Day first"
; ConvDate("2020-10-05") => 20201005 - Not affected by "Month first" or "Day first"
; ConvDate("5 okt 2020") => 20201005 - Not affected by "Month first" or "Day first"
; ConvDate("05 okt 2020") => 20201005 - Not affected by "Month first" or "Day first"
; ConvDate("5 oktober 2020") => 20201005 - Not affected by "Month first" or "Day first"
; ConvDate("5-10-2020") => 20201005 - Not affected by "Month first" or "Day first"
; ConvDate("Okt 5, 2020") => 20201005 - Not affected by "Month first" or "Day first"
;
; ConvDate("20200119") => 20200119 - Not affected by "Month first" or "Day first"
; ConvDate("20200119") => 20200119 - Not affected by "Month first" or "Day first"
; ConvDate("01192020") => 20200119 - "Only first" or "Day first"
;
; ConvDate("10.8.20") ERROR => 1020 (- Input not handled)
; ConvDate("10.8.20",,"Day first") ERROR => 0820 (- Input not handled)
; ConvDate("8.19.2020",,"Day first") ERROR => 20201908 (- Wrong date input)
; ConvDate("01192020",, "Day first") ERROR => 20201901 (- Wrong date input)
; different test dates
; vDate := ConvDate("Tue Aug 11 13:59:27 2020", 1, "Month first")
; vDate := ConvDate("30 juli 2020",, "Day first")
; vDate := ConvDate("09-10-20",, "Day first") ; dd-mm-yy
; vDate := ConvDate("15-05-20",, "Day first") ; dd-mm-yy
; vDate := ConvDate("200820",, "Day first") ; ddmmyy
; vDate := ConvDate("200730") ; yymmdd
; vDate := ConvDate("200818") ; yymmdd
; vDate := ConvDate("200928") ; yymmdd
; vDate := ConvDate("20201013")
; vDate := ConvDate("20-05-06") ; yy-mm-dd
; vDate := ConvDate("20.07.03") ; yy.mm.dd
; vDate := ConvDate("18.08.2020",, "Day first")
; vDate := ConvDate("13.10.2020",, "Day first")
mos := { jan: 1
, feb: 2
, mar: 3
, apr: 4
, maj: 5
, may: 5
, jun: 6
, jul: 7
, aug: 8
, sep: 9
, okt: 10
, oct: 10
, nov: 11
, dec: 12}
yeard := False
part := []
new := []
dt := StrReplace(dateString, ",") ; Rensa bort alla "kommatecken"
If dateType
{ If dateType = 1
{ ; To Convert - Tue Aug 11 13:59:27 2020 - to (20200811)
locale := "Day first"
dt1 := StrSplit(dateString, A_Space)
dt := dt1.3 " " dt1.2 " " dt1.5
}
}
md := locale = "Month first" ? True : False
If RegExMatch(dt, "^\d{2}[-.]\d{2}[-.]\d{2}$")
dt := RegExReplace(dt, "[-.]")
If RegExMatch(dt, "^\d{6}$")
{ f2 := SubStr(dt, 1, 2)
If (f2 > 19)
dt := f2 + 2000 SubStr(dt, 3)
else
dt := SubStr(dt, 1, 4) Substr(dt, 5) + 2000
}
If RegExMatch(dt, "\d{8}")
ff := (SubStr(dt, 1, 4) > 1999) * 2,
. dt := SubStr(dt, 1, (ff + 3) - 1) "-"
. SubStr(dt, ff + 3, (ff + 5)- (ff + 3)) "-"
. SubStr(dt, ff + 5)
part := StrSplit(dt, [A_Space, "/", "-", "."]) ; Get year, month, day
For index, this in part
{ If this is alpha
{ For moName, moNum in mos
{ If SubStr(this, 1, 3) = moName ; Month matched a known string
part[index] := moNum, md := index = 1 ? True : False ; If string starts with alpha month, then day follows it
}
}
}
For index, this in part
{ If !RegExMatch(this, "\d{4}") ; This part is not the year
{ md := this > 12 ? (index > 1 ? True : False) : md ; For numbers > 12, if it's second, then month precedes it
new[new[3-md] ? md+2 : 3-md] := this ; Populate the month or day slot
} Else new[1] := this, md := index = 1 ? True : md ; If year comes first, then the month follows it
}
Return Format("{}-{:02}-{:02}", new[1], new[2], new[3])
}
I am fairly familiar with regex, and I do not think that some of those expressions will compile correctly (specifically "?:" within a literal string). Try using ErrorLevel by msgbox after each regexMatch call. I'd be willing to bet it is throwing errors silently about compile issues.
Were I in this position, I would figure out a limited number of formats that you are going to run into and simply check through each one with regexMatch. I think this would be much more maintainable code.
i want to print like this output
1
121
12312
1234123
123454321
and here is my code
var no = 1
var numberOfRow = 5
for i in 1...numberOfRow {
for _ in 1..<(6-i) {
print("_", terminator: " ")
}
for _ in 1...i {
//no += 1
print("\(no)", terminator: " ")
no += 1
}
for _ in 1..<no - 1 {
no -= 1
print("\(no - 1)", terminator: " ")
}
print("\(no)")
}
but its output shows that like bellow
_ _ _ _ 1 2
_ _ _ 2 3 2 1 2
_ _ 2 3 4 3 2 1 2
_ 2 3 4 5 4 3 2 1 2
2 3 4 5 6 5 4 3 2 1 2
where is my problem in this code?
Please check this :
var no = 1
var numberOfRow = 5
for i in 1...numberOfRow {
for _ in 1..<(6-i) {
print(" ", terminator: " ")
}
for j in 1...i {
print("\(j)", terminator: " ")
no = j
}
for k in 1..<no {
no -= 1
print("\(no)", terminator: " ")
}
print(" ")
}
here is the pseudo-code for you. change your for loops accordingly.
int rc = 5;
for(int i=1;i<=rc;i++)
{
for(int j=0;j<(rc-i);j++)
{
Print("_");
}
for(int k=0;k<i;k++)
{
Print(k + 1);
}
for(int l=(i-1);l>0;l--)
{
Print(l);
}
print("\(no)")
}
I have a list of timestamp tuples of the form List((startTime,endTime)), which are basically denoting periods of time throughout the day.
For example:
(("2016-03-28 14:00:00","2016-03-28 15:00:00"),
("2016-03-28 17:00:00","2016-03-28 21:00:00"),
("2016-03-28 01:00:00","2016-03-28 13:00:00"))
I want to get a list of the durations which are not included in this list.
So the output should be:
(("2016-03-28 00:00:00","2016-03-28 01:00:00"),
("2016-03-28 13:00:00","2016-03-28 14:00:00"),
("2016-03-28 15:00:00","2016-03-28 17:00:00"),
("2016-03-28 17:00:00","2016-03-28 24:00:00"))
Can anyone suggest a good and efficient way of doing that in Scala?
The naive solution that I've tried so far is as follows:
import java.sql.Timestamp
import scala.collection.mutable.ListBuffer
def comparator(first: (Timestamp,Timestamp), second: (Timestamp, Timestamp)) = first._2.getTime <= second._1.getTime
val data = List((Timestamp.valueOf("2016-03-28 00:00:00"),Timestamp.valueOf("2016-03-28 10:00:00")),
(Timestamp.valueOf("2016-03-28 12:00:00"),Timestamp.valueOf("2016-03-28 15:00:00")),
(Timestamp.valueOf("2016-03-28 23:00:00"),Timestamp.valueOf("2016-03-28 23:59:59")),
(Timestamp.valueOf("2016-03-28 16:00:00"),Timestamp.valueOf("2016-03-28 21:00:00"))
).sortWith(comparator)
var emptySlots = new ListBuffer[(Timestamp,Timestamp)]()
var currTime = Timestamp.valueOf("2016-03-28 00:00:00")
var index = 0
var cond = 0
while(cond == 0){
if (currTime.compareTo(Timestamp.valueOf("2016-03-28 23:59:59")) < 0 && index >= data.size){
emptySlots += ((currTime,Timestamp.valueOf("2016-03-28 23:59:59") ))
cond = 1
}
else if(index >= data.size)
{
cond = 1
}
else if(currTime.compareTo(data(index)._1) < 0) {
emptySlots += ((currTime, data(index)._1))
currTime = data(index)._2
index += 1
}
else if(currTime.compareTo(data(index)._1) >= 0 && currTime.compareTo(data(index)._2) < 0 ) {
currTime = data(index)._2
index += 1
}
else if(currTime.compareTo(data(index)._1) > 0 && currTime.compareTo(data(index)._2) > 0 ) {
index += 1
}
}
emptySlots.toList
In PL/SQL, is there any way to calculate next serial number from another one like 'A1B0010C'. Next serial no will be A1B0011C (+1). My idea is retrieve number part, increase it get return string back.
I can do this in java, but passing more than 1000 elements to Oracle will cause problems in IN clause.
So please help, any suggestion is appreciated.
Try to write some recursive function like this
This Function returns next character. ex: D after C. 0 after Z.
create or replace function get_next_character(ch char)
return char
is
result_ch char(1) := ch;
begin
IF UPPER(ch) = 'Z' or ch = '9' THEN
result_ch := 'A';
ELSE
result_ch := chr(ascii(ch) + 1);
END IF;
return upper(result_ch);
end get_next_character;
and this is your actual function which returns the next serial number
So, it generates 'A1B0010D' if your input is 'A1B0010C'
create or replace function get_next_serial(p_serial IN varchar2) -- PASS THE REQUIRED ARGUMENTS
return varchar2
IS
v_ret_serial varchar2(100);
v_in_serial varchar2(100) := p_serial;
tmp varchar2(100);
last_char char(1);
begin
tmp := v_in_serial;
for i in reverse 1..length(v_in_serial) loop
last_char := substr(tmp, length(tmp));
last_char := get_next_character(last_char);
tmp := substr(v_in_serial, 1, length(tmp)-1);
v_in_serial := substr(v_in_serial, 1, i-1) || last_char || substr(v_in_serial, i+1);
IF last_char <> 'A' then
exit;
END IF;
end loop;
IF last_char = 'A' THEN
v_in_serial:= 'A'||v_in_serial;
END IF;
return UPPER(v_in_serial);
exception
when NO_DATA_FOUND then
return 'AA';
when others then
return null;
end get_next_serial;
you can call this function (get_next_serial('abc')) where ever you want;
select get_next_serial('ALK0989KJ') from dual
You can place these two functions in a package and use at your convenience.
You can achieve it by using the following mix of Regexp_* functions:
SQL> with t1 as(
2 select 'A1B0010C' col from dual
3 )
4 select regexp_replace(col, '[[:digit:]]+'
5 , to_char(to_number(regexp_substr(col, '[[:digit:]]+',1,2) + 1), 'fm0000')
6 , 1
7 , 2
8 ) as Res
9 from t1
10 ;
RES
------------
A1B0011C
UPDATE In response to the comment.
SQL> with t1 as(
2 select 'A1B0010C' col from dual union all
3 select 'A1B0010C2' col from dual union all
4 select 'A1B0012C001' col from dual union all
5 select 'S000001' col from dual
6 )
7 select col
8 , regexp_replace(col
9 , '([[:digit:]]+)([[:alpha:]]+$|$)'
10 , LPad(to_char(to_number(num) + 1), length(num), '0') || '\2'
11 ) as Res
12 from (select col
13 , regexp_substr(col, '([[:digit:]]+)([[:alpha:]]+$|$)', 1, 1, 'i', 1) as num
14 from t1
15 )
16 ;
COL RES
----------- -----------------
A1B0010C A1B0011C
A1B0010C2 A1B0010C3
A1B0012C001 A1B0012C002
S000001 S000002
create or replace function nextSerial(s in varchar2) return varchar2 as
i integer;
done boolean := false;
c char;
newserial varchar2(4000);
begin
newserial := s;
i := length(newserial);
while i>0 and not done loop
c := substr(newserial, i, 1);
if c='9' then
newserial := substr(newserial, 1, i-1) || '0' || substr(newserial, i+1);
elsif c>='0' and c<='8' then
c := chr(ascii(c)+1);
newserial := substr(newserial, 1, i-1) || c || substr(newserial, i+1);
done := true;
end if;
i := i-1;
end loop;
if not done then
newserial := '1' || newserial;
end if;
return newserial;
end;
Just a basic Casaer Cipher. I've tested all of the sub functions, just encryptChar() does not particularly work. I get an infinite loop. It's supposed to be recursive. Here's the all code:
fun replace (str : string, index : int, newChar : char) : string = String.substring(str,0,index) ^ String.str(newChar) ^ String.substring(str,index+1,(size str) - index - 1;
fun encryptChar (msgStr : string, shiftAmnt : int, index : int) : string =
let val asciiCode = 0
in
if (not (String.sub(msgStr, index) = #" ")) then
(
asciiCode = ord( String.sub(msgStr, index) ) + shiftAmnt;
if (asciiCode < ord(#"A")) then asciiCode = asciiCode + 26
else if (asciiCode > ord(#"Z")) then asciiCode = asciiCode - 26
else asciiCode = asciiCode;
msgStr = replace(msgStr, index, chr(asciiCode))
)
else asciiCode = asciiCode;
index = index + 1;
if (index < (size msgStr - 1)) then encryptChar(msgStr, shiftAmnt, index)
else msgStr
end
;
fun encrypt(msgStr : string, shiftAmnt : int) : string = encryptChar (String.map Char.toUpper msgStr, shiftAmnt mod 26, 0);
The problem here is that you're misusing =. Outside of a variable definition, = is simply a boolean function which checks its arguments for equality. So if you do for example asciiCode = ord( String.sub(msgStr, index) ) + shiftAmnt;, it will simply return false (because asciiCode is not equal to ord( String.sub(msgStr, index) ) + shiftAmnt) and then throw that result away (because you have additional expressions after the ;). It will not reassign asciiCode.
Variables in SML are immutable. If you want to emulate mutable variables you can use refs and the := operator. However I would not recommend that approach as it is generally not good functional style and not necessary in this case. The preferable approach would be to rewrite the code in a way that each variable is only assigned once.
This is very basic indeed, and it's surprising that you ran into it in such a complicated situation.
Did you port this from some other language?
You need to forget everything you know about programming using assignments.
let val x = y in something
means more or less "within 'something', replace the identifier 'x' with the value of 'y'".
There is no way for you to change the value of x.
Do the substitution (this is not the actual evaluation order or anything, but it should give you an idea of what's going on):
encryptChar("THIS", amount, 0)
=>
let val asciiCode = 0
in
if (not (String.sub("THIS", 0) = #" ")) then
(
asciiCode = ord( String.sub("THIS", 0) ) + amount;
if (asciiCode < ord(#"A")) then asciiCode = asciiCode + 26
else if (asciiCode > ord(#"Z")) then asciiCode = asciiCode - 26
else asciiCode = asciiCode;
"THIS" = replace("THIS", 0, chr(asciiCode))
)
else asciiCode = asciiCode;
0 = 0 + 1;
if (0 < (size "THIS" - 1)) then encryptChar("THIS", amount, 0)
else str
end ;
=>
if (not (String.sub("THIS", 0) = #" ")) then
(
0 = ord( String.sub("THIS", 0) ) + amount;
if (0 < ord(#"A")) then 0 = 0 + 26
else if (0 > ord(#"Z")) then 0 = 0 - 26
else 0 = 0;
"THIS" = replace("THIS", 0, chr(0))
)
else 0 = 0;
0 = 0 + 1;
if (0 < (size "THIS" - 1)) then encryptChar("THIS", amount, 0)
else str
=>
if (not (String.sub("THIS", 0) = #" ")) then
(
0 = ord( String.sub("THIS", 0) ) + amount;
if true then false
else if false then false
else true;
false
)
else true;
false;
if (0 < (size "THIS" - 1)) then encryptChar("THIS", amount, 0)
else "this"
->
if (not false) then
(
false;
false;
false
)
else true;
false;
if true then encryptChar("THIS", amount, 0)
else "THIS"
=>
(
false;
false;
false
)
false;
encryptChar("THIS", amount, 0)
=>
encryptChar("THIS", amount, 0)
Which is where your infinite loop came from.
You would do well to get hold of an introductory text about ML programming.