joebodo

sys.apis.minify.ParseLua.lua

Sep 6th, 2016
141
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 37.55 KB | None | 0 0
  1.  
  2. --
  3. -- ParseLua.lua
  4. --
  5. -- The main lua parser and lexer.
  6. -- LexLua returns a Lua token stream, with tokens that preserve
  7. -- all whitespace formatting information.
  8. -- ParseLua returns an AST, internally relying on LexLua.
  9. --
  10.  
  11. -- require'strict'
  12.  
  13. local util = require'Util'
  14. local lookupify = util.lookupify
  15.  
  16. local WhiteChars = lookupify{' ', '\n', '\t', '\r'}
  17. local EscapeLookup = {['\r'] = '\\r', ['\n'] = '\\n', ['\t'] = '\\t', ['"'] = '\\"', ["'"] = "\\'"}
  18. local LowerChars = lookupify{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
  19.                              'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
  20.                              's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}
  21. local UpperChars = lookupify{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
  22.                              'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
  23.                              'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'}
  24. local Digits = lookupify{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}
  25. local HexDigits = lookupify{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
  26.                             'A', 'a', 'B', 'b', 'C', 'c', 'D', 'd', 'E', 'e', 'F', 'f'}
  27.  
  28. local Symbols = lookupify{'+', '-', '*', '/', '^', '%', ',', '{', '}', '[', ']', '(', ')', ';', '#'}
  29. local Scope = require'Scope'
  30.  
  31. local Keywords = lookupify{
  32.     'and', 'break', 'do', 'else', 'elseif',
  33.     'end', 'false', 'for', 'function', 'if',
  34. -- removing goto for Lua 5.1
  35. --  'end', 'false', 'for', 'function', 'goto', 'if',
  36.     'in', 'local', 'nil', 'not', 'or', 'repeat',
  37.     'return', 'then', 'true', 'until', 'while',
  38. };
  39.  
  40. local function LexLua(src)
  41.     --token dump
  42.     local tokens = {}
  43.  
  44.     local st, err = pcall(function()
  45.         --line / char / pointer tracking
  46.         local p = 1
  47.         local line = 1
  48.         local char = 1
  49.  
  50.         --get / peek functions
  51.         local function get()
  52.             local c = src:sub(p,p)
  53.             if c == '\n' then
  54.                 char = 1
  55.                 line = line + 1
  56.             else
  57.                 char = char + 1
  58.             end
  59.             p = p + 1
  60.             return c
  61.         end
  62.         local function peek(n)
  63.             n = n or 0
  64.             return src:sub(p+n,p+n)
  65.         end
  66.         local function consume(chars)
  67.             local c = peek()
  68.             for i = 1, #chars do
  69.                 if c == chars:sub(i,i) then return get() end
  70.             end
  71.         end
  72.  
  73.         --shared stuff
  74.         local function generateError(err)
  75.             return error(">> :"..line..":"..char..": "..err, 0)
  76.         end
  77.  
  78.         local function tryGetLongString()
  79.             local start = p
  80.             if peek() == '[' then
  81.                 local equalsCount = 0
  82.                 local depth = 1
  83.                 while peek(equalsCount+1) == '=' do
  84.                     equalsCount = equalsCount + 1
  85.                 end
  86.                 if peek(equalsCount+1) == '[' then
  87.                     --start parsing the string. Strip the starting bit
  88.                     for _ = 0, equalsCount+1 do get() end
  89.  
  90.                     --get the contents
  91.                     local contentStart = p
  92.                     while true do
  93.                         --check for eof
  94.                         if peek() == '' then
  95.                             generateError("Expected `]"..string.rep('=', equalsCount).."]` near <eof>.", 3)
  96.                         end
  97.  
  98.                         --check for the end
  99.                         local foundEnd = true
  100.                         if peek() == ']' then
  101.                             for i = 1, equalsCount do
  102.                                 if peek(i) ~= '=' then foundEnd = false end
  103.                             end
  104.                             if peek(equalsCount+1) ~= ']' then
  105.                                 foundEnd = false
  106.                             end
  107.                         else
  108.                             if peek() == '[' then
  109.                                 -- is there an embedded long string?
  110.                                 local embedded = true
  111.                                 for i = 1, equalsCount do
  112.                                     if peek(i) ~= '=' then
  113.                                         embedded = false
  114.                                         break
  115.                                     end
  116.                                 end
  117.                                 if peek(equalsCount + 1) == '[' and embedded then
  118.                                     -- oh look, there was
  119.                                     depth = depth + 1
  120.                                     for i = 1, (equalsCount + 2) do
  121.                                         get()
  122.                                     end
  123.                                 end
  124.                             end
  125.                             foundEnd = false
  126.                         end
  127.                         --
  128.                         if foundEnd then
  129.                             depth = depth - 1
  130.                             if depth == 0 then
  131.                                 break
  132.                             else
  133.                                 for i = 1, equalsCount + 2 do
  134.                                     get()
  135.                                 end
  136.                             end
  137.                         else
  138.                             get()
  139.                         end
  140.                     end
  141.  
  142.                     --get the interior string
  143.                     local contentString = src:sub(contentStart, p-1)
  144.  
  145.                     --found the end. Get rid of the trailing bit
  146.                     for i = 0, equalsCount+1 do get() end
  147.  
  148.                     --get the exterior string
  149.                     local longString = src:sub(start, p-1)
  150.  
  151.                     --return the stuff
  152.                     return contentString, longString
  153.                 else
  154.                     return nil
  155.                 end
  156.             else
  157.                 return nil
  158.             end
  159.         end
  160.  
  161.         --main token emitting loop
  162.         while true do
  163.             --get leading whitespace. The leading whitespace will include any comments
  164.             --preceding the token. This prevents the parser needing to deal with comments
  165.             --separately.
  166.             local leading = { }
  167.             local leadingWhite = ''
  168.             local longStr = false
  169.             while true do
  170.                 local c = peek()
  171.                 if c == '#' and peek(1) == '!' and line == 1 then
  172.                     -- #! shebang for linux scripts
  173.                     get()
  174.                     get()
  175.                     leadingWhite = "#!"
  176.                     while peek() ~= '\n' and peek() ~= '' do
  177.                         leadingWhite = leadingWhite .. get()
  178.                     end
  179.                     local token = {
  180.                         Type = 'Comment',
  181.                         CommentType = 'Shebang',
  182.                         Data = leadingWhite,
  183.                         Line = line,
  184.                         Char = char
  185.                     }
  186.                     token.Print = function()
  187.                         return "<"..(token.Type .. string.rep(' ', 7-#token.Type)).."  "..(token.Data or '').." >"
  188.                     end
  189.                     leadingWhite = ""
  190.                     table.insert(leading, token)
  191.                 end
  192.                 if c == ' ' or c == '\t' then
  193.                     --whitespace
  194.                     --leadingWhite = leadingWhite..get()
  195.                     local c2 = get() -- ignore whitespace
  196.                     table.insert(leading, { Type = 'Whitespace', Line = line, Char = char, Data = c2 })
  197.                 elseif c == '\n' or c == '\r' then
  198.                     local nl = get()
  199.                     if leadingWhite ~= "" then
  200.                         local token = {
  201.                             Type = 'Comment',
  202.                             CommentType = longStr and 'LongComment' or 'Comment',
  203.                             Data = leadingWhite,
  204.                             Line = line,
  205.                             Char = char,
  206.                         }
  207.                         token.Print = function()
  208.                             return "<"..(token.Type .. string.rep(' ', 7-#token.Type)).."  "..(token.Data or '').." >"
  209.                         end
  210.                         table.insert(leading, token)
  211.                         leadingWhite = ""
  212.                     end
  213.                     table.insert(leading, { Type = 'Whitespace', Line = line, Char = char, Data = nl })
  214.                 elseif c == '-' and peek(1) == '-' then
  215.                     --comment
  216.                     get()
  217.                     get()
  218.                     leadingWhite = leadingWhite .. '--'
  219.                     local _, wholeText = tryGetLongString()
  220.                     if wholeText then
  221.                         leadingWhite = leadingWhite..wholeText
  222.                         longStr = true
  223.                     else
  224.                         while peek() ~= '\n' and peek() ~= '' do
  225.                             leadingWhite = leadingWhite..get()
  226.                         end
  227.                     end
  228.                 else
  229.                     break
  230.                 end
  231.             end
  232.             if leadingWhite ~= "" then
  233.                 local token = {
  234.                     Type = 'Comment',
  235.                     CommentType = longStr and 'LongComment' or 'Comment',
  236.                     Data = leadingWhite,
  237.                     Line = line,
  238.                     Char = char,
  239.                 }
  240.                 token.Print = function()
  241.                     return "<"..(token.Type .. string.rep(' ', 7-#token.Type)).."  "..(token.Data or '').." >"
  242.                 end
  243.                 table.insert(leading, token)
  244.             end
  245.  
  246.             --get the initial char
  247.             local thisLine = line
  248.             local thisChar = char
  249.             local errorAt = ":"..line..":"..char..":> "
  250.             local c = peek()
  251.  
  252.             --symbol to emit
  253.             local toEmit = nil
  254.  
  255.             --branch on type
  256.             if c == '' then
  257.                 --eof
  258.                 toEmit = { Type = 'Eof' }
  259.  
  260.             elseif UpperChars[c] or LowerChars[c] or c == '_' then
  261.                 --ident or keyword
  262.                 local start = p
  263.                 repeat
  264.                     get()
  265.                     c = peek()
  266.                 until not (UpperChars[c] or LowerChars[c] or Digits[c] or c == '_')
  267.                 local dat = src:sub(start, p-1)
  268.                 if Keywords[dat] then
  269.                     toEmit = {Type = 'Keyword', Data = dat}
  270.                 else
  271.                     toEmit = {Type = 'Ident', Data = dat}
  272.                 end
  273.  
  274.             elseif Digits[c] or (peek() == '.' and Digits[peek(1)]) then
  275.                 --number const
  276.                 local start = p
  277.                 if c == '0' and peek(1) == 'x' then
  278.                     get();get()
  279.                     while HexDigits[peek()] do get() end
  280.                     if consume('Pp') then
  281.                         consume('+-')
  282.                         while Digits[peek()] do get() end
  283.                     end
  284.                 else
  285.                     while Digits[peek()] do get() end
  286.                     if consume('.') then
  287.                         while Digits[peek()] do get() end
  288.                     end
  289.                     if consume('Ee') then
  290.                         consume('+-')
  291.                         while Digits[peek()] do get() end
  292.                     end
  293.                 end
  294.                 toEmit = {Type = 'Number', Data = src:sub(start, p-1)}
  295.  
  296.             elseif c == '\'' or c == '\"' then
  297.                 local start = p
  298.                 --string const
  299.                 local delim = get()
  300.                 local contentStart = p
  301.                 while true do
  302.                     local c = get()
  303.                     if c == '\\' then
  304.                         get() --get the escape char
  305.                     elseif c == delim then
  306.                         break
  307.                     elseif c == '' then
  308.                         generateError("Unfinished string near <eof>")
  309.                     end
  310.                 end
  311.                 local content = src:sub(contentStart, p-2)
  312.                 local constant = src:sub(start, p-1)
  313.                 toEmit = {Type = 'String', Data = constant, Constant = content}
  314.  
  315.             elseif c == '[' then
  316.                 local content, wholetext = tryGetLongString()
  317.                 if wholetext then
  318.                     toEmit = {Type = 'String', Data = wholetext, Constant = content}
  319.                 else
  320.                     get()
  321.                     toEmit = {Type = 'Symbol', Data = '['}
  322.                 end
  323.  
  324.             elseif consume('>=<') then
  325.                 if consume('=') then
  326.                     toEmit = {Type = 'Symbol', Data = c..'='}
  327.                 else
  328.                     toEmit = {Type = 'Symbol', Data = c}
  329.                 end
  330.  
  331.             elseif consume('~') then
  332.                 if consume('=') then
  333.                     toEmit = {Type = 'Symbol', Data = '~='}
  334.                 else
  335.                     generateError("Unexpected symbol `~` in source.", 2)
  336.                 end
  337.  
  338.             elseif consume('.') then
  339.                 if consume('.') then
  340.                     if consume('.') then
  341.                         toEmit = {Type = 'Symbol', Data = '...'}
  342.                     else
  343.                         toEmit = {Type = 'Symbol', Data = '..'}
  344.                     end
  345.                 else
  346.                     toEmit = {Type = 'Symbol', Data = '.'}
  347.                 end
  348.  
  349.             elseif consume(':') then
  350.                 if consume(':') then
  351.                     toEmit = {Type = 'Symbol', Data = '::'}
  352.                 else
  353.                     toEmit = {Type = 'Symbol', Data = ':'}
  354.                 end
  355.  
  356.             elseif Symbols[c] then
  357.                 get()
  358.                 toEmit = {Type = 'Symbol', Data = c}
  359.  
  360.             else
  361.                 local contents, all = tryGetLongString()
  362.                 if contents then
  363.                     toEmit = {Type = 'String', Data = all, Constant = contents}
  364.                 else
  365.                     generateError("Unexpected Symbol `"..c.."` in source.", 2)
  366.                 end
  367.             end
  368.  
  369.             --add the emitted symbol, after adding some common data
  370.             toEmit.LeadingWhite = leading -- table of leading whitespace/comments
  371.             --for k, tok in pairs(leading) do
  372.             --  tokens[#tokens + 1] = tok
  373.             --end
  374.  
  375.             toEmit.Line = thisLine
  376.             toEmit.Char = thisChar
  377.             toEmit.Print = function()
  378.                 return "<"..(toEmit.Type..string.rep(' ', 7-#toEmit.Type)).."  "..(toEmit.Data or '').." >"
  379.             end
  380.             tokens[#tokens+1] = toEmit
  381.  
  382.             --halt after eof has been emitted
  383.             if toEmit.Type == 'Eof' then break end
  384.         end
  385.     end)
  386.     if not st then
  387.         return false, err
  388.     end
  389.  
  390.     --public interface:
  391.     local tok = {}
  392.     local savedP = {}
  393.     local p = 1
  394.    
  395.     function tok:getp()
  396.         return p
  397.     end
  398.    
  399.     function tok:setp(n)
  400.         p = n
  401.     end
  402.    
  403.     function tok:getTokenList()
  404.         return tokens
  405.     end
  406.    
  407.     --getters
  408.     function tok:Peek(n)
  409.         n = n or 0
  410.         return tokens[math.min(#tokens, p+n)]
  411.     end
  412.     function tok:Get(tokenList)
  413.         local t = tokens[p]
  414.         p = math.min(p + 1, #tokens)
  415.         if tokenList then
  416.             table.insert(tokenList, t)
  417.         end
  418.         return t
  419.     end
  420.     function tok:Is(t)
  421.         return tok:Peek().Type == t
  422.     end
  423.  
  424.     --save / restore points in the stream
  425.     function tok:Save()
  426.         savedP[#savedP+1] = p
  427.     end
  428.     function tok:Commit()
  429.         savedP[#savedP] = nil
  430.     end
  431.     function tok:Restore()
  432.         p = savedP[#savedP]
  433.         savedP[#savedP] = nil
  434.     end
  435.  
  436.     --either return a symbol if there is one, or return true if the requested
  437.     --symbol was gotten.
  438.     function tok:ConsumeSymbol(symb, tokenList)
  439.         local t = self:Peek()
  440.         if t.Type == 'Symbol' then
  441.             if symb then
  442.                 if t.Data == symb then
  443.                     self:Get(tokenList)
  444.                     return true
  445.                 else
  446.                     return nil
  447.                 end
  448.             else
  449.                 self:Get(tokenList)
  450.                 return t
  451.             end
  452.         else
  453.             return nil
  454.         end
  455.     end
  456.  
  457.     function tok:ConsumeKeyword(kw, tokenList)
  458.         local t = self:Peek()
  459.         if t.Type == 'Keyword' and t.Data == kw then
  460.             self:Get(tokenList)
  461.             return true
  462.         else
  463.             return nil
  464.         end
  465.     end
  466.  
  467.     function tok:IsKeyword(kw)
  468.         local t = tok:Peek()
  469.         return t.Type == 'Keyword' and t.Data == kw
  470.     end
  471.  
  472.     function tok:IsSymbol(s)
  473.         local t = tok:Peek()
  474.         return t.Type == 'Symbol' and t.Data == s
  475.     end
  476.  
  477.     function tok:IsEof()
  478.         return tok:Peek().Type == 'Eof'
  479.     end
  480.  
  481.     return true, tok
  482. end
  483.  
  484.  
  485. local function ParseLua(src)
  486.     local st, tok
  487.     if type(src) ~= 'table' then
  488.         st, tok = LexLua(src)
  489.     else
  490.         st, tok = true, src
  491.     end
  492.     if not st then
  493.         return false, tok
  494.     end
  495.     --
  496.     local function GenerateError(msg)
  497.         local err = ">> :"..tok:Peek().Line..":"..tok:Peek().Char..": "..msg.."\n"
  498.         --find the line
  499.         local lineNum = 0
  500.         if type(src) == 'string' then
  501.             for line in src:gmatch("[^\n]*\n?") do
  502.                 if line:sub(-1,-1) == '\n' then line = line:sub(1,-2) end
  503.                 lineNum = lineNum+1
  504.                 if lineNum == tok:Peek().Line then
  505.                     err = err..">> `"..line:gsub('\t','    ').."`\n"
  506.                     for i = 1, tok:Peek().Char do
  507.                         local c = line:sub(i,i)
  508.                         if c == '\t' then
  509.                             err = err..'    '
  510.                         else
  511.                             err = err..' '
  512.                         end
  513.                     end
  514.                     err = err.."   ^^^^"
  515.                     break
  516.                 end
  517.             end
  518.         end
  519.         return err
  520.     end
  521.     --
  522.     local VarUid = 0
  523.     -- No longer needed: handled in Scopes now local GlobalVarGetMap = {}
  524.     local VarDigits = {'_', 'a', 'b', 'c', 'd'}
  525.     local function CreateScope(parent)
  526.         --[[
  527.         local scope = {}
  528.         scope.Parent = parent
  529.         scope.LocalList = {}
  530.         scope.LocalMap = {}
  531.  
  532.         function scope:ObfuscateVariables()
  533.             for _, var in pairs(scope.LocalList) do
  534.                 local id = ""
  535.                 repeat
  536.                     local chars = "QWERTYUIOPASDFGHJKLZXCVBNMqwertyuioplkjhgfdsazxcvbnm_"
  537.                     local chars2 = "QWERTYUIOPASDFGHJKLZXCVBNMqwertyuioplkjhgfdsazxcvbnm_1234567890"
  538.                     local n = math.random(1, #chars)
  539.                     id = id .. chars:sub(n, n)
  540.                     for i = 1, math.random(0,20) do
  541.                         local n = math.random(1, #chars2)
  542.                         id = id .. chars2:sub(n, n)
  543.                     end
  544.                 until not GlobalVarGetMap[id] and not parent:GetLocal(id) and not scope.LocalMap[id]
  545.                 var.Name = id
  546.                 scope.LocalMap[id] = var
  547.             end
  548.         end
  549.        
  550.         scope.RenameVars = scope.ObfuscateVariables
  551.  
  552.         -- Renames a variable from this scope and down.
  553.         -- Does not rename global variables.
  554.         function scope:RenameVariable(old, newName)
  555.             if type(old) == "table" then -- its (theoretically) an AstNode variable
  556.                 old = old.Name
  557.             end
  558.             for _, var in pairs(scope.LocalList) do
  559.                 if var.Name == old then
  560.                     var.Name = newName
  561.                     scope.LocalMap[newName] = var
  562.                 end
  563.             end
  564.         end
  565.  
  566.         function scope:GetLocal(name)
  567.             --first, try to get my variable
  568.             local my = scope.LocalMap[name]
  569.             if my then return my end
  570.  
  571.             --next, try parent
  572.             if scope.Parent then
  573.                 local par = scope.Parent:GetLocal(name)
  574.                 if par then return par end
  575.             end
  576.  
  577.             return nil
  578.         end
  579.  
  580.         function scope:CreateLocal(name)
  581.             --create my own var
  582.             local my = {}
  583.             my.Scope = scope
  584.             my.Name = name
  585.             my.CanRename = true
  586.             --
  587.             scope.LocalList[#scope.LocalList+1] = my
  588.             scope.LocalMap[name] = my
  589.             --
  590.             return my
  591.         end]]
  592.         local scope = Scope:new(parent)
  593.         scope.RenameVars = scope.ObfuscateLocals
  594.         scope.ObfuscateVariables = scope.ObfuscateLocals
  595.         scope.Print = function() return "<Scope>" end
  596.         return scope
  597.     end
  598.  
  599.     local ParseExpr
  600.     local ParseStatementList
  601.     local ParseSimpleExpr,
  602.             ParseSubExpr,
  603.             ParsePrimaryExpr,
  604.             ParseSuffixedExpr
  605.  
  606.     local function ParseFunctionArgsAndBody(scope, tokenList)
  607.         local funcScope = CreateScope(scope)
  608.         if not tok:ConsumeSymbol('(', tokenList) then
  609.             return false, GenerateError("`(` expected.")
  610.         end
  611.  
  612.         --arg list
  613.         local argList = {}
  614.         local isVarArg = false
  615.         while not tok:ConsumeSymbol(')', tokenList) do
  616.             if tok:Is('Ident') then
  617.                 local arg = funcScope:CreateLocal(tok:Get(tokenList).Data)
  618.                 argList[#argList+1] = arg
  619.                 if not tok:ConsumeSymbol(',', tokenList) then
  620.                     if tok:ConsumeSymbol(')', tokenList) then
  621.                         break
  622.                     else
  623.                         return false, GenerateError("`)` expected.")
  624.                     end
  625.                 end
  626.             elseif tok:ConsumeSymbol('...', tokenList) then
  627.                 isVarArg = true
  628.                 if not tok:ConsumeSymbol(')', tokenList) then
  629.                     return false, GenerateError("`...` must be the last argument of a function.")
  630.                 end
  631.                 break
  632.             else
  633.                 return false, GenerateError("Argument name or `...` expected")
  634.             end
  635.         end
  636.  
  637.         --body
  638.         local st, body = ParseStatementList(funcScope)
  639.         if not st then return false, body end
  640.  
  641.         --end
  642.         if not tok:ConsumeKeyword('end', tokenList) then
  643.             return false, GenerateError("`end` expected after function body")
  644.         end
  645.         local nodeFunc = {}
  646.         nodeFunc.AstType   = 'Function'
  647.         nodeFunc.Scope     = funcScope
  648.         nodeFunc.Arguments = argList
  649.         nodeFunc.Body      = body
  650.         nodeFunc.VarArg    = isVarArg
  651.         nodeFunc.Tokens    = tokenList
  652.         --
  653.         return true, nodeFunc
  654.     end
  655.  
  656.  
  657.     function ParsePrimaryExpr(scope)
  658.         local tokenList = {}
  659.  
  660.         if tok:ConsumeSymbol('(', tokenList) then
  661.             local st, ex = ParseExpr(scope)
  662.             if not st then return false, ex end
  663.             if not tok:ConsumeSymbol(')', tokenList) then
  664.                 return false, GenerateError("`)` Expected.")
  665.             end
  666.             if false then
  667.                 --save the information about parenthesized expressions somewhere
  668.                 ex.ParenCount = (ex.ParenCount or 0) + 1
  669.                 return true, ex
  670.             else
  671.                 local parensExp = {}
  672.                 parensExp.AstType   = 'Parentheses'
  673.                 parensExp.Inner     = ex
  674.                 parensExp.Tokens    = tokenList
  675.                 return true, parensExp
  676.             end
  677.  
  678.         elseif tok:Is('Ident') then
  679.             local id = tok:Get(tokenList)
  680.             local var = scope:GetLocal(id.Data)
  681.             if not var then
  682.                 var = scope:GetGlobal(id.Data)
  683.                 if not var then
  684.                     var = scope:CreateGlobal(id.Data)
  685.                 else
  686.                     var.References = var.References + 1
  687.                 end
  688.             else
  689.                 var.References = var.References + 1
  690.             end
  691.             --
  692.             local nodePrimExp = {}
  693.             nodePrimExp.AstType   = 'VarExpr'
  694.             nodePrimExp.Name      = id.Data
  695.             nodePrimExp.Variable  = var
  696.             nodePrimExp.Tokens    = tokenList
  697.             --
  698.             return true, nodePrimExp
  699.         else
  700.             return false, GenerateError("primary expression expected")
  701.         end
  702.     end
  703.  
  704.     function ParseSuffixedExpr(scope, onlyDotColon)
  705.         --base primary expression
  706.         local st, prim = ParsePrimaryExpr(scope)
  707.         if not st then return false, prim end
  708.         --
  709.         while true do
  710.             local tokenList = {}
  711.  
  712.             if tok:IsSymbol('.') or tok:IsSymbol(':') then
  713.                 local symb = tok:Get(tokenList).Data
  714.                 if not tok:Is('Ident') then
  715.                     return false, GenerateError("<Ident> expected.")
  716.                 end
  717.                 local id = tok:Get(tokenList)
  718.                 local nodeIndex = {}
  719.                 nodeIndex.AstType  = 'MemberExpr'
  720.                 nodeIndex.Base     = prim
  721.                 nodeIndex.Indexer  = symb
  722.                 nodeIndex.Ident    = id
  723.                 nodeIndex.Tokens   = tokenList
  724.                 --
  725.                 prim = nodeIndex
  726.  
  727.             elseif not onlyDotColon and tok:ConsumeSymbol('[', tokenList) then
  728.                 local st, ex = ParseExpr(scope)
  729.                 if not st then return false, ex end
  730.                 if not tok:ConsumeSymbol(']', tokenList) then
  731.                     return false, GenerateError("`]` expected.")
  732.                 end
  733.                 local nodeIndex = {}
  734.                 nodeIndex.AstType  = 'IndexExpr'
  735.                 nodeIndex.Base     = prim
  736.                 nodeIndex.Index    = ex
  737.                 nodeIndex.Tokens   = tokenList
  738.                 --
  739.                 prim = nodeIndex
  740.  
  741.             elseif not onlyDotColon and tok:ConsumeSymbol('(', tokenList) then
  742.                 local args = {}
  743.                 while not tok:ConsumeSymbol(')', tokenList) do
  744.                     local st, ex = ParseExpr(scope)
  745.                     if not st then return false, ex end
  746.                     args[#args+1] = ex
  747.                     if not tok:ConsumeSymbol(',', tokenList) then
  748.                         if tok:ConsumeSymbol(')', tokenList) then
  749.                             break
  750.                         else
  751.                             return false, GenerateError("`)` Expected.")
  752.                         end
  753.                     end
  754.                 end
  755.                 local nodeCall = {}
  756.                 nodeCall.AstType   = 'CallExpr'
  757.                 nodeCall.Base      = prim
  758.                 nodeCall.Arguments = args
  759.                 nodeCall.Tokens    = tokenList
  760.                 --
  761.                 prim = nodeCall
  762.  
  763.             elseif not onlyDotColon and tok:Is('String') then
  764.                 --string call
  765.                 local nodeCall = {}
  766.                 nodeCall.AstType    = 'StringCallExpr'
  767.                 nodeCall.Base       = prim
  768.                 nodeCall.Arguments  = { tok:Get(tokenList) }
  769.                 nodeCall.Tokens     = tokenList
  770.                 --
  771.                 prim = nodeCall
  772.  
  773.             elseif not onlyDotColon and tok:IsSymbol('{') then
  774.                 --table call
  775.                 local st, ex = ParseSimpleExpr(scope)
  776.                 -- FIX: ParseExpr(scope) parses the table AND and any following binary expressions.
  777.                 -- We just want the table
  778.                 if not st then return false, ex end
  779.                 local nodeCall = {}
  780.                 nodeCall.AstType   = 'TableCallExpr'
  781.                 nodeCall.Base      = prim
  782.                 nodeCall.Arguments = { ex }
  783.                 nodeCall.Tokens    = tokenList
  784.                 --
  785.                 prim = nodeCall
  786.  
  787.             else
  788.                 break
  789.             end
  790.         end
  791.         return true, prim
  792.     end
  793.  
  794.  
  795.     function ParseSimpleExpr(scope)
  796.         local tokenList = {}
  797.  
  798.         if tok:Is('Number') then
  799.             local nodeNum = {}
  800.             nodeNum.AstType = 'NumberExpr'
  801.             nodeNum.Value   = tok:Get(tokenList)
  802.             nodeNum.Tokens  = tokenList
  803.             return true, nodeNum
  804.  
  805.         elseif tok:Is('String') then
  806.             local nodeStr = {}
  807.             nodeStr.AstType = 'StringExpr'
  808.             nodeStr.Value   = tok:Get(tokenList)
  809.             nodeStr.Tokens  = tokenList
  810.             return true, nodeStr
  811.  
  812.         elseif tok:ConsumeKeyword('nil', tokenList) then
  813.             local nodeNil = {}
  814.             nodeNil.AstType = 'NilExpr'
  815.             nodeNil.Tokens  = tokenList
  816.             return true, nodeNil
  817.  
  818.         elseif tok:IsKeyword('false') or tok:IsKeyword('true') then
  819.             local nodeBoolean = {}
  820.             nodeBoolean.AstType = 'BooleanExpr'
  821.             nodeBoolean.Value   = (tok:Get(tokenList).Data == 'true')
  822.             nodeBoolean.Tokens  = tokenList
  823.             return true, nodeBoolean
  824.  
  825.         elseif tok:ConsumeSymbol('...', tokenList) then
  826.             local nodeDots = {}
  827.             nodeDots.AstType  = 'DotsExpr'
  828.             nodeDots.Tokens   = tokenList
  829.             return true, nodeDots
  830.  
  831.         elseif tok:ConsumeSymbol('{', tokenList) then
  832.             local v = {}
  833.             v.AstType = 'ConstructorExpr'
  834.             v.EntryList = {}
  835.             --
  836.             while true do
  837.                 if tok:IsSymbol('[', tokenList) then
  838.                     --key
  839.                     tok:Get(tokenList)
  840.                     local st, key = ParseExpr(scope)
  841.                     if not st then
  842.                         return false, GenerateError("Key Expression Expected")
  843.                     end
  844.                     if not tok:ConsumeSymbol(']', tokenList) then
  845.                         return false, GenerateError("`]` Expected")
  846.                     end
  847.                     if not tok:ConsumeSymbol('=', tokenList) then
  848.                         return false, GenerateError("`=` Expected")
  849.                     end
  850.                     local st, value = ParseExpr(scope)
  851.                     if not st then
  852.                         return false, GenerateError("Value Expression Expected")
  853.                     end
  854.                     v.EntryList[#v.EntryList+1] = {
  855.                         Type  = 'Key';
  856.                         Key   = key;
  857.                         Value = value;
  858.                     }
  859.  
  860.                 elseif tok:Is('Ident') then
  861.                     --value or key
  862.                     local lookahead = tok:Peek(1)
  863.                     if lookahead.Type == 'Symbol' and lookahead.Data == '=' then
  864.                         --we are a key
  865.                         local key = tok:Get(tokenList)
  866.                         if not tok:ConsumeSymbol('=', tokenList) then
  867.                             return false, GenerateError("`=` Expected")
  868.                         end
  869.                         local st, value = ParseExpr(scope)
  870.                         if not st then
  871.                             return false, GenerateError("Value Expression Expected")
  872.                         end
  873.                         v.EntryList[#v.EntryList+1] = {
  874.                             Type  = 'KeyString';
  875.                             Key   = key.Data;
  876.                             Value = value;
  877.                         }
  878.  
  879.                     else
  880.                         --we are a value
  881.                         local st, value = ParseExpr(scope)
  882.                         if not st then
  883.                             return false, GenerateError("Value Exected")
  884.                         end
  885.                         v.EntryList[#v.EntryList+1] = {
  886.                             Type = 'Value';
  887.                             Value = value;
  888.                         }
  889.  
  890.                     end
  891.                 elseif tok:ConsumeSymbol('}', tokenList) then
  892.                     break
  893.  
  894.                 else
  895.                     --value
  896.                     local st, value = ParseExpr(scope)
  897.                     v.EntryList[#v.EntryList+1] = {
  898.                         Type = 'Value';
  899.                         Value = value;
  900.                     }
  901.                     if not st then
  902.                         return false, GenerateError("Value Expected")
  903.                     end
  904.                 end
  905.  
  906.                 if tok:ConsumeSymbol(';', tokenList) or tok:ConsumeSymbol(',', tokenList) then
  907.                     --all is good
  908.                 elseif tok:ConsumeSymbol('}', tokenList) then
  909.                     break
  910.                 else
  911.                     return false, GenerateError("`}` or table entry Expected")
  912.                 end
  913.             end
  914.             v.Tokens  = tokenList
  915.             return true, v
  916.  
  917.         elseif tok:ConsumeKeyword('function', tokenList) then
  918.             local st, func = ParseFunctionArgsAndBody(scope, tokenList)
  919.             if not st then return false, func end
  920.             --
  921.             func.IsLocal = true
  922.             return true, func
  923.  
  924.         else
  925.             return ParseSuffixedExpr(scope)
  926.         end
  927.     end
  928.  
  929.  
  930.     local unops = lookupify{'-', 'not', '#'}
  931.     local unopprio = 8
  932.     local priority = {
  933.         ['+'] = {6,6};
  934.         ['-'] = {6,6};
  935.         ['%'] = {7,7};
  936.         ['/'] = {7,7};
  937.         ['*'] = {7,7};
  938.         ['^'] = {10,9};
  939.         ['..'] = {5,4};
  940.         ['=='] = {3,3};
  941.         ['<'] = {3,3};
  942.         ['<='] = {3,3};
  943.         ['~='] = {3,3};
  944.         ['>'] = {3,3};
  945.         ['>='] = {3,3};
  946.         ['and'] = {2,2};
  947.         ['or'] = {1,1};
  948.     }
  949.     function ParseSubExpr(scope, level)
  950.         --base item, possibly with unop prefix
  951.         local st, exp
  952.         if unops[tok:Peek().Data] then
  953.             local tokenList = {}
  954.             local op = tok:Get(tokenList).Data
  955.             st, exp = ParseSubExpr(scope, unopprio)
  956.             if not st then return false, exp end
  957.             local nodeEx = {}
  958.             nodeEx.AstType = 'UnopExpr'
  959.             nodeEx.Rhs     = exp
  960.             nodeEx.Op      = op
  961.             nodeEx.OperatorPrecedence = unopprio
  962.             nodeEx.Tokens  = tokenList
  963.             exp = nodeEx
  964.         else
  965.             st, exp = ParseSimpleExpr(scope)
  966.             if not st then return false, exp end
  967.         end
  968.  
  969.         --next items in chain
  970.         while true do
  971.             local prio = priority[tok:Peek().Data]
  972.             if prio and prio[1] > level then
  973.                 local tokenList = {}
  974.                 local op = tok:Get(tokenList).Data
  975.                 local st, rhs = ParseSubExpr(scope, prio[2])
  976.                 if not st then return false, rhs end
  977.                 local nodeEx = {}
  978.                 nodeEx.AstType = 'BinopExpr'
  979.                 nodeEx.Lhs     = exp
  980.                 nodeEx.Op      = op
  981.                 nodeEx.OperatorPrecedence = prio[1]
  982.                 nodeEx.Rhs     = rhs
  983.                 nodeEx.Tokens  = tokenList
  984.                 --
  985.                 exp = nodeEx
  986.             else
  987.                 break
  988.             end
  989.         end
  990.  
  991.         return true, exp
  992.     end
  993.  
  994.  
  995.     ParseExpr = function(scope)
  996.         return ParseSubExpr(scope, 0)
  997.     end
  998.  
  999.  
  1000.     local function ParseStatement(scope)
  1001.         local stat = nil
  1002.         local tokenList = {}
  1003.         if tok:ConsumeKeyword('if', tokenList) then
  1004.             --setup
  1005.             local nodeIfStat = {}
  1006.             nodeIfStat.AstType = 'IfStatement'
  1007.             nodeIfStat.Clauses = {}
  1008.  
  1009.             --clauses
  1010.             repeat
  1011.                 local st, nodeCond = ParseExpr(scope)
  1012.                 if not st then return false, nodeCond end
  1013.                 if not tok:ConsumeKeyword('then', tokenList) then
  1014.                     return false, GenerateError("`then` expected.")
  1015.                 end
  1016.                 local st, nodeBody = ParseStatementList(scope)
  1017.                 if not st then return false, nodeBody end
  1018.                 nodeIfStat.Clauses[#nodeIfStat.Clauses+1] = {
  1019.                     Condition = nodeCond;
  1020.                     Body = nodeBody;
  1021.                 }
  1022.             until not tok:ConsumeKeyword('elseif', tokenList)
  1023.  
  1024.             --else clause
  1025.             if tok:ConsumeKeyword('else', tokenList) then
  1026.                 local st, nodeBody = ParseStatementList(scope)
  1027.                 if not st then return false, nodeBody end
  1028.                 nodeIfStat.Clauses[#nodeIfStat.Clauses+1] = {
  1029.                     Body = nodeBody;
  1030.                 }
  1031.             end
  1032.  
  1033.             --end
  1034.             if not tok:ConsumeKeyword('end', tokenList) then
  1035.                 return false, GenerateError("`end` expected.")
  1036.             end
  1037.  
  1038.             nodeIfStat.Tokens = tokenList
  1039.             stat = nodeIfStat
  1040.  
  1041.         elseif tok:ConsumeKeyword('while', tokenList) then
  1042.             --setup
  1043.             local nodeWhileStat = {}
  1044.             nodeWhileStat.AstType = 'WhileStatement'
  1045.  
  1046.             --condition
  1047.             local st, nodeCond = ParseExpr(scope)
  1048.             if not st then return false, nodeCond end
  1049.  
  1050.             --do
  1051.             if not tok:ConsumeKeyword('do', tokenList) then
  1052.                 return false, GenerateError("`do` expected.")
  1053.             end
  1054.  
  1055.             --body
  1056.             local st, nodeBody = ParseStatementList(scope)
  1057.             if not st then return false, nodeBody end
  1058.  
  1059.             --end
  1060.             if not tok:ConsumeKeyword('end', tokenList) then
  1061.                 return false, GenerateError("`end` expected.")
  1062.             end
  1063.  
  1064.             --return
  1065.             nodeWhileStat.Condition = nodeCond
  1066.             nodeWhileStat.Body      = nodeBody
  1067.             nodeWhileStat.Tokens    = tokenList
  1068.             stat = nodeWhileStat
  1069.  
  1070.         elseif tok:ConsumeKeyword('do', tokenList) then
  1071.             --do block
  1072.             local st, nodeBlock = ParseStatementList(scope)
  1073.             if not st then return false, nodeBlock end
  1074.             if not tok:ConsumeKeyword('end', tokenList) then
  1075.                 return false, GenerateError("`end` expected.")
  1076.             end
  1077.  
  1078.             local nodeDoStat = {}
  1079.             nodeDoStat.AstType = 'DoStatement'
  1080.             nodeDoStat.Body    = nodeBlock
  1081.             nodeDoStat.Tokens  = tokenList
  1082.             stat = nodeDoStat
  1083.  
  1084.         elseif tok:ConsumeKeyword('for', tokenList) then
  1085.             --for block
  1086.             if not tok:Is('Ident') then
  1087.                 return false, GenerateError("<ident> expected.")
  1088.             end
  1089.             local baseVarName = tok:Get(tokenList)
  1090.             if tok:ConsumeSymbol('=', tokenList) then
  1091.                 --numeric for
  1092.                 local forScope = CreateScope(scope)
  1093.                 local forVar = forScope:CreateLocal(baseVarName.Data)
  1094.                 --
  1095.                 local st, startEx = ParseExpr(scope)
  1096.                 if not st then return false, startEx end
  1097.                 if not tok:ConsumeSymbol(',', tokenList) then
  1098.                     return false, GenerateError("`,` Expected")
  1099.                 end
  1100.                 local st, endEx = ParseExpr(scope)
  1101.                 if not st then return false, endEx end
  1102.                 local st, stepEx;
  1103.                 if tok:ConsumeSymbol(',', tokenList) then
  1104.                     st, stepEx = ParseExpr(scope)
  1105.                     if not st then return false, stepEx end
  1106.                 end
  1107.                 if not tok:ConsumeKeyword('do', tokenList) then
  1108.                     return false, GenerateError("`do` expected")
  1109.                 end
  1110.                 --
  1111.                 local st, body = ParseStatementList(forScope)
  1112.                 if not st then return false, body end
  1113.                 if not tok:ConsumeKeyword('end', tokenList) then
  1114.                     return false, GenerateError("`end` expected")
  1115.                 end
  1116.                 --
  1117.                 local nodeFor = {}
  1118.                 nodeFor.AstType  = 'NumericForStatement'
  1119.                 nodeFor.Scope    = forScope
  1120.                 nodeFor.Variable = forVar
  1121.                 nodeFor.Start    = startEx
  1122.                 nodeFor.End      = endEx
  1123.                 nodeFor.Step     = stepEx
  1124.                 nodeFor.Body     = body
  1125.                 nodeFor.Tokens   = tokenList
  1126.                 stat = nodeFor
  1127.             else
  1128.                 --generic for
  1129.                 local forScope = CreateScope(scope)
  1130.                 --
  1131.                 local varList = { forScope:CreateLocal(baseVarName.Data) }
  1132.                 while tok:ConsumeSymbol(',', tokenList) do
  1133.                     if not tok:Is('Ident') then
  1134.                         return false, GenerateError("for variable expected.")
  1135.                     end
  1136.                     varList[#varList+1] = forScope:CreateLocal(tok:Get(tokenList).Data)
  1137.                 end
  1138.                 if not tok:ConsumeKeyword('in', tokenList) then
  1139.                     return false, GenerateError("`in` expected.")
  1140.                 end
  1141.                 local generators = {}
  1142.                 local st, firstGenerator = ParseExpr(scope)
  1143.                 if not st then return false, firstGenerator end
  1144.                 generators[#generators+1] = firstGenerator
  1145.                 while tok:ConsumeSymbol(',', tokenList) do
  1146.                     local st, gen = ParseExpr(scope)
  1147.                     if not st then return false, gen end
  1148.                     generators[#generators+1] = gen
  1149.                 end
  1150.                 if not tok:ConsumeKeyword('do', tokenList) then
  1151.                     return false, GenerateError("`do` expected.")
  1152.                 end
  1153.                 local st, body = ParseStatementList(forScope)
  1154.                 if not st then return false, body end
  1155.                 if not tok:ConsumeKeyword('end', tokenList) then
  1156.                     return false, GenerateError("`end` expected.")
  1157.                 end
  1158.                 --
  1159.                 local nodeFor = {}
  1160.                 nodeFor.AstType      = 'GenericForStatement'
  1161.                 nodeFor.Scope        = forScope
  1162.                 nodeFor.VariableList = varList
  1163.                 nodeFor.Generators   = generators
  1164.                 nodeFor.Body         = body
  1165.                 nodeFor.Tokens       = tokenList
  1166.                 stat = nodeFor
  1167.             end
  1168.  
  1169.         elseif tok:ConsumeKeyword('repeat', tokenList) then
  1170.             local st, body = ParseStatementList(scope)
  1171.             if not st then return false, body end
  1172.             --
  1173.             if not tok:ConsumeKeyword('until', tokenList) then
  1174.                 return false, GenerateError("`until` expected.")
  1175.             end
  1176.             -- FIX: Used to parse in parent scope
  1177.             -- Now parses in repeat scope
  1178.             local st, cond = ParseExpr(body.Scope)
  1179.             if not st then return false, cond end
  1180.             --
  1181.             local nodeRepeat = {}
  1182.             nodeRepeat.AstType   = 'RepeatStatement'
  1183.             nodeRepeat.Condition = cond
  1184.             nodeRepeat.Body      = body
  1185.             nodeRepeat.Tokens    = tokenList
  1186.             stat = nodeRepeat
  1187.  
  1188.         elseif tok:ConsumeKeyword('function', tokenList) then
  1189.             if not tok:Is('Ident') then
  1190.                 return false, GenerateError("Function name expected")
  1191.             end
  1192.             local st, name = ParseSuffixedExpr(scope, true) --true => only dots and colons
  1193.             if not st then return false, name end
  1194.             --
  1195.             local st, func = ParseFunctionArgsAndBody(scope, tokenList)
  1196.             if not st then return false, func end
  1197.             --
  1198.             func.IsLocal = false
  1199.             func.Name    = name
  1200.             stat = func
  1201.  
  1202.         elseif tok:ConsumeKeyword('local', tokenList) then
  1203.             if tok:Is('Ident') then
  1204.                 local varList = { tok:Get(tokenList).Data }
  1205.                 while tok:ConsumeSymbol(',', tokenList) do
  1206.                     if not tok:Is('Ident') then
  1207.                         return false, GenerateError("local var name expected")
  1208.                     end
  1209.                     varList[#varList+1] = tok:Get(tokenList).Data
  1210.                 end
  1211.  
  1212.                 local initList = {}
  1213.                 if tok:ConsumeSymbol('=', tokenList) then
  1214.                     repeat
  1215.                         local st, ex = ParseExpr(scope)
  1216.                         if not st then return false, ex end
  1217.                         initList[#initList+1] = ex
  1218.                     until not tok:ConsumeSymbol(',', tokenList)
  1219.                 end
  1220.  
  1221.                 --now patch var list
  1222.                 --we can't do this before getting the init list, because the init list does not
  1223.                 --have the locals themselves in scope.
  1224.                 for i, v in pairs(varList) do
  1225.                     varList[i] = scope:CreateLocal(v)
  1226.                 end
  1227.  
  1228.                 local nodeLocal = {}
  1229.                 nodeLocal.AstType   = 'LocalStatement'
  1230.                 nodeLocal.LocalList = varList
  1231.                 nodeLocal.InitList  = initList
  1232.                 nodeLocal.Tokens    = tokenList
  1233.                 --
  1234.                 stat = nodeLocal
  1235.  
  1236.             elseif tok:ConsumeKeyword('function', tokenList) then
  1237.                 if not tok:Is('Ident') then
  1238.                     return false, GenerateError("Function name expected")
  1239.                 end
  1240.                 local name = tok:Get(tokenList).Data
  1241.                 local localVar = scope:CreateLocal(name)
  1242.                 --
  1243.                 local st, func = ParseFunctionArgsAndBody(scope, tokenList)
  1244.                 if not st then return false, func end
  1245.                 --
  1246.                 func.Name         = localVar
  1247.                 func.IsLocal      = true
  1248.                 stat = func
  1249.  
  1250.             else
  1251.                 return false, GenerateError("local var or function def expected")
  1252.             end
  1253.  
  1254.         elseif tok:ConsumeSymbol('::', tokenList) then
  1255.             if not tok:Is('Ident') then
  1256.                 return false, GenerateError('Label name expected')
  1257.             end
  1258.             local label = tok:Get(tokenList).Data
  1259.             if not tok:ConsumeSymbol('::', tokenList) then
  1260.                 return false, GenerateError("`::` expected")
  1261.             end
  1262.             local nodeLabel = {}
  1263.             nodeLabel.AstType = 'LabelStatement'
  1264.             nodeLabel.Label   = label
  1265.             nodeLabel.Tokens  = tokenList
  1266.             stat = nodeLabel
  1267.  
  1268.         elseif tok:ConsumeKeyword('return', tokenList) then
  1269.             local exList = {}
  1270.             if not tok:IsKeyword('end') then
  1271.                 local st, firstEx = ParseExpr(scope)
  1272.                 if st then
  1273.                     exList[1] = firstEx
  1274.                     while tok:ConsumeSymbol(',', tokenList) do
  1275.                         local st, ex = ParseExpr(scope)
  1276.                         if not st then return false, ex end
  1277.                         exList[#exList+1] = ex
  1278.                     end
  1279.                 end
  1280.             end
  1281.  
  1282.             local nodeReturn = {}
  1283.             nodeReturn.AstType   = 'ReturnStatement'
  1284.             nodeReturn.Arguments = exList
  1285.             nodeReturn.Tokens    = tokenList
  1286.             stat = nodeReturn
  1287.  
  1288.         elseif tok:ConsumeKeyword('break', tokenList) then
  1289.             local nodeBreak = {}
  1290.             nodeBreak.AstType = 'BreakStatement'
  1291.             nodeBreak.Tokens  = tokenList
  1292.             stat = nodeBreak
  1293.  
  1294. --[[
  1295.         elseif tok:ConsumeKeyword('goto', tokenList) then
  1296.             if not tok:Is('Ident') then
  1297.                 return false, GenerateError("Label expected")
  1298.             end
  1299.             local label = tok:Get(tokenList).Data
  1300.             local nodeGoto = {}
  1301.             nodeGoto.AstType = 'GotoStatement'
  1302.             nodeGoto.Label   = label
  1303.             nodeGoto.Tokens  = tokenList
  1304.             stat = nodeGoto
  1305. --]]
  1306.         else
  1307.             --statementParseExpr
  1308.             local st, suffixed = ParseSuffixedExpr(scope)
  1309.             if not st then return false, suffixed end
  1310.  
  1311.             --assignment or call?
  1312.             if tok:IsSymbol(',') or tok:IsSymbol('=') then
  1313.                 --check that it was not parenthesized, making it not an lvalue
  1314.                 if (suffixed.ParenCount or 0) > 0 then
  1315.                     return false, GenerateError("Can not assign to parenthesized expression, is not an lvalue")
  1316.                 end
  1317.  
  1318.                 --more processing needed
  1319.                 local lhs = { suffixed }
  1320.                 while tok:ConsumeSymbol(',', tokenList) do
  1321.                     local st, lhsPart = ParseSuffixedExpr(scope)
  1322.                     if not st then return false, lhsPart end
  1323.                     lhs[#lhs+1] = lhsPart
  1324.                 end
  1325.  
  1326.                 --equals
  1327.                 if not tok:ConsumeSymbol('=', tokenList) then
  1328.                     return false, GenerateError("`=` Expected.")
  1329.                 end
  1330.  
  1331.                 --rhs
  1332.                 local rhs = {}
  1333.                 local st, firstRhs = ParseExpr(scope)
  1334.                 if not st then return false, firstRhs end
  1335.                 rhs[1] = firstRhs
  1336.                 while tok:ConsumeSymbol(',', tokenList) do
  1337.                     local st, rhsPart = ParseExpr(scope)
  1338.                     if not st then return false, rhsPart end
  1339.                     rhs[#rhs+1] = rhsPart
  1340.                 end
  1341.  
  1342.                 --done
  1343.                 local nodeAssign = {}
  1344.                 nodeAssign.AstType = 'AssignmentStatement'
  1345.                 nodeAssign.Lhs     = lhs
  1346.                 nodeAssign.Rhs     = rhs
  1347.                 nodeAssign.Tokens  = tokenList
  1348.                 stat = nodeAssign
  1349.  
  1350.             elseif suffixed.AstType == 'CallExpr' or
  1351.                    suffixed.AstType == 'TableCallExpr' or
  1352.                    suffixed.AstType == 'StringCallExpr'
  1353.             then
  1354.                 --it's a call statement
  1355.                 local nodeCall = {}
  1356.                 nodeCall.AstType    = 'CallStatement'
  1357.                 nodeCall.Expression = suffixed
  1358.                 nodeCall.Tokens     = tokenList
  1359.                 stat = nodeCall
  1360.             else
  1361.                 return false, GenerateError("Assignment Statement Expected")
  1362.             end
  1363.         end
  1364.  
  1365.         if tok:IsSymbol(';') then
  1366.             stat.Semicolon = tok:Get( stat.Tokens )
  1367.         end
  1368.         return true, stat
  1369.     end
  1370.  
  1371.  
  1372.     local statListCloseKeywords = lookupify{'end', 'else', 'elseif', 'until'}
  1373.  
  1374.     ParseStatementList = function(scope)
  1375.         local nodeStatlist   = {}
  1376.         nodeStatlist.Scope   = CreateScope(scope)
  1377.         nodeStatlist.AstType = 'Statlist'
  1378.         nodeStatlist.Body    = { }
  1379.         nodeStatlist.Tokens  = { }
  1380.         --
  1381.         --local stats = {}
  1382.         --
  1383.         while not statListCloseKeywords[tok:Peek().Data] and not tok:IsEof() do
  1384.             local st, nodeStatement = ParseStatement(nodeStatlist.Scope)
  1385.             if not st then return false, nodeStatement end
  1386.             --stats[#stats+1] = nodeStatement
  1387.             nodeStatlist.Body[#nodeStatlist.Body + 1] = nodeStatement
  1388.         end
  1389.  
  1390.         if tok:IsEof() then
  1391.             local nodeEof = {}
  1392.             nodeEof.AstType = 'Eof'
  1393.             nodeEof.Tokens  = { tok:Get() }
  1394.             nodeStatlist.Body[#nodeStatlist.Body + 1] = nodeEof
  1395.         end
  1396.  
  1397.         --
  1398.         --nodeStatlist.Body = stats
  1399.         return true, nodeStatlist
  1400.     end
  1401.  
  1402.  
  1403.     local function mainfunc()
  1404.         local topScope = CreateScope()
  1405.         return ParseStatementList(topScope)
  1406.     end
  1407.  
  1408.     local st, main = mainfunc()
  1409.     --print("Last Token: "..PrintTable(tok:Peek()))
  1410.     return st, main
  1411. end
  1412.  
  1413. return { LexLua = LexLua, ParseLua = ParseLua }
Add Comment
Please, Sign In to add comment