aboutsummaryrefslogtreecommitdiff
path: root/src/glum.lua
blob: f4385c001d039eebfd1a1d66b2305f64839dab9f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
--[[
This moudle allows you to minify gLua code
    Use:
        local x = require("glum.lua")
        local str ="
        --Here is some code to be minified!\n
        for a=1,10,2 do\n
            print(a)\n
        end
        "
        print(x.minify(str))
    Dependencies:
        lua-parser
        lpeg
]]

--Abandon all hope, ye who enter here
--Refer to the comments at the top of parser.lua for what each function should do.
--If anyone ever decides to add new language features, they need to be added to BOTH parser.lua and here.
--Someone should rewrite this much cleaner.

local parser
local msg
if include ~= nil then
    parser = include("./parser.lua")
    msg = Msg
else
    parser = dofile("../src/parser.lua")
    msg = io.write
end
local lpeg = require("lpeg")
lpeg.locale(lpeg)

local glum = {}

--Creates a deep copy of a table
local function deepcopy(orig)
    local orig_type = type(orig)
    local copy
    if orig_type == "table" then
        copy = {}
        for orig_key, orig_value in next, orig, nil do
            copy[deepcopy(orig_key)] = deepcopy(orig_value)
        end
        setmetatable(copy, deepcopy(getmetatable(orig)))
    else -- number, string, boolean, etc
        copy = orig
    end
    return copy
end

--Is the last character in the built-so-far string a character?
--Used to know if we should insert a space after it
local last = false

--A list of reserved words that cannot be used as variable names
local nonames = {"if","for","end","do","local","then","else","elseif","return","goto","function","nil","false","true","repeat","return","break","and","or","not","in","repeat","until","while","continue"}
local reservednames = {}
for k,v in ipairs(nonames) do
    reservednames[v] = true
end

--A function that generates the next valid variable name from the last valid variable name.
local varchars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXY"
local function getnextvarname(lname)
    local place = string.find(lname,"[" .. varchars .. "]")
    local length = string.len(lname)
    if place == nil then
        return string.rep("a", length + 1)
    else
        local lbyte = string.byte(lname,place)
        local newchar = string.char(lbyte + (lbyte < 122 and 1 or -57))
        local after = string.sub(lname, place + 1, length)
        local before = string.rep("a", place-1)
        local output = before .. newchar .. after
        while reservednames[output] or _G[output] do
            output = getnextvarname(output)
        end
        return output
    end
end

--A debugging function, a replacement for glua PrintTable
local function printtable(tbl, tabset)
    tabset = tabset or 0
    for k,v in pairs(tbl) do
        for i = 0,tabset do msg("\t") end
        msg(k .. ":")
        if type(v) == "table" then
            msg("\n")
            printtable(v, tabset + 1)
        else
            msg(tostring(v) .. "\n")
        end
    end
end

local stringreps
local function findstrings(ast)
    if type(ast) ~= "table" then return end
    if ast and ast.tag == "String" then
        local lnum = stringreps[ast[1]]
        stringreps[ast[1]] = lnum and lnum + 1 or 1
        return
    end
    for k = 1, #ast do
        findstrings(ast[k])
    end
end

local getstringreps = function(ast)
    stringreps = {}
    findstrings(ast)

    local function bytessaved(str,instances,bytereplacement)
        local spacetaken = string.len(str) * instances
        local minspacetaken = instances * bytereplacement
        return spacetaken - minspacetaken
    end

    local sstbl = {}
    for k,v in pairs(stringreps) do table.insert(sstbl,{k,bytessaved(k,v,2)}) end
    table.sort(sstbl,function(a,b) return a[2] > b[2] end)

    return sstbl
end

local syntax = {}

local function stringfor(ast,tbl)
    if syntax[ast.tag] ~= nil then
        return syntax[ast.tag](ast,tbl)
    else
        print("Valid tags are:")
        for k,v in pairs(syntax) do
            print(k)
        end
        printtable(ast)
        error("Attempted to use unknown tag type:" .. ast.tag)
    end
end

syntax = {
    ["Call"] = function(ast,tbl)
        print("ast for call is")
        printtable(ast)
        local exprname = stringfor(ast[1],tbl)
        print("String for expname is",exprname)
        last = false
        local argnames = {}
        local cursor = 2
        while ast[cursor] ~= nil do
            argnames[cursor-1] = stringfor(ast[cursor],tbl)
            cursor = cursor + 1
            last = false
        end
        local argstring = table.concat(argnames,",")
        local ostring = table.concat({exprname,"(",argstring,")"})
        last = false
        return ostring
    end,
    ["Invoke"] = function(ast,tbl)
        local ret = {}
        ret[1] = stringfor(ast[1],tbl) -- The table
        last = false
        --If it's a . then use oo notation
        if ast[2].tag == "String" and ast[2][1]:find(" ") == nil and tbl.strings[ast[2][1]] == nil then
            ret[2] = ":"
            ret[3] = ast[2][1]
            ret[4] = "("
        elseif tbl.strings[ast[2][1]] ~= nil then
            ret[2] = "["
            ret[3] = tbl.strings[ast[2][1]]
            ret[4] = "]("
        else
            last = false
            ret[2] = "["
            ret[3] = stringfor(ast[2],tbl)
            ret[4] = "]("
            ret[5] = ast[1]
            ret[6] = ","
        end
        last = false
        local args = {}
        for k = 3,#ast do
            args[#args + 1] = stringfor(ast[k],tbl)
            last = false
        end
        ret[#ret+1] = table.concat(args,",")
        ret[#ret+1] = ")"
        last = false
        return table.concat(ret)
    end,
    ["String"] = function(ast,tbl)
        local sop,eop = "\"","\""
        --print("looking for",ast[1],"in")
        --printtable(tbl.strings)
        if tbl.strings[ast[1]] then
            --print("Found it, it is", tbl.strings[ast[1]])
            return tbl.strings[ast[1]]
        end
        if tbl.strings[ast[1]] == nil then
            if string.find(ast[1],"\"") then
                sop = "[["
                eop = "]]"
            end
            return table.concat({sop,ast[1],eop})
        end
        --print("Returning non-catated string")
        last = false
        return tbl.strings[ast[1]]
    end,
    ["Id"] = function(ast,tbl)
        local ret
        if last then ret = " " else ret = "" end
        if ast[1] == "pairs" then print("in id, ast1 is pairs") printtable(tbl.ids) end
        if tbl.ids[ast[1]] == nil then
            ret = ret .. ast[1]
            last = true
            return ret
        end
        ret = ret .. tbl.ids[ast[1]]
        last = true
        return ret
    end,
    ["Index"] = function(ast,tbl)
        local globalvar = stringfor(ast[1],tbl)
        if ast[2].tag == "String" then
            last = true
            return table.concat({globalvar, ".", ast[2][1]})
        end
        last = false
        local ret = table.concat({globalvar, "[", stringfor(ast[2],tbl), "]"})
        last = false
        return ret
    end,
    ["Paren"] = function(ast,tbl)
        last = false
        return table.concat({"(",stringfor(ast[1],tbl),")"})
    end,
    ["Dots"] = function(ast,tbl)
        last = false
        return "..."
    end,
    ["Forin"] = function(ast,tbl)
        print("At forin, ast is")
        printtable(ast)
        local codetbl = {}
        if last then codetbl[1] = " for" else codetbl[1] = "for" end
        last = true
        local nadd = deepcopy(tbl)
        local nl = stringfor(ast[1],nadd)
        codetbl[2] = nl
        if last then codetbl[3] = " in" else codetbl[3] = "in" end
        last = true
        nadd.numlocals = nadd.numlocals + #ast[1]
        local el = stringfor(ast[2],nadd)
        codetbl[4] = el
        if last then codetbl[5] = " do" else codetbl[5] = "do" end
        last = true
        local code = stringfor(ast[3],nadd)
        codetbl[6] = code
        if last then codetbl[7] = " end" else codetbl[7] = "end" end
        last = true
        local output = table.concat(codetbl)
        tbl.numlocals = nadd.numlocals
        return output
    end,
    ["NameList"] = function(ast,tbl)
        local outputtbl = {}
        local bef
        if last then bef = " " else bef = "" end
        for k = 1,#ast do
            if ast[k].tag ~= "Id" then
                outputtbl[#outputtbl + 1] = stringfor(ast[k])
            else
                if tbl.ids[ast[k][1]] ~= nil then
                    outputtbl[#outputtbl + 1] = tbl.ids[ast[k][1]]
                else
                    local newvar = getnextvarname(tbl.lname)
                    tbl.lname = newvar
                    tbl.ids[ast[k][1]] = newvar
                    outputtbl[#outputtbl + 1] = newvar
                end
                last = false
            end
        end
        local output = bef .. table.concat(outputtbl, ",")
        last = true
        return output
    end,
    ["ExpList"] = function(ast,tbl)
        local exprs = {}
        local bef
        if last then bef = " " else bef = "" end
        for k = 1,#ast do
            exprs[k] = stringfor(ast[k],tbl)
            last = false
        end
        last = true
        return table.concat(exprs,",")
    end,
    ["Nil"] = function(ast,tbl)
        local ret
        if last then ret = " nil" else ret = "nil" end
        last = true
        return ret
    end,
    ["True"] = function(ast,tbl)
        local ret
        if last then ret = " true" else ret = "true" end
        last = true
        return ret
    end,
    ["False"] = function(ast,tbl)
        local ret
        if last then ret = " false" else ret = "false" end
        last = true
        return ret
    end,
    ["Return"] = function(ast,tbl)
        local retargs = {}
        local ccat
        if last then
            ccat = " return"
        else
            ccat = "return"
        end
        last = true
        for k,v in ipairs(ast) do
            retargs[k] = stringfor(v,tbl)
            last = false
        end
        last = true
        return ccat .. table.concat(retargs,",")
    end,
    ["Do"] = function(ast,tbl)
        local ntbl = deepcopy(tbl)
        local argparts = {}
        if last then argparts[1] = " do" else argparts[1] = "do" end
        last = true
        local allst = {}
        for k = 1,#ast do
            allst[k] = stringfor(ast[k],ntbl)
        end
        local code = table.concat(allst,";")
        argparts[2] = code
        tbl.numlocals = ntbl.numlocals
        if last then argparts[3] = " end" else argparts[3] = "end" end
        last = true
        return table.concat(argparts)
    end,
    ["If"] = function(ast,tbl)
        local exparts = {}
        if last then exparts[1] = " if" else exparts[1] = "if" end
        last = true
        local expr1 = stringfor(ast[1],tbl)
        exparts[2] = expr1
        if last then exparts[3] = " then" else exparts[3] = "then" end
        last = true
        local block1 = stringfor(ast[2],tbl)
        exparts[4] = block1
        local codeblocks = {}
        codeblocks[#codeblocks + 1] = table.concat(exparts)

        for k = 3,#ast-1,2 do
            local efargs = {}
            if last then efargs[1] = " elseif" else efargs[1] = "elseif" end
            last = true
            local expr = stringfor(ast[k],tbl)
            efargs[2] = expr
            if last then efargs[3] = " then" else efargs[3] = "then" end
            last = true
            local block = stringfor(ast[k + 1],tbl)
            codeblocks[#codeblocks + 1] = table.concat(efargs)
        end

        if #ast % 2 == 1 then
            local block = stringfor(ast[#ast],tbl)
            if block ~= "" then --If for some reason there's an empty else block, forget about it.
                if last then
                    codeblocks[#codeblocks + 1] = " else" .. block
                else
                    codeblocks[#codeblocks + 1] = "else" .. block
                end
            end
        end
        local estr
        if last then estr = " end" else estr = "end" end
        codeblocks[#codeblocks + 1] = estr
        last = true
        return table.concat(codeblocks)
    end,
    ["Fornum"] = function(ast,tbl)
        local spargs = {}
        if last then spargs[1] = " for " else spargs[1] = "for " end
        last = false
        local var
        assert(ast[1].tag == "Id","Oh no, I was expecting an ID!")
        if ast[1].tag == "Id" then
            if tbl.ids[ast[1][1]] ~= nil then
                var = tbl.ids[ast[1][1]]
            else
                local newvar = getnextvarname(tbl.lname)
                tbl.lname = newvar
                tbl.ids[ast[1][1]] = newvar
                var = newvar
            end
            --[[
        else
            var = stringfor(ast[1],tbl)
            ]]
        end
        spargs[2] = var
        spargs[3] = "="
        last = false
        local start = stringfor(ast[2],tbl)
        spargs[4] = start
        spargs[5] = ","
        last = false
        local endnum = stringfor(ast[3],tbl)
        spargs[6] = endnum
        local incrementer = 1
        local code = ""
        spargs[7] = ""
        if ast[4].tag ~= "Block" then -- incrementer
            incrementer = stringfor(ast[4],tbl)
            if incrementer ~= 1 then
                spargs[7] = "," .. incrementer
            end
            if last then spargs[8] = " do" else spargs[8] = "do" end
            last = true
            code = stringfor(ast[5],tbl)
            spargs[9] = code
            if last then spargs[10] = " end" else spargs[10] = "end" end
            last = true
        else
            if last then spargs[8] = " do" else spargs[8] = "do" end
            last = true
            code = stringfor(ast[4],tbl)
            spargs[9] = code
            if last then spargs[10] = " end" else spargs[10] = "end" end
            last = true
        end
        local incstr = incrementer ~= 1 and ("," .. incrementer) or ""
        tbl[var] = nil
        tbl.numlocals = tbl.numlocals + 1
        --print("Found 1 locals as Fornum")
        return table.concat(spargs)
    end,
    ["Op"] = function(ast,tbl)
        --NOTE: Bitwise operators << and >> are not supported in LuaJIT (lua 5.1) and were introduced in lua 5.3, if the operators are ever supported, stuff should just work.
        local binop = {
            ["or"] = "or",      ["and"] = "and",
            ["ne"] = "~=",      ["eq"] = "==",
            ["le"] = "<=",      ["ge"] = ">=",
            ["lt"] = "<",       ["gt"] = ">",
            ["bor"] = "|",      ["bxor"] = "~",
            ["band"] = "&",     ["shl"] = "<<",
            ["shr"] = ">>",     ["concat"] = "..",
            ["add"] = "+",      ["sub"] = "-",
            ["mul"] = "*",      ["div"] = "/",
            ["mod"] = "%",      ["pow"] = "^",
        }
        local uniop = {
            ["len"] = "#",      ["not"] = "not",
            ["unm"] = "-",      ["bnot"] = "~",
        }
        local opname = ast[1]
        if uniop[opname] ~= nil then
            --Some special case where the parser messes up, fix it here.
            --It translates ~= into not ==, but the order of operations makes it so == is evaluated first, and not second.
            if opname == "not" and ast[2]["tag"] == "Op" and ast[2][1] == "eq" then
                ast[2][1] = "ne"
                local ret = stringfor(ast[2],tbl)
                return ret
            end
            local rhs = stringfor(ast[2],tbl)
            local bef
            if last then bef = " " else bef = "" end
            return bef .. uniop[opname] .. rhs
        end
        local sargs = {}
        local lhs = stringfor(ast[2],tbl)
        sargs[1] = lhs
        if last and (opname == "or" or opname == "and") then
            sargs[2] = " "
            last = true
        else
            sargs[2] = ""
            last = false
        end
        sargs[3] = binop[opname]
        local rhs = stringfor(ast[3],tbl)
        sargs[4] = rhs
        local output = table.concat(sargs)
        return output
    end,
    ["Pair"] = function(ast,tbl)
        local lhs = stringfor(ast[1],tbl)
        local rhs = stringfor(ast[2],tbl)
        return table.concat({"[",lhs,"]=",rhs})
    end,
    ["Table"] = function(ast,tbl)
        local fields = {}
        last = false
        for k = 1, #ast do
            fields[#fields + 1] = stringfor(ast[k],tbl)
            last = false
        end
        local fieldstr = table.concat(fields,",")
        last = false
        return table.concat({"{",fieldstr,"}"})
    end,
    ["Number"] = function(ast,tbl)
        local ret
        if last then ret =  " " .. ast[1] else ret = ast[1] end
        last = true
        return ret
    end,
    ["Local"] = function(ast,tbl)
        local bef
        if last then bef = " " else bef = "" end
        last = true
        local lhs,rhs = stringfor(ast[1],tbl),nil
        tbl.numlocals = tbl.numlocals + #ast[1]
        --print("Found",#ast[1],"locals as Local")
        local output = bef .. "local" .. lhs
        if ast[2].tag ~= nil then
            last = false
            rhs = stringfor(ast[2],tbl)
            output = output .. "=" .. rhs
        end
        return output
    end,
    ["VarList"] = function(ast,tbl)
        local vars = {}
        for k = 1,#ast do
            vars[#vars + 1] = stringfor(ast[k],tbl)
            last = false
        end
        last = true
        return table.concat(vars,",")
    end,
    ["Set"] = function(ast,tbl)
        local lhs = {}
        local a1 = ast[1].tag ~= nil and ast[1] or ast[1][1]
        for k = 1,#ast[1] do
            lhs[#lhs + 1] = stringfor(a1,tbl)
            last = false
        end
        local rhs = {}
        local a2 = ast[2].tag ~= nil and ast[2] or ast[2][1]
        for k = 1,#ast[2] do
            last = false
            rhs[#rhs + 1] = stringfor(a2,tbl)
        end
        local ostring = table.concat(lhs,",")
        ostring = ostring .. "=" .. table.concat(rhs,",")
        return ostring
    end,
    ["Label"] = function(ast,tbl)
        if tbl.nids[ast[1]] == nil then
            local nextvar = getnextvarname(tbl.lname)
            tbl.lname = nextvar
            tbl.nids[ast[1]] = nextvar
        end
        last = false
        return "::" .. tbl.nids[ast[1]] .. "::"
    end,
    ["Goto"] = function(ast,tbl)
        if tbl.nids[ast[1]] == nil then
            local nextvar = getnextvarname(tbl.lname)
            tbl.lname = nextvar
            tbl.nids[ast[1]] = nextvar
        end
        last = true
        return (last and " " or "") .. "goto " .. tbl.nids[ast[1]]
    end,
    ["Function"] = function(ast,tbl)
        local funcstr
        if last then funcstr = " function(" else funcstr = "function(" end
        last = false
        local funcargs = ast[1].tag ~= nil and stringfor(ast[1],tbl) or ""
        last = false
        local code = stringfor(ast[2],tbl)
        local endstr
        if last then endstr = " end" else endstr = "end" end
        last = true
        return table.concat({funcstr,funcargs,")",code,endstr})
    end,
    ["Localrec"] = function(ast,tbl)
        local ident
        if tbl.ids[ast[1][1]] ~= nil then
            ident = tbl.ids[ast[1][1]]
        else
            local newvar = getnextvarname(tbl.lname)
            tbl.lname = newvar
            tbl.ids[ast[1][1][1]] = newvar
            ident = newvar
        end
        local locfuncstr
        if last then locfuncstr = " local function " else locfuncstr = "local function " end
        last = false
        local argstr = ast[2][1][1].tag ~= nil and stringfor(ast[2][1][1],tbl) or ""
        last = false
        local expr = stringfor(ast[2][1][2],tbl)
        local endstr
        if last then endstr = " end" else endstr = "end" end
        last = true
        tbl.numlocals = tbl.numlocals + 1
        print(string.format("At localrec, locfuncstr:%q ident:%q argstr:%q expr:%q endstr:%q last:%q",locfuncstr,ident,argstr,expr,endstr,tostring(last)))
        --print("Found 1 local as Localrec")
        return table.concat({locfuncstr,ident,"(",argstr,")",expr,endstr})
    end,
    ["Continue"] = function(ast,tbl)
        local ret
        if last then ret = " continue" else ret = "continue" end
        last = true
        return ret
    end,
    ["While"] = function(ast,tbl)
        local whilestr
        if last then whilestr = " while" else whilestr = "while" end
        last = true
        local expr = stringfor(ast[1],tbl)
        local dostr
        if last then dostr = " do" else dostr = "do" end
        last = true
        local block = stringfor(ast[2],tbl)
        local endstr
        if last then endstr = " end" else endstr = "end" end
        last = true
        local output = table.concat({whilestr, expr , dostr, block , endstr})
        return output
    end,
    ["Break"] = function(ast,tbl)
        local ret
        if last then ret = " break" else ret = "break" end
        last = true
        return ret
    end,
    ["Block"] = function(ast,oldtbl)
        local tbl = deepcopy(oldtbl)
        oldtbl.block = true
        local codeblocks = {}
        for k = 1,#ast do
            codeblocks[#codeblocks + 1] = stringfor(ast[k],tbl)
        end
        local code = table.concat(codeblocks)
        oldtbl.numlocals = tbl.numlocals
        return code
    end,
}



--Removes extra spaces and duplicated ; from a string
--[[
local function removespaces(str)
    local removables = {
        {"%s*%)%s*","%)"}, --Spaces before or after )
        {"%s*%(%s*","%("}, --Spaces before or after (
        {"%s*;%s*",";"}, --Spaces before or after ;
        {"%s*,%s*",","}, --Spaces before or after ,
        {";+",";"}, --Multiple ; in a row
        {"^%s*",""}, --Spaces at the beginning of the file
        {"%s*$",""}, --Spaces at the end of the file
        {"%s+"," "}, --Multiple spaces in a row
    }
    --Order is important
    for k,v in ipairs(removables) do
        str = string.gsub(str,v[1],v[2])
    end
    return str
end
]]

--Compress the string, and adds a little decompression code at the top.
--local function compress(str)
--
--end

glum.minify = function(str, name)
    name = name or "anonymous"
    local ast, error_msg = parser.parse(str, name)
    if not ast then
        error(error_msg)
        return nil
    end
    print("in glum.minify, ast is ")
    printtable(ast)
    print("Finding string reps")
    local strreps = getstringreps(ast)
    printtable(strreps)

    local olocalvar = {
        ["numlocals"] = 0,
        ["strings"] = {},
        ["ids"] = {},
        ["lname"] = "",
        ["nids"] = {},
    }

    --printtable(ast)
    local lvt = deepcopy(olocalvar)
    local numstrreplaced = 0
    local maxlocals = lvt.numlocals
    while
        (numstrreplaced + maxlocals < 200) and --We have some locals left
        (numstrreplaced < #strreps) and --We have more strings to replace
        ((strreps[numstrreplaced + 1][2] * #strreps[numstrreplaced + 1][1]) > (strreps[numstrreplaced + 1][2] * 4 + 6)) do --Replaceing this string will at least cover the cost of "local " and []
        numstrreplaced = numstrreplaced + 1
        local nvar = getnextvarname(olocalvar.lname)
        olocalvar.strings[strreps[numstrreplaced][1]] = nvar
        olocalvar.lname = nvar
    end

    local lhss,rhss = {},{}
    for k,v in pairs(olocalvar.strings) do
            lhss[#lhss + 1] = v
            rhss[#rhss + 1] = string.format("%q",k)
    end
    local inits = ""
    --print("lhss is")
    --printtable(lhss)
    local lhs = " local " .. table.concat(lhss,",")
    local rhs = table.concat(rhss,",")
    if string.len(rhs) > 0 then
        inits = table.concat({lhs, "=", rhs, ";"})
    end

    --print("Before doing stringfor for the second time, olocalvar is")
    --printtable(olocalvar)

    return inits .. stringfor(ast,olocalvar)
end

return glum