LoganDark

Lex

Jan 18th, 2018
1,012
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. -- Lex, by LoganDark
  2. -- Can be loaded using os.loadAPI, has only a single function: lex.lex('code here')
  3. -- If loaded using dofile(), it returns the lex function (for environments outside ComputerCraft)
  4. -- It returns a list of lists, where each list is one line.
  5. -- Each line contains tokens (in the order they are found), where each token is formatted like this:
  6. -- {
  7. --  type = one of the token types below,
  8. --  data = the source code that makes up the token,
  9. --  posFirst = the position (inclusive) within THAT LINE that the token starts
  10. --  posLast = the position (inclusive) within THAT LINE that the token ends
  11. -- }
  12.  
  13. -- Possible token types:
  14. --  whitespace: Self-explanatory. Can match spaces, newlines, tabs, and carriage returns (although I don't know why anyone would use those... WINDOWS)
  15. --  comment: Either multi-line or single-line comments.
  16. --  string: A string. Usually the part of the string that is not an escape.
  17. --  escape: Can only be found within strings (although they are separate tokens)
  18. --  keyword: Keywords. Like "while", "end", "do", etc
  19. --  value: Special values. Only true, false, and nil.
  20. --  ident: Identifier. Variables, function names, etc..
  21. --  number: Numbers!
  22. --  symbol: Symbols, like brackets, parenthesis, ., .., ... etc
  23. --  operator: Operators, like =, ==, >=, <=, ~=, etc
  24. --  unidentified: Anything that isn't one of the above tokens. Consider them ERRORS.
  25.  
  26. local chars = {
  27.     whitespace = {
  28.         [' '] = true,
  29.         ['\n'] = true,
  30.         ['\t'] = true,
  31.         ['\r'] = true
  32.     },
  33.  
  34.     validEscapes = {
  35.         ['a'] = true,
  36.         ['b'] = true,
  37.         ['f'] = true,
  38.         ['n'] = true,
  39.         ['r'] = true,
  40.         ['t'] = true,
  41.         ['v'] = true,
  42.         ['"'] = true,
  43.         ['\''] = true,
  44.         ['\\'] = true,
  45.         ['\n'] = true
  46.     },
  47.  
  48.     ident = {
  49.         ['a'] = true,
  50.         ['b'] = true,
  51.         ['c'] = true,
  52.         ['d'] = true,
  53.         ['e'] = true,
  54.         ['f'] = true,
  55.         ['g'] = true,
  56.         ['h'] = true,
  57.         ['i'] = true,
  58.         ['j'] = true,
  59.         ['k'] = true,
  60.         ['l'] = true,
  61.         ['m'] = true,
  62.         ['n'] = true,
  63.         ['o'] = true,
  64.         ['p'] = true,
  65.         ['q'] = true,
  66.         ['r'] = true,
  67.         ['s'] = true,
  68.         ['t'] = true,
  69.         ['u'] = true,
  70.         ['v'] = true,
  71.         ['w'] = true,
  72.         ['x'] = true,
  73.         ['y'] = true,
  74.         ['z'] = true,
  75.         ['A'] = true,
  76.         ['B'] = true,
  77.         ['C'] = true,
  78.         ['D'] = true,
  79.         ['E'] = true,
  80.         ['F'] = true,
  81.         ['G'] = true,
  82.         ['H'] = true,
  83.         ['I'] = true,
  84.         ['J'] = true,
  85.         ['K'] = true,
  86.         ['L'] = true,
  87.         ['M'] = true,
  88.         ['N'] = true,
  89.         ['O'] = true,
  90.         ['P'] = true,
  91.         ['Q'] = true,
  92.         ['R'] = true,
  93.         ['S'] = true,
  94.         ['T'] = true,
  95.         ['U'] = true,
  96.         ['V'] = true,
  97.         ['W'] = true,
  98.         ['X'] = true,
  99.         ['Y'] = true,
  100.         ['Z'] = true,
  101.         ['_'] = true,
  102.         ['0'] = true,
  103.         ['1'] = true,
  104.         ['2'] = true,
  105.         ['3'] = true,
  106.         ['4'] = true,
  107.         ['5'] = true,
  108.         ['6'] = true,
  109.         ['7'] = true,
  110.         ['8'] = true,
  111.         ['9'] = true,
  112.  
  113.         start = {
  114.             ['a'] = true,
  115.             ['b'] = true,
  116.             ['c'] = true,
  117.             ['d'] = true,
  118.             ['e'] = true,
  119.             ['f'] = true,
  120.             ['g'] = true,
  121.             ['h'] = true,
  122.             ['i'] = true,
  123.             ['j'] = true,
  124.             ['k'] = true,
  125.             ['l'] = true,
  126.             ['m'] = true,
  127.             ['n'] = true,
  128.             ['o'] = true,
  129.             ['p'] = true,
  130.             ['q'] = true,
  131.             ['r'] = true,
  132.             ['s'] = true,
  133.             ['t'] = true,
  134.             ['u'] = true,
  135.             ['v'] = true,
  136.             ['w'] = true,
  137.             ['x'] = true,
  138.             ['y'] = true,
  139.             ['z'] = true,
  140.             ['A'] = true,
  141.             ['B'] = true,
  142.             ['C'] = true,
  143.             ['D'] = true,
  144.             ['E'] = true,
  145.             ['F'] = true,
  146.             ['G'] = true,
  147.             ['H'] = true,
  148.             ['I'] = true,
  149.             ['J'] = true,
  150.             ['K'] = true,
  151.             ['L'] = true,
  152.             ['M'] = true,
  153.             ['N'] = true,
  154.             ['O'] = true,
  155.             ['P'] = true,
  156.             ['Q'] = true,
  157.             ['R'] = true,
  158.             ['S'] = true,
  159.             ['T'] = true,
  160.             ['U'] = true,
  161.             ['V'] = true,
  162.             ['W'] = true,
  163.             ['X'] = true,
  164.             ['Y'] = true,
  165.             ['Z'] = true,
  166.             ['_'] = true
  167.         },
  168.     },
  169.  
  170.     digits = {
  171.         ['0'] = true,
  172.         ['1'] = true,
  173.         ['2'] = true,
  174.         ['3'] = true,
  175.         ['4'] = true,
  176.         ['5'] = true,
  177.         ['6'] = true,
  178.         ['7'] = true,
  179.         ['8'] = true,
  180.         ['9'] = true,
  181.  
  182.         hex = {
  183.             ['0'] = true,
  184.             ['1'] = true,
  185.             ['2'] = true,
  186.             ['3'] = true,
  187.             ['4'] = true,
  188.             ['5'] = true,
  189.             ['6'] = true,
  190.             ['7'] = true,
  191.             ['8'] = true,
  192.             ['9'] = true,
  193.             ['a'] = true,
  194.             ['b'] = true,
  195.             ['c'] = true,
  196.             ['d'] = true,
  197.             ['e'] = true,
  198.             ['f'] = true,
  199.             ['A'] = true,
  200.             ['B'] = true,
  201.             ['C'] = true,
  202.             ['D'] = true,
  203.             ['E'] = true,
  204.             ['F'] = true
  205.         }
  206.     },
  207.  
  208.     symbols = {
  209.         ['+'] = true,
  210.         ['-'] = true,
  211.         ['*'] = true,
  212.         ['/'] = true,
  213.         ['^'] = true,
  214.         ['%'] = true,
  215.         [','] = true,
  216.         ['{'] = true,
  217.         ['}'] = true,
  218.         ['['] = true,
  219.         [']'] = true,
  220.         ['('] = true,
  221.         [')'] = true,
  222.         [';'] = true,
  223.         ['#'] = true,
  224.         ['.'] = true,
  225.         [':'] = true,
  226.  
  227.         equality = {
  228.             ['~'] = true,
  229.             ['='] = true,
  230.             ['>'] = true,
  231.             ['<'] = true
  232.         },
  233.  
  234.         operators = {
  235.             ['+'] = true,
  236.             ['-'] = true,
  237.             ['*'] = true,
  238.             ['/'] = true,
  239.             ['^'] = true,
  240.             ['%'] = true,
  241.             ['#'] = true
  242.         }
  243.     }
  244. }
  245.  
  246. local keywords = {
  247.     structure = {
  248.         ['and'] = true,
  249.         ['break'] = true,
  250.         ['do'] = true,
  251.         ['else'] = true,
  252.         ['elseif'] = true,
  253.         ['end'] = true,
  254.         ['for'] = true,
  255.         ['function'] = true,
  256.         ['goto'] = true,
  257.         ['if'] = true,
  258.         ['in'] = true,
  259.         ['local'] = true,
  260.         ['not'] = true,
  261.         ['or'] = true,
  262.         ['repeat'] = true,
  263.         ['return'] = true,
  264.         ['then'] = true,
  265.         ['until'] = true,
  266.         ['while'] = true
  267.     },
  268.  
  269.     values = {
  270.         ['true'] = true,
  271.         ['false'] = true,
  272.         ['nil'] = true
  273.     }
  274. }
  275.  
  276. function lex(text)
  277.     local pos = 1
  278.     local start = 1
  279.     local len = #text
  280.     local buffer = {}
  281.     local lines = {}
  282.  
  283.     local function look(delta)
  284.         delta = pos + (delta or 0)
  285.  
  286.         return text:sub(delta, delta)
  287.     end
  288.  
  289.     local function get()
  290.         local char = text:sub(pos, pos)
  291.  
  292.         pos = pos + 1
  293.  
  294.         return char
  295.     end
  296.  
  297.     local function getLevel()
  298.         local num = 0
  299.  
  300.         while look(num) == '=' do
  301.             num = num + 1
  302.         end
  303.  
  304.         if look(num) == '[' then
  305.             pos = pos + num
  306.  
  307.             return num
  308.         else
  309.             return nil
  310.         end
  311.     end
  312.  
  313.     local function getToken()
  314.         return text:sub(start, pos - 1)
  315.     end
  316.  
  317.     local currentLineLength = 0
  318.     local lineoffset = 0
  319.  
  320.     local function token(type, text)
  321.         local tk = buffer[#buffer]
  322.  
  323.         if not tk or tk.type ~= type then
  324.             local tk = {
  325.                 type = type,
  326.                 data = text or getToken(),
  327.                 posFirst = start - lineoffset,
  328.                 posLast = pos - 1 - lineoffset
  329.             }
  330.  
  331.             if tk.data ~= '' then
  332.                 buffer[#buffer + 1] = tk
  333.             end
  334.         else
  335.             tk.data = tk.data .. (text or getToken())
  336.             tk.posLast = tk.posFirst + #tk.data - 1
  337.             --tk.posLast = getCol(pos - 1)
  338.         end
  339.  
  340.         currentLineLength = currentLineLength + (text or getToken()):len()
  341.  
  342.         start = pos
  343.         return tk
  344.     end
  345.  
  346.     local function newline()
  347.         lines[#lines + 1] = buffer
  348.         buffer = {}
  349.         get()
  350.         token('newline')
  351.         buffer[1] = nil
  352.         lineoffset = lineoffset + currentLineLength
  353.         currentLineLength = 0
  354.     end
  355.  
  356.     local function getData(level, type)
  357.         while true do
  358.             local char = get()
  359.  
  360.             if char == '' then
  361.                 return
  362.             elseif char == '\n' then
  363.                 pos = pos - 1
  364.                 token(type)
  365.                 newline()
  366.             elseif char == ']' then
  367.                 local valid = true
  368.  
  369.                 for i = 1, level do
  370.                     if look() == '=' then
  371.                         pos = pos + 1
  372.                     else
  373.                         valid = false
  374.                         break
  375.                     end
  376.                 end
  377.  
  378.                 if valid and look() == ']' then
  379.                     pos = pos + 1
  380.  
  381.                     return
  382.                 end
  383.             end
  384.         end
  385.     end
  386.  
  387.     while true do
  388.         while true do
  389.             local char = look()
  390.  
  391.             if char == '\n' then
  392.                 token('whitespace')
  393.                 newline()
  394.             elseif chars.whitespace[char] then
  395.                 pos = pos + 1
  396.             else
  397.                 break
  398.             end
  399.         end
  400.  
  401.         token('whitespace')
  402.  
  403.         local char = get()
  404.  
  405.         if char == '' then
  406.             break
  407.         elseif char == '-' and look() == '-' then
  408.             pos = pos + 1
  409.  
  410.             if look() == '[' then
  411.                 pos = pos + 1
  412.  
  413.                 local level = getLevel()
  414.  
  415.                 if level then
  416.                     getData(level, 'comment')
  417.                 else
  418.                     while true do
  419.                         local char2 = get()
  420.  
  421.                         if char2 == '' or char2 == '\n' then
  422.                             pos = pos - 1
  423.                             token('comment')
  424.  
  425.                             if char2 == '\n' then
  426.                                 newline()
  427.                             end
  428.  
  429.                             break
  430.                         end
  431.                     end
  432.                 end
  433.             else
  434.                 while true do
  435.                     local char2 = get()
  436.  
  437.                     if char2 == '' or char2 == '\n' then
  438.                         pos = pos - 1
  439.                         token('comment')
  440.  
  441.                         if char2 == '\n' then
  442.                             newline()
  443.                         end
  444.  
  445.                         break
  446.                     end
  447.                 end
  448.             end
  449.  
  450.             token('comment')
  451.         elseif char == '\'' or char == '"' then
  452.             while true do
  453.                 local char2 = get()
  454.  
  455.                 if char2 == '\\' then
  456.                     pos = pos - 1
  457.                     token('string')
  458.                     get()
  459.  
  460.                     local char3 = get()
  461.  
  462.                     if chars.digits[char3] then
  463.                         for i = 1, 2 do
  464.                             if chars.digits[look()] then
  465.                                 pos = pos + 1
  466.                             end
  467.                         end
  468.                     elseif char3 == 'x' then
  469.                         if chars.digits.hex[look()] and chars.digits.hex[look(1)] then
  470.                             pos = pos + 2
  471.                         else
  472.                             token('unidentified')
  473.                         end
  474.                     elseif char3 == '\n' then
  475.                         pos = pos - 1
  476.                         token('escape')
  477.                         newline()
  478.                     elseif not chars.validEscapes[char3] then
  479.                         token('unidentified')
  480.                     end
  481.  
  482.                     token('escape')
  483.                 elseif char2 == '\n' then
  484.                     pos = pos - 1
  485.                     token('string')
  486.                     newline()
  487.  
  488.                     break
  489.                 elseif char2 == char or char2 == '' then
  490.                     break
  491.                 end
  492.             end
  493.  
  494.             token('string')
  495.         elseif chars.ident.start[char] then
  496.             while chars.ident[look()] do
  497.                 pos = pos + 1
  498.             end
  499.  
  500.             local word = getToken()
  501.  
  502.             if keywords.structure[word] then
  503.                 token('keyword')
  504.             elseif keywords.values[word] then
  505.                 token('value')
  506.             else
  507.                 token('ident')
  508.             end
  509.         elseif chars.digits[char] or (char == '.' and chars.digits[look()]) then
  510.             if char == '0' and look() == 'x' then
  511.                 pos = pos + 1
  512.  
  513.                 while chars.digits.hex[look()] do
  514.                     pos = pos + 1
  515.                 end
  516.             else
  517.                 while chars.digits[look()] do
  518.                     pos = pos + 1
  519.                 end
  520.  
  521.                 if look() == '.' then
  522.                     pos = pos + 1
  523.  
  524.                     while chars.digits[look()] do
  525.                         pos = pos + 1
  526.                     end
  527.                 end
  528.  
  529.                 if look():lower() == 'e' then
  530.                     pos = pos + 1
  531.  
  532.                     if look() == '-' then
  533.                         pos = pos + 1
  534.                     end
  535.  
  536.                     while chars.digits[look()] do
  537.                         pos = pos + 1
  538.                     end
  539.                 end
  540.             end
  541.  
  542.             token('number')
  543.         elseif char == '[' then
  544.             local level = getLevel()
  545.  
  546.             if level then
  547.                 getData(level, 'string')
  548.                 token('string')
  549.             else
  550.                 token('symbol')
  551.             end
  552.         elseif char == '.' then
  553.             if look() == '.' then
  554.                 pos = pos + 1
  555.  
  556.                 if look() == '.' then
  557.                     pos = pos + 1
  558.                 end
  559.             end
  560.  
  561.             token('symbol')
  562.         elseif chars.symbols.equality[char] then
  563.             if look() == '=' then
  564.                 pos = pos + 1
  565.             end
  566.  
  567.             token('operator')
  568.         elseif chars.symbols[char] then
  569.             if chars.symbols.operators[char] then
  570.                 token('operator')
  571.             else
  572.                 token('symbol')
  573.             end
  574.         else
  575.             token('unidentified')
  576.         end
  577.     end
  578.  
  579.     lines[#lines + 1] = buffer
  580.  
  581.     return lines
  582. end
  583.  
  584. return lex
RAW Paste Data Copied