package main import ( "fmt" "strconv" "strings" ) var anyAndNewline = "((.)|(\\n))" var variableOnly = "([a-zA-Z_])([a-zA-Z0-9_])*" var variableWithIndexes = variableOnly + "(\\[" + anyAndNewline + "+\\]|\\." + variableOnly + ")*" var stringCompile = makeRegex("(( *)\"((\\\\([a-z\\\"'`]))|[^\\\"])*\"( *))|(( *)'((\\\\([a-z\\'\"`]))|[^\\'])*'( *))|(( *)(`(\\\\[a-z\\\"'`\\n]|[^\\`])*`)( *))") var numberCompile = makeRegex("( *)(\\-)?(([0-9]*(\\.[0-9]+)?)(e((\\-|\\+)?([0-9]+(\\.[0-9]+)?)))?)( *)") var whileCompile = makeRegex("( *)(while( )+" + anyAndNewline + "+( )+\\[" + anyAndNewline + "*)( *)") var subCompile = makeRegex("( *)(sub( )+" + variableOnly + "\\(" + anyAndNewline + "*\\)( )+\\[" + anyAndNewline + "*)( *)") var ifCompile = makeRegex("( *)(if( )+" + anyAndNewline + "+( )+\\[" + anyAndNewline + "*)( *)") var elseifCompile = makeRegex("( *)(\\]( )+else( )+if( )+" + anyAndNewline + "+( )+\\[" + anyAndNewline + "*)( *)") var elseCompile = makeRegex("( *)\\]( )+else( )+\\[" + anyAndNewline + "*( *)") var closeCompile = makeRegex("( *)\\]( *)") var switchCloseCompile = makeRegex("( *)" + anyAndNewline + "*\\]" + anyAndNewline + "*\\[" + anyAndNewline + "*( *)") var importCompile = makeRegex("( *)((import " + anyAndNewline + "+)|(from( +)" + anyAndNewline + "+( +)import( +)(( *)" + variableOnly + "( *)(\\,( *)" + variableOnly + "( *))*)))( *)") var bracketsCompile = makeRegex("( *)\\(" + anyAndNewline + "*\\)( *)") var functionCompile = makeRegex("( *)" + anyAndNewline + "+\\(" + anyAndNewline + "*\\)( *)") var indexCompile = makeRegex("( *)" + anyAndNewline + "+\\[" + anyAndNewline + "+\\]( *)") var variableTextCompile = makeRegex("( *)" + variableWithIndexes + "( *)") var variableonlyCompile = makeRegex(variableOnly) var setVariableCompile = makeRegex("( *)(((const|var) (" + variableOnly + "( *))=( *)" + anyAndNewline + "+)|(( *)" + variableOnly + "( *))(( *)=( *)" + anyAndNewline + "+))( *)") var callErrorCompile = makeRegex("( *)(error(( )+" + anyAndNewline + "+))( *)") var returnStatementCompile = makeRegex("( *)(return(( )+" + anyAndNewline + "*)?)( *)") var deleteStatmentCompile = makeRegex("( *)(delete( )+" + variableWithIndexes + ")( *)") var itemsCompiled = makeRegex("( *)(\\[" + anyAndNewline + "*\\])( *)") var breakStatementCompile = makeRegex("( *)break( *)") var continueStatementCompile = makeRegex("( *)continue( *)") var skipCompile = makeRegex("( *)(#(.*))?") var tryCompile = makeRegex("( *)(try( )+\\[" + anyAndNewline + "*)( *)") var catchCompile = makeRegex("( *)(\\]( )+catch( )+\\[" + anyAndNewline + "*)( *)") var negetiveCompile = makeRegex("( *)(\\-" + anyAndNewline + "+)( *)") var processfunc func(codeseg code) (any, bool, any) var linefunc func(i int, codearray []code) (any, int, any) var opperators = [][]string{ { "&&", " and ", }, { "||", " or ", }, { "!@", " not in ", " is not in ", " isnt in ", }, { "@", " in ", " is in ", }, { "<=", " is less than or equal to ", " is smaller than or equal to ", }, { ">=", " is bigger than or equal to ", " is greater than or equal to ", " is more than or equal to ", }, { "<", " is less than ", " is smaller than ", }, { ">", " is bigger than ", " is greater than ", " is more than ", }, { "!=", " is not ", " is not equal to ", " isnt equal to ", " isnt ", "!==", }, { "==", " equals ", " is equal to ", " is ", "===", }, { " plus ", " add ", "+", }, { " minus ", " subtract ", "-", }, { "*", " x ", " times ", " multiplied by ", }, { " mod ", " modulo ", "%", }, { " div ", " floor division of ", " floor divistion ", "//", "$", }, { " over ", " divided by ", "/", }, { "!**", "!^", " root ", "√", }, { " to the power of ", "^", "**", }} func opensIndent(str string) bool { return tryCompile.MatchString(str) || ifCompile.MatchString(str) || whileCompile.MatchString(str) || subCompile.MatchString(str) } func init() { processfunc = translateprocess linefunc = translateline } func getValuesFromCommas(str string, line int) ([]any, bool, any) { output := []any{} temp := []byte{} stringlen := len(str) for i := 0; i < stringlen; i++ { if str[i] == 44 { tempstr := string(temp[:]) resp, worked, err := processfunc(code{code: tempstr, line: line}) if err != nil { return nil, false, err } if worked { output = append(output, resp) temp = []byte{} continue } } temp = append(temp, str[i]) } tempstr := string(temp[:]) if tempstr != "" { resp, worked, err := processfunc(code{code: tempstr, line: line}) if err != nil { return nil, false, err } if worked { output = append(output, resp) } else { return nil, false, nil } } return output, true, nil } // make a function that converts backslashes in a string to their correct character // e.g. "\n" -> newline // e.g. "\t" -> tab // e.g. "\r" -> carriage return // e.g. "\b" -> backspace // e.g. "\f" -> formfeed // e.g. "\a" -> alert // e.g. "\v" -> vertical tab // e.g. "\0" -> null // e.g. "\'" -> single quote // e.g. "\"" -> double quote // e.g. "\\" -> backslash // e.g. unicode characters like \u00A0 -> unicode func unescape(str string) string { output := []byte{} stringlen := len(str) for i := 0; i < stringlen; i++ { if str[i] == 92 { if i+1 < stringlen { i++ switch str[i] { case 110: output = append(output, 10) case 116: output = append(output, 9) case 114: output = append(output, 13) case 98: output = append(output, 8) case 102: output = append(output, 12) case 118: output = append(output, 11) case 120: output = append(output, 0) case 34: output = append(output, 34) case 39: output = append(output, 39) case 92: output = append(output, 92) case 85: val, err := strconv.Unquote(`"\U` + str[i+1:i+9] + `"`) if err == nil { output = append(output, val...) i += 8 } } } } else { output = append(output, str[i]) } } return string(output[:]) } func getParamNames(str string, line int) ([]string, any) { output := []string{} temp := []byte{} stringlen := len(str) for i := 0; i < stringlen; i++ { if str[i] == 44 { tempstr := strings.Trim(string(temp[:]), " ") if !variableonlyCompile.MatchString(tempstr) { return nil, ("invalid param name on line" + fmt.Sprint(line+1)) } output = append(output, tempstr) temp = []byte{} } else { temp = append(temp, str[i]) } } tempstr := strings.Trim(string(temp[:]), " ") if variableonlyCompile.MatchString(tempstr) { output = append(output, tempstr) } return output, nil } func split_by_semicolon_and_newline(str string) []code { output := []code{} temp := []byte{} line := 0 quote := false backtick := false speech := false str = strings.ReplaceAll(str, "\r", "") stringlen := len(str) for i := 0; i < stringlen; i++ { if str[i] == 34 && (len(temp) == 0 || str[i-1] != 92) && !speech && !backtick { quote = !quote } else if str[i] == 96 && (len(temp) == 0 || str[i-1] != 92) && !speech && !quote { backtick = !backtick } else if str[i] == 39 && (len(temp) == 0 || str[i-1] != 92) && !quote && !backtick { speech = !speech } if (str[i] == 59 && !quote && !speech && !backtick) || (str[i] == 10 && !backtick) { output = append(output, code{code: string(temp[:]), line: line}) if str[i] == 10 { line++ } temp = []byte{} } else { temp = append(temp, str[i]) } } output = append(output, code{code: string(temp[:]), line: line}) return output } var getCodeInIndent = func(i int, codearray []code, TYPE string) ([]code, int, any) { var result []code indent := 0 start := codearray[i] result = append(result, code{ code: strings.SplitN(codearray[i].code, "[", 2)[1], line: codearray[i].line, }) i++ for { if i == len(codearray) { return nil, 0, ("invalid opening brackets starting on line " + fmt.Sprint(start.line+1) + ": " + start.code) } if (TYPE != "try" && closeCompile.MatchString(codearray[i].code) && !setVariableCompile.MatchString(codearray[i].code) && !switchCloseCompile.MatchString(codearray[i].code)) || (TYPE == "if" && (elseCompile.MatchString(codearray[i].code) || elseifCompile.MatchString(codearray[i].code)) || (TYPE == "try" && catchCompile.MatchString(codearray[i].code))) { indent-- if indent < 0 { break } } else if opensIndent(codearray[i].code) { indent++ } result = append(result, codearray[i]) i++ } i++ return result, i, nil } var translateprocess = func(codeseg code) (any, bool, any) { for i := 0; i < len(opperators); i++ { for j := 0; j < len(opperators[i]); j++ { split := strings.Split(codeseg.code, opperators[i][j]) if len(split) > 1 { vals := []any{} current := 0 for k := 0; k < len(split); k++ { currentstr := strings.Join(split[current:k], opperators[i][j]) val, worked, _ := processfunc(code{ code: currentstr, line: codeseg.line, }) if worked { vals = append(vals, val) current = k } } if len(vals) >= 1 { currentstr := strings.Join(split[current:], opperators[i][j]) val, worked, err := processfunc(code{ code: currentstr, line: codeseg.line, }) if err != nil { return nil, false, err } if worked { vals = append(vals, val) return opperator{ t: i, vals: vals, line: codeseg.line, }, true, nil } } } } } if stringCompile.MatchString(codeseg.code) { return unescape(stringencode(codeseg.code)), true, nil } else if negetiveCompile.MatchString(codeseg.code) { str := strings.Trim(codeseg.code, " ") val, worked, err := processfunc(code{ code: str[1:], line: codeseg.line, }) if err != nil { return val, worked, err } if !worked { return nil, false, "invalid value on line " + fmt.Sprint(codeseg.line+1) } return opperator{ t: 11, vals: []any{0, val}, line: codeseg.line, }, true, nil } else if functionCompile.MatchString(codeseg.code) { str := strings.Trim(codeseg.code, " ") indent := 0 output := []byte{} for i := len(str) - 2; i >= 0; i-- { if str[i] == ')' { indent++ } else if str[i] == '(' { indent-- if indent < 0 { break } } output = append([]byte{str[i]}, output...) } params := string(output[:]) paramstranslated, worked, err := getValuesFromCommas(params, codeseg.line) if !worked { return nil, false, "invalid params on line " + fmt.Sprint(codeseg.line+1) } if err != nil { return nil, false, err } FUNC, worked, err := processfunc(code{ code: str[:len(str)-len(params)-2], line: codeseg.line, }) if !worked { return nil, false, "invalid value on line " + fmt.Sprint(codeseg.line+1) } if err != nil { return nil, false, err } if worked { return funcCallType{ FUNC: FUNC, args: paramstranslated, line: codeseg.line, }, true, nil } } else if bracketsCompile.MatchString(codeseg.code) { shorter := strings.Trim(codeseg.code, " ") shorter = shorter[1 : len(shorter)-1] brackets, ran, err := processfunc(code{ code: shorter, line: codeseg.line, }) return brackets, ran, err } else if itemsCompiled.MatchString(codeseg.code) { inside := strings.Trim(codeseg.code, " ")[1 : len(strings.Trim(codeseg.code, " "))-1] vals, worked, err := getValuesFromCommas(inside, codeseg.line) if err != nil { return nil, false, err } if worked { return itemsType{ vals: vals, line: codeseg.line, }, true, nil } } else if indexCompile.MatchString(codeseg.code) { str := strings.Trim(codeseg.code, " ") indent := 0 output := []byte{} for i := len(str) - 2; i >= 0; i-- { if str[i] == ']' { indent++ } else if str[i] == '[' { indent-- if indent < 0 { break } } output = append([]byte{str[i]}, output...) } value := string(output[:]) resp, worked, err := processfunc(code{ code: value, line: codeseg.line, }) if !worked { return nil, false, "invalid index value on line " + fmt.Sprint(codeseg.line+1) } if err != nil { return nil, false, err } index := resp resp, worked, err = processfunc(code{ code: str[:len(str)-len(value)-2], line: codeseg.line, }) if !worked { return nil, false, "invalid value on line " + fmt.Sprint(codeseg.line+1) } if err != nil { return nil, false, err } return indexType{ to: resp, index: index, line: codeseg.line, }, true, nil } else if numberCompile.MatchString(codeseg.code) { if s, err := strconv.ParseFloat(strings.Trim(codeseg.code, " "), 64); err == nil { return s, true, nil } else { return nil, false, err } } else if variableTextCompile.MatchString(codeseg.code) { return variable{ variable: strings.Trim(codeseg.code, " "), line: codeseg.line, }, true, nil } return nil, false, nil } var translateline = func(i int, codearray []code) (any, int, any) { codeseg := codearray[i] if returnStatementCompile.MatchString(codeseg.code) { var val any = strings.Trim(codeseg.code, " ")[6:] if val != "" { respval, worked, err := translateprocess(code{code: val.(string), line: codeseg.line}) if !worked || err != nil { return nil, 0, ("invalid value on line " + fmt.Sprint(codeseg.line+1)) } val = respval } else { val = nil } return returnType{ val: val, line: codeseg.line, }, i + 1, nil } else if breakStatementCompile.MatchString(codeseg.code) { return breakType{ line: codeseg.line, }, i + 1, nil } else if continueStatementCompile.MatchString(codeseg.code) { return continueType{ line: codeseg.line, }, i + 1, nil } else if skipCompile.MatchString(codeseg.code) { return nil, i + 1, nil } else if setVariableCompile.MatchString(codeseg.code) { VAR := strings.Split(codeseg.code, "=") firstSplit := strings.Split(strings.Trim(VAR[0], " "), " ") TYPE := "preset" if len(firstSplit) > 1 { TYPE = firstSplit[0] } vari := firstSplit[len(firstSplit)-1] resp, worked, err := translateprocess(code{code: VAR[1], line: codeseg.line}) if err != nil { return nil, 0, err } if !worked { return nil, 0, ("invalid value on line " + fmt.Sprint(codeseg.line+1) + ": " + VAR[1]) } return setVariable{ TYPE: TYPE, variable: variable{ variable: vari, line: codeseg.line, }, value: resp, line: codeseg.line, }, i + 1, nil } resp, worked, err := translateprocess(codeseg) if err != nil { return nil, 0, err } if worked { return resp, i + 1, nil } else if deleteStatmentCompile.MatchString(codeseg.code) { str := strings.Trim(codeseg.code, " ") return deleteType{ variable: variable{ variable: str[7:], line: codeseg.line, }, line: codeseg.line, }, i + 1, nil } else if whileCompile.MatchString(codeseg.code) { str := strings.Trim(codeseg.code, " ") strlen := len(str) str = str[5:strlen] split := strings.SplitN(str, "[", 2) condition, worked, err := translateprocess(code{code: split[0], line: codeseg.line}) if err != nil { return nil, 0, err } if !worked { return nil, 0, ("invalid value on line " + fmt.Sprint(codeseg.line+1)) } codedata, x, err := getCodeInIndent(i, codearray, "none") if err != nil { return nil, 0, err } codeoutput := []any{} codelen := len(codedata) for i := 0; i < codelen; { resp, x, err := linefunc(i, codedata) if err != nil { return nil, 0, err } i = x if resp != nil { codeoutput = append(codeoutput, resp) } } return whileLoop{ condition: condition, code: codeoutput, }, x, nil } else if tryCompile.MatchString(codeseg.code) { codedata, x, err := getCodeInIndent(i, codearray, "try") if err != nil { return nil, 0, err } codeoutput := []any{} codelen := len(codedata) for i := 0; i < codelen; { resp, x, err := linefunc(i, codedata) if err != nil { return nil, 0, err } i = x if resp != nil { codeoutput = append(codeoutput, resp) } } catchdata, x, err := getCodeInIndent(x-1, codearray, "none") if err != nil { return nil, 0, err } catchoutput := []any{} catchlen := len(catchdata) for i := 0; i < catchlen; { resp, x, err := linefunc(i, catchdata) if err != nil { return nil, 0, err } i = x if resp != nil { catchoutput = append(catchoutput, resp) } } return tryType{ code: codeoutput, catch: catchoutput, line: codeseg.line, }, x, nil } else if ifCompile.MatchString(codeseg.code) { str := strings.Trim(codeseg.code, " ") strlen := len(str) str = str[2:strlen] split := strings.SplitN(str, "[", 2) condition, worked, err := translateprocess(code{code: split[0], line: codeseg.line}) if err != nil { return nil, 0, err } if !worked { return nil, 0, ("invalid value on line " + fmt.Sprint(codeseg.line+1) + ": " + split[0]) } codedata, x, err := getCodeInIndent(i, codearray, "if") if err != nil { return nil, 0, err } codeoutput := []any{} codelen := len(codedata) for i := 0; i < codelen; { resp, x, err := linefunc(i, codedata) if err != nil { return nil, 0, err } i = x if resp != nil { codeoutput = append(codeoutput, resp) } } ifstatementcode := []iftype{ { condition: condition, code: codeoutput, }, } elsecode := []any{} for { if elseifCompile.MatchString(codearray[x-1].code) { str := strings.Trim(codearray[x-1].code, " ") str = strings.SplitN(str[1:len(str)-1], "if", 2)[1] condition, worked, err = translateprocess(code{code: str, line: codearray[x-1].line}) if err != nil { return nil, 0, err } if !worked { return nil, 0, "invalid value on line " + fmt.Sprint(codearray[x-1].line+1) + ": " + split[0] } codedata, j, err := getCodeInIndent(x-1, codearray, "if") if err != nil { return nil, 0, err } codeoutput := []any{} x = j codelen := len(codedata) for i := 0; i < codelen; { resp, x, err := linefunc(i, codedata) if err != nil { return nil, 0, err } i = x if resp != nil { codeoutput = append(codeoutput, resp) } } ifstatementcode = append(ifstatementcode, iftype{ condition: condition, code: codeoutput, }) } else { break } } if elseCompile.MatchString(codearray[x-1].code) { codedata, j, err := getCodeInIndent(x-1, codearray, "none") if err != nil { return nil, 0, err } x += j - 3 codelen := len(codedata) for i := 0; i < codelen; { resp, x, err := linefunc(i, codedata) if err != nil { return nil, 0, err } i = x if resp != nil { elsecode = append(elsecode, resp) } } } return ifstatement{ statments: ifstatementcode, FALSE: elsecode, }, x, nil } else if subCompile.MatchString(codeseg.code) { sub := strings.Trim(codeseg.code, " ") sub = sub[4:] split := strings.SplitN(sub, "[", 2) functioninfo := strings.SplitN(strings.Trim(split[0], " "), "(", 2) functionname := functioninfo[0] argstr := functioninfo[1] argstr = argstr[:len(argstr)-1] args, err := getParamNames(argstr, codeseg.line) if err != nil { return nil, 0, err } codedata, x, err := getCodeInIndent(i, codearray, "none") if err != nil { return nil, 0, err } codeoutput := []any{} codelen := len(codedata) for i := 0; i < codelen; { resp, x, err := linefunc(i, codedata) if err != nil { return nil, 0, err } i = x if resp != nil { codeoutput = append(codeoutput, resp) } } return setFunction{ name: functionname, FUNC: callableFunction{ name: functionname, args: args, code: codeoutput, line: codeseg.line, }, line: codeseg.line, }, x, nil } else if importCompile.MatchString(codeseg.code) { str := strings.Trim(codeseg.code, " ") var toImport any = nil var path any if strings.HasPrefix(str, "import") { if variableonlyCompile.MatchString(strings.Trim(str[7:], " ")) { path = strings.Trim(str[7:], " ") } else { p, worked, err := processfunc(code{code: str[7:], line: codeseg.line}) if err != nil { return nil, 0, err } path = p if !worked { return nil, 0, "invalid import path on line " + fmt.Sprint(codeseg.line+1) } } } else { info := strings.SplitN(str[5:], " import ", 2) if variableonlyCompile.MatchString(strings.Trim(info[0], " ")) { path = strings.Trim(info[0], " ") } else { p, worked, err := processfunc(code{code: info[0], line: codeseg.line}) if err != nil { return nil, 0, err } path = p if !worked { return nil, 0, "invalid import path on line " + fmt.Sprint(codeseg.line+1) } } toImport, err = getParamNames(info[1], codeseg.line) if err != nil { return nil, 0, err } } return importType{ path: path, toImport: toImport, line: codeseg.line, }, i + 1, nil } else if callErrorCompile.MatchString(codeseg.code) { str := strings.Trim(codeseg.code, " ") val, worked, err := processfunc(code{code: str[6:], line: codeseg.line}) if err != nil { return nil, 0, err } if worked { return errorType{ val, }, i + 1, nil } else { return nil, 0, "invalid error value on line " + fmt.Sprint(codeseg.line+1) } } else { err := "Invalid syntax on line " return nil, 0, ("\n\nLine " + fmt.Sprint(codeseg.line+1) + ": " + codeseg.code + "\n" + err + "\n\n") } } var translate = func(str string) ([]any, any) { code := split_by_semicolon_and_newline(str) codelen := len(code) output := []any{} i := 0 for i < codelen { resp, x, err := translateline(i, code) if err != nil { return nil, err } i = x if resp != nil { output = append(output, resp) } } return output, nil }