Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Loadstring and local variables

edited January 2015 in Bugs Posts: 104

Not sure if this counts as a bug or not, but the following code throws an error whereas EITHER of the following work fine:-

  1. Take out the "local" to make SHOPCOL1 a global variable (but still run the fill through loadstring)
    OR
  2. Run a simple fill command without executing it with loadstring (while keeping the color variable local)

Looks like loadstring reacts weirdly with local variables - can anyone else verify?


function setup() end function draw() background(71, 127, 162, 255) local SHOPCOL1 = color(114, 30, 38, 255) loadstring("fill(SHOPCOL1)")() rectMode(CENTER) rect(WIDTH/2, HEIGHT/2, 300) end

Comments

  • Posts: 136

    Yeah, that seems to be the case. Probably because calling loadstring is the equivalent of calling a different function, and SHOPCOL1 is local to the draw function, making it nil in any other function, if you choose to look at it that way.

  • Ah that makes sense. I'm still experimenting with loadstring, so I wasn't thinking about it like that.

  • IgnatzIgnatz Mod
    Posts: 5,396

    I expect that loadstring is treated as a separate block of code, so local variables in the calling function are not recognised

  • edited January 2015 Posts: 2,049

    I think you could fix this with a lua function called setfenv but I'm not sure, you'd have to look into it

  • edited January 2015 Posts: 100

    Untested

    Try this:


    function setup() end function draw() background(71, 127, 162, 255) local SHOPCOL1 = color(114, 30, 38, 255) setfenv(loadstring("fill(SHOPCOL1)"), getfenv())() rectMode(CENTER) rect(WIDTH/2, HEIGHT/2, 300) end

    I believe this might fix your problem. I'm not entirely sure however. Report back with any errors.

  • No, this still throws the same error (numbed expected as args to fill, got nil). I have no experience manipulating environments

  • IgnatzIgnatz Mod
    Posts: 5,396

    This seems to work, but it seems very clumsy. Do you really need to do this? You have to give the context table the name of every function or variable it will need.

    function draw()
        background(71, 127, 162, 255)
        --create a sandbox environment
        --give it the fill function and the colour
        context={fill=fill}
        context.SHOPCOL1 = color(114, 30, 38, 255)
        LoadCode("fill(SHOPCOL1)",context)() --run it
        rectMode(CENTER)
        rect(WIDTH/2, HEIGHT/2, 300)
    end
    
    function LoadCode(code, environ)
        local f=assert(loadstring(code))
        setfenv(f,environ)
        return f
    end
    
  • Thanks Ignatz. I can work around it in other less clumsy ways. I had a shop screen, with different inventory tabs in different colours, and I thought this would be a neat way to cycle through them with a for loop(SHOPCOL1, SHOPCOL2, SHOPCOL3) and the principle might be of broader use to me. It took a bit of work to realise that it was local variables on my shop screen that were giving rise to the error, so by the time I'd done that I thought it was worth sharing.

    Another time saving gambit that's taken up far more time than it will ever save :)

  • IgnatzIgnatz Mod
    Posts: 5,396

    @epicurus101 - I wouldn't use loadstring for that. In fact, I wouldn't use loadstring for anything I didn't have to. Keep it simple if you can.

  • Honestly, there's no reason to use loadstring unless you have to run code from something the user input.

  • Whenever I discover a new function I get a bit over enthusiastic and see if I can use it. Tips noted, thank you.

  • Do you mean set up a function which cycles through the colors as up values?

    In the end my alternative was to put some fill functions in a local table, and call each function in turn in my for loop. Seems to work ok but I'll have to find a use for closures sometime soon so they actually lodge in my memory. I had read your closures tute before, but it didn't even occur to me when it came to addressing this issue.

  • IgnatzIgnatz Mod
    Posts: 5,396

    No, I didn't mean for you to use closures in this case, I just suggested them as something interesting for you to discover

  • Jmv38Jmv38 Mod
    Posts: 3,297

    i use loadstring to run forum code from a codea program.

  • The use I definitely intend to put loadstring to is capturing big, difficult to read tables as text files. I have a table describing the attributes of the many bad guys in my game which runs to over 700 lines. It's going to be much easier to manage if I keep it as a text file. As I understand it, readtext only gives you the content as a string. Loadstring is my best bet to turn that into a table I think.

  • Rather than create another thread, I'm going to use this one to air a question about an unrelated quirk!

    It's about how lua evaluates if statements in relation to tables. If you create an empty table, called Example:-

    Example = {}

    and you then set a condition of:-

    if Example[1] then

    this will evaluate as false, because there is no first entry in Example. It's empty. Same as if you try to access a variable that hasn't been defined and so is "nil".

    However if you try to assess the existence of a non-existent key in a non-existent item, then it throws an error. So for example:-

    if Example[1].category then

    generates an error, rather than simply evaluating as false.

    Why is there this distinction? Example[1] is just as non existent as Example[1].category isn't it?

    I've found that you can get around it by saying:-

    if Example[1] and Example[1].category then

    because Lua stops at the first false condition and doesn't bother to evaluate the second.

    However that looks a bit clumsy. Interested in thoughts. If anyone approaches this sort of question in a different way I'd love to hear it.

  • IgnatzIgnatz Mod
    edited January 2015 Posts: 5,396

    @epicurus101 - The error is not so much that both Example[1] and category don't exist, but that Example[1] is not defined as a table. Lua is very picky about this. You can't look up a table value in a variable that is not defined as a table.

    For example, if you want to fill a 2D table, you need to do it like this

    T = {}
    for i=1,6 do
        T[i] = {}  --<---this is essential
        for j = 1, 6 do
            T[i][j] = 1
        end
    end
    

    Bear in mind that when you create a table, Lua assigns memory for it, and stores the memory address (a number) in your variable - rather than the actual table. So when you try to use a variable as a table, using the dot syntax, Lua expects to find the address of a table in the variable. If it's not there, you get an error, whether or not the variable itself exists.

    I can assure you it's not clumsy. Lua is very elegant, and tables are amazingly powerful and versatile.

  • Jmv38Jmv38 Mod
    edited January 2015 Posts: 3,297
    if Example[1] and Example[1].category then
    

    is exactly the right way to manage this pb. Congrats for finding it yourself!
    Whan i started LUA i felt exactly as your do. But now i find it rather logical. That is the good point of LUA: there is no weird exception: everything behaves just as it should, once you've got the philosophy. And there is very little to know. When you have a bug, it is always from your side, not from LUA, which make debugging really easy (compared to javascript or php).
    When you get used to LUA you'll love it.
    The only points that are a bit tricky are:
    - as you noticed nil evaluates as false, and i regularly get caught with a = last_a or true when last_a==false this doesnt work (a is set true, while the or true is intended only for last_a is nil, on startup). This is tricky because it works perfctly with all other types except boolean.
    - Child = class(Parent) yield to some pb when you want to access the parent init in a cascade of inheritance, never completely understood that one, but the best way is to call directly Child:init() Parent.init(self) ....... end which is (i feel) clumsy. I would have found more logical Child:init() self:init() ....... end the init inside referring to Parent.init.

  • @Jmv38 Lua not LUA. It's not an acronym.

Sign In or Register to comment.