From a484f5ca37d3f38b72f78b00b27a341d4d09a247 Mon Sep 17 00:00:00 2001 From: Gekko Wrld Date: Sun, 22 Dec 2024 03:00:53 +0300 Subject: [PATCH 1/7] chore: Implement a simple module system A simple module that for now only works for the std lib. The std lib is named as `mkt`. A simple import can look like: where::file or where::dirs::file The file is the last part of the import. An example: ```nuru tumia mkt::hisabati andika("Thamani ya PI ni: {0}\n".panga(hisabati.PI())) ``` Faili hizi zinatafutwa: kwenye thamani ya mazingira: MAKTABA_YA_NURU mfano: MAKTABA_YA_NURU=$HOME/code/nr/maktaba -> /usr/lib/nuru/maktaba (something similar in Windows) -> /lib/nuru/maktaba Kama haitapatikana hapo, kutakuwa na kosa kuonyesha kuwa haijapatikana. Kwa sasa maktaba ya kawaida hayaandikwa, mifano hii haitafanya. Maktaba ya awali yalikuwa yameandikwa kwa lugha ya programu ya Go. Kutumia mabadilisho yanatumahi kuanzisha kuandika maktaba ya kawaida kwenye lugha ya programu ya Nuru. Faili zitakazo angaliwa ni ".nuru" badala ya zile za awali za ".sw" ama ".nr". Signed-off-by: Gekko Wrld --- ast/ast.go | 8 ++++ evaluator/evaluator.go | 2 - evaluator/import.go | 103 +++++++++++++++++++++++++++++++++++------ evaluator/package.go | 18 ------- lexer/lexer.go | 6 ++- main.go | 2 +- maktaba/hisabati.nuru | 4 ++ parser/import.go | 68 +++++++++++++++++++++++++-- parser/package.go | 18 ------- parser/parser.go | 8 +++- token/token.go | 3 +- 11 files changed, 177 insertions(+), 63 deletions(-) delete mode 100644 evaluator/package.go create mode 100644 maktaba/hisabati.nuru delete mode 100644 parser/package.go diff --git a/ast/ast.go b/ast/ast.go index c42241f..c7bcb74 100644 --- a/ast/ast.go +++ b/ast/ast.go @@ -617,6 +617,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 } diff --git a/evaluator/evaluator.go b/evaluator/evaluator.go index 5fd219d..1ea527c 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: diff --git a/evaluator/import.go b/evaluator/import.go index 8b5eb23..7fb35f5 100644 --- a/evaluator/import.go +++ b/evaluator/import.go @@ -2,42 +2,106 @@ 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: + mkt => 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 "mkt": + loc := mahali_pa_maktaba(MAKTABA_YA_NURU) + ss := strings.Split(v.Value, "/")[1:] + + fi__ := filepath.Join(loc) + g__ := filepath.Join(ss...) + fi := filepath.Join(fi__, g__) + + 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) 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 + 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 +109,8 @@ func addSearchPath(path string) { } func findFile(name string) string { - basename := fmt.Sprintf("%s.nr", name) + basename := fmt.Sprintf("%s.nuru", name) + fmt.Println(basename) for _, path := range searchPaths { file := filepath.Join(path, basename) if fileExists(file) { @@ -60,7 +125,7 @@ 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)} @@ -72,16 +137,24 @@ func evaluateFile(file string, env *object.Environment) (*object.Environment, ob 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/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/lexer/lexer.go b/lexer/lexer.go index 7d83728..329a49b 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,8 @@ 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) case rune('('): tok = newToken(token.LPAREN, l.line, l.ch) case rune(')'): diff --git a/main.go b/main.go index ba00894..823b0d3 100644 --- a/main.go +++ b/main.go @@ -52,7 +52,7 @@ 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])) diff --git a/maktaba/hisabati.nuru b/maktaba/hisabati.nuru new file mode 100644 index 0000000..d7eff05 --- /dev/null +++ b/maktaba/hisabati.nuru @@ -0,0 +1,4 @@ +// Hii ni thamni ya PI +PI = unda() { + rudisha 3.141592653589793 +} diff --git a/parser/import.go b/parser/import.go index c2f75f7..4f229c6 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,72 @@ 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} + + 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} 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..4e0a448 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -111,7 +111,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 +140,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 +162,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/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, } From 21056f73c85c247da210d847c5a3b983bb2e3700 Mon Sep 17 00:00:00 2001 From: Gekko Wrld Date: Sun, 22 Dec 2024 03:12:41 +0300 Subject: [PATCH 2/7] chore: Remove module directory The module directory has been removed to make way for start of std rewriting. Signed-off-by: Gekko Wrld --- module/hisabati.go | 731 --------------------------------------------- module/json.go | 120 -------- module/module.go | 13 - module/net.go | 201 ------------- module/os.go | 56 ---- module/time.go | 82 ----- 6 files changed, 1203 deletions(-) delete mode 100644 module/hisabati.go delete mode 100644 module/json.go delete mode 100644 module/module.go delete mode 100644 module/net.go delete mode 100644 module/os.go delete mode 100644 module/time.go 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)} -} From 9bda514a55b65f445fb91351d38759140062fc5e Mon Sep 17 00:00:00 2001 From: Gekko Wrld Date: Sun, 22 Dec 2024 13:16:30 +0300 Subject: [PATCH 3/7] fix: Make most of the tests pass From previous changes, the tests started to fail because of added token. This is now fixed for most modules except the evaluator module. The remaining problem may be related to the NEWLINE token. The tests check the last token, which previously would be the value. But now the last token would be the NEWLINE instead of another token like INTEGER. When running, the code works correctly. This strongly indicates the problem to be in the tests instead of the code (unlike the other tests). Signed-off-by: Gekko Wrld --- evaluator/evaluator.go | 8 +++++++- evaluator/evaluator_test.go | 5 +++-- lexer/lexer.go | 1 + lexer/lexer_test.go | 35 +++++++++++++++++++++++++++++++++++ parser/switch.go | 4 ++++ 5 files changed, 50 insertions(+), 3 deletions(-) diff --git a/evaluator/evaluator.go b/evaluator/evaluator.go index 1ea527c..7869348 100644 --- a/evaluator/evaluator.go +++ b/evaluator/evaluator.go @@ -296,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..b02de95 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" @@ -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/lexer/lexer.go b/lexer/lexer.go index 329a49b..9cc2822 100644 --- a/lexer/lexer.go +++ b/lexer/lexer.go @@ -58,6 +58,7 @@ func (l *Lexer) NextToken() token.Token { 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/parser/switch.go b/parser/switch.go index ebffc63..6d3f9dd 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) From e4c175fc708790f9190f2abe4e24c6f9fe55b401 Mon Sep 17 00:00:00 2001 From: Gekko Wrld Date: Sun, 22 Dec 2024 13:27:51 +0300 Subject: [PATCH 4/7] chore: Remove module references Some modules were hardcoded, this has been removed. Tests pass as before (except module related ones which don't currently exist, and evaluator ones ofc). Signed-off-by: Gekko Wrld --- evaluator/method.go | 4 ---- object/module.go | 20 -------------------- 2 files changed, 24 deletions(-) delete mode 100644 object/module.go 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/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 } From 8c8a9df357614eca4d4f01f26275d6158754dd6b Mon Sep 17 00:00:00 2001 From: Gekko Wrld Date: Sun, 22 Dec 2024 15:06:41 +0300 Subject: [PATCH 5/7] chore: Resolve `hapa` in various locations Modules that are referenced by `hapa` e.g: tumia hapa::faili can be read succesfully. Add a new information to the token `Filename`. This is used for resolving, so its empty in most parts. its initialized by the import parser. The stdlib will be referenced by `msingi` instead of `mkt`. This is more approriate and sensible than the last. Signed-off-by: Gekko Wrld --- evaluator/import.go | 39 +++++++++++++++++++++++++++++++-------- main.go | 2 +- maktaba/nuru.toml | 7 +++++++ parser/import.go | 2 ++ parser/parser.go | 5 +++-- repl/docs.go | 4 ++-- repl/repl.go | 6 +++--- token/token.go | 1 + 8 files changed, 50 insertions(+), 16 deletions(-) create mode 100644 maktaba/nuru.toml diff --git a/evaluator/import.go b/evaluator/import.go index 7fb35f5..b5d197e 100644 --- a/evaluator/import.go +++ b/evaluator/import.go @@ -28,7 +28,7 @@ const ( func evalImport(node *ast.Import, env *object.Environment) object.Object { /* Maktaba yanafuata mkondo wa: - mkt => ni maktaba ya kawaida (stdlib) + 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 */ @@ -41,13 +41,18 @@ func evalImport(node *ast.Import, env *object.Environment) object.Object { } switch vs[0] { - case "mkt": + 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.Token.Filename) - fi__ := filepath.Join(loc) - g__ := filepath.Join(ss...) - fi := filepath.Join(fi__, g__) + ss := strings.Split(v.Value, "/")[1:] + fi := filepath.Join(loc, filepath.Join(ss...)) return evalImportFile(k, &ast.Identifier{Value: fi}, env) default: @@ -59,7 +64,7 @@ func evalImport(node *ast.Import, env *object.Environment) object.Object { return NULL } -func mahali_pa_maktaba(mkt maktaba) string { +func mahali_pa_maktaba(mkt maktaba, mahali ...string) string { switch mkt { case MAKTABA_YA_NURU: loc := os.Getenv("MAKTABA_YA_NURU") @@ -85,6 +90,25 @@ func mahali_pa_maktaba(mkt maktaba) string { } 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 "" } @@ -110,7 +134,6 @@ func addSearchPath(path string) { func findFile(name string) string { basename := fmt.Sprintf("%s.nuru", name) - fmt.Println(basename) for _, path := range searchPaths { file := filepath.Join(path, basename) if fileExists(file) { @@ -131,7 +154,7 @@ func evaluateFile(name, file string, env *object.Environment) (*object.Environme 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"))} diff --git a/main.go b/main.go index 823b0d3..e329890 100644 --- a/main.go +++ b/main.go @@ -59,7 +59,7 @@ func main() { 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/nuru.toml b/maktaba/nuru.toml new file mode 100644 index 0000000..10e5a56 --- /dev/null +++ b/maktaba/nuru.toml @@ -0,0 +1,7 @@ +[pakeji] +jina = "msingi" +mahali = "https://github.com/NuruProgramming/Nuru" +fafanuo = "Hii ni maktaba ya msingi ya nuru" + +[inategemea] +# Pakeji hii haitegemei pakeji yeyote ye nje diff --git a/parser/import.go b/parser/import.go index 4f229c6..ec83bfc 100644 --- a/parser/import.go +++ b/parser/import.go @@ -54,6 +54,7 @@ func (p *Parser) parseImport() ast.Expression { jina.WriteString(mhss[len(mhss)-1]) } exp.Identifiers[jina.String()] = &ast.Identifier{Value: mhs} + exp.Token.Filename = p.filname mahali.Reset() p.nextToken() @@ -77,6 +78,7 @@ func (p *Parser) parseImport() ast.Expression { jina.WriteString(mhss[len(mhss)-1]) } exp.Identifiers[jina.String()] = &ast.Identifier{Value: mhs} + exp.Token.Filename = p.filname return exp } diff --git a/parser/parser.go b/parser/parser.go index 4e0a448..9cea249 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 + filname 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{}, filname: filename} p.nextToken() p.nextToken() 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..e845afe 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 825d9a8..3528d2c 100644 --- a/token/token.go +++ b/token/token.go @@ -8,6 +8,7 @@ type Token struct { Type TokenType Literal string Line int + Filename string } const ( From 2dc6b7c6bf7734df3813187f2fdb06fc4cef7a7c Mon Sep 17 00:00:00 2001 From: Gekko Wrld Date: Sun, 22 Dec 2024 16:46:00 +0300 Subject: [PATCH 6/7] fix: Fix parser calls Signed-off-by: Gekko Wrld --- ast/ast.go | 1 + evaluator/evaluator_test.go | 2 +- evaluator/import.go | 6 ++-- main.go | 2 +- maktaba/nuru.toml | 1 + parser/import.go | 4 +-- parser/parser.go | 4 +-- parser/parser_test.go | 58 +++++++++++++++++++------------------ parser/switch.go | 2 +- repl/repl.go | 2 +- token/token.go | 1 - 11 files changed, 43 insertions(+), 40 deletions(-) diff --git a/ast/ast.go b/ast/ast.go index c7bcb74..d743e50 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 } diff --git a/evaluator/evaluator_test.go b/evaluator/evaluator_test.go index b02de95..bd989d7 100644 --- a/evaluator/evaluator_test.go +++ b/evaluator/evaluator_test.go @@ -107,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() diff --git a/evaluator/import.go b/evaluator/import.go index b5d197e..c3b4829 100644 --- a/evaluator/import.go +++ b/evaluator/import.go @@ -49,7 +49,7 @@ func evalImport(node *ast.Import, env *object.Environment) object.Object { return evalImportFile(k, &ast.Identifier{Value: fi}, env) case "hapa": - loc := mahali_pa_maktaba(MAKTABA_YA_HAPA, node.Token.Filename) + loc := mahali_pa_maktaba(MAKTABA_YA_HAPA, node.Filename) ss := strings.Split(v.Value, "/")[1:] fi := filepath.Join(loc, filepath.Join(ss...)) @@ -97,7 +97,7 @@ func mahali_pa_maktaba(mkt maktaba, mahali ...string) string { // Check if the current dir has "nuru.toml" for { - if filepath.Dir(mkt__) == mkt__ { + if filepath.Dir(mkt__) == mkt__ { break } @@ -154,7 +154,7 @@ func evaluateFile(name, file string, env *object.Environment) (*object.Environme return nil, &object.Error{Message: fmt.Sprintf("Tumeshindwa kufungua pakeji: %s", file)} } l := lexer.New(string(source)) - p := parser.New(l,file) + 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"))} diff --git a/main.go b/main.go index e329890..fc697d9 100644 --- a/main.go +++ b/main.go @@ -59,7 +59,7 @@ func main() { os.Exit(1) } - repl.Read(file,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/nuru.toml b/maktaba/nuru.toml index 10e5a56..976ab7c 100644 --- a/maktaba/nuru.toml +++ b/maktaba/nuru.toml @@ -2,6 +2,7 @@ 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/parser/import.go b/parser/import.go index ec83bfc..6483994 100644 --- a/parser/import.go +++ b/parser/import.go @@ -54,7 +54,7 @@ func (p *Parser) parseImport() ast.Expression { jina.WriteString(mhss[len(mhss)-1]) } exp.Identifiers[jina.String()] = &ast.Identifier{Value: mhs} - exp.Token.Filename = p.filname + exp.Filename = p.filename mahali.Reset() p.nextToken() @@ -78,7 +78,7 @@ func (p *Parser) parseImport() ast.Expression { jina.WriteString(mhss[len(mhss)-1]) } exp.Identifiers[jina.String()] = &ast.Identifier{Value: mhs} - exp.Token.Filename = p.filname + exp.Filename = p.filename return exp } diff --git a/parser/parser.go b/parser/parser.go index 9cea249..c295088 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -71,7 +71,7 @@ type Parser struct { prefixParseFns map[token.TokenType]prefixParseFn infixParseFns map[token.TokenType]infixParseFn postfixParseFns map[token.TokenType]postfixParseFn - filname string + filename string } func (p *Parser) registerPrefix(tokenType token.TokenType, fn prefixParseFn) { @@ -87,7 +87,7 @@ func (p *Parser) registerPostfix(tokenType token.TokenType, fn postfixParseFn) { } func New(l *lexer.Lexer, filename string) *Parser { - p := &Parser{l: l, errors: []string{}, filname: filename} + p := &Parser{l: l, errors: []string{}, filename: filename} p.nextToken() p.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 6d3f9dd..6c8bef2 100644 --- a/parser/switch.go +++ b/parser/switch.go @@ -31,7 +31,7 @@ func (p *Parser) parseSwitchStatement() ast.Expression { p.nextToken() for !p.curTokenIs(token.RBRACE) { - if p.curTokenIs(token.NEWLINE){ + if p.curTokenIs(token.NEWLINE) { p.nextToken() continue } diff --git a/repl/repl.go b/repl/repl.go index e845afe..9dd95ac 100644 --- a/repl/repl.go +++ b/repl/repl.go @@ -28,7 +28,7 @@ func Read(filename, contents string) { env := object.NewEnvironment() l := lexer.New(contents) - p := parser.New(l,filename) + p := parser.New(l, filename) program := p.ParseProgram() diff --git a/token/token.go b/token/token.go index 3528d2c..825d9a8 100644 --- a/token/token.go +++ b/token/token.go @@ -8,7 +8,6 @@ type Token struct { Type TokenType Literal string Line int - Filename string } const ( From be679b4eb263d9c2809b3cb52d2317c0c4cc3dd7 Mon Sep 17 00:00:00 2001 From: Gekko Wrld Date: Sun, 29 Dec 2024 14:15:20 +0300 Subject: [PATCH 7/7] fix: Allow all types to be exportable Only functions were allowed to be imported/exported. If a variable or a function name (without a call), then it will fail. This restricted the way that packages can be written. It meant that to export anything, you need to wrap it inside a function. If a function that is not executed is printed, it follows the nodejs format. [Function: ] i.e [undo: ]. If the function has parameters, then they can are displayed also. [undo: ; inahitaji: <[vinavyohitajika]>] This is more cleaner than trying to piece together the function body. Add extra logic for when assignment is to a function. This is to add the function name which was lost when handing over from assign to functionEval. Signed-off-by: Gekko Wrld --- ast/ast.go | 4 ++-- evaluator/assign.go | 6 ++++++ evaluator/property.go | 6 ++++-- maktaba/hisabati.nuru | 4 +--- object/function.go | 17 +++++++++-------- 5 files changed, 22 insertions(+), 15 deletions(-) diff --git a/ast/ast.go b/ast/ast.go index d743e50..ba9a009 100644 --- a/ast/ast.go +++ b/ast/ast.go @@ -624,7 +624,7 @@ type NoOp struct { func (np *NoOp) expressionNode() {} func (np *NoOp) TokenLiteral() string { return np.Token.Literal } -func (np *NoOp) String() string { return "??" } +func (np *NoOp) String() string { return "" } type At struct { Token token.Token @@ -653,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/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/maktaba/hisabati.nuru b/maktaba/hisabati.nuru index d7eff05..6fa1e32 100644 --- a/maktaba/hisabati.nuru +++ b/maktaba/hisabati.nuru @@ -1,4 +1,2 @@ // Hii ni thamni ya PI -PI = unda() { - rudisha 3.141592653589793 -} +PI = 3.141592653589793 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() }