diff --git a/main.go b/main.go index a39aec0..0575f8e 100755 --- a/main.go +++ b/main.go @@ -41,7 +41,7 @@ func main() { args := os.Args if len(args) < 2 { - help := styles.HelpStyle.Render("šŸ’” Use exit() or quit() to exit") + help := styles.HelpStyle.Render("šŸ’” Use exit() to exit") fmt.Println(lipgloss.JoinVertical(lipgloss.Left, NewLogo, "\n", help)) repl.Start() return diff --git a/object/error.go b/object/error.go index 1ebcb11..a2736c6 100755 --- a/object/error.go +++ b/object/error.go @@ -1,13 +1,20 @@ package object -import "fmt" +import ( + "fmt" +) +// Error represents a custom error type with an associated message. type Error struct { Message string } +// Inspect returns a formatted string representation of the error. func (e *Error) Inspect() string { - msg := fmt.Sprintf("\x1b[%dm%s\x1b[0m", 31, "Error: ") - return msg + e.Message + return fmt.Sprintf("\x1b[1;31mError:\x1b[0m %s", e.Message) +} + +// Type returns the object type of the error. +func (e *Error) Type() ObjectType { + return ERROR_OBJ } -func (e *Error) Type() ObjectType { return ERROR_OBJ } diff --git a/object/strings.go b/object/strings.go index e0b71a0..6cf61fa 100755 --- a/object/strings.go +++ b/object/strings.go @@ -1,40 +1,48 @@ package object import ( - "fmt" + // "fmt" "hash/fnv" - "strconv" + // "strconv" "strings" ) -// String represents a string object in the system +// String represents a string object in the system. type String struct { Value string offset int } -func (s *String) Inspect() string { return s.Value } +// Inspect returns the string representation of the String object. +func (s *String) Inspect() string { return s.Value } + +// Type returns the object type for String. func (s *String) Type() ObjectType { return STRING_OBJ } +// HashKey generates a hash key for the String object. func (s *String) HashKey() HashKey { h := fnv.New64a() h.Write([]byte(s.Value)) return HashKey{Type: s.Type(), Value: h.Sum64()} } +// Next implements an iterator for the String, returning the next character and its index. func (s *String) Next() (Object, Object) { - offset := s.offset - if len(s.Value) > offset { - s.offset = offset + 1 - return &Integer{Value: int64(offset)}, &String{Value: string(s.Value[offset])} + if s.offset < len(s.Value) { + char := string(s.Value[s.offset]) + index := &Integer{Value: int64(s.offset)} + s.offset++ + return index, &String{Value: char} } return nil, nil } +// Reset resets the iterator offset to the beginning of the string. func (s *String) Reset() { s.offset = 0 } +// Method dynamically dispatches string-related methods. func (s *String) Method(method string, args []Object) Object { switch method { case "len": @@ -45,131 +53,132 @@ func (s *String) Method(method string, args []Object) Object { return s.lower(args) case "split": return s.split(args) - case "format": - return s.format(args) + case "trim": + return s.trim(args) + case "contains": + return s.contains(args) + case "replace": + return s.replace(args) + case "reverse": + return s.reverse(args) + // case "format": + // return s.format(args) default: return newError("Method '%s' is not supported on strings", method) } } +// len returns the length of the string. func (s *String) len(args []Object) Object { if len(args) != 0 { - return newError("Expected 0 arguments, but got %d", len(args)) + return newError("len() expects 0 arguments, got %d", len(args)) } return &Integer{Value: int64(len(s.Value))} } +// upper converts the string to uppercase. func (s *String) upper(args []Object) Object { if len(args) != 0 { - return newError("Expected 0 arguments, but got %d", len(args)) + return newError("upper() expects 0 arguments, got %d", len(args)) } return &String{Value: strings.ToUpper(s.Value)} } +// lower converts the string to lowercase. func (s *String) lower(args []Object) Object { if len(args) != 0 { - return newError("Expected 0 arguments, but got %d", len(args)) + return newError("lower() expects 0 arguments, got %d", len(args)) } return &String{Value: strings.ToLower(s.Value)} } +// split splits the string by a given delimiter. func (s *String) split(args []Object) Object { - if len(args) > 1 { - return newError("Expected 1 or 0 arguments, but got %d", len(args)) - } - sep := " " + sep := "" if len(args) == 1 { - strArg, ok := args[0].(*String) + arg, ok := args[0].(*String) if !ok { - return newError("Expected argument of type STRING, but got %s", args[0].Type()) + return newError("split() expects a STRING argument, got %s", args[0].Type()) } - sep = strArg.Value + sep = arg.Value + } else if len(args) > 1 { + return newError("split() expects at most 1 argument, got %d", len(args)) } + parts := strings.Split(s.Value, sep) elements := make([]Object, len(parts)) - for i, v := range parts { - elements[i] = &String{Value: v} + for i, part := range parts { + elements[i] = &String{Value: part} } return &Array{Elements: elements} } -func (s *String) format(args []Object) Object { - value, err := formatStr(s.Value, args) - if err != nil { - return newError(err.Error()) - } - return &String{Value: value} -} - -func formatStr(format string, options []Object) (string, error) { - var str, val strings.Builder - checkVal := false - escapeChar := false - optsLen := len(options) - - type optM struct { - used bool - obj Object +// trim removes leading and trailing whitespace or specified characters. +func (s *String) trim(args []Object) Object { + if len(args) > 1 { + return newError("trim() expects at most 1 argument, got %d", len(args)) } - optionsMap := make(map[int]optM, optsLen) - for i, opt := range options { - optionsMap[i] = optM{used: false, obj: opt} + chars := " \t\n\r" + if len(args) == 1 { + arg, ok := args[0].(*String) + if !ok { + return newError("trim() expects a STRING argument, got %s", args[0].Type()) + } + chars = arg.Value } - for _, ch := range format { - if !escapeChar && ch == '\\' { - escapeChar = true - continue - } + return &String{Value: strings.Trim(s.Value, chars)} +} - if ch == '{' && !escapeChar { - checkVal = true - continue - } +// contains checks if the string contains a given substring. +func (s *String) contains(args []Object) Object { + if len(args) != 1 { + return newError("contains() expects 1 argument, got %d", len(args)) + } - if escapeChar { - if ch != '{' && ch != '}' { - str.WriteRune('\\') - } - escapeChar = false - } + arg, ok := args[0].(*String) + if !ok { + return newError("contains() expects a STRING argument, got %s", args[0].Type()) + } - if checkVal && ch == '}' { - index, err := strconv.Atoi(strings.TrimSpace(val.String())) - if err != nil { - return "", fmt.Errorf("invalid placeholder: `%s` is not a number", val.String()) - } - if index >= optsLen { - return "", fmt.Errorf("placeholder index %d exceeds available arguments (%d)", index, optsLen) - } - - opt := optionsMap[index] - str.WriteString(opt.obj.Inspect()) - optionsMap[index] = optM{used: true, obj: opt.obj} - - checkVal = false - val.Reset() - continue - } + return &Boolean{Value: strings.Contains(s.Value, arg.Value)} +} - if checkVal { - val.WriteRune(ch) - continue - } +// replace replaces occurrences of a substring with another substring. +func (s *String) replace(args []Object) Object { + if len(args) != 2 { + return newError("replace() expects 2 arguments, got %d", len(args)) + } - str.WriteRune(ch) + old, ok1 := args[0].(*String) + new, ok2 := args[1].(*String) + if !ok1 || !ok2 { + return newError("replace() expects STRING arguments, got %s and %s", args[0].Type(), args[1].Type()) } - if checkVal { - return "", fmt.Errorf("unmatched '{' detected: `%s`", val.String()) + return &String{Value: strings.ReplaceAll(s.Value, old.Value, new.Value)} +} + +// reverse reverses the string. +func (s *String) reverse(args []Object) Object { + if len(args) != 0 { + return newError("reverse() expects 0 arguments, got %d", len(args)) } - for i, opt := range optionsMap { - if !opt.used { - return "", fmt.Errorf("argument at index %d (%s) was provided but not used", i, opt.obj.Inspect()) - } + runes := []rune(s.Value) + for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 { + runes[i], runes[j] = runes[j], runes[i] } - return str.String(), nil + return &String{Value: string(runes)} } + +// format applies formatting to the string with provided arguments. +// func (s *String) format(args []Object) Object { +// value, err := formatStr(s.Value, args) +// if err != nil { +// return newError(err.Error()) +// } +// return &String{Value: value} +// } diff --git a/repl/repl.go b/repl/repl.go index 04c1d28..0d43733 100755 --- a/repl/repl.go +++ b/repl/repl.go @@ -69,16 +69,16 @@ type dummy struct { func (d *dummy) executor(in string) { if strings.TrimSpace(in) == "exit()" { style := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#FFD700")). // Gold text - Background(lipgloss.Color("#282C34")). // Dark background - Bold(true). - Padding(1, 2). - Margin(1). - Border(lipgloss.DoubleBorder()). - BorderForeground(lipgloss.Color("#FF4500")) // Bright orange border - - message := style.Render("\nšŸ”„ Thank you for using Vint! Goodbye and happy coding! šŸ”„") - fmt.Println(message) + Foreground(lipgloss.Color("#FFD700")). // Gold text + Background(lipgloss.Color("#282C34")). // Dark background + Bold(true). + Padding(1, 2). + Margin(1). + Border(lipgloss.DoubleBorder()). + BorderForeground(lipgloss.Color("#FF4500")) // Bright orange border + + message := style.Render("\nThank you for using Vint! Goodbye and happy coding!") + fmt.Println(message) os.Exit(0) } l := lexer.New(in) @@ -107,7 +107,6 @@ func completer(in prompt.Document) []prompt.Suggest { func Docs() { zone.NewGlobal() - // Removed the Swahili language option languageChoice := []list.Item{ languages{title: "English", desc: "Read documentation in English", dir: "en"}, }