diff --git a/ast/ast.go b/ast/ast.go index c42241f..ba9a009 100644 --- a/ast/ast.go +++ b/ast/ast.go @@ -565,6 +565,7 @@ func (me *MethodExpression) String() string { type Import struct { Token token.Token + Filename string Identifiers map[string]*Identifier } @@ -617,6 +618,14 @@ func (p *Package) String() string { return out.String() } +type NoOp struct { + Token token.Token +} + +func (np *NoOp) expressionNode() {} +func (np *NoOp) TokenLiteral() string { return np.Token.Literal } +func (np *NoOp) String() string { return "" } + type At struct { Token token.Token } @@ -644,4 +653,4 @@ type PropertyExpression struct { func (pe *PropertyExpression) expressionNode() {} func (pe *PropertyExpression) TokenLiteral() string { return pe.Token.Literal } -func (pe *PropertyExpression) String() string { return "Ngl I'm tired part two" } +func (pe *PropertyExpression) String() string { return "Property Expression" } diff --git a/evaluator/assign.go b/evaluator/assign.go index f867321..b949619 100644 --- a/evaluator/assign.go +++ b/evaluator/assign.go @@ -6,6 +6,12 @@ import ( ) func evalAssign(node *ast.Assign, env *object.Environment) object.Object { + // Preserve the name of the Function for use in later cases + switch node.Value.(type) { + case *ast.FunctionLiteral: + node.Value.(*ast.FunctionLiteral).Name = node.Name.Value + } + val := Eval(node.Value, env) if isError(val) { return val diff --git a/evaluator/evaluator.go b/evaluator/evaluator.go index 5fd219d..7869348 100644 --- a/evaluator/evaluator.go +++ b/evaluator/evaluator.go @@ -124,8 +124,6 @@ func Eval(node ast.Node, env *object.Environment) object.Object { // return evalForExpression(node, env) case *ast.ForIn: return evalForInExpression(node, env, node.Token.Line) - case *ast.Package: - return evalPackage(node, env) case *ast.PropertyExpression: return evalPropertyExpression(node, env) case *ast.PropertyAssignment: @@ -298,7 +296,13 @@ func applyFunction(fn object.Object, args []object.Object, line int) object.Obje if fn != nil { return newError("Mstari %d: Hiki sio kiendesha: %s", line, fn.Type()) } else { - return newError("Bro how did you even get here??? Contact language maker asap!") + // Commit a484f5ca37d3f38b72f78b00b27a341d4d09a247 introduced a way to trigger this. + // My guess is how white spaces are processed. + return newError(`Hii ni muundo ambayo programu yako haifai kuwa. Tumejaribu miundo mingine lakini programu yako haieleweki. + +Tuma sehemu ya programu ilifananya hii kosa kuonyeshwa hapa: +https://github.com/nuruprogramming/Nuru/issues +`) } } diff --git a/evaluator/evaluator_test.go b/evaluator/evaluator_test.go index b6e830d..bd989d7 100644 --- a/evaluator/evaluator_test.go +++ b/evaluator/evaluator_test.go @@ -3,7 +3,6 @@ package evaluator import ( "fmt" "testing" - "time" "github.com/NuruProgramming/Nuru/lexer" "github.com/NuruProgramming/Nuru/object" @@ -108,7 +107,7 @@ func TestBangOperator(t *testing.T) { func testEval(input string) object.Object { l := lexer.New(input) - p := parser.New(l) + p := parser.New(l, "") program := p.ParseProgram() env := object.NewEnvironment() @@ -1111,6 +1110,8 @@ func TestStringMethods(t *testing.T) { } } +// Should be updated to test new module model +/* func TestTimeModule(t *testing.T) { input := ` tumia muda @@ -1127,4 +1128,4 @@ func TestTimeModule(t *testing.T) { if err != nil { t.Errorf("Wrong time value: got=%v", err) } -} +} */ diff --git a/evaluator/import.go b/evaluator/import.go index 8b5eb23..c3b4829 100644 --- a/evaluator/import.go +++ b/evaluator/import.go @@ -2,42 +2,130 @@ package evaluator import ( "fmt" + "log" "os" "path/filepath" + "runtime" "strings" "github.com/NuruProgramming/Nuru/ast" "github.com/NuruProgramming/Nuru/lexer" - "github.com/NuruProgramming/Nuru/module" + "github.com/NuruProgramming/Nuru/object" "github.com/NuruProgramming/Nuru/parser" ) var searchPaths []string +type maktaba int + +const ( + MAKTABA_YA_NURU maktaba = iota + MAKTABA_YA_HAPA + MAKTABA_YA_NJE +) + func evalImport(node *ast.Import, env *object.Environment) object.Object { + /* + Maktaba yanafuata mkondo wa: + msingi => ni maktaba ya kawaida (stdlib) + hapa => ni maktaba haya, tunaanza na faili "nuru.toml" (ama faili ya kisawa) + _ => majina mengine ni ya maktaba ya nje + */ + + // Go through the Identifiers for k, v := range node.Identifiers { - if mod, ok := module.Mapper[v.Value]; ok { - env.Set(k, mod) - } else { - return evalImportFile(k, v, env) + vs := strings.Split(v.Value, "/") + if len(vs) <= 0 { + break + } + + switch vs[0] { + case "msingi": + loc := mahali_pa_maktaba(MAKTABA_YA_NURU) + + ss := strings.Split(v.Value, "/")[1:] + fi := filepath.Join(loc, filepath.Join(ss...)) + + return evalImportFile(k, &ast.Identifier{Value: fi}, env) + case "hapa": + loc := mahali_pa_maktaba(MAKTABA_YA_HAPA, node.Filename) + + ss := strings.Split(v.Value, "/")[1:] + fi := filepath.Join(loc, filepath.Join(ss...)) + + return evalImportFile(k, &ast.Identifier{Value: fi}, env) + default: + log.Printf("Maktaba ya nje hazijazaidiwa '%s'\n", k) + return NULL } } + return NULL } +func mahali_pa_maktaba(mkt maktaba, mahali ...string) string { + switch mkt { + case MAKTABA_YA_NURU: + loc := os.Getenv("MAKTABA_YA_NURU") + if len(loc) > 0 { + return loc + } + + var usr_lib string + var lib_ string + + if runtime.GOOS == "windows" { + uhd, _ := os.UserHomeDir() + usr_lib = filepath.Join(filepath.Base(uhd), "nuru", "maktaba") + } else { + usr_lib = "/usr/lib/nuru/maktaba" + lib_ = "/lib/nuru/maktaba" + } + if fileExists(usr_lib) { + return usr_lib + } + if fileExists(lib_) { + return lib_ + } + + return usr_lib + case MAKTABA_YA_HAPA: + // Hii tunahitaji kuenda nyuma hadi mahali tutakapo pata faili "nuru.toml" + var mkt__ string = mahali[0] + var nuru_config string = "nuru.toml" + + // Check if the current dir has "nuru.toml" + for { + if filepath.Dir(mkt__) == mkt__ { + break + } + + if fileExists(filepath.Join(mkt__, nuru_config)) { + return mkt__ + } + + mkt__ = filepath.Dir(mkt__) + } + + return mkt__ + default: + return "" + } +} + func evalImportFile(name string, ident *ast.Identifier, env *object.Environment) object.Object { addSearchPath("") - filename := findFile(name) + filename := findFile(ident.Value) if filename == "" { return newError("Moduli %s haipo", name) } var scope *object.Environment - scope, err := evaluateFile(filename, env) + scope, err := evaluateFile(name, filename, env) if err != nil { return err } - return importFile(name, ident, env, scope) + return importFile(name, env, scope) } func addSearchPath(path string) { @@ -45,7 +133,7 @@ func addSearchPath(path string) { } func findFile(name string) string { - basename := fmt.Sprintf("%s.nr", name) + basename := fmt.Sprintf("%s.nuru", name) for _, path := range searchPaths { file := filepath.Join(path, basename) if fileExists(file) { @@ -60,28 +148,36 @@ func fileExists(file string) bool { return err == nil } -func evaluateFile(file string, env *object.Environment) (*object.Environment, object.Object) { +func evaluateFile(name, file string, env *object.Environment) (*object.Environment, object.Object) { source, err := os.ReadFile(file) if err != nil { return nil, &object.Error{Message: fmt.Sprintf("Tumeshindwa kufungua pakeji: %s", file)} } l := lexer.New(string(source)) - p := parser.New(l) + p := parser.New(l, file) program := p.ParseProgram() if len(p.Errors()) != 0 { return nil, &object.Error{Message: fmt.Sprintf("Pakeji %s ina makosa yafuatayo:\n%s", file, strings.Join(p.Errors(), "\n"))} } - scope := object.NewEnvironment() - result := Eval(program, scope) + pkg := &object.Package{ + Name: &ast.Identifier{Value: name}, + Env: env, + Scope: object.NewEnclosedEnvironment(env), + } + + result := Eval(program, pkg.Scope) + + env.Set(name, pkg) + if isError(result) { return nil, result } - return scope, nil + return pkg.Env, nil } -func importFile(name string, ident *ast.Identifier, env *object.Environment, scope *object.Environment) object.Object { - value, ok := scope.Get(ident.Value) +func importFile(name string, env *object.Environment, scope *object.Environment) object.Object { + value, ok := scope.Get(name) if !ok { return newError("%s sio pakeji", name) } diff --git a/evaluator/method.go b/evaluator/method.go index 2bf2bd6..8d99c89 100644 --- a/evaluator/method.go +++ b/evaluator/method.go @@ -40,10 +40,6 @@ func applyMethod(obj object.Object, method ast.Expression, args []object.Object, default: return obj.Method(method.(*ast.Identifier).Value, args) } - case *object.Module: - if fn, ok := obj.Functions[method.(*ast.Identifier).Value]; ok { - return fn(args, defs) - } case *object.Instance: if fn, ok := obj.Package.Scope.Get(method.(*ast.Identifier).Value); ok { fn.(*object.Function).Env.Set("@", obj) diff --git a/evaluator/package.go b/evaluator/package.go deleted file mode 100644 index 07e9bee..0000000 --- a/evaluator/package.go +++ /dev/null @@ -1,18 +0,0 @@ -package evaluator - -import ( - "github.com/NuruProgramming/Nuru/ast" - "github.com/NuruProgramming/Nuru/object" -) - -func evalPackage(node *ast.Package, env *object.Environment) object.Object { - pakeji := &object.Package{ - Name: node.Name, - Env: env, - Scope: object.NewEnclosedEnvironment(env), - } - - Eval(node.Block, pakeji.Scope) - env.Set(node.Name.Value, pakeji) - return pakeji -} diff --git a/evaluator/property.go b/evaluator/property.go index c935c4f..6801ae0 100644 --- a/evaluator/property.go +++ b/evaluator/property.go @@ -10,6 +10,7 @@ func evalPropertyExpression(node *ast.PropertyExpression, env *object.Environmen if isError(left) { return left } + switch left.(type) { case *object.Instance: obj := left.(*object.Instance) @@ -20,7 +21,8 @@ func evalPropertyExpression(node *ast.PropertyExpression, env *object.Environmen case *object.Package: obj := left.(*object.Package) prop := node.Property.(*ast.Identifier).Value - if val, ok := obj.Env.Get(prop); ok { + + if val, ok := obj.Scope.Get(prop); ok { return val } // case *object.Module: @@ -30,7 +32,7 @@ func evalPropertyExpression(node *ast.PropertyExpression, env *object.Environmen // return val() // } } - return newError("Value %s sii sahihi kwenye %s", node.Property.(*ast.Identifier).Value, left.Inspect()) + return newError("Thamani %s sii sahihi kwenye %s", node.Property.(*ast.Identifier).Value, left.Inspect()) } func evalPropertyAssignment(name *ast.PropertyExpression, val object.Object, env *object.Environment) object.Object { diff --git a/lexer/lexer.go b/lexer/lexer.go index 7d83728..9cc2822 100644 --- a/lexer/lexer.go +++ b/lexer/lexer.go @@ -33,7 +33,9 @@ func (l *Lexer) readChar() { func (l *Lexer) NextToken() token.Token { var tok token.Token - l.skipWhitespace() + if !(l.ch == '\n' || l.ch == '\r') { + l.skipWhitespace() + } if l.ch == rune('/') && l.peekChar() == rune('/') { l.skipSingleLineComment() return l.NextToken() @@ -54,6 +56,9 @@ func (l *Lexer) NextToken() token.Token { } case rune(';'): tok = newToken(token.SEMICOLON, l.line, l.ch) + case rune('\n'), rune('\r'): + tok = newToken(token.NEWLINE, l.line, l.ch) + l.line++ case rune('('): tok = newToken(token.LPAREN, l.line, l.ch) case rune(')'): diff --git a/lexer/lexer_test.go b/lexer/lexer_test.go index a9252bd..f257105 100644 --- a/lexer/lexer_test.go +++ b/lexer/lexer_test.go @@ -61,16 +61,20 @@ func TestNextToken(t *testing.T) { expectedType token.TokenType expectedLiteral string }{ + {token.NEWLINE, "\n"}, {token.LET, "fanya"}, {token.IDENT, "tano"}, {token.ASSIGN, "="}, {token.INT, "5"}, {token.SEMICOLON, ";"}, + {token.NEWLINE, "\n"}, {token.LET, "fanya"}, {token.IDENT, "kumi"}, {token.ASSIGN, "="}, {token.INT, "10"}, {token.SEMICOLON, ";"}, + {token.NEWLINE, "\n"}, + {token.NEWLINE, "\n"}, {token.LET, "fanya"}, {token.IDENT, "jumla"}, {token.ASSIGN, "="}, @@ -81,12 +85,16 @@ func TestNextToken(t *testing.T) { {token.IDENT, "y"}, {token.RPAREN, ")"}, {token.LBRACE, "{"}, + {token.NEWLINE, "\n"}, {token.IDENT, "x"}, {token.PLUS, "+"}, {token.IDENT, "y"}, {token.SEMICOLON, ";"}, + {token.NEWLINE, "\n"}, {token.RBRACE, "}"}, {token.SEMICOLON, ";"}, + {token.NEWLINE, "\n"}, + {token.NEWLINE, "\n"}, {token.LET, "fanya"}, {token.IDENT, "jibu"}, {token.ASSIGN, "="}, @@ -97,17 +105,22 @@ func TestNextToken(t *testing.T) { {token.IDENT, "kumi"}, {token.RPAREN, ")"}, {token.SEMICOLON, ";"}, + {token.NEWLINE, "\n"}, + {token.NEWLINE, "\n"}, {token.BANG, "!"}, {token.MINUS, "-"}, {token.SLASH, "/"}, {token.INT, "5"}, {token.SEMICOLON, ";"}, + {token.NEWLINE, "\n"}, {token.INT, "5"}, {token.LT, "<"}, {token.INT, "10"}, {token.GT, ">"}, {token.INT, "5"}, {token.SEMICOLON, ";"}, + {token.NEWLINE, "\n"}, + {token.NEWLINE, "\n"}, {token.IF, "kama"}, {token.LPAREN, "("}, {token.INT, "5"}, @@ -115,63 +128,85 @@ func TestNextToken(t *testing.T) { {token.INT, "10"}, {token.RPAREN, ")"}, {token.LBRACE, "{"}, + {token.NEWLINE, "\n"}, {token.RETURN, "rudisha"}, {token.TRUE, "kweli"}, {token.SEMICOLON, ";"}, + {token.NEWLINE, "\n"}, {token.RBRACE, "}"}, {token.ELSE, "sivyo"}, {token.LBRACE, "{"}, + {token.NEWLINE, "\n"}, {token.RETURN, "rudisha"}, {token.FALSE, "sikweli"}, {token.SEMICOLON, ";"}, + {token.NEWLINE, "\n"}, {token.RBRACE, "}"}, + {token.NEWLINE, "\n"}, + {token.NEWLINE, "\n"}, {token.INT, "10"}, {token.EQ, "=="}, {token.INT, "10"}, {token.SEMICOLON, ";"}, + {token.NEWLINE, "\n"}, {token.INT, "10"}, {token.NOT_EQ, "!="}, {token.INT, "9"}, {token.SEMICOLON, ";"}, {token.INT, "5"}, + {token.NEWLINE, "\n"}, {token.STRING, "bangi"}, + {token.NEWLINE, "\n"}, {token.STRING, "ba ngi"}, + {token.NEWLINE, "\n"}, {token.LBRACKET, "["}, {token.INT, "1"}, {token.COMMA, ","}, {token.INT, "2"}, {token.RBRACKET, "]"}, {token.SEMICOLON, ";"}, + {token.NEWLINE, "\n"}, {token.LBRACE, "{"}, {token.STRING, "mambo"}, {token.COLON, ":"}, {token.STRING, "vipi"}, {token.RBRACE, "}"}, + {token.NEWLINE, "\n"}, {token.DOT, "."}, {token.IMPORT, "tumia"}, {token.IDENT, "muda"}, + {token.NEWLINE, "\n"}, {token.SWITCH, "badili"}, {token.LPAREN, "("}, {token.IDENT, "a"}, {token.RPAREN, ")"}, {token.LBRACE, "{"}, + {token.NEWLINE, "\n"}, {token.CASE, "ikiwa"}, {token.INT, "2"}, {token.LBRACE, "{"}, + {token.NEWLINE, "\n"}, {token.IDENT, "andika"}, {token.LPAREN, "("}, {token.INT, "2"}, {token.RPAREN, ")"}, + {token.NEWLINE, "\n"}, {token.RBRACE, "}"}, + {token.NEWLINE, "\n"}, {token.DEFAULT, "kawaida"}, {token.LBRACE, "{"}, + {token.NEWLINE, "\n"}, {token.IDENT, "andika"}, {token.LPAREN, "("}, {token.INT, "0"}, {token.RPAREN, ")"}, + {token.NEWLINE, "\n"}, {token.RBRACE, "}"}, + {token.NEWLINE, "\n"}, {token.RBRACE, "}"}, + {token.NEWLINE, "\n"}, {token.NULL, "tupu"}, + {token.NEWLINE, "\n"}, {token.FOR, "kwa"}, {token.IDENT, "i"}, {token.COMMA, ","}, diff --git a/main.go b/main.go index ba00894..fc697d9 100644 --- a/main.go +++ b/main.go @@ -52,14 +52,14 @@ func main() { default: file := args[1] - if strings.HasSuffix(file, "nr") || strings.HasSuffix(file, ".sw") { + if strings.HasSuffix(file, "nr") || strings.HasSuffix(file, ".sw") || strings.HasSuffix(file, ".nuru") { contents, err := os.ReadFile(file) if err != nil { fmt.Println(styles.ErrorStyle.Render("Error: Nuru imeshindwa kusoma faili: ", args[1])) os.Exit(1) } - repl.Read(string(contents)) + repl.Read(file, string(contents)) } else { fmt.Println(styles.ErrorStyle.Render("'"+file+"'", "sii faili sahihi. Tumia faili la '.nr' au '.sw'")) os.Exit(1) diff --git a/maktaba/hisabati.nuru b/maktaba/hisabati.nuru new file mode 100644 index 0000000..6fa1e32 --- /dev/null +++ b/maktaba/hisabati.nuru @@ -0,0 +1,2 @@ +// Hii ni thamni ya PI +PI = 3.141592653589793 diff --git a/maktaba/nuru.toml b/maktaba/nuru.toml new file mode 100644 index 0000000..976ab7c --- /dev/null +++ b/maktaba/nuru.toml @@ -0,0 +1,8 @@ +[pakeji] +jina = "msingi" +mahali = "https://github.com/NuruProgramming/Nuru" +fafanuo = "Hii ni maktaba ya msingi ya nuru" +leseni = "GPLv2" + +[inategemea] +# Pakeji hii haitegemei pakeji yeyote ye nje diff --git a/module/hisabati.go b/module/hisabati.go deleted file mode 100644 index c1d84f1..0000000 --- a/module/hisabati.go +++ /dev/null @@ -1,731 +0,0 @@ -package module - -import ( - "math" - "math/rand" - "time" - - "github.com/NuruProgramming/Nuru/object" -) - -var MathFunctions = map[string]object.ModuleFunction{ - "PI": pi, - "e": e, - "phi": phi, - "ln10": ln10, - "ln2": ln2, - "log10e": log10e, - "log2e": log2e, - "log2": log2, - "sqrt1_2": sqrt1_2, - "sqrt2": sqrt2, - "sqrt3": sqrt3, - "sqrt5": sqrt5, - "EPSILON": epsilon, - "abs": abs, - "sign": sign, - "ceil": ceil, - "floor": floor, - "sqrt": sqrt, - "cbrt": cbrt, - "root": root, - "hypot": hypot, - "random": random, - "factorial": factorial, - "round": round, - "max": max, - "min": min, - "exp": exp, - "expm1": expm1, - "log": log, - "log10": log10, - "log1p": log1p, - "cos": cos, - "sin": sin, - "tan": tan, - "acos": acos, - "asin": asin, - "atan": atan, - "cosh": cosh, - "sinh": sinh, - "tanh": tanh, - "acosh": acosh, - "asinh": asinh, - "atanh": atanh, - "atan2": atan2, -} - -var Constants = map[string]object.Object{ - "PI": &object.Float{Value: math.Pi}, - "e": &object.Float{Value: math.E}, - "phi": &object.Float{Value: (1 + math.Sqrt(5)) / 2}, - "ln10": &object.Float{Value: math.Log10E}, - "ln2": &object.Float{Value: math.Ln2}, - "log10e": &object.Float{Value: math.Log10E}, - "log2e": &object.Float{Value: math.Log2E}, - "sqrt1_2": &object.Float{Value: 1 / math.Sqrt2}, - "sqrt2": &object.Float{Value: math.Sqrt2}, - "sqrt3": &object.Float{Value: math.Sqrt(3)}, - "sqrt5": &object.Float{Value: math.Sqrt(5)}, - "EPSILON": &object.Float{Value: 2.220446049250313e-16}, -} - -func pi(args []object.Object, defs map[string]object.Object) object.Object { - return &object.Float{Value: math.Pi} -} - -func e(args []object.Object, defs map[string]object.Object) object.Object { - return &object.Float{Value: math.E} -} - -func phi(args []object.Object, defs map[string]object.Object) object.Object { - return &object.Float{Value: (1 + math.Sqrt(5)) / 2} -} - -func ln10(args []object.Object, defs map[string]object.Object) object.Object { - return &object.Float{Value: math.Log10E} -} - -func ln2(args []object.Object, defs map[string]object.Object) object.Object { - return &object.Float{Value: math.Ln2} -} - -func log10e(args []object.Object, defs map[string]object.Object) object.Object { - return &object.Float{Value: math.Log10E} -} - -func log2e(args []object.Object, defs map[string]object.Object) object.Object { - return &object.Float{Value: math.Log2E} -} - -func sqrt1_2(args []object.Object, defs map[string]object.Object) object.Object { - return &object.Float{Value: 1 / math.Sqrt2} -} - -func sqrt2(args []object.Object, defs map[string]object.Object) object.Object { - return &object.Float{Value: math.Sqrt2} -} - -func sqrt3(args []object.Object, defs map[string]object.Object) object.Object { - return &object.Float{Value: math.Sqrt(3)} -} - -func sqrt5(args []object.Object, defs map[string]object.Object) object.Object { - return &object.Float{Value: math.Sqrt(5)} -} - -func epsilon(args []object.Object, defs map[string]object.Object) object.Object { - return &object.Float{Value: 2.220446049250313e-16} -} - -func abs(args []object.Object, defs map[string]object.Object) object.Object { - if len(defs) != 0 { - return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} - } - if len(args) != 1 { - return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} - } - if args[0].Type() != object.INTEGER_OBJ && args[0].Type() != object.FLOAT_OBJ { - return &object.Error{Message: "Hoja lazima iwe namba"} - } - switch arg := args[0].(type) { - case *object.Integer: - if arg.Value < 0 { - return &object.Integer{Value: -arg.Value} - } - return arg - case *object.Float: - if arg.Value < 0 { - return &object.Float{Value: -arg.Value} - } - return arg - default: - return &object.Error{Message: "Hoja lazima iwe namba"} - } -} - -func sign(args []object.Object, defs map[string]object.Object) object.Object { - if len(defs) != 0 { - return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} - } - if len(args) != 1 { - return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} - } - switch arg := args[0].(type) { - case *object.Integer: - if arg.Value == 0 { - return &object.Integer{Value: 0} - } else if arg.Value > 0 { - return &object.Integer{Value: 1} - } else { - return &object.Integer{Value: -1} - } - case *object.Float: - if arg.Value == 0 { - return &object.Integer{Value: 0} - } else if arg.Value > 0 { - return &object.Integer{Value: 1} - } else { - return &object.Integer{Value: -1} - } - default: - return &object.Error{Message: "Hoja lazima iwe namba"} - } -} - -func ceil(args []object.Object, defs map[string]object.Object) object.Object { - if len(defs) != 0 { - return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} - } - if len(args) != 1 { - return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} - } - if args[0].Type() != object.INTEGER_OBJ && args[0].Type() != object.FLOAT_OBJ { - return &object.Error{Message: "Hoja lazima iwe namba"} - } - switch arg := args[0].(type) { - case *object.Integer: - return &object.Integer{Value: arg.Value} - case *object.Float: - return &object.Integer{Value: int64(math.Ceil(arg.Value))} - default: - return &object.Error{Message: "Hoja lazima iwe namba"} - } -} - -func floor(args []object.Object, defs map[string]object.Object) object.Object { - if len(defs) != 0 { - return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} - } - if len(args) != 1 { - return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} - } - if args[0].Type() != object.INTEGER_OBJ && args[0].Type() != object.FLOAT_OBJ { - return &object.Error{Message: "Hoja lazima iwe namba"} - } - switch arg := args[0].(type) { - case *object.Integer: - return &object.Integer{Value: arg.Value} - case *object.Float: - return &object.Integer{Value: int64(math.Floor(arg.Value))} - default: - return &object.Error{Message: "Hoja lazima iwe namba"} - } -} - -func sqrt(args []object.Object, defs map[string]object.Object) object.Object { - if len(defs) != 0 { - return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} - } - if len(args) != 1 { - return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} - } - if args[0].Type() != object.INTEGER_OBJ && args[0].Type() != object.FLOAT_OBJ { - return &object.Error{Message: "Hoja lazima iwe namba"} - } - switch arg := args[0].(type) { - case *object.Integer: - return &object.Float{Value: math.Sqrt(float64(arg.Value))} - case *object.Float: - return &object.Float{Value: math.Sqrt(arg.Value)} - default: - return &object.Error{Message: "Hoja lazima iwe namba"} - } -} - -func cbrt(args []object.Object, defs map[string]object.Object) object.Object { - if len(defs) != 0 { - return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} - } - if len(args) != 1 { - return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} - } - if args[0].Type() != object.INTEGER_OBJ && args[0].Type() != object.FLOAT_OBJ { - return &object.Error{Message: "Hoja lazima iwe namba"} - } - switch arg := args[0].(type) { - case *object.Integer: - return &object.Float{Value: math.Cbrt(float64(arg.Value))} - case *object.Float: - return &object.Float{Value: math.Cbrt(arg.Value)} - default: - return &object.Error{Message: "Hoja lazima iwe namba"} - } -} - -func root(args []object.Object, defs map[string]object.Object) object.Object { - if len(defs) != 0 { - return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} - } - if len(args) != 2 { - return &object.Error{Message: "Undo hili linahitaji hoja mbili tu"} - } - if args[0].Type() != object.INTEGER_OBJ && args[0].Type() != object.FLOAT_OBJ { - return &object.Error{Message: "Hoja ya kwanza lazima iwe namba"} - } - if args[1].Type() != object.INTEGER_OBJ { - return &object.Error{Message: "Hoja ya pili lazima iwe namba"} - } - base, ok := args[0].(*object.Float) - if !ok { - base = &object.Float{Value: float64(args[0].(*object.Integer).Value)} - } - exp := args[1].(*object.Integer).Value - - if exp == 0 { - return &object.Float{Value: 1.0} - } else if exp < 0 { - return &object.Error{Message: "Second Hoja lazima iwe a non-negative integer"} - } - - x := 1.0 - for i := 0; i < 10; i++ { - x = x - (math.Pow(x, float64(exp))-base.Value)/(float64(exp)*math.Pow(x, float64(exp-1))) - } - - return &object.Float{Value: x} -} - -func hypot(args []object.Object, defs map[string]object.Object) object.Object { - if len(defs) != 0 { - return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} - } - if len(args) < 2 { - return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} - } - var sumOfSquares float64 - for _, arg := range args { - if arg.Type() != object.INTEGER_OBJ && arg.Type() != object.FLOAT_OBJ { - return &object.Error{Message: "Hoja lazima ziwe namba"} - } - switch num := arg.(type) { - case *object.Integer: - sumOfSquares += float64(num.Value) * float64(num.Value) - case *object.Float: - sumOfSquares += num.Value * num.Value - } - } - return &object.Float{Value: math.Sqrt(sumOfSquares)} -} - -func factorial(args []object.Object, defs map[string]object.Object) object.Object { - if len(defs) != 0 { - return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} - } - if len(args) != 1 { - return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} - } - if args[0].Type() != object.INTEGER_OBJ { - return &object.Error{Message: "Hoja lazima iwe namba"} - } - n := args[0].(*object.Integer).Value - if n < 0 { - return &object.Error{Message: "Hoja lazima iwe a non-negative integer"} - } - result := int64(1) - for i := int64(2); i <= n; i++ { - result *= i - } - return &object.Integer{Value: result} -} - -func round(args []object.Object, defs map[string]object.Object) object.Object { - if len(defs) != 0 { - return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} - } - if len(args) != 1 { - return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} - } - if args[0].Type() != object.FLOAT_OBJ { - return &object.Error{Message: "Hoja lazima iwe desimali"} - } - - num := args[0].(*object.Float).Value - return &object.Integer{Value: int64(num + 0.5)} -} - -func max(args []object.Object, defs map[string]object.Object) object.Object { - if len(defs) != 0 { - return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} - } - if len(args) != 1 { - return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} - } - - arg, ok := args[0].(*object.Array) - if !ok { - return &object.Error{Message: "Hoja lazima iwe an array"} - } - - if len(arg.Elements) == 0 { - return &object.Error{Message: "Orodha haipaswi kuwa tupu"} - } - - var maxNum float64 - - for _, element := range arg.Elements { - if element.Type() != object.INTEGER_OBJ && element.Type() != object.FLOAT_OBJ { - return &object.Error{Message: "Vipengee vya orodha lazima viwe namba"} - } - - switch num := element.(type) { - case *object.Integer: - if float64(num.Value) > maxNum { - maxNum = float64(num.Value) - } - case *object.Float: - if num.Value > maxNum { - maxNum = num.Value - } - default: - return &object.Error{Message: "Vipengee vya orodha lazima viwe namba"} - } - } - - return &object.Float{Value: maxNum} -} - -func min(args []object.Object, defs map[string]object.Object) object.Object { - if len(defs) != 0 { - return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} - } - if len(args) != 1 { - return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} - } - - arg, ok := args[0].(*object.Array) - if !ok { - return &object.Error{Message: "Hoja lazima iwe an array"} - } - - if len(arg.Elements) == 0 { - return &object.Error{Message: "Orodha haipaswi kuwa tupu"} - } - - minNum := math.MaxFloat64 - - for _, element := range arg.Elements { - if element.Type() != object.INTEGER_OBJ && element.Type() != object.FLOAT_OBJ { - return &object.Error{Message: "Vipengee vya orodha lazima viwe namba"} - } - - switch num := element.(type) { - case *object.Integer: - if float64(num.Value) < minNum { - minNum = float64(num.Value) - } - case *object.Float: - if num.Value < minNum { - minNum = num.Value - } - default: - return &object.Error{Message: "Vipengee vya orodha lazima viwe namba"} - } - } - - return &object.Float{Value: minNum} -} - -func exp(args []object.Object, defs map[string]object.Object) object.Object { - if len(defs) != 0 { - return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} - } - if len(args) != 1 { - return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} - } - if args[0].Type() != object.FLOAT_OBJ { - return &object.Error{Message: "Hoja lazima iwe desimali"} - } - num := args[0].(*object.Float).Value - return &object.Float{Value: math.Exp(num)} -} - -func expm1(args []object.Object, defs map[string]object.Object) object.Object { - if len(defs) != 0 { - return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} - } - if len(args) != 1 { - return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} - } - if args[0].Type() != object.FLOAT_OBJ { - return &object.Error{Message: "Hoja lazima iwe desimali"} - } - num := args[0].(*object.Float).Value - return &object.Float{Value: math.Expm1(num)} -} - -func log(args []object.Object, defs map[string]object.Object) object.Object { - if len(defs) != 0 { - return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} - } - if len(args) != 1 { - return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} - } - if args[0].Type() != object.FLOAT_OBJ { - return &object.Error{Message: "Hoja lazima iwe desimali"} - } - num := args[0].(*object.Float).Value - return &object.Float{Value: math.Log(num)} -} - -func log10(args []object.Object, defs map[string]object.Object) object.Object { - if len(defs) != 0 { - return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} - } - if len(args) != 1 { - return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} - } - if args[0].Type() != object.FLOAT_OBJ { - return &object.Error{Message: "Hoja lazima iwe desimali"} - } - num := args[0].(*object.Float).Value - return &object.Float{Value: math.Log10(num)} -} - -func log2(args []object.Object, defs map[string]object.Object) object.Object { - if len(defs) != 0 { - return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} - } - if len(args) != 1 { - return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} - } - if args[0].Type() != object.INTEGER_OBJ && args[0].Type() != object.FLOAT_OBJ { - return &object.Error{Message: "Hoja lazima iwe namba"} - } - - arg := extractFloatValue(args[0]) - - if arg <= 0 { - return &object.Error{Message: "Hoja lazima iwe kubwa kuliko 0"} - } - - return &object.Float{Value: math.Log2(arg)} -} - -func extractFloatValue(obj object.Object) float64 { - switch obj := obj.(type) { - case *object.Integer: - return float64(obj.Value) - case *object.Float: - return obj.Value - default: - return 0 - } -} - -func log1p(args []object.Object, defs map[string]object.Object) object.Object { - if len(defs) != 0 { - return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} - } - if len(args) != 1 { - return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} - } - if args[0].Type() != object.FLOAT_OBJ { - return &object.Error{Message: "Hoja lazima iwe desimali"} - } - num := args[0].(*object.Float).Value - return &object.Float{Value: math.Log1p(num)} -} - -func cos(args []object.Object, defs map[string]object.Object) object.Object { - if len(defs) != 0 { - return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} - } - if len(args) != 1 { - return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} - } - if args[0].Type() != object.FLOAT_OBJ { - return &object.Error{Message: "Hoja lazima iwe desimali"} - } - num := args[0].(*object.Float).Value - return &object.Float{Value: math.Cos(num)} -} - -func sin(args []object.Object, defs map[string]object.Object) object.Object { - if len(defs) != 0 { - return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} - } - if len(args) != 1 { - return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} - } - if args[0].Type() != object.FLOAT_OBJ { - return &object.Error{Message: "Hoja lazima iwe desimali"} - } - num := args[0].(*object.Float).Value - return &object.Float{Value: math.Sin(num)} -} - -func tan(args []object.Object, defs map[string]object.Object) object.Object { - if len(defs) != 0 { - return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} - } - if len(args) != 1 { - return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} - } - if args[0].Type() != object.FLOAT_OBJ { - return &object.Error{Message: "Hoja lazima iwe desimali"} - } - num := args[0].(*object.Float).Value - return &object.Float{Value: math.Tan(num)} -} - -func acos(args []object.Object, defs map[string]object.Object) object.Object { - if len(defs) != 0 { - return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} - } - if len(args) != 1 { - return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} - } - if args[0].Type() != object.FLOAT_OBJ { - return &object.Error{Message: "Hoja lazima iwe desimali"} - } - num := args[0].(*object.Float).Value - return &object.Float{Value: math.Acos(num)} -} - -func asin(args []object.Object, defs map[string]object.Object) object.Object { - if len(defs) != 0 { - return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} - } - if len(args) != 1 { - return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} - } - if args[0].Type() != object.FLOAT_OBJ { - return &object.Error{Message: "Hoja lazima iwe desimali"} - } - num := args[0].(*object.Float).Value - return &object.Float{Value: math.Asin(num)} -} - -func atan(args []object.Object, defs map[string]object.Object) object.Object { - if len(defs) != 0 { - return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} - } - if len(args) != 1 { - return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} - } - if args[0].Type() != object.FLOAT_OBJ { - return &object.Error{Message: "Hoja lazima iwe desimali"} - } - num := args[0].(*object.Float).Value - return &object.Float{Value: math.Atan(num)} -} - -func cosh(args []object.Object, defs map[string]object.Object) object.Object { - if len(defs) != 0 { - return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} - } - if len(args) != 1 { - return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} - } - if args[0].Type() != object.FLOAT_OBJ { - return &object.Error{Message: "Hoja lazima iwe desimali"} - } - num := args[0].(*object.Float).Value - return &object.Float{Value: math.Cosh(num)} -} - -func sinh(args []object.Object, defs map[string]object.Object) object.Object { - if len(defs) != 0 { - return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} - } - if len(args) != 1 { - return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} - } - if args[0].Type() != object.FLOAT_OBJ { - return &object.Error{Message: "Hoja lazima iwe desimali"} - } - num := args[0].(*object.Float).Value - return &object.Float{Value: math.Sinh(num)} -} - -func tanh(args []object.Object, defs map[string]object.Object) object.Object { - if len(defs) != 0 { - return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} - } - if len(args) != 1 { - return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} - } - if args[0].Type() != object.FLOAT_OBJ { - return &object.Error{Message: "Hoja lazima iwe desimali"} - } - num := args[0].(*object.Float).Value - return &object.Float{Value: math.Tanh(num)} -} - -func acosh(args []object.Object, defs map[string]object.Object) object.Object { - if len(defs) != 0 { - return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} - } - if len(args) != 1 { - return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} - } - if args[0].Type() != object.FLOAT_OBJ { - return &object.Error{Message: "Hoja lazima iwe desimali"} - } - num := args[0].(*object.Float).Value - return &object.Float{Value: math.Acosh(num)} -} - -func asinh(args []object.Object, defs map[string]object.Object) object.Object { - if len(defs) != 0 { - return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} - } - if len(args) != 1 { - return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} - } - if args[0].Type() != object.FLOAT_OBJ { - return &object.Error{Message: "Hoja lazima iwe desimali"} - } - num := args[0].(*object.Float).Value - return &object.Float{Value: math.Asinh(num)} -} - -func atan2(args []object.Object, defs map[string]object.Object) object.Object { - if len(defs) != 0 { - return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} - } - if len(args) != 2 { - return &object.Error{Message: "Undo hili linahitaji hoja mbili tu."} - } - if args[0].Type() != object.INTEGER_OBJ && args[0].Type() != object.FLOAT_OBJ { - return &object.Error{Message: "Hoja lazima ziwe namba"} - } - if args[1].Type() != object.INTEGER_OBJ && args[1].Type() != object.FLOAT_OBJ { - return &object.Error{Message: "Hoja lazima ziwe namba"} - } - - y := extractFloatValue(args[0]) - x := extractFloatValue(args[1]) - - return &object.Float{Value: math.Atan2(y, x)} -} - -func atanh(args []object.Object, defs map[string]object.Object) object.Object { - if len(defs) != 0 { - return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} - } - if len(args) != 1 { - return &object.Error{Message: "Undo hili linahitaji hoja moja tu"} - } - if args[0].Type() != object.FLOAT_OBJ { - return &object.Error{Message: "Hoja lazima iwe desimali"} - } - num := args[0].(*object.Float).Value - return &object.Float{Value: math.Atanh(num)} -} - -func random(args []object.Object, defs map[string]object.Object) object.Object { - if len(defs) != 0 { - return &object.Error{Message: "Undo hili haliruhusu ufafanuzi."} - } - - if len(args) != 0 { - return &object.Error{Message: "Undo hili halipaswi kupokea hoja."} - } - - rand.Seed(time.Now().UnixNano()) - value := rand.Float64() - - return &object.Float{Value: value} -} diff --git a/module/json.go b/module/json.go deleted file mode 100644 index 51a133f..0000000 --- a/module/json.go +++ /dev/null @@ -1,120 +0,0 @@ -package module - -import ( - "encoding/json" - - "github.com/NuruProgramming/Nuru/object" -) - -var JsonFunctions = map[string]object.ModuleFunction{} - -func init() { - JsonFunctions["dikodi"] = decode - JsonFunctions["enkodi"] = encode -} - -func decode(args []object.Object, defs map[string]object.Object) object.Object { - if len(defs) != 0 { - return &object.Error{Message: "Hoja hii hairuhusiwi"} - } - if len(args) != 1 { - return &object.Error{Message: "Tunahitaji hoja moja tu"} - } - - if args[0].Type() != object.STRING_OBJ { - return &object.Error{Message: "Hoja lazima iwe neno"} - } - - var i interface{} - - input := args[0].(*object.String).Value - err := json.Unmarshal([]byte(input), &i) - if err != nil { - return &object.Error{Message: "Hii data sio jsoni"} - } - - return convertWhateverToObject(i) -} - -func convertWhateverToObject(i interface{}) object.Object { - switch v := i.(type) { - case map[string]interface{}: - dict := &object.Dict{} - dict.Pairs = make(map[object.HashKey]object.DictPair) - - for k, v := range v { - pair := object.DictPair{ - Key: &object.String{Value: k}, - Value: convertWhateverToObject(v), - } - dict.Pairs[pair.Key.(object.Hashable).HashKey()] = pair - } - - return dict - case []interface{}: - list := &object.Array{} - for _, e := range v { - list.Elements = append(list.Elements, convertWhateverToObject(e)) - } - - return list - case string: - return &object.String{Value: v} - case int64: - return &object.Integer{Value: v} - case float64: - return &object.Float{Value: v} - case bool: - if v { - return &object.Boolean{Value: true} - } else { - return &object.Boolean{Value: false} - } - } - return &object.Null{} -} - -func encode(args []object.Object, defs map[string]object.Object) object.Object { - if len(defs) != 0 { - return &object.Error{Message: "Hoja hii hairuhusiwi"} - } - - input := args[0] - i := convertObjectToWhatever(input) - data, err := json.Marshal(i) - - if err != nil { - return &object.Error{Message: "Siwezi kubadilisha data hii kuwa jsoni"} - } - - return &object.String{Value: string(data)} -} - -func convertObjectToWhatever(obj object.Object) interface{} { - switch v := obj.(type) { - case *object.Dict: - m := make(map[string]interface{}) - for _, pair := range v.Pairs { - key := pair.Key.(*object.String).Value - m[key] = convertObjectToWhatever(pair.Value) - } - return m - case *object.Array: - list := make([]interface{}, len(v.Elements)) - for i, e := range v.Elements { - list[i] = convertObjectToWhatever(e) - } - return list - case *object.String: - return v.Value - case *object.Integer: - return v.Value - case *object.Float: - return v.Value - case *object.Boolean: - return v.Value - case *object.Null: - return nil - } - return nil -} diff --git a/module/module.go b/module/module.go deleted file mode 100644 index f4c17c7..0000000 --- a/module/module.go +++ /dev/null @@ -1,13 +0,0 @@ -package module - -import "github.com/NuruProgramming/Nuru/object" - -var Mapper = map[string]*object.Module{} - -func init() { - Mapper["os"] = &object.Module{Name: "os", Functions: OsFunctions} - Mapper["muda"] = &object.Module{Name: "time", Functions: TimeFunctions} - Mapper["mtandao"] = &object.Module{Name: "net", Functions: NetFunctions} - Mapper["jsoni"] = &object.Module{Name: "json", Functions: JsonFunctions} - Mapper["hisabati"] = &object.Module{Name: "hisabati", Functions: MathFunctions} -} diff --git a/module/net.go b/module/net.go deleted file mode 100644 index 421d67b..0000000 --- a/module/net.go +++ /dev/null @@ -1,201 +0,0 @@ -package module - -import ( - "bytes" - "encoding/json" - "io/ioutil" - "net/http" - - "github.com/NuruProgramming/Nuru/object" -) - -var NetFunctions = map[string]object.ModuleFunction{} - -func init() { - NetFunctions["peruzi"] = getRequest - NetFunctions["tuma"] = postRequest -} - -func getRequest(args []object.Object, defs map[string]object.Object) object.Object { - - if len(defs) != 0 { - var url *object.String - var headers, params *object.Dict - for k, v := range defs { - switch k { - case "yuareli": - strUrl, ok := v.(*object.String) - if !ok { - return &object.Error{Message: "Yuareli iwe neno"} - } - url = strUrl - case "vichwa": - dictHead, ok := v.(*object.Dict) - if !ok { - return &object.Error{Message: "Vichwa lazima viwe kamusi"} - } - headers = dictHead - case "mwili": - dictHead, ok := v.(*object.Dict) - if !ok { - return &object.Error{Message: "Mwili lazima iwe kamusi"} - } - params = dictHead - default: - return &object.Error{Message: "Hoja si sahihi. Tumia yuareli na vichwa."} - } - } - if url.Value == "" { - return &object.Error{Message: "Yuareli ni lazima"} - } - - var responseBody *bytes.Buffer - if params != nil { - booty := convertObjectToWhatever(params) - - jsonBody, err := json.Marshal(booty) - - if err != nil { - return &object.Error{Message: "Huku format query yako vizuri."} - } - - responseBody = bytes.NewBuffer(jsonBody) - } - - var req *http.Request - var err error - if responseBody != nil { - req, err = http.NewRequest("GET", url.Value, responseBody) - } else { - req, err = http.NewRequest("GET", url.Value, nil) - } - if err != nil { - return &object.Error{Message: "Tumeshindwa kufanya request"} - } - - if headers != nil { - for _, val := range headers.Pairs { - req.Header.Set(val.Key.Inspect(), val.Value.Inspect()) - } - } - client := &http.Client{} - - resp, err := client.Do(req) - - if err != nil { - return &object.Error{Message: "Tumeshindwa kutuma request."} - } - defer resp.Body.Close() - respBody, err := ioutil.ReadAll(resp.Body) - if err != nil { - return &object.Error{Message: "Tumeshindwa kusoma majibu."} - } - - return &object.String{Value: string(respBody)} - - } - - if len(args) == 1 { - url, ok := args[0].(*object.String) - if !ok { - return &object.Error{Message: "Yuareli lazima iwe neno"} - } - req, err := http.NewRequest("GET", url.Value, nil) - if err != nil { - return &object.Error{Message: "Tumeshindwa kufanya request"} - } - - client := &http.Client{} - - resp, err := client.Do(req) - - if err != nil { - return &object.Error{Message: "Tumeshindwa kutuma request."} - } - defer resp.Body.Close() - respBody, err := ioutil.ReadAll(resp.Body) - if err != nil { - return &object.Error{Message: "Tumeshindwa kusoma majibu."} - } - - return &object.String{Value: string(respBody)} - } - return &object.Error{Message: "Hoja si sahihi. Tumia yuareli na vichwa."} -} - -func postRequest(args []object.Object, defs map[string]object.Object) object.Object { - if len(defs) != 0 { - var url *object.String - var headers, params *object.Dict - for k, v := range defs { - switch k { - case "yuareli": - strUrl, ok := v.(*object.String) - if !ok { - return &object.Error{Message: "Yuareli iwe neno"} - } - url = strUrl - case "vichwa": - dictHead, ok := v.(*object.Dict) - if !ok { - return &object.Error{Message: "Vichwa lazima viwe kamusi"} - } - headers = dictHead - case "mwili": - dictHead, ok := v.(*object.Dict) - if !ok { - return &object.Error{Message: "Mwili lazima iwe kamusi"} - } - params = dictHead - default: - return &object.Error{Message: "Hoja si sahihi. Tumia yuareli na vichwa."} - } - } - if url.Value == "" { - return &object.Error{Message: "Yuareli ni lazima"} - } - var responseBody *bytes.Buffer - if params != nil { - booty := convertObjectToWhatever(params) - - jsonBody, err := json.Marshal(booty) - - if err != nil { - return &object.Error{Message: "Huku format query yako vizuri."} - } - - responseBody = bytes.NewBuffer(jsonBody) - } - var req *http.Request - var err error - if responseBody != nil { - req, err = http.NewRequest("POST", url.Value, responseBody) - } else { - req, err = http.NewRequest("POST", url.Value, nil) - } - if err != nil { - return &object.Error{Message: "Tumeshindwa kufanya request"} - } - if headers != nil { - for _, val := range headers.Pairs { - req.Header.Set(val.Key.Inspect(), val.Value.Inspect()) - } - } - req.Header.Add("Content-Type", "application/json") - - client := &http.Client{} - - resp, err := client.Do(req) - - if err != nil { - return &object.Error{Message: "Tumeshindwa kutuma request."} - } - defer resp.Body.Close() - respBody, err := ioutil.ReadAll(resp.Body) - if err != nil { - return &object.Error{Message: "Tumeshindwa kusoma majibu."} - } - return &object.String{Value: string(respBody)} - } - return &object.Error{Message: "Hoja si sahihi. Tumia yuareli na vichwa."} -} diff --git a/module/os.go b/module/os.go deleted file mode 100644 index 53194f4..0000000 --- a/module/os.go +++ /dev/null @@ -1,56 +0,0 @@ -package module - -import ( - "os" - "os/exec" - "strings" - - "github.com/NuruProgramming/Nuru/object" -) - -var OsFunctions = map[string]object.ModuleFunction{} - -func init() { - OsFunctions["toka"] = exit - OsFunctions["kimbiza"] = run -} - -func exit(args []object.Object, defs map[string]object.Object) object.Object { - if len(args) > 1 { - return &object.Error{Message: "Hoja sii sahihi"} - } - - if len(args) == 1 { - status, ok := args[0].(*object.Integer) - if !ok { - return &object.Error{Message: "Hoja sii namba"} - } - os.Exit(int(status.Value)) - return nil - } - - os.Exit(0) - - return nil -} - -func run(args []object.Object, defs map[string]object.Object) object.Object { - if len(args) != 1 { - return &object.Error{Message: "Idadi ya hoja sii sahihi"} - } - - cmd, ok := args[0].(*object.String) - if !ok { - return &object.Error{Message: "Hoja lazima iwe neno"} - } - cmdMain := cmd.Value - cmdArgs := strings.Split(cmdMain, " ") - cmdArgs = cmdArgs[1:] - - out, err := exec.Command(cmdMain, cmdArgs...).Output() - if err != nil { - return &object.Error{Message: "Tumeshindwa kukimbiza komandi"} - } - - return &object.String{Value: string(out)} -} diff --git a/module/time.go b/module/time.go deleted file mode 100644 index d1fbd0d..0000000 --- a/module/time.go +++ /dev/null @@ -1,82 +0,0 @@ -package module - -import ( - "fmt" - "strconv" - "time" - - "github.com/NuruProgramming/Nuru/object" -) - -var TimeFunctions = map[string]object.ModuleFunction{} - -func init() { - TimeFunctions["hasahivi"] = now - TimeFunctions["lala"] = sleep - TimeFunctions["tangu"] = since -} - -func now(args []object.Object, defs map[string]object.Object) object.Object { - if len(args) != 0 || len(defs) != 0 { - return &object.Error{Message: "hatuhitaji hoja kwenye hasahivi"} - } - - tn := time.Now() - time_string := tn.Format("15:04:05 02-01-2006") - - return &object.Time{TimeValue: time_string} -} - -func sleep(args []object.Object, defs map[string]object.Object) object.Object { - if len(defs) != 0 { - return &object.Error{Message: "Hoja hii hairuhusiwi"} - } - if len(args) != 1 { - return &object.Error{Message: "tunahitaji hoja moja tu"} - } - - objvalue := args[0].Inspect() - inttime, err := strconv.Atoi(objvalue) - - if err != nil { - return &object.Error{Message: "namba tu zinaruhusiwa kwenye hoja"} - } - - time.Sleep(time.Duration(inttime) * time.Second) - - return nil -} - -func since(args []object.Object, defs map[string]object.Object) object.Object { - if len(defs) != 0 { - return &object.Error{Message: "Hoja hii hairuhusiwi"} - } - if len(args) != 1 { - return &object.Error{Message: "tunahitaji hoja moja tu"} - } - - var ( - t time.Time - err error - ) - - switch m := args[0].(type) { - case *object.Time: - t, _ = time.Parse("15:04:05 02-01-2006", m.TimeValue) - case *object.String: - t, err = time.Parse("15:04:05 02-01-2006", m.Value) - if err != nil { - return &object.Error{Message: fmt.Sprintf("Hoja %s sii sahihi", args[0].Inspect())} - } - default: - return &object.Error{Message: fmt.Sprintf("Hoja %s sii sahihi", args[0].Inspect())} - } - - current_time := time.Now().Format("15:04:05 02-01-2006") - ct, _ := time.Parse("15:04:05 02-01-2006", current_time) - - diff := ct.Sub(t) - durationInSeconds := diff.Seconds() - - return &object.Integer{Value: int64(durationInSeconds)} -} diff --git a/object/function.go b/object/function.go index f7ad6b8..ae2cf86 100644 --- a/object/function.go +++ b/object/function.go @@ -1,7 +1,7 @@ package object import ( - "bytes" + "fmt" "strings" "github.com/NuruProgramming/Nuru/ast" @@ -17,19 +17,20 @@ type Function struct { func (f *Function) Type() ObjectType { return FUNCTION_OBJ } func (f *Function) Inspect() string { - var out bytes.Buffer + var out strings.Builder params := []string{} for _, p := range f.Parameters { params = append(params, p.String()) } - out.WriteString("unda") - out.WriteString("(") - out.WriteString(strings.Join(params, ", ")) - out.WriteString(") {\n") - out.WriteString(f.Body.String()) - out.WriteString("\n}") + out.WriteString(fmt.Sprintf("[undo: %s", f.Name)) + + if len(params) > 0 { + out.WriteString(fmt.Sprintf("; inahitaji: %s", strings.Join(params, ", "))) + } + + out.WriteRune(']') return out.String() } diff --git a/object/module.go b/object/module.go deleted file mode 100644 index 127c741..0000000 --- a/object/module.go +++ /dev/null @@ -1,20 +0,0 @@ -package object - -type ModuleFunction func(args []Object, defs map[string]Object) Object - -type Module struct { - Name string - Functions map[string]ModuleFunction -} - -func (m *Module) Type() ObjectType { - switch m.Name { - case "time": - return TIME_OBJ - case "json": - return JSON_OBJ - default: - return MODULE_OBJ - } -} -func (m *Module) Inspect() string { return "Module: " + m.Name } diff --git a/parser/import.go b/parser/import.go index c2f75f7..6483994 100644 --- a/parser/import.go +++ b/parser/import.go @@ -1,6 +1,9 @@ package parser import ( + "log" + "strings" + "github.com/NuruProgramming/Nuru/ast" "github.com/NuruProgramming/Nuru/token" ) @@ -8,17 +11,74 @@ import ( func (p *Parser) parseImport() ast.Expression { exp := &ast.Import{Token: p.curToken} exp.Identifiers = make(map[string]*ast.Identifier) + // Eka mahali hapa kwa mfano kawaida::hisabati + var mahali strings.Builder + // jina la maktaba hili kwa mafano hisabati + var jina strings.Builder + for p.curToken.Line == p.peekToken.Line { - p.nextToken() - identifier := &ast.Identifier{Value: p.curToken.Literal} - exp.Identifiers[p.curToken.Literal] = identifier - if p.peekTokenIs(token.COMMA) { + if p.curTokenIs(token.IMPORT) { + p.nextToken() + continue + } + + // kwa kuruka :: + if p.curTokenIs(token.COLON) && p.peekTokenIs(token.COLON) { + p.nextToken() + p.nextToken() + mahali.WriteRune('/') + } + + if p.curTokenIs(token.IDENT) { + mahali.WriteString(p.curToken.Literal) + p.nextToken() + continue + } + + if p.curTokenIs(token.ASSIGN) { + p.nextToken() + + // Check the value + if p.curTokenIs(token.IDENT) { + jina.WriteString(p.curToken.Literal) + p.nextToken() + } + + continue + } + + if p.curTokenIs(token.COMMA) { + mhs := mahali.String() + if jina.Len() <= 0 { + mhss := strings.Split(mhs, "/") + jina.WriteString(mhss[len(mhss)-1]) + } + exp.Identifiers[jina.String()] = &ast.Identifier{Value: mhs} + exp.Filename = p.filename + + mahali.Reset() p.nextToken() + jina.Reset() + print(p.curToken.Literal + "\n") + continue } - if p.peekTokenIs(token.EOF) { + + if p.peekTokenIs(token.EOF) || p.peekTokenIs(token.NEWLINE) { break } + + // Just log it and continue, should have a better way to indicate errors + log.Printf("%s haijulikani", p.curToken.Literal) + p.nextToken() + } + + mhs := mahali.String() + if jina.Len() == 0 { + mhss := strings.Split(mhs, "/") + jina.WriteString(mhss[len(mhss)-1]) } + exp.Identifiers[jina.String()] = &ast.Identifier{Value: mhs} + exp.Filename = p.filename return exp } diff --git a/parser/package.go b/parser/package.go deleted file mode 100644 index 7e5a6c6..0000000 --- a/parser/package.go +++ /dev/null @@ -1,18 +0,0 @@ -package parser - -import ( - "github.com/NuruProgramming/Nuru/ast" - "github.com/NuruProgramming/Nuru/token" -) - -func (p *Parser) parsePackage() ast.Expression { - expression := &ast.Package{Token: p.curToken} - p.nextToken() - expression.Name = &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal} - - if !p.expectPeek(token.LBRACE) { - return nil - } - expression.Block = p.parseBlockStatement() - return expression -} diff --git a/parser/parser.go b/parser/parser.go index 0bcf5f8..c295088 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -71,6 +71,7 @@ type Parser struct { prefixParseFns map[token.TokenType]prefixParseFn infixParseFns map[token.TokenType]infixParseFn postfixParseFns map[token.TokenType]postfixParseFn + filename string } func (p *Parser) registerPrefix(tokenType token.TokenType, fn prefixParseFn) { @@ -85,8 +86,8 @@ func (p *Parser) registerPostfix(tokenType token.TokenType, fn postfixParseFn) { p.postfixParseFns[tokenType] = fn } -func New(l *lexer.Lexer) *Parser { - p := &Parser{l: l, errors: []string{}} +func New(l *lexer.Lexer, filename string) *Parser { + p := &Parser{l: l, errors: []string{}, filename: filename} p.nextToken() p.nextToken() @@ -111,7 +112,7 @@ func New(l *lexer.Lexer) *Parser { p.registerPrefix(token.FOR, p.parseForExpression) p.registerPrefix(token.SWITCH, p.parseSwitchStatement) p.registerPrefix(token.IMPORT, p.parseImport) - p.registerPrefix(token.PACKAGE, p.parsePackage) + //p.registerPrefix(token.PACKAGE, p.parsePackage) p.registerPrefix(token.AT, p.parseAt) p.infixParseFns = make(map[token.TokenType]infixParseFn) @@ -140,6 +141,8 @@ func New(l *lexer.Lexer) *Parser { p.registerInfix(token.IN, p.parseInfixExpression) p.registerInfix(token.DOT, p.parseMethod) + p.registerPrefix(token.NEWLINE, p.parseNewline) + p.postfixParseFns = make(map[token.TokenType]postfixParseFn) p.registerPostfix(token.PLUS_PLUS, p.parsePostfixExpression) p.registerPostfix(token.MINUS_MINUS, p.parsePostfixExpression) @@ -160,6 +163,10 @@ func (p *Parser) ParseProgram() *ast.Program { return program } +func (p *Parser) parseNewline() ast.Expression { + return &ast.NoOp{Token: p.curToken} +} + // manage token literals: func (p *Parser) nextToken() { diff --git a/parser/parser_test.go b/parser/parser_test.go index 50e880e..5f36542 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -8,6 +8,8 @@ import ( "github.com/NuruProgramming/Nuru/lexer" ) +const input_file string = "" + func TestLetStatements(t *testing.T) { tests := []struct { input string @@ -21,7 +23,7 @@ func TestLetStatements(t *testing.T) { for _, tt := range tests { l := lexer.New(tt.input) - p := New(l) + p := New(l, input_file) program := p.ParseProgram() checkParserErrors(t, p) @@ -93,7 +95,7 @@ func TestReturnStatements(t *testing.T) { for _, tt := range tests { l := lexer.New(tt.input) - p := New(l) + p := New(l, input_file) program := p.ParseProgram() checkParserErrors(t, p) @@ -121,7 +123,7 @@ func TestIdentifierExpression(t *testing.T) { input := "foobar;" l := lexer.New(input) - p := New(l) + p := New(l, input_file) program := p.ParseProgram() checkParserErrors(t, p) @@ -153,7 +155,7 @@ func TestIntergerLiteral(t *testing.T) { input := "5;" l := lexer.New(input) - p := New(l) + p := New(l, input_file) program := p.ParseProgram() checkParserErrors(t, p) @@ -194,7 +196,7 @@ func TestParsingPrefixExpressions(t *testing.T) { for _, tt := range prefixTests { l := lexer.New(tt.input) - p := New(l) + p := New(l, input_file) program := p.ParseProgram() checkParserErrors(t, p) @@ -268,7 +270,7 @@ func TestParsingInfixExpressions(t *testing.T) { for _, tt := range infixTests { l := lexer.New(tt.input) - p := New(l) + p := New(l, input_file) program := p.ParseProgram() checkParserErrors(t, p) @@ -400,7 +402,7 @@ func TestOperatorPrecedenceParsing(t *testing.T) { for _, tt := range tests { l := lexer.New(tt.input) - p := New(l) + p := New(l, input_file) program := p.ParseProgram() checkParserErrors(t, p) @@ -508,7 +510,7 @@ func TestBooleanExpression(t *testing.T) { for _, tt := range tests { l := lexer.New(tt.input) - p := New(l) + p := New(l, input_file) program := p.ParseProgram() checkParserErrors(t, p) @@ -537,7 +539,7 @@ func TestIfExpression(t *testing.T) { input := `kama (x < y) { x }` l := lexer.New(input) - p := New(l) + p := New(l, input_file) program := p.ParseProgram() checkParserErrors(t, p) @@ -581,7 +583,7 @@ func TestIfElseExpression(t *testing.T) { input := `kama (x < y) { x } sivyo { y }` l := lexer.New(input) - p := New(l) + p := New(l, input_file) program := p.ParseProgram() checkParserErrors(t, p) @@ -640,7 +642,7 @@ func TestFunctionLiteralParsing(t *testing.T) { input := `unda(x, y) {x + y}` l := lexer.New(input) - p := New(l) + p := New(l, input_file) program := p.ParseProgram() checkParserErrors(t, p) @@ -689,7 +691,7 @@ func TestFunctionParameterParsing(t *testing.T) { for _, tt := range tests { l := lexer.New(tt.input) - p := New(l) + p := New(l, input_file) program := p.ParseProgram() checkParserErrors(t, p) @@ -710,7 +712,7 @@ func TestCallExpressionParsing(t *testing.T) { input := "jumlisha(1, 2 * 3, 4 + 5);" l := lexer.New(input) - p := New(l) + p := New(l, input_file) program := p.ParseProgram() checkParserErrors(t, p) @@ -746,7 +748,7 @@ func TestStringLiteralExpression(t *testing.T) { input := `"habari yako"` l := lexer.New(input) - p := New(l) + p := New(l, input_file) program := p.ParseProgram() checkParserErrors(t, p) @@ -765,7 +767,7 @@ func TestParsingArrayLiterals(t *testing.T) { input := "[1,2*2,3+3]" l := lexer.New(input) - p := New(l) + p := New(l, input_file) program := p.ParseProgram() checkParserErrors(t, p) @@ -784,7 +786,7 @@ func TestParsingIndexExpressions(t *testing.T) { input := "myArray[1+1]" l := lexer.New(input) - p := New(l) + p := New(l, input_file) program := p.ParseProgram() checkParserErrors(t, p) @@ -807,7 +809,7 @@ func TestParsingDictLiteralsStringKeys(t *testing.T) { input := `{"one": 1, "two": 2, "three": 3}` l := lexer.New(input) - p := New(l) + p := New(l, input_file) program := p.ParseProgram() checkParserErrors(t, p) @@ -842,7 +844,7 @@ func TestParsingDictLiteralsIntegerKeys(t *testing.T) { input := `{1: 1, 2: 2, 3: 3}` l := lexer.New(input) - p := New(l) + p := New(l, input_file) program := p.ParseProgram() checkParserErrors(t, p) @@ -877,7 +879,7 @@ func TestParsingDictLiteralsBoolKeys(t *testing.T) { input := `{kweli: 1, sikweli: 2}` l := lexer.New(input) - p := New(l) + p := New(l, input_file) program := p.ParseProgram() checkParserErrors(t, p) @@ -911,7 +913,7 @@ func TestParsingDictLiteralWithExpressions(t *testing.T) { input := `{"one": 0+1, "two": 100-98, "three": 15/5}` l := lexer.New(input) - p := New(l) + p := New(l, input_file) program := p.ParseProgram() checkParserErrors(t, p) @@ -958,7 +960,7 @@ func TestParsingEmptyDict(t *testing.T) { input := "{}" l := lexer.New(input) - p := New(l) + p := New(l, input_file) program := p.ParseProgram() checkParserErrors(t, p) @@ -977,7 +979,7 @@ func TestWhileLoop(t *testing.T) { input := `wakati ( x > y ) { fanya x = 2 }` l := lexer.New(input) - p := New(l) + p := New(l, input_file) program := p.ParseProgram() checkParserErrors(t, p) @@ -1026,7 +1028,7 @@ func TestShorthandAssignment(t *testing.T) { for _, txt := range input { l := lexer.New(txt) - p := New(l) + p := New(l, input_file) _ = p.ParseProgram() checkParserErrors(t, p) } @@ -1036,7 +1038,7 @@ func TestForExpression(t *testing.T) { input := `kwa i, v ktk j {andika(i)}` l := lexer.New(input) - p := New(l) + p := New(l, input_file) program := p.ParseProgram() checkParserErrors(t, p) @@ -1072,7 +1074,7 @@ func TestParsePostfix(t *testing.T) { for _, txt := range input { l := lexer.New(txt) - p := New(l) + p := New(l, input_file) _ = p.ParseProgram() checkParserErrors(t, p) } @@ -1086,7 +1088,7 @@ func TestParseDot(t *testing.T) { for _, txt := range input { l := lexer.New(txt) - p := New(l) + p := New(l, input_file) _ = p.ParseProgram() checkParserErrors(t, p) } @@ -1108,7 +1110,7 @@ func TestParseSwitch(t *testing.T) { ` l := lexer.New(input) - p := New(l) + p := New(l, input_file) _ = p.ParseProgram() checkParserErrors(t, p) } @@ -1120,7 +1122,7 @@ func TestParseImport(t *testing.T) { ` l := lexer.New(input) - p := New(l) + p := New(l, input_file) _ = p.ParseProgram() checkParserErrors(t, p) } diff --git a/parser/switch.go b/parser/switch.go index ebffc63..6c8bef2 100644 --- a/parser/switch.go +++ b/parser/switch.go @@ -31,6 +31,10 @@ func (p *Parser) parseSwitchStatement() ast.Expression { p.nextToken() for !p.curTokenIs(token.RBRACE) { + if p.curTokenIs(token.NEWLINE) { + p.nextToken() + continue + } if p.curTokenIs(token.EOF) { msg := fmt.Sprintf("Mstari %d: Haukufunga ENDAPO (SWITCH)", p.curToken.Line) diff --git a/repl/docs.go b/repl/docs.go index b8fd428..5785439 100644 --- a/repl/docs.go +++ b/repl/docs.go @@ -142,7 +142,7 @@ func (pg playground) Update(msg tea.Msg) (tea.Model, tea.Cmd) { pg.code = code env := object.NewEnvironment() l := lexer.New(pg.code) - p := parser.New(l) + p := parser.New(l, "nuru-docs") program := p.ParseProgram() if len(p.Errors()) != 0 { pg.output.Style = styles.ErrorStyle.PaddingLeft(3) @@ -185,7 +185,7 @@ func (pg playground) Update(msg tea.Msg) (tea.Model, tea.Cmd) { pg.code = code env := object.NewEnvironment() l := lexer.New(pg.code) - p := parser.New(l) + p := parser.New(l, "") program := p.ParseProgram() if len(p.Errors()) != 0 { pg.output.Style = styles.ErrorStyle.PaddingLeft(3) diff --git a/repl/repl.go b/repl/repl.go index e9c73c0..9dd95ac 100644 --- a/repl/repl.go +++ b/repl/repl.go @@ -24,11 +24,11 @@ const PROMPT = ">>> " //go:embed docs var res embed.FS -func Read(contents string) { +func Read(filename, contents string) { env := object.NewEnvironment() l := lexer.New(contents) - p := parser.New(l) + p := parser.New(l, filename) program := p.ParseProgram() @@ -74,7 +74,7 @@ func (d *dummy) executor(in string) { os.Exit(0) } l := lexer.New(in) - p := parser.New(l) + p := parser.New(l, "") program := p.ParseProgram() diff --git a/token/token.go b/token/token.go index 18c4f6a..825d9a8 100644 --- a/token/token.go +++ b/token/token.go @@ -77,7 +77,7 @@ const ( CASE = "IKIWA" DEFAULT = "KAWAIDA" IMPORT = "TUMIA" - PACKAGE = "PAKEJI" + NEWLINE = "NEWLINE" ) var keywords = map[string]TokenType{ @@ -99,7 +99,6 @@ var keywords = map[string]TokenType{ "ikiwa": CASE, "kawaida": DEFAULT, "tumia": IMPORT, - "pakeji": PACKAGE, "@": AT, }