From 53b6d026e7d2aea11f6ceb55655ba15fec7b7d42 Mon Sep 17 00:00:00 2001 From: tacheraSasi Date: Fri, 6 Dec 2024 16:38:55 +0300 Subject: [PATCH] test implemantion of the http module FAILED --- evaluator/evaluator_test.go | 70 ++++++++--------- module/http.go | 145 ++++++++++++++++++++++++++++++++++++ 2 files changed, 180 insertions(+), 35 deletions(-) create mode 100644 module/http.go diff --git a/evaluator/evaluator_test.go b/evaluator/evaluator_test.go index 3336381..69ab423 100755 --- a/evaluator/evaluator_test.go +++ b/evaluator/evaluator_test.go @@ -263,7 +263,7 @@ if (10 > 1) { }, { `{"jina": "Avi"}[func(x) {x}];`, - "Line 1: Samahani, UNDO (FUNCTION) haitumiki if ufunguo", + "Line 1: Samahani, FUNC (FUNCTION) haitumiki if ufunguo", }, } @@ -603,31 +603,31 @@ func TestInExpression(t *testing.T) { expected bool }{ { - "'a' ktk 'habari'", + "'a' in 'habari'", true, }, { - "'c' ktk 'habari'", + "'c' in 'habari'", false, }, { - "1 ktk [1, 2, 3]", + "1 in [1, 2, 3]", true, }, { - "4 ktk [1, 2, 3]", + "4 in [1, 2, 3]", false, }, { - "'a' ktk {'a': 'apple', 'b': 'banana'}", + "'a' in {'a': 'apple', 'b': 'banana'}", true, }, { - "'apple' ktk {'a': 'apple', 'b': 'banana'}", + "'apple' in {'a': 'apple', 'b': 'banana'}", false, }, { - "'c' ktk {'a': 'apple', 'b': 'banana'}", + "'c' in {'a': 'apple', 'b': 'banana'}", false, }, } @@ -748,7 +748,7 @@ func TestPostfixExpression(t *testing.T) { func TestWhileLoop(t *testing.T) { input := ` i = 10 - wakati (i > 0){ + while (i > 0){ i-- } i @@ -768,7 +768,7 @@ func TestWhileLoop(t *testing.T) { func TestForLoop(t *testing.T) { input := ` output = "" - kwa i ktk "mojo" { + for i in "mojo" { output += i } output @@ -787,9 +787,9 @@ func TestForLoop(t *testing.T) { func TestBreakLoop(t *testing.T) { input := ` i = 0 - wakati (i < 10) { + while (i < 10) { if (i == 5) { - vunja + break } i++ } @@ -807,10 +807,10 @@ func TestBreakLoop(t *testing.T) { input = ` output = "" - kwa i ktk "mojo" { + for i in "mojo" { output += i if (i == 'o') { - vunja + break } } output @@ -830,10 +830,10 @@ func TestBreakLoop(t *testing.T) { func TestContinueLoop(t *testing.T) { input := ` i = 0 - wakati (i < 10) { + while (i < 10) { i++ if (i == 5) { - endelea + continue } i++ } @@ -851,9 +851,9 @@ func TestContinueLoop(t *testing.T) { input = ` output = "" - kwa i ktk "mojo" { + for i in "mojo" { if (i == 'o') { - endelea + continue } output += i } @@ -880,14 +880,14 @@ func TestSwitchStatement(t *testing.T) { ` i = 5 switch (i) { - ikiwa 2 { + case 2 { output = 2 } - ikiwa 5 { + case 5 { output = 5 } - kawaida { - output = "haijulikani" + default { + output = "failed" } } output @@ -898,29 +898,29 @@ func TestSwitchStatement(t *testing.T) { ` i = 5 switch (i) { - ikiwa 2 { + case 2 { output = 2 } - kawaida { - output = "haijulikani" + default { + output = "failed" } } output `, - "haijulikani", + "failed", }, { ` i = 5 switch (i) { - ikiwa 5 { + case 5 { output = 5 } - ikiwa 2 { + case 2 { output = 2 } - kawaida { - output = "haijulikani" + default { + output = "failed" } } output @@ -942,7 +942,7 @@ func TestSwitchStatement(t *testing.T) { } if s.Value != tt.expected { - t.Errorf("Wrong Value, want='haijulikani', got=%s", s.Value) + t.Errorf("Wrong Value, want='failed', got=%s", s.Value) } } @@ -1029,19 +1029,19 @@ func TestStringMethods(t *testing.T) { expected interface{} }{ { - "'mambo'.idadi()", + "'mambo'.len()", 5, }, { - "'mambo'.herufikubwa()", + "'mambo'.upper()", "MAMBO", }, { - "'MaMbO'.herufindogo()", + "'MaMbO'.lower()", "mambo", }, { - "'habari'.gawa('a')", + "'habari'.split('a')", "[h, b, ri]", }, } diff --git a/module/http.go b/module/http.go new file mode 100644 index 0000000..d723adb --- /dev/null +++ b/module/http.go @@ -0,0 +1,145 @@ +package module + +import ( + "encoding/json" + "net/http" + "strings" + + "github.com/ekilie/vint-lang/object" +) + +var HttpFunctions = map[string]object.ModuleFunction{} + +func init() { + HttpFunctions["createServer"] = createServer + HttpFunctions["listen"] = listen +} + +type HttpServer struct { + routes map[string]map[string]*object.Function // Method -> Path -> Handler +} + +// Global map of created servers +var servers = map[string]*HttpServer{} + +func createServer(args []object.Object, env *object.Environment) object.Object { + serverID := "default" + if len(args) > 0 { + id, ok := args[0].(*object.String) + if !ok { + return &object.Error{Message: "Server ID must be a string"} + } + serverID = id.Value + } + + if _, exists := servers[serverID]; exists { + return &object.Error{Message: "Server already exists with ID: " + serverID} + } + + servers[serverID] = &HttpServer{routes: make(map[string]map[string]*object.Function)} + return &object.String{Value: serverID} +} + +func listen(args []object.Object, env *object.Environment) object.Object { + if len(args) < 2 { + return &object.Error{Message: "Usage: listen(serverID, port)"} + } + + serverID, ok := args[0].(*object.String) + if !ok { + return &object.Error{Message: "Server ID must be a string"} + } + + port, ok := args[1].(*object.String) + if !ok { + return &object.Error{Message: "Port must be a string"} + } + + server, exists := servers[serverID.Value] + if !exists { + return &object.Error{Message: "No server found with ID: " + serverID.Value} + } + + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + method := strings.ToUpper(r.Method) + path := r.URL.Path + + if server.routes[method] == nil || server.routes[method][path] == nil { + http.NotFound(w, r) + return + } + + handler := server.routes[method][path] + + // Prepare arguments for the handler function + params := map[string]object.Object{ + "method": &object.String{Value: method}, + "path": &object.String{Value: path}, + } + + // Parse JSON body if provided + if r.Body != nil && r.Method == "POST" { + defer r.Body.Close() + var body map[string]interface{} + if err := json.NewDecoder(r.Body).Decode(&body); err == nil { + params["body"] = convertGoMapToDict(body) + } + } + + handlerEnv := object.NewEnclosedEnvironment(handler.Env) + for key, value := range params { + handlerEnv.Set(key, value) + } + + // Call the handler function + result := Eval(handler.Body, handlerEnv) + if errObj, ok := result.(*object.Error); ok { + http.Error(w, errObj.Message, http.StatusInternalServerError) + } else { + w.WriteHeader(http.StatusOK) + w.Write([]byte(result.Inspect())) + } + }) + + go func() { + if err := http.ListenAndServe(":"+port.Value, nil); err != nil { + panic(err) + } + }() + + return &object.String{Value: "Server listening on port " + port.Value} +} + +func (s *HttpServer) AddRoute(method, path string, handler *object.Function) object.Object { + if s.routes[method] == nil { + s.routes[method] = make(map[string]*object.Function) + } + s.routes[method][path] = handler + return &object.Boolean{Value: true} +} + +// Converts Go map to object.Dict +func convertGoMapToDict(data map[string]interface{}) *object.Dict { + dict := &object.Dict{Pairs: make(map[object.HashKey]object.DictPair)} + for key, value := range data { + strKey := &object.String{Value: key} + var valObj object.Object + + switch v := value.(type) { + case string: + valObj = &object.String{Value: v} + case float64: + valObj = &object.Float{Value: v} + case bool: + valObj = &object.Boolean{Value: v} + case map[string]interface{}: + valObj = convertGoMapToDict(v) + default: + valObj = &object.Null{} + } + + hashKey := strKey.HashKey() + dict.Pairs[hashKey] = object.DictPair{Key: strKey, Value: valObj} + } + return dict +}