Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 114 additions & 10 deletions evaluator/builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"io"
"os"
"strconv"
"strings"

"github.com/NuruProgramming/Nuru/object"
Expand Down Expand Up @@ -40,17 +41,21 @@ var builtins = map[string]*object.Builtin{
Fn: func(args ...object.Object) object.Object {
if len(args) == 0 {
fmt.Println("")
} else {
var arr []string
for _, arg := range args {
if arg == nil {
return newError("Hauwezi kufanya operesheni hii")
}
arr = append(arr, arg.Inspect())
}
str := strings.Join(arr, " ")
fmt.Println(str)
return nil
}

if args[0].Type() != "NENO" {
// Try and give a meaningful message instead of this generic error
return newError(fmt.Sprintf("chaguo la kwanza linafaa kuwa neno, umepena %s", args[0].Type()))
}

str, err := formatStr(args[0], args[1:])
if err != nil {
return newError(err.Error())
}

fmt.Printf(str)

return nil
},
},
Expand Down Expand Up @@ -226,3 +231,102 @@ func getIntValue(obj object.Object) (int64, error) {
return 0, fmt.Errorf("expected integer, got %T", obj)
}
}

// Format the string like "Hello {0}", jina ...
func formatStr(format object.Object, options []object.Object) (string, error) {
var str strings.Builder
var val strings.Builder
var check_val bool
var opts_len int = len(options)

// This is to enable escaping the {} braces if you want them included
var escapeChar bool

type optM struct {
val bool
obj object.Object
}

var optionsMap = make(map[int]optM, opts_len)

// convert the options into a map (may not be the most efficient but we are not going fast)
for i, optm := range options {
optionsMap[i] = optM{val: false, obj: optm}
}

// Now go through the format string and do the replacement(s)
// this has approx time complexity of O(n) (bestest case)
for _, opt := range format.Inspect() {

if !escapeChar && opt == '\\' {
escapeChar = true
continue
}

if opt == '{' && !escapeChar {
check_val = true
continue
}

if escapeChar {
if opt != '{' && opt != '}' {
str.WriteRune('\\')
}
escapeChar = false
}

if check_val && opt == '}' {
vstr := strings.TrimSpace(val.String()) // remove accidental spaces

// check the value and try to convert to int
arrv, err := strconv.Atoi(vstr)
if err != nil {
// return non-cryptic go errors
return "", fmt.Errorf(fmt.Sprintf("Ulichopeana si NAMBA, jaribu tena: `%s'", vstr))
}

oVal, exists := optionsMap[arrv]

if !exists {
return "", fmt.Errorf(fmt.Sprintf("Nambari ya chaguo unalolitaka %d ni kubwa kuliko ulizopeana (%d)", arrv, opts_len))
}

str.WriteString(oVal.obj.Inspect())
// may be innefficient
optionsMap[arrv] = optM{val: true, obj: oVal.obj}

check_val = false
val.Reset()
continue
}

if check_val {
val.WriteRune(opt)
continue
}

str.WriteRune(opt)
}

// A check if they never closed the formatting braces e.g '{0'
if check_val {
return "", fmt.Errorf(fmt.Sprintf("Haukufunga '{', tuliokota kabla ya kufika mwisho `%s'", val.String()))
}

// Another innefficient loop
for _, v := range optionsMap {
if !v.val {
return "", fmt.Errorf(fmt.Sprintf("Ulipeana hili chaguo (%s) {%s} lakini haukutumia", v.obj.Inspect(), v.obj.Type()))
}
}

// !start
// Here we can talk about wtf just happened
// 3 loops to do just formatting, formatting for codes sake.
// it can be done in 2 loops, the last one is just to confirm that you didn't forget anything.
// this is not required but a nice syntatic sugar, we are not here for speed, so ergonomics matter instead of speed
// finally we can say we are slower than python!, what an achievement
// done!

return str.String(), nil
}