views:

128

answers:

2

Edit: You can get the full source here: http://pastebin.com/m26693

Edit again: I added some highlights to the pastebin page. http://pastebin.com/m10f8d239

I'm probably going to regret asking such a long question, but I'm stumped with this bug and I could use some guidance. You're going to have to run this code (edit: not anymore. I couldn't include all of the code -- it was truncated) to help in order to really see what's going on, unless you're God or something, then by all means figure it out without running it. Actually I kind of hope that I can explain it well enough so that's not necessary, and I do apologize if I don't accomplish that.

First I'll give you some output. (Edit: There's new output below)

argc 1 [<__main__.RESULT instance at 0x94f91ec>]
(<__main__.RESULT instance at 0x9371f8c>, <__main__.RESULT instance at 0x94f91ec>)
None
bar
internal error: unknown result type 0
argc 1 [<__main__.RESULT instance at 0x94f92ac>]
(<__main__.RESULT instance at 0x94f91ac>, <__main__.RESULT instance at 0x94f92ac>)
None
bar
internal error: unknown result type 0
argc 1 [<__main__.RESULT instance at 0x94f91ec>]
(<__main__.RESULT instance at 0x94f91ec>,)
String: 'bar'

We have 3 divisions in the output. Notice that argc is always 1. At the point where that is printed, an argument list has been built to be passed to a plugin (Plugins are simply commands in the expression interpreter. Most of this code is the expression interpreter.) The list of a single RESULT instance representation that follows argc is the argument list. The next line is the argument list once it reaches the Python method being called. Notice it has two arguments at this point. The first of these two is trash. The second one is what I wanted. However, as you can see on the lines starting "argc 1" that argument list is always 1 RESULT wide. Where's the stray argument coming from?

Like I said, there are 3 divisions in the output. The first division is a class by itself. The second division is a subclassed class. And the third division is no class/instance at all. The only division that outputs what I expected is the 3rd. Notice it has 1 argument member both before the call and within the call, and the last line is the intended output. It simply echoes/returns the argument "bar".

Are there any peculiarities with variable argument lists that I should be aware of? What I mean is the following:

def foo(result, *argv):
    print argv[0]

I really think the bug has something to do with this, because that is where the trash seems to come from -- in between the call and the execution arrival in the method.

Edit: Ok, so they limit the size of these questions. :) I'll try my best to show what's going on. Here's the relevant part of EvalTree. Note that there's only 2 divisions in this code. I messed up that other file and deleted it.

def EvalTree(self, Root):
    type = -1
    number = 0.0
    freeme = 0


    if Root.Token == T_NUMBER or Root.Token == T_STRING:
        return 0

    elif Root.Token == T_VARIABLE:
        self.CopyResult(Root.Result, Root.Variable.value)
        return 0

    elif Root.Token == T_FUNCTION:
        argc = Root.Children
        param = resizeList([], argc, RESULT)
        print "argc", argc
        for i in range(argc):
            self.EvalTree(Root.Child[i])
            param[i] = Root.Child[i].Result

        self.DelResult(Root.Result)
        Root.Function.func(Root.Result, *param) # I should have never ever programmed Lua ever.
        return 0

Here's the Plugin's class.

class Foo:
    def __init__(self, visitor):
        visitor.AddFunction("foo", -1, self.foo)
    def foo(self, result, *argv):
        print argv

Here's where it's all executed.

if __name__ == "__main__":
    evaluator = Evaluator()

    expression = "foo2('bar')"
    #expression = "uptime('test')"
    evaluator.SetVariableString("test", "Foo")
    def func(self, result, *arg1):
        print arg1
        evaluator.SetResult(result, R_STRING, evaluator.R2S(arg1[0]))

    evaluator.AddFunction('foo2', -1, func)

    result = RESULT(0, 0, 0, None)
    tree = evaluator.Compile(expression)
    if tree != -1:
        evaluator.Eval(tree, result)
        if result.type == R_NUMBER:
            print "Number: %g" % (evaluator.R2N(result))
        elif result.type == R_STRING:
            print "String: '%s'" % (result.string) #(evaluator.R2S(result))
        elif result.type == (R_NUMBER | R_STRING):
            print "String: '%s' Number: (%g)" % (evaluator.R2S(result), evaluator.R2N(result))
        else:
            print "internal error: unknown result type %d" % (result.type)

    expression = "foo('test')"
        result = RESULT(0, 0, 0, None)
        tree = evaluator.Compile(expression)
        if tree != -1:
                evaluator.Eval(tree, result)
                if result.type == R_NUMBER:
                        print "Number: %g" % (evaluator.R2N(result))
                elif result.type == R_STRING:
                        print "String: '%s'" % (result.string) #(evaluator.R2S(result))
                elif result.type == (R_NUMBER | R_STRING):
                        print "String: '%s' Number: (%g)" % (evaluator.R2S(result), evaluator.R2N(result))
                else:
                        print "internal error: unknown result type %d" % (result.type)

This is the new output:

argc 1
(<__main__.RESULT instance at 0x9ffcf4c>,)
String: 'bar'
argc 1
(<__main__.RESULT instance at 0xa0030cc>, <__main__.RESULT instance at 0xa0030ec>)
internal error: unknown result type 0
+2  A: 

It appears that your code was truncated, so I can't look through it.

Given that you only get the extra argument on methods defined in a class, though, might it be the self variable? Every method on a Python class receives self as the first parameter, and if you don't account for it, you'll get things wrong.

In other words, should this:

def foo(result, *argv):
    print argv[0]

actually be this:

def foo(self, result, *argv):
    print argv[0]

If so, then the value traditionally held by self will be assigned to result, and your result value will be in the first position of argv.

If that's not it, you'll need to give more code. At the very least, the code that actually runs the tests.

BJ Homer
Ok I updated it with the relevant code.
Scott
I've thought about your answer some. If that were the case then you'd see an instance of instances of Evaluator and RESULT instead of RESULT and RESULT. I agree though, the fact that the bug occurs only when you're working with instance methods suggests it may have something to do with self. I just can't find it.
Scott
I misunderstood some of your words. I understand now, and you were right.
Scott
Wait no I'm wrong. This bug's confusing.
Scott
lol yeah :) That was it.
Scott
+1  A: 

In class Foo, when you call


    def __init__(self, visitor):
        visitor.AddFunction("foo", -1, self.foo)

...you are adding what's called a "bound" method argument (that is, self.foo). It is like a function that already has the self argument specified. The reason is, when you call self.foo(bar, baz), you don't specify "self" again in the argument list. If you call


    def __init__(self, visitor):
        visitor.AddFunction("foo", -1, Foo.foo)

...you'd get the same result as with your free function. However, I don't think this is quite what you want. Besides, EvalTree passes its own self as the first arg to the function. I think what you might want is to declare foo like this:


class Foo:
    def __init__(self, visitor):
        visitor.AddFunction("foo", -1, self.foo)
    def foo(self, tree, result, *argv):
        print argv
Doug
"Besides, EvalTree passes its own self as the first arg to the function." - That was my problem. I got in the habit of passing everything in Lua while playing WoW and I typed self in there. It shouldn't have been passing self as the first argument. I know that. I can't believe I didn't see it. At any rate, it's working now. Thanks for looking into it and for answering.
Scott