diff --git a/interpreter/functions.go b/interpreter/functions.go index fdbaae8..cbe7322 100644 --- a/interpreter/functions.go +++ b/interpreter/functions.go @@ -626,6 +626,9 @@ func typeName(v Value) string { t = "map" case functionType: t = "function" + case *userObject: + t = "object" + default: // Interpreter should never give us this panic(fmt.Sprintf("type() got unexpected type %T", v)) diff --git a/interpreter/interpreter.go b/interpreter/interpreter.go index 4c3d48a..0429905 100644 --- a/interpreter/interpreter.go +++ b/interpreter/interpreter.go @@ -446,9 +446,34 @@ func (interp *interpreter) evaluate(expr parser.Expression) Value { case *parser.SemiTag: return nil case *parser.MethodCall: - // Evaluate the object and method name - print("Method call: ", e.Method) - return nil + + // The method name + methodName := e.Method + + print("Method name: ", methodName) + + // Evaluate the object on which the method is being called + object := interp.evaluate(e.Object) + //print("Object: ", object) + + //// Evaluate arguments passed to the method + args := []Value{} + for _, arg := range e.Arguments { + args = append(args, interp.evaluate(arg)) + } + // + //// Check if the object is of a type that supports method calls + if objWithMethods, ok := object.(objectWithMethodsType); ok { + // Call the method on the object + method := objWithMethods.lookupMethod(methodName) + if method == nil { + panic(typeError(e.Position(), "method %q not found on type %s", methodName, typeName(object))) + } + + return interp.callFunction(e.Position(), method, args) + } + // + panic(typeError(e.Position(), "type %s does not support method calls", typeName(object))) case *parser.NewExpression: // Evaluate the class name and arguments @@ -469,6 +494,10 @@ func (interp *interpreter) evaluate(expr parser.Expression) Value { } } +type objectWithMethodsType interface { + lookupMethod(methodName string) functionType +} + func (interp *interpreter) pushScope(scope map[string]Value) { interp.vars = append(interp.vars, scope) } diff --git a/parser/parser.go b/parser/parser.go index c4678e9..3cb23d7 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -92,6 +92,19 @@ func (p *parser) statement() Statement { } pos := p.pos expr := p.expression() + if p.tok == OBJECT_OPERATOR { + pos = p.pos + p.expect(OBJECT_OPERATOR) + + expr = &MethodCall{pos, expr, p.val, []Expression{}} + + p.expect(NAME) + p.expect(LPAREN) + p.expect(RPAREN) + + return &ExpressionStatement{pos, expr} + + } if p.tok == ASSIGN { pos = p.pos switch expr.(type) { @@ -442,27 +455,27 @@ func (p *parser) primary() Expression { args, _ := p.params() return &NewExpression{pos, className, args} - case OBJECT_OPERATOR: - pos := p.pos - p.next() // Move past the OBJECT_OPERATOR token - - if p.tok != NAME { - p.error("expected a method or property name after '->'") - return nil - } - - methodName := p.val - p.next() // Move past the method name - - //// Assuming you want to handle method calls like $test->call(args...) - if p.tok == LPAREN { - p.next() // Skip '(' - args := []Expression{} - p.expect(RPAREN) // Ensure method call is closed with ')' - return &MethodCall{pos, nil, methodName, args} - } - - return &PropertyAccess{pos, nil, methodName} + //case OBJECT_OPERATOR: + // pos := p.pos + // p.next() // Move past the OBJECT_OPERATOR token + // + // if p.tok != NAME { + // p.error("expected a method or property name after '->'") + // return nil + // } + // + // methodName := p.val + // p.next() // Move past the method name + // + // //// Assuming you want to handle method calls like $test->call(args...) + // if p.tok == LPAREN { + // p.next() // Skip '(' + // args := []Expression{} + // p.expect(RPAREN) // Ensure method call is closed with ')' + // return &MethodCall{pos, nil, methodName, args} + // } + // + // return &PropertyAccess{pos, nil, methodName} default: formatter := prettyjson.NewFormatter()