List Variables

Does anyone know a way of listing all the variables used in a program by tab or class, I thought I had seen something on the forum previously but I cannot find. I try and use variable names that are memorable, but when you come back to complete a project after a few weeks, I tend to lose track of what variable names I have used.

@timber This might be close enough to what you’re after. Key in the project name and press go. You can include comments using the slider.

Project updated below

Updated version. Tap a project name and it will list the names. Tap again for another project.

-- code viewer

viewer.mode=STANDARD

function setup()
    pasteboard.copy("")
    project=""
    textMode(CORNER)
    font("Courier")
    parameter.boolean("Include_Comments",false,setup1)
    list()
end

function setup1()
    tab,tab1={},{}
    dy,per=0,0
    comment=false
    longComment=false
    process()
end

function draw()
    background(40,40,50)
    fill(255)
    if pasteboard.text~="" then
        project=pasteboard.text
        pasteboard.copy("")
        setup1()
    end
    if lp==nil then
        return
    end
    if #lp==0 then
        text("Project not found...  "..project,50,HEIGHT/2)
        return
    end
    if dy==0 then
        text(project,30,HEIGHT-30)
        text("Nbr    Count    Name            Entries  "..#tab1,30,HEIGHT-70)
        text("Scroll up/down",WIDTH/2+80,HEIGHT-30)
    end
    for a,b in pairs(tab1) do
        text(string.format("%5d   %5d      %-s",a,tab[b],b),2,HEIGHT-80-a*20+dy)
    end
end

function touched(t)
    if t.state==CHANGED then
        dy=dy+t.deltaY
        if dy<0 then
            dy=0
        end
    end
end

function list()
    output.clear()
    temp=listProjects()
    for a,b in pairs(temp) do
        print(b)
    end
    pasteboard.copy("")
end

function process()
    str=""
    project=string.gsub(project,"\
","")
    lp=listProjectTabs(project)
    if #lp==0 then        
        return
    end
    for a,b in pairs(lp) do
        local rp=readProjectTab(project..":"..b)
        str=str..rp
    end
    str1=""
    proc()
end

function get4bytes(z)
    v=string.sub(str,z,z)
    v1=string.sub(str,z+1,z+1)
    v2=string.sub(str,z+2,z+2)
    v3=string.sub(str,z+3,z+3)
end

function checkComments()
    if v=="\
" then
        comment=false
    end
    if v=="-" and v1=="-" and v2=="[" and v3=="[" then
        longComment=true
    elseif longComment and v=="-" and v1=="-" and v2=="]" and v3=="]" then
        longComment=false
    elseif v=="-" and v1=="-" then
        comment=true
    end
    if Include_Comments then
        comment,longComment=false,false
    end
end

function proc()
    for z=1,string.len(str) do
        get4bytes(z)
        checkComments()
        if comment or longComment then
            -- ignore until end of comment 
        else
            proc1()
        end
    end
    proc2()
end

function proc1()
    if v>="a" and v<="z" or v>="A" and v<="Z" then
        per=0
        str1=str1..v
    elseif v>="0" and v<="9" then
        per=0
        str1=str1..v
    elseif str1~="" and v=="_" then
        per=0
        str1=str1..v
    elseif str1~="" and v=="'" then
        per=0
        str1=str1..v
    elseif str1~="" and v=="." then
        per=per+1
        if per==2 then
            per=0
            str1=string.sub(str1,1,string.len(str1)-1)
            if tab[str1]==nil then
                tab[str1]=1
            else
                tab[str1]=tab[str1]+1
            end
            str1=""
        else
            str1=str1..v
        end
    elseif str1~="" then
        per=0
        local len=string.len(str1)
        if string.sub(str1,len,len)=="." then
            str1=string.sub(str1,1,len-1)
        end
        if string.sub(str1,len,len)=="'" then
            str1=string.sub(str1,1,len-1) 
        end 
        if tab[str1]==nil then
            tab[str1]=1
        else
            tab[str1]=tab[str1]+1
        end
        str1=""
    else
        str1=""
    end
end

function proc2()
    for a,b in pairs(tab) do
        table.insert(tab1,a)
    end
    table.sort(tab1)
end

Here’s an updated version. It shows a list of all projects. You can tap on a project name and that will fill in the parameter project name. Then press Process for a list of names in that project. To view another, tap List of projects and select another name.

Updated code below

@dave1707 thank you, this will be a good starting point. I’ve not studied the code closely, but it looks like you create a list of tabs that make up the selected project and iterate through them. If that is the case, I could theoretically add another parameter to select only the tab I wanted to interrogate. I.e Select project → Select tab → Process. The reason for this would be to differentiate between variables with the same name but in different classes.

I think this might be better done with string patterns, but “if it works do it”.

@RonJeffries Just curious, how would string patterns work.

Here’s a version that gives the counts by tab name.

-- code viewer, breakdown by tabs

viewer.mode=STANDARD

function setup()
    pasteboard.copy("")
    project=""
    textMode(CORNER)
    font("Courier")
    parameter.boolean("Include_Comments",false,setup1)
    list()
end

function setup1()
    tab,tab1,tab2={},{},{}
    dy,per=0,0
    comment=false
    longComment=false
    process()
end

function draw()
    background(40,40,50)
    fill(255)
    if pasteboard.text~="" then
        project=pasteboard.text
        pasteboard.copy("")
        setup1()
    end
    if lp==nil then
        return
    end
    if #lp==0 then
        text("Project not found...  "..project,50,HEIGHT/2)
        return
    end
    if dy==0 then
        text(project,30,HEIGHT-30)
        text("Nbr    Count    Name    < tab >",30,HEIGHT-70)
        text("Entries  "..#tab1,WIDTH-200,HEIGHT-70)
        text("Scroll up/down",WIDTH-200,HEIGHT-30)
    end
    for a,b in pairs(tab2) do
        st,en=string.find(b,"#*",1,true)
        str=string.sub(b,en+1)
        text(string.format("%5d   %-s",a,str),2,HEIGHT-80-a*20+dy)
    end
end

function touched(t)
    if t.state==CHANGED then
        dy=dy+t.deltaY
        if dy<0 then
            dy=0
        end
    end
end

function list()
    output.clear()
    temp=listProjects()
    for a,b in pairs(temp) do
        print(b)
    end
    pasteboard.copy("")
end

function process()
    str=""
    project=string.gsub(project,"\
","")
    lp=listProjectTabs(project)
    if #lp==0 then        
        return
    end
    for a,b in pairs(lp) do
        str=""
        tabName=b
        local rp=readProjectTab(project..":"..b)
        str=str..rp
        str1=""
        proc()
    end
    table.sort(tab2)
end

function get4bytes(z)
    v=string.sub(str,z,z)
    v1=string.sub(str,z+1,z+1)
    v2=string.sub(str,z+2,z+2)
    v3=string.sub(str,z+3,z+3)
end

function checkComments()
    if v=="\
" then
        comment=false
    end
    if v=="-" and v1=="-" and v2=="[" and v3=="[" then
        longComment=true
    elseif longComment and v=="-" and v1=="-" and v2=="]" and v3=="]" then
        longComment=false
    elseif v=="-" and v1=="-" then
        comment=true
    end
    if Include_Comments then
        comment,longComment=false,false
    end
end

function proc()
    for z=1,string.len(str) do
        get4bytes(z)
        checkComments()
        if comment or longComment then
            -- ignore until end of comment 
        else
            proc1()
        end
    end
    proc2()
end

function proc1()
    if v>="a" and v<="z" or v>="A" and v<="Z" then
        per=0
        str1=str1..v
    elseif v>="0" and v<="9" then
        per=0
        str1=str1..v
    elseif str1~="" and v=="_" then
        per=0
        str1=str1..v
    elseif str1~="" and v=="'" then
        per=0
        str1=str1..v
    elseif str1~="" and v=="." then
        per=per+1
        if per==2 then
            per=0
            str1=string.sub(str1,1,string.len(str1)-1)
            if tab[str1]==nil then
                tab[str1]=1
            else
                tab[str1]=tab[str1]+1
            end
            str1=""
        else
            str1=str1..v
        end
    elseif str1~="" then
        per=0
        local len=string.len(str1)
        if string.sub(str1,len,len)=="." then
            str1=string.sub(str1,1,len-1)
        end
        if string.sub(str1,len,len)=="'" then
            str1=string.sub(str1,1,len-1)
        end 
        str1=str1.."     < "..tabName.." >"
        if tab[str1]==nil then
            tab[str1]=1
        else
            tab[str1]=tab[str1]+1
        end
        str1=""
    else
        str1=""
    end
end

function proc2()
    for a,b in pairs(tab) do
        table.insert(tab1,a)
    end
    createTab2()
    tab={}
end

function createTab2()
    for a,b in pairs(tab) do
        ss=string.format("%s#*%4d       %-s",a,b,a)
        table.insert(tab2,ss)
    end
end

if i understand what your program does, it basically looks for a pattern of _, alpha and numbers in between punctuation and white space. so we’d express that as a pattern, something like

[%s\\+\\-\\*\\/]*([_%a][_%w]*)[%s\\+\\-\\*\\/]
-- not name  name             not name

hideous, yes, but once we get the pattern right, tabContents:gmatch(pattern) would return all the matches.

See my recent Codea Stats articles for examples of finding class and method definitions.

Trouble with patters is they are hard to write and read. but when the are applicable, they are mighty.

I might try to do this problem in an upcoming stats article. it’ll be a bear!

@RonJeffries i do patterns sometimes, but it seems to take forever to get it right. Probably the more you use it, the easier it becomes.

if it ever gets easy, I’ve not done it enough. :slight_smile:

here’s a thing:

-- find

function setup()
    s = readProjectTab("Main")
    t = {}
    for w in string.gmatch(s, "[_%a][_%w]*") do
        table.insert(t,w)
    end
    table.sort(t)
    w = ""
    for _,word in ipairs(t) do
        if word~= w then print(word) end
        w = word
    end
end

@RonJeffries That’s a start. If I add a=1234, it doesn’t find the 1234. Also, it splits up the commands that have a . in them (ie table.sort). There would probably have to be multiple gmatch commands to find each type of word, number, whatever.

why should it find 1234, that’s not a variable?
is table.sort one word? i’d think not …
i think i’d winnow out dups, then remove all the keywords.
and of course remove comments, which is hard but doable i think.

@RonJeffries Here’s a modification. It finds the number and counts everything. My original code doesn’t look for just variables, but everything that makes up the code. I’m not sure if patterns could do everything the way I’m doing it.

function setup()
    s = readProjectTab("Main")
    t = {}
    for w in string.gmatch(s, "[%a%d]*") do
        if t[w]==nil then
            t[w]=1
        else
            t[w]=t[w]+1
        end
    end
    a=1234
    table.sort(t)
    print("============")
    for a,b in pairs(t) do
        print(t[a].." = "..a)
    end
end

misses _i, but easily fixed.

i prefer t[w] = (t[w] or 0) + 1

i’m not sure if patterns could do what yours does, not least because i don’t know what it does. it’d have to study it to know.

horses for courses, certainly.

i didn’t know that pasteboard trick. very cool.

@RonJeffries My code doesn’t catch the _i, but it does catch the i . Don’t know if that’s really important or not, but probably should for clarity. I’d like to redo the way I check things and separate them so I can have a comment showing what I’m trying to catch with each if .

The pasteboard code makes it easy to select projects from a list.

As for the t[w]= code, for me that’s not easily understandable. It saves code, but if I looked at it some time from now, I wouldn’t know what it was really doing.

ah. i’m used to it. old C programmer or something. :smile:

@dave1707 Thanks, the added tab reference is very useful.