From 3fceaa4b62b20b682a843e3c39eea1865b822547 Mon Sep 17 00:00:00 2001 From: Bozhidar Date: Tue, 20 Aug 2024 23:54:31 +0300 Subject: [PATCH] add string functions --- interpreter/functions.go | 200 ++++++++++++++++++++++++++++++- interpreter/functions/strings.go | 145 ++++++++++++++++++++++ 2 files changed, 344 insertions(+), 1 deletion(-) create mode 100644 interpreter/functions/strings.go diff --git a/interpreter/functions.go b/interpreter/functions.go index e3351cf..b03c0ff 100644 --- a/interpreter/functions.go +++ b/interpreter/functions.go @@ -90,7 +90,6 @@ var builtins = map[string]builtinFunction{ "int": {intFunction, "int"}, "join": {joinFunction, "join"}, "len": {lenFunction, "len"}, - "lower": {lowerFunction, "lower"}, "echo": {echoFunction, "echo"}, "range": {rangeFunction, "range"}, "read": {readFunction, "read"}, @@ -101,7 +100,17 @@ var builtins = map[string]builtinFunction{ "explode": {explodeFunction, "explode"}, "str": {strFunction, "str"}, "type": {typeFunction, "type"}, + "lower": {lowerFunction, "lower"}, "upper": {upperFunction, "upper"}, + "upFirst": {upFirstFunction, "upFirst"}, + "upWords": {upWordsFunction, "upWords"}, + "lowerFirst": {lowerFirstFunction, "lowerFirst"}, + "lowerWords": {lowerWordsFunction, "lowerWords"}, + "camelCase": {camelCaseFunction, "camelCase"}, + "snakeCase": {snakeCaseFunction, "snakeCase"}, + "kebabCase": {kebabCaseFunction, "kebabCase"}, + "pascalCase": {pascalCaseFunction, "pascalCase"}, + "dotCase": {dotCaseFunction, "dotCase"}, "time": {timeFunction, "time"}, "fileGetContents": {fileGetContentsFunction, "fileGetContents"}, "httpRegister": {httpRegisterFunction, "httpRegister"}, @@ -703,6 +712,195 @@ func upperFunction(interp *interpreter, pos Position, args []Value) Value { panic(typeError(pos, "upper() requires a str")) } +/** + * function: upFirst + * args: string + * return: str + * example: upFirst("hello") => "Hello" + * output: "Hello" + * description: Convert the first character of a string to uppercase. + * title: Up First + * category: String + */ +func upFirstFunction(interp *interpreter, pos Position, args []Value) Value { + + ensureNumArgs(pos, "upFirstFunction", args, 1) + + if len(args) != 1 { + panic(fmt.Sprintf("upFirstFunction expected 1 argument, got %d", len(args))) + } + + str, ok := args[0].(string) + if !ok { + panic(fmt.Sprintf("upFirstFunction expected a string argument, got %T", args[0])) + } + + return Value(functions.UpFirst(str)) + +} + +/** + * function: upWords + * args: string + * return: str + * example: upWords("hello, world!") + * output: "Hello, World!" + * description: Convert all words in a string to uppercase. + * title: Up Words + * category: String + */ +func upWordsFunction(interp *interpreter, pos Position, args []Value) Value { + ensureNumArgs(pos, "upWords", args, 1) + + if s, ok := args[0].(string); ok { + return functions.UpWords(s) + } + + panic(typeError(pos, "upWords() requires a str")) +} + +/** + * function: lowerFirst + * args: string + * return: str + * example: lowerFirst("Hello") + * output: "hello" + * description: Convert the first character of a string to lowercase. + * title: Lower First + * category: String + */ +func lowerFirstFunction(interp *interpreter, pos Position, args []Value) Value { + + ensureNumArgs(pos, "lowerFirstFunction", args, 1) + if len(args) != 1 { + panic(fmt.Sprintf("lowerFirstFunction expected 1 argument, got %d", len(args))) + } + + str, ok := args[0].(string) + if !ok { + panic(fmt.Sprintf("lowerFirstFunction expected a string argument, got %T", args[0])) + } + + return functions.LowerFirst(str) + +} + +/** + * function: lowerWords + * args: string + * return: str + * example: lowerWords("Hello, World!") + * output: "hello, world!" + * description: Convert all words in a string to lowercase. + * title: Lower Words + * category: String + */ +func lowerWordsFunction(interp *interpreter, pos Position, args []Value) Value { + ensureNumArgs(pos, "lowerWords", args, 1) + if s, ok := args[0].(string); ok { + return functions.LowerWords(s) + } + panic(typeError(pos, "lowerWords() requires a str")) +} + +/** + * function: camelCase + * args: string + * return: str + * example: camelCase("Hello, World!") + * output: "helloWorld" + * description: Convert a string to camelCase. + * title: Camel Case + * category: String + */ +func camelCaseFunction(interp *interpreter, pos Position, args []Value) Value { + ensureNumArgs(pos, "camelCase", args, 1) + + if len(args) != 1 { + panic(fmt.Sprintf("camelCaseFunction expected 1 argument, got %d", len(args))) + } + + str, ok := args[0].(string) + if !ok { + panic(fmt.Sprintf("camelCaseFunction expected a string argument, got %T", args[0])) + } + + return functions.ToCamelCase(str) +} + +/** + * function: snakeCase + * args: string + * return: str + * example: snakeCase("Hello, World!") + * output: "hello_world" + * description: Convert a string to snake_case. + * title: Snake Case + * category: String + */ +func snakeCaseFunction(interp *interpreter, pos Position, args []Value) Value { + + ensureNumArgs(pos, "snakeCase", args, 1) + if s, ok := args[0].(string); ok { + return functions.ToSnakeCase(s) + } + panic(typeError(pos, "snakeCase() requires a str")) +} + +/** + * function: kebabCase + * args: string + * return: str + * example: kebabCase("Hello, World!") + * output: "hello-world" + * description: Convert a string to kebab-case. + * title: Kebab Case + * category: String + */ +func kebabCaseFunction(interp *interpreter, pos Position, args []Value) Value { + ensureNumArgs(pos, "kebabCase", args, 1) + if s, ok := args[0].(string); ok { + return functions.ToKebabCase(s) + } + panic(typeError(pos, "kebabCase() requires a str")) +} + +/** + * function: pascalCase + * args: string + * return: str + * example: pascalCase("Hello, World!") + * output: "HelloWorld" + * description: Convert a string to PascalCase. + * title: Pascal Case + * category: String + */ +func pascalCaseFunction(interp *interpreter, pos Position, args []Value) Value { + ensureNumArgs(pos, "pascalCase", args, 1) + if s, ok := args[0].(string); ok { + return functions.ToPascalCase(s) + } + panic(typeError(pos, "pascalCase() requires a str")) +} + +/** + * function: dotCase + * args: string + * return: str + * example: dotCase("Hello, World!") + * output: "hello.world" + * description: Convert a string to dot.case. + * title: Dot Case + * category: String + */ +func dotCaseFunction(interp *interpreter, pos Position, args []Value) Value { + ensureNumArgs(pos, "dotCase", args, 1) + if s, ok := args[0].(string); ok { + return functions.ToDotCase(s) + } + panic(typeError(pos, "dotCase() requires a str")) +} + /** * function: time * args: none diff --git a/interpreter/functions/strings.go b/interpreter/functions/strings.go new file mode 100644 index 0000000..bd08c51 --- /dev/null +++ b/interpreter/functions/strings.go @@ -0,0 +1,145 @@ +package functions + +import ( + "strings" + "unicode" +) + +// toCamelCase converts a string to camelCase. +func ToCamelCase(s string) string { + words := strings.Fields(s) + if len(words) == 0 { + return "" + } + + // Convert the first word to lowercase + words[0] = strings.ToLower(words[0]) + + // Capitalize the first letter of the remaining words + for i := 1; i < len(words); i++ { + words[i] = Capitalize(words[i]) + } + + return strings.Join(words, "") +} + +// capitalize capitalizes the first letter of a word. +func Capitalize(word string) string { + if len(word) == 0 { + return word + } + runes := []rune(word) + runes[0] = unicode.ToUpper(runes[0]) + return string(runes) +} + +// lowerFirst converts the first letter of the first word in the input string to lowercase. +func LowerFirst(s string) string { + if len(s) == 0 { + return s + } + + runes := []rune(s) + runes[0] = unicode.ToLower(runes[0]) + return string(runes) +} + +// lowerWords converts all words in the input string to lowercase. +func LowerWords(s string) string { + return strings.ToLower(s) +} + +// upFirst capitalizes the first letter of the first word in the input string. +func UpFirst(s string) string { + if len(s) == 0 { + return s + } + + runes := []rune(s) + runes[0] = unicode.ToUpper(runes[0]) + return string(runes) +} + +func UpWords(s string) string { + return strings.Title(s) +} + +// ToSnakeCase converts a string to snake_case. +func ToSnakeCase(s string) string { + var result []rune + for i, r := range s { + if unicode.IsUpper(r) { + // Add an underscore before uppercase letters (except at the start) + if i > 0 { + result = append(result, '_') + } + // Convert the letter to lowercase + r = unicode.ToLower(r) + } else if r == ' ' || r == '-' || r == '.' || r == ',' { + // Replace spaces and certain punctuation with underscores + r = '_' + } + result = append(result, r) + } + return string(result) +} + +// ToKebabCase converts a string to kebab-case. +func ToKebabCase(s string) string { + var result []rune + for i, r := range s { + if unicode.IsUpper(r) { + // Add a hyphen before uppercase letters (except at the start) + if i > 0 { + result = append(result, '-') + } + // Convert the letter to lowercase + r = unicode.ToLower(r) + } else if r == ' ' || r == '_' || r == '.' || r == ',' { + // Replace spaces and certain punctuation with hyphens + r = '-' + } + result = append(result, r) + } + return string(result) +} + +// ToPascalCase converts a string to PascalCase. +func ToPascalCase(s string) string { + var result []rune + capitalizeNext := true + + for _, r := range s { + if unicode.IsLetter(r) || unicode.IsDigit(r) { + if capitalizeNext { + r = unicode.ToUpper(r) + capitalizeNext = false + } else { + r = unicode.ToLower(r) + } + result = append(result, r) + } else { + capitalizeNext = true + } + } + return string(result) +} + +// ToDotCase converts a string to dot.case. +func ToDotCase(s string) string { + var result []rune + for i, r := range s { + if unicode.IsLetter(r) || unicode.IsDigit(r) { + if i > 0 && (unicode.IsUpper(r) || (!unicode.IsLetter([]rune(s)[i-1]) && unicode.IsLetter(r))) { + result = append(result, '.') + } + result = append(result, unicode.ToLower(r)) + } else if len(result) > 0 && result[len(result)-1] != '.' { + result = append(result, '.') + } + } + if len(result) > 0 && result[len(result)-1] == '.' { + result = result[:len(result)-1] // Remove trailing dot, if any + } + return string(result) +}