Advertisement
XZTablets

Lexer

Apr 16th, 2020
297
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 7.65 KB | None | 0 0
  1. function lookupify(src, list)
  2.     list = list or {}
  3.  
  4.     if type(src) == 'string' then
  5.         for i = 1, src:len() do
  6.             list[src:sub(i, i)] = true
  7.         end
  8.     elseif type(src) == 'table' then
  9.         for i = 1, #src do
  10.             list[src[i]] = true
  11.         end
  12.     end
  13.  
  14.     return list
  15. end
  16.  
  17. local base_ident = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_'
  18. local base_digits = '0123456789'
  19. local base_operators = '+-*/^%#'
  20.  
  21. local chars = {
  22.     whitespace = lookupify(' \n\t\r'),
  23.     validEscapes = lookupify('abfnrtv"\'\\'),
  24.     ident = lookupify(
  25.         base_ident .. base_digits,
  26.         {
  27.             start = lookupify(base_ident),
  28.         }
  29.     ),
  30.  
  31.     digits = lookupify(
  32.         base_digits,
  33.         {
  34.             hex = lookupify(base_digits .. 'abcdefABCDEF')
  35.         }
  36.     ),
  37.  
  38.     symbols = lookupify(
  39.         base_operators .. ',{}[]();.:', {
  40.             equality = lookupify('~=><'),
  41.             operators = lookupify(base_operators)
  42.         }
  43.     )
  44. }
  45.  
  46. local keywords = {
  47.     structure = lookupify({
  48.         'and', 'break', 'do', 'else', 'elseif', 'end', 'for', 'function',
  49.         'goto', 'if', 'in', 'local', 'not', 'or', 'repeat', 'return', 'then',
  50.         'until', 'while'
  51.     }),
  52.  
  53.     values = lookupify({
  54.         'true', 'false', 'nil'
  55.     })
  56. }
  57.  
  58. local builtin = {
  59.     structure = lookupify({
  60.         'pairs', 'print', 'Color3', 'Enum', 'UDim', 'UDim2', 'Instance', 'game'
  61.     })
  62. }
  63.  
  64. return function(text)
  65.     local pos = 1
  66.     local start = 1
  67.     local line = 0
  68.     local buffer = {}
  69.     local lines = {}
  70.  
  71.     local function look(delta)
  72.         delta = pos + (delta or 0)
  73.  
  74.         return text:sub(delta, delta)
  75.     end
  76.  
  77.     local function get()
  78.         pos = pos + 1
  79.  
  80.         return look(-1)
  81.     end
  82.  
  83.     local function getDataLevel()
  84.         local num = 0
  85.  
  86.         while look(num) == '=' do
  87.             num = num + 1
  88.         end
  89.  
  90.         if look(num) == '[' then
  91.             pos = pos + num + 1
  92.  
  93.             return num
  94.         end
  95.     end
  96.  
  97.     local function getCurrentTokenText()
  98.         return text:sub(start, pos - 1)
  99.     end
  100.  
  101.     local currentLineLength = 0
  102.     local lineoffset = 0
  103.     local waitingOnLine = false
  104.     local line = 0
  105.  
  106.     local function pushToken(type, text)
  107.         text = text or getCurrentTokenText()
  108.  
  109.         local tk = buffer[#buffer]
  110.        
  111.         if not tk or tk.type ~= type then
  112.             if waitingOnLine and type ~= "newline" then
  113.                 line = line + 1
  114.                 waitingOnLine = false
  115.             end
  116.             tk = {
  117.                 type = type,
  118.                 data = text,
  119.                 posFirst = start - lineoffset,
  120.                 posLast = pos - 1 - lineoffset,
  121.                 line = line    
  122.             }
  123.            
  124.             if tk.data ~= '' then
  125.                 buffer[#buffer + 1] = tk
  126.             end
  127.         else
  128.             tk.data = tk.data .. text
  129.             tk.posLast = tk.posLast + text:len()
  130.         end
  131.    
  132.         currentLineLength = currentLineLength + text:len()
  133.         start = pos
  134.    
  135.         return tk
  136.     end
  137.  
  138.     local function newline()
  139.         lines[#lines + 1] = buffer
  140.         waitingOnLine = true
  141.         buffer = {}
  142.  
  143.         get()
  144.         pushToken('newline')
  145.         buffer[1] = nil
  146.  
  147.         lineoffset = lineoffset + currentLineLength
  148.         currentLineLength = 0
  149.     end
  150.  
  151.     local function getData(level, type)
  152.         while true do
  153.             local char = get()
  154.  
  155.             if char == '' then
  156.                 return
  157.             elseif char == '\n' then
  158.                 pos = pos - 1
  159.                 pushToken(type)
  160.                 newline()
  161.             elseif char == ']' then
  162.                 local valid = true
  163.  
  164.                 for i = 1, level do
  165.                     if look() == '=' then
  166.                         pos = pos + 1
  167.                     else
  168.                         valid = false
  169.                         break
  170.                     end
  171.                 end
  172.  
  173.                 if valid and look() == ']' then
  174.                     pos = pos - level - 1
  175.  
  176.                     return
  177.                 end
  178.             end
  179.         end
  180.     end
  181.  
  182.     local function chompWhitespace()
  183.         while true do
  184.             local char = look()
  185.  
  186.             if char == '\n' then
  187.                 pushToken('whitespace')
  188.                 newline()
  189.             elseif chars.whitespace[char] then
  190.                 pos = pos + 1
  191.             else
  192.                 break
  193.             end
  194.         end
  195.  
  196.         pushToken('whitespace')
  197.     end
  198.  
  199.     while true do
  200.         chompWhitespace()
  201.  
  202.         local char = get()
  203.  
  204.         if char == '' then
  205.             break
  206.         elseif char == '-' and look() == '-' then
  207.             pos = pos + 1
  208.  
  209.             if look() == '[' then
  210.                 pos = pos + 1
  211.  
  212.                 local level = getDataLevel()
  213.  
  214.                 if level then
  215.                     getData(level, 'comment')
  216.  
  217.                     pos = pos + level + 2
  218.                     pushToken('comment')
  219.                 else
  220.                     while true do
  221.                         local char2 = get()
  222.  
  223.                         if char2 == '' or char2 == '\n' then
  224.                             pos = pos - 1
  225.                             pushToken('comment')
  226.  
  227.                             if char2 == '\n' then
  228.                                 newline()
  229.                             end
  230.  
  231.                             break
  232.                         end
  233.                     end
  234.                 end
  235.             else
  236.                 while true do
  237.                     local char2 = get()
  238.  
  239.                     if char2 == '' or char2 == '\n' then
  240.                         pos = pos - 1
  241.                         pushToken('comment')
  242.  
  243.                         if char2 == '\n' then
  244.                             newline()
  245.                         end
  246.  
  247.                         break
  248.                     end
  249.                 end
  250.             end
  251.  
  252.             pushToken('comment')
  253.         elseif char == '\'' or char == '"' then
  254.             pushToken('string_start')
  255.  
  256.             while true do
  257.                 local char2 = get()
  258.  
  259.                 if char2 == '\\' then
  260.                     pos = pos - 1
  261.                     pushToken('string')
  262.                     get()
  263.  
  264.                     local char3 = get()
  265.  
  266.                     if chars.digits[char3] then
  267.                         for i = 1, 2 do
  268.                             if chars.digits[look()] then
  269.                                 pos = pos + 1
  270.                             end
  271.                         end
  272.                     elseif char3 == 'x' then
  273.                         if chars.digits.hex[look()] and chars.digits.hex[look(1)] then
  274.                             pos = pos + 2
  275.                         else
  276.                             pushToken('unidentified')
  277.                         end
  278.                     elseif char3 == '\n' then
  279.                         pos = pos - 1
  280.                         pushToken('escape')
  281.                         newline()
  282.                     elseif not chars.validEscapes[char3] then
  283.                         pushToken('unidentified')
  284.                     end
  285.  
  286.                     pushToken('escape')
  287.                 elseif char2 == '\n' then
  288.                     pos = pos - 1
  289.                     pushToken('string')
  290.                     newline()
  291.  
  292.                     break
  293.                 elseif char2 == char or char2 == '' then
  294.                     pos = pos - 1
  295.                     pushToken('string')
  296.                     get()
  297.  
  298.                     break
  299.                 end
  300.             end
  301.  
  302.             pushToken('string_end')
  303.         elseif chars.ident.start[char] then
  304.             while chars.ident[look()] do
  305.                 pos = pos + 1
  306.             end
  307.  
  308.             local word = getCurrentTokenText()
  309.  
  310.             if keywords.structure[word] then
  311.                 pushToken('keyword')
  312.             elseif keywords.values[word] then
  313.                 pushToken('value')
  314.             elseif builtin.structure[word] then
  315.                 pushToken('builtin')
  316.             else
  317.                 pushToken('ident')
  318.             end
  319.         elseif chars.digits[char] or (char == '.' and chars.digits[look()]) then
  320.             if char == '0' and look() == 'x' then
  321.                 pos = pos + 1
  322.  
  323.                 while chars.digits.hex[look()] do
  324.                     pos = pos + 1
  325.                 end
  326.             else
  327.                 while chars.digits[look()] do
  328.                     pos = pos + 1
  329.                 end
  330.  
  331.                 if look() == '.' then
  332.                     pos = pos + 1
  333.  
  334.                     while chars.digits[look()] do
  335.                         pos = pos + 1
  336.                     end
  337.                 end
  338.  
  339.                 if look():lower() == 'e' then
  340.                     pos = pos + 1
  341.  
  342.                     if look() == '-' then
  343.                         pos = pos + 1
  344.                     end
  345.  
  346.                     while chars.digits[look()] do
  347.                         pos = pos + 1
  348.                     end
  349.                 end
  350.             end
  351.  
  352.             pushToken('number')
  353.         elseif char == '[' then
  354.             local level = getDataLevel()
  355.  
  356.             if level then
  357.                 pushToken('string_start')
  358.  
  359.                 getData(level, 'string')
  360.                 pushToken('string')
  361.  
  362.                 pos = pos + level + 2
  363.                 pushToken('string_end')
  364.             else
  365.                 pushToken('symbol')
  366.             end
  367.         elseif char == '.' then
  368.             if look() == '.' then
  369.                 pos = pos + 1
  370.  
  371.                 if look() == '.' then
  372.                     pos = pos + 1
  373.                 end
  374.             end
  375.  
  376.             if getCurrentTokenText():len() == 3 then
  377.                 pushToken('vararg')
  378.             else
  379.                 pushToken('symbol')
  380.             end
  381.         elseif char == ':' and look() == ':' then
  382.             get()
  383.  
  384.             pushToken('label_start')
  385.  
  386.             chompWhitespace()
  387.  
  388.             if chars.ident.start[look()] then
  389.                 get()
  390.  
  391.                 while chars.ident[look()] do
  392.                     get()
  393.                 end
  394.  
  395.                 pushToken('label')
  396.  
  397.                 chompWhitespace()
  398.  
  399.                 if look() == ':' and look(1) == ':' then
  400.                     get()
  401.                     get()
  402.  
  403.                     pushToken('label_end')
  404.                 end
  405.             end
  406.         elseif chars.symbols.equality[char] then
  407.             if look() == '=' then
  408.                 pos = pos + 1
  409.             end
  410.  
  411.             pushToken('operator')
  412.         elseif chars.symbols[char] then
  413.             if chars.symbols.operators[char] then
  414.                 pushToken('operator')
  415.             else
  416.                 pushToken('symbol')
  417.             end
  418.         else
  419.             pushToken('unidentified')
  420.         end
  421.     end
  422.  
  423.     lines[#lines + 1] = buffer
  424.  
  425.     return lines
  426. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement