Advertisement
asianhaydenxd

cc-lorth

Jan 30th, 2022 (edited)
292
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 23.18 KB | None | 0 0
  1. local file_name = arg[1]
  2. local script = assert(io.open(file_name, "rb")):read("*all")
  3.  
  4. local function cc_parse(token, tokens)
  5.     local function push_token(item) table.insert(tokens, item) end
  6. end
  7.  
  8. local function cc_compile(token, stack)
  9.     local function push(item) table.insert(stack, item) end
  10.     local function pop(item) table.remove(stack, #stack) end
  11. end
  12.  
  13. local function remove_comments(text)
  14.     local commentless_text = {}
  15.     for line in text:gmatch("[^\r\n]+") do
  16.         local parts = {}
  17.         for str in line:gmatch("[^#]+") do
  18.             table.insert(parts, str)
  19.         end
  20.         table.insert(commentless_text, parts[1])
  21.     end
  22.     return table.concat(commentless_text, "\n")
  23. end
  24.  
  25. local function raise(message, index)
  26.     print("Exception raised: "..message.." (index "..index..")")
  27.     os.exit()
  28. end
  29.  
  30. local function split(text) -- Credit: Paul Kulchenko (stackoverflow)
  31.     -- Split text using whitespace but keep single and double quotes intact
  32.     text = remove_comments(text)
  33.     local split_text = {}
  34.     local spat, epat, buf, quoted = [=[^(['"])]=], [=[(['"])$]=], nil, nil
  35.    for str in text:gmatch("%S+") do
  36.        local squoted = str:match(spat)
  37.        local equoted = str:match(epat)
  38.        local escaped = str:match([=[(\*)['"]$]=])
  39.         if squoted and not quoted and not equoted then
  40.             buf, quoted = str, squoted
  41.         elseif buf and equoted == quoted and #escaped % 2 == 0 then
  42.             str, buf, quoted = buf .. ' ' .. str, nil, nil
  43.         elseif buf then
  44.             buf = buf .. ' ' .. str
  45.         end
  46.         if not buf then
  47.             table.insert(split_text, str)
  48.         end
  49.     end
  50.     if buf then
  51.         raise("missing matching quote for "..buf, #split_text+1)
  52.         return nil
  53.     end
  54.     return split_text
  55. end
  56.  
  57. local function parse(code)
  58.     local tokens = {}
  59.     local functs = {}
  60.     local consts = {}
  61.     local params = {}
  62.  
  63.     code = split(code)
  64.    
  65.     local function is_within(token, a, b)
  66.         return token:sub(1, 1) == a and token:sub(#token) == b
  67.     end
  68.  
  69.     local function push(token)
  70.         table.insert(tokens, token)
  71.     end
  72.  
  73.     local index = 0
  74.     while index < #code do
  75.         index = index + 1
  76.         local token = code[index]
  77.         if token == "require" then
  78.             index = index + 1
  79.            
  80.             local req_name = code[index]
  81.             local req_script
  82.  
  83.             if not pcall(function ()
  84.                 req_script = assert(io.open(req_name..".lorth", "rb")):read("*all")
  85.             end) then
  86.                 raise("invalid file name for require", index)
  87.             end
  88.  
  89.             local req_alias = req_name
  90.             if code[index + 1] == "as" then
  91.                 index = index + 2
  92.                 req_alias = code[index]
  93.             end
  94.  
  95.             local parsed_script = parse(req_script)
  96.  
  97.             for index, new_token in ipairs(split(req_script)) do
  98.                 local t = {}
  99.                 for str in string.gmatch(parsed_script[index], "([^:]+)") do
  100.                     table.insert(t, str)
  101.                 end
  102.                 local req_token = t[1]
  103.                 if req_token == "FUNCT_NAME"
  104.                    or req_token == "CALL_FUNCT"
  105.                    or req_token == "CONST_NAME"
  106.                    or req_token == "CALL_CONST" then
  107.                     table.insert(code, req_alias..":"..new_token)
  108.                 else
  109.                     table.insert(code, new_token)
  110.                 end
  111.             end
  112.         end
  113.     end
  114.  
  115.     index = 0
  116.     while index < #code do
  117.         index = index + 1
  118.         local token = code[index]
  119.         if token == "funct" then
  120.             index = index + 1
  121.             functs[code[index]] = true
  122.             while code[index+1] ~= "do" do
  123.                 index = index + 1
  124.             end
  125.  
  126.         elseif token == "const" then
  127.             index = index + 1
  128.             consts[code[index]] = true
  129.         end
  130.     end
  131.  
  132.     index = 0
  133.     while index < #code do
  134.         index = index + 1
  135.         local token = code[index]
  136.        
  137.         -- Data types
  138.         if is_within(token, [["]], [["]]) or is_within(token, [[']], [[']]) then
  139.             push("OP_PUSH_STR:"..token:sub(2, #token-1))
  140.  
  141.         elseif tonumber(token) then
  142.             push("OP_PUSH_NUM:"..token)
  143.  
  144.         elseif token == "true" then
  145.             push("OP_PUSH_BOOL:true")
  146.  
  147.         elseif token == "false" then
  148.             push("OP_PUSH_BOOL:false")
  149.  
  150.         -- Arithmetic operators
  151.         elseif token == "+" then
  152.             push("OP_ADD")
  153.  
  154.         elseif token == "-" then
  155.             push("OP_SUBTRACT")
  156.  
  157.         elseif token == "*" then
  158.             push("OP_MULTIPLY")
  159.  
  160.         elseif token == "/" then
  161.             push("OP_DIVIDE")
  162.  
  163.         elseif token == "%" then
  164.             push("OP_MODULUS")
  165.        
  166.         -- Comparison operators
  167.         elseif token == "=" then
  168.             push("OP_EQUAL")
  169.  
  170.         elseif token == "!=" then
  171.             push("OP_NOT_EQUAL")
  172.        
  173.         elseif token == ">" then
  174.             push("OP_GREATER_THAN")
  175.        
  176.         elseif token == "<" then
  177.             push("OP_LESS_THAN")
  178.        
  179.         elseif token == ">=" then
  180.             push("OP_GREATER_EQUAL")
  181.        
  182.         elseif token == "<=" then
  183.             push("OP_LESS_EQUAL")
  184.  
  185.         -- Logical operators
  186.         elseif token == "and" then
  187.             push("OP_AND")
  188.        
  189.         elseif token == "or" then
  190.             push("OP_OR")
  191.        
  192.         elseif token == "not" then
  193.             push("OP_NOT")
  194.        
  195.         -- Keywords
  196.         elseif token == "print" then
  197.             push("PRINT")
  198.  
  199.         elseif token == "read" then
  200.             push("READ")
  201.  
  202.         elseif token == "printall" then
  203.             push("PRINT_STACK")
  204.  
  205.         elseif token == "stacklen" then
  206.             push("PUSH_STACK_LENGTH")
  207.  
  208.         elseif token == "type" then
  209.             push("TYPE")
  210.  
  211.         elseif token == "assert" then
  212.             push("ASSERT")
  213.  
  214.         elseif token == "dup" then
  215.             push("DUPLICATE")
  216.  
  217.         elseif token == "del" then
  218.             push("REMOVE")
  219.        
  220.         elseif token == "if" then
  221.             push("IF")
  222.        
  223.         elseif token == "then" then
  224.             push("THEN")
  225.        
  226.         elseif token == "else" then
  227.             push("ELSE")
  228.  
  229.         elseif token == "elif" then
  230.             push("ELIF")
  231.        
  232.         elseif token == "while" then
  233.             push("WHILE")
  234.  
  235.         elseif token == "const" then
  236.             push("CONST")
  237.             index = index + 1
  238.             push("CONST_NAME:"..code[index])
  239.        
  240.         elseif token == "funct" then
  241.             push("FUNCT")
  242.             index = index + 1
  243.             push("FUNCT_NAME:"..code[index])
  244.             while code[index+1] ~= "do" do
  245.                 index = index + 1
  246.                 push("PARAM:"..code[index])
  247.                 params[code[index]] = true
  248.             end
  249.        
  250.         elseif token == "let" then
  251.             push("LET")
  252.             local init_index = index -- for debugging
  253.             while code[index+1] ~= "do" do
  254.                 index = index + 1
  255.                 if code[index] == nil then raise("let does not have closing do", init_index) end
  256.                 push("PARAM:"..code[index])
  257.                 params[code[index]] = true
  258.             end
  259.        
  260.         elseif token == "peek" then
  261.             push("PEEK")
  262.             local init_index = index -- for debugging
  263.             while code[index+1] ~= "do" do
  264.                 index = index + 1
  265.                 if code[index] == nil then raise("peek does not have closing do", init_index) end
  266.                 push("PARAM:"..code[index])
  267.                 params[code[index]] = true
  268.             end
  269.        
  270.         elseif token == "do" then
  271.             push("DO")
  272.  
  273.         elseif token == "end" then
  274.             local temp_i = index -- Temporary index
  275.             local nesting = 0 -- Unmatched bracket counter; workaround for nesting
  276.             while true do
  277.                 temp_i = temp_i - 1
  278.                 local ctk = tokens[temp_i]
  279.  
  280.                 if ctk == "END" then
  281.                     nesting = nesting + 1
  282.  
  283.                 elseif ctk == "DO" or ctk == "THEN" or ctk == "ELSE" or ctk == "CONST" then
  284.                     if nesting == 0 then
  285.                         break
  286.                     else
  287.                         nesting = nesting - 1
  288.                     end
  289.                
  290.                 -- To destroy params before being called outside
  291.                 elseif params[code[temp_i]] ~= nil then
  292.                     params[code[temp_i]] = nil
  293.  
  294.                 elseif ctk == nil then
  295.                     raise("unmatched end", index)
  296.                 end
  297.             end
  298.  
  299.             push("END")
  300.        
  301.         elseif token == "exit" then
  302.             push("EXIT")
  303.  
  304.         elseif token == "tokens" then
  305.             push("PRINT_TOKENS")
  306.  
  307.         elseif token == "require" then
  308.             push("REQUIRE")
  309.             index = index + 1
  310.             push("REQUIRE_FILE:"..code[index])
  311.             if code[index+1] == "as" then
  312.                 index = index + 1
  313.                 push("REQUIRE_AS")
  314.                 index = index + 1
  315.                 push("REQUIRE_ALIAS:"..code[index])
  316.             end
  317.        
  318.         elseif functs[token] ~= nil then
  319.             push("CALL_FUNCT:"..token)
  320.  
  321.         elseif consts[token] ~= nil then
  322.             push("CALL_CONST:"..token)
  323.        
  324.         elseif params[token] ~= nil then
  325.             push("PUSH_PARAM:"..token)
  326.  
  327.         else
  328.             cc_parse(token, tokens)
  329.         end
  330.     end
  331.  
  332.     return tokens
  333. end
  334.  
  335. local function compile(code)
  336.     local stack = {}
  337.     local params = {}
  338.     local function_addresses = {}
  339.     local function_calls = {}
  340.     local constant_addresses = {}
  341.     local constant_calls = {}
  342.     local tokens = parse(code)
  343.  
  344.     -- print(table.concat(tokens, " "))
  345.  
  346.     -- Hoisting functions and constants
  347.     local index = 0
  348.     while index < #tokens do
  349.         index = index + 1
  350.  
  351.         local t = {}
  352.         for str in string.gmatch(tokens[index], "([^:]+)") do
  353.             table.insert(t, str)
  354.         end
  355.         local token = t[1]
  356.         table.remove(t, 1)
  357.         local value = table.concat(t, ":")
  358.  
  359.         if token == "FUNCT_NAME" then
  360.             local init_index = index - 1
  361.             local funct_name = value
  362.             while true do
  363.                 index = index + 1
  364.                 if tokens[index] == "DO" then
  365.                     break
  366.                 end
  367.             end
  368.             function_addresses[funct_name] = init_index
  369.  
  370.         elseif token == "CONST_NAME" then
  371.             constant_addresses[value] = index
  372.         end
  373.     end
  374.  
  375.     -- Run everything else
  376.     index = 0
  377.     while index < #tokens do
  378.         index = index + 1
  379.         -- print(index)
  380.  
  381.         local t = {}
  382.         for str in string.gmatch(tokens[index], "([^:]+)") do
  383.             table.insert(t, str)
  384.         end
  385.         local token = t[1]
  386.         table.remove(t, 1)
  387.         local value = table.concat(t, ":")
  388.        
  389.         -- Data types
  390.         if token == "OP_PUSH_STR" then
  391.             table.insert(stack, value)
  392.  
  393.         elseif token == "OP_PUSH_NUM" then
  394.             table.insert(stack, tonumber(value))
  395.  
  396.         elseif token == "OP_PUSH_BOOL" then
  397.             local toboolean = {["true"]=true, ["false"]=false}
  398.             table.insert(stack, toboolean[value])
  399.  
  400.         -- Arithmetic operators
  401.         elseif token == "OP_ADD" then
  402.             local a = stack[#stack-1]
  403.             local b = stack[#stack]
  404.             table.remove(stack, #stack)
  405.             table.remove(stack, #stack)
  406.             table.insert(stack, a + b)
  407.  
  408.         elseif token == "OP_SUBTRACT" then
  409.             local a = stack[#stack-1]
  410.             local b = stack[#stack]
  411.             table.remove(stack, #stack)
  412.             table.remove(stack, #stack)
  413.             table.insert(stack, a - b)
  414.            
  415.         elseif token == "OP_MULTIPLY" then
  416.             local a = stack[#stack-1]
  417.             local b = stack[#stack]
  418.             table.remove(stack, #stack)
  419.             table.remove(stack, #stack)
  420.             table.insert(stack, a*b)
  421.  
  422.         elseif token == "OP_DIVIDE" then
  423.             local a = stack[#stack-1]
  424.             local b = stack[#stack]
  425.             table.remove(stack, #stack)
  426.             table.remove(stack, #stack)
  427.             table.insert(stack, a/b)
  428.        
  429.         elseif token == "OP_MODULUS" then
  430.             local a = stack[#stack-1]
  431.             local b = stack[#stack]
  432.             table.remove(stack, #stack)
  433.             table.remove(stack, #stack)
  434.             table.insert(stack, math.fmod(a, b))
  435.  
  436.         elseif token == "OP_EQUAL" then
  437.             local a = stack[#stack-1]
  438.             local b = stack[#stack]
  439.             table.remove(stack, #stack)
  440.             table.remove(stack, #stack)
  441.             table.insert(stack, a == b)
  442.        
  443.         elseif token == "OP_NOT_EQUAL" then
  444.             local a = stack[#stack-1]
  445.             local b = stack[#stack]
  446.             table.remove(stack, #stack)
  447.             table.remove(stack, #stack)
  448.             table.insert(stack, a ~= b)
  449.        
  450.         elseif token == "OP_GREATER_THAN" then
  451.             local a = stack[#stack-1]
  452.             local b = stack[#stack]
  453.             table.remove(stack, #stack)
  454.             table.remove(stack, #stack)
  455.             table.insert(stack, a > b)
  456.  
  457.         elseif token == "OP_LESS_THAN" then
  458.             local a = stack[#stack-1]
  459.             local b = stack[#stack]
  460.             table.remove(stack, #stack)
  461.             table.remove(stack, #stack)
  462.             table.insert(stack, a < b)
  463.  
  464.         elseif token == "OP_GREATER_EQUAL" then
  465.             local a = stack[#stack-1]
  466.             local b = stack[#stack]
  467.             table.remove(stack, #stack)
  468.             table.remove(stack, #stack)
  469.             table.insert(stack, a >= b)
  470.        
  471.         elseif token == "OP_LESS_EQUAL" then
  472.             local a = stack[#stack-1]
  473.             local b = stack[#stack]
  474.             table.remove(stack, #stack)
  475.             table.remove(stack, #stack)
  476.             table.insert(stack, a <= b)
  477.  
  478.         -- Logical operators
  479.         elseif token == "OP_AND" then
  480.             local a = stack[#stack-1]
  481.             local b = stack[#stack]
  482.             table.remove(stack, #stack)
  483.             table.remove(stack, #stack)
  484.             table.insert(stack, a and b)
  485.  
  486.         elseif token == "OP_OR" then
  487.             local a = stack[#stack-1]
  488.             local b = stack[#stack]
  489.             table.remove(stack, #stack)
  490.             table.remove(stack, #stack)
  491.             table.insert(stack, a or b)
  492.  
  493.         elseif token == "OP_NOT" then
  494.             local a = stack[#stack]
  495.             table.remove(stack, #stack)
  496.             table.insert(stack, not a)
  497.  
  498.         -- Keywords
  499.         elseif token == "PRINT" then
  500.             if #stack == 0 then raise("cannot interpret empty stack", index) end
  501.             print(stack[#stack])
  502.             table.remove(stack, #stack)
  503.        
  504.         elseif token == "PRINT_STACK" then
  505.             local out = ""
  506.             for address, element in ipairs(stack) do
  507.                 if address ~= 1 then
  508.                     out = out..", "
  509.                 end
  510.                 out = out..element
  511.             end
  512.             print(out)
  513.  
  514.         elseif token == "TYPE" then
  515.             local item = stack[#stack]
  516.             table.remove(stack, #stack)
  517.             table.insert(stack, type(item))
  518.  
  519.         elseif token == "ASSERT" then
  520.             if stack[#stack] == false then
  521.                 raise("assertion failed", index)
  522.             end
  523.  
  524.         elseif token == "DUPLICATE" then
  525.             table.insert(stack, stack[#stack])
  526.  
  527.         elseif token == "REMOVE" then
  528.             table.remove(stack, #stack)
  529.  
  530.         elseif token == "THEN" then
  531.             local bool = stack[#stack]
  532.             table.remove(stack, #stack)
  533.             if not bool then
  534.                 local nesting = 0 -- Workaround for nesting
  535.                 while true do
  536.                     index = index + 1
  537.                     local ctk = tokens[index]
  538.  
  539.                     if ctk == "DO" or ctk == "IF" or ctk == "CONST" then
  540.                         nesting = nesting + 1
  541.  
  542.                     elseif ctk == "END" or ctk == "ELSE" or ctk == "ELIF" then
  543.                         if nesting == 0 then
  544.                             break
  545.                         else
  546.                             nesting = nesting - 1
  547.                         end
  548.                     end
  549.                 end
  550.             end
  551.  
  552.         elseif token == "ELSE" then
  553.             local nesting = 0 -- Workaround for nesting
  554.             while true do
  555.                 index = index + 1
  556.                 local ctk = tokens[index]
  557.  
  558.                 if ctk == "DO" or ctk == "IF" or ctk == "CONST" then
  559.                     nesting = nesting + 1
  560.  
  561.                 elseif ctk == "END" then
  562.                     if nesting == 0 then
  563.                         break
  564.                     else
  565.                         nesting = nesting - 1
  566.                     end
  567.                 end
  568.             end
  569.  
  570.         elseif token == "ELIF" then
  571.             local nesting = 0 -- Workaround for nesting
  572.             while true do
  573.                 index = index + 1
  574.                 local ctk = tokens[index]
  575.  
  576.                 if ctk == "DO" or ctk == "IF" or ctk == "CONST" then
  577.                     nesting = nesting + 1
  578.  
  579.                 elseif ctk == "END" then
  580.                     if nesting == 0 then
  581.                         break
  582.                     else
  583.                         nesting = nesting - 1
  584.                     end
  585.                 end
  586.             end
  587.  
  588.         elseif token == "DO" then -- only ever called on WHILE
  589.             local bool = stack[#stack]
  590.             table.remove(stack, #stack)
  591.             if not bool then
  592.                 local nesting = 0 -- Workaround for nesting
  593.                 while true do
  594.                     index = index + 1
  595.                     local ctk = tokens[index]
  596.  
  597.                     if ctk == "DO" or ctk == "IF" or ctk == "CONST" then
  598.                         nesting = nesting + 1
  599.  
  600.                     elseif ctk == "END" then
  601.                         if nesting == 0 then
  602.                             break
  603.                         else
  604.                             nesting = nesting - 1
  605.                         end
  606.                     end
  607.                 end
  608.             end
  609.  
  610.         elseif token == "END" then
  611.             local temp_i = index
  612.             local nesting = 0 -- Workaround for nesting
  613.             while true do
  614.                 temp_i = temp_i - 1
  615.                 local ctk = tokens[temp_i]
  616.  
  617.                 if ctk == "END" then
  618.                     nesting = nesting + 1
  619.  
  620.                 elseif ctk == "WHILE" or ctk == "FUNCT" or ctk == "LET" or ctk == "PEEK" or ctk == "IF" or ctk == "CONST" then
  621.                     if nesting == 0 then
  622.                         if ctk == "WHILE" then
  623.                             index = temp_i
  624.                         end
  625.                         if ctk == "FUNCT" then
  626.                             index = function_calls[#function_calls]
  627.                             table.remove(function_calls, #function_calls)
  628.                         end
  629.                         if ctk == "CONST" then
  630.                             index = constant_calls[#constant_calls]
  631.                             table.remove(constant_calls, #constant_calls)
  632.                         end
  633.                         break
  634.                     else
  635.                         nesting = nesting - 1
  636.                     end
  637.                 end
  638.             end
  639.  
  640.         elseif token == "CONST" then
  641.             local nesting = 0 -- Workaround for nesting
  642.             while true do
  643.                 index = index + 1
  644.                 local ctk = tokens[index]
  645.  
  646.                 if ctk == "DO" or ctk == "IF" or ctk == "CONST" then
  647.                     nesting = nesting + 1
  648.  
  649.                 elseif ctk == "END" then
  650.                     if nesting == 0 then
  651.                         break
  652.                     else
  653.                         nesting = nesting - 1
  654.                     end
  655.                 end
  656.             end
  657.  
  658.         elseif token == "CALL_CONST" then
  659.             table.insert(constant_calls, index)
  660.             index = constant_addresses[value]
  661.  
  662.         elseif token == "FUNCT" then
  663.             index = index + 1
  664.             while true do
  665.                 index = index + 1
  666.                 if tokens[index] == "DO" then
  667.                     break
  668.                 end
  669.             end
  670.             local nesting = 0 -- Workaround for nesting
  671.             while true do
  672.                 index = index + 1
  673.                 local ctk = tokens[index]
  674.  
  675.                 if ctk == "DO" or ctk == "IF" or ctk == "CONST" then
  676.                     nesting = nesting + 1
  677.  
  678.                 elseif ctk == "END" then
  679.                     if nesting == 0 then
  680.                         break
  681.                     else
  682.                         nesting = nesting - 1
  683.                     end
  684.                 end
  685.             end
  686.        
  687.         elseif token == "CALL_FUNCT" then
  688.             table.insert(function_calls, index)
  689.             index = function_addresses[value]
  690.             local unordered_params = {}
  691.             while true do
  692.                 index = index + 1
  693.                
  694.                 if tokens[index] == "DO" then
  695.                     break
  696.                 else
  697.                     table.insert(unordered_params, tokens[index])
  698.                 end
  699.             end
  700.             for i = #unordered_params, 1, -1 do
  701.                 params[unordered_params[i]:sub(7)] = stack[#stack]
  702.                 table.remove(stack, #stack)
  703.             end
  704.  
  705.         elseif token == "LET" then
  706.             local unordered_params = {}
  707.             while true do
  708.                 index = index + 1
  709.                
  710.                 if tokens[index] == "DO" then
  711.                     break
  712.                 else
  713.                     table.insert(unordered_params, tokens[index])
  714.                 end
  715.             end
  716.             for i = #unordered_params, 1, -1 do
  717.                 params[unordered_params[i]:sub(7)] = stack[#stack]
  718.                 table.remove(stack, #stack)
  719.             end
  720.        
  721.         elseif token == "PEEK" then
  722.             local unordered_params = {}
  723.             while true do
  724.                 index = index + 1
  725.                
  726.                 if tokens[index] == "DO" then
  727.                     break
  728.                 else
  729.                     table.insert(unordered_params, tokens[index])
  730.                 end
  731.             end
  732.             for i = #unordered_params, 1, -1 do
  733.                 params[unordered_params[i]:sub(7)] = stack[#stack-#unordered_params+i]
  734.             end
  735.  
  736.         elseif token == "EXIT" then
  737.             if stack[#stack] and stack[#stack] ~= 0 then
  738.                 raise(stack[#stack], index)
  739.             end
  740.             os.exit()
  741.  
  742.         elseif token == "PRINT_TOKENS" then
  743.             print(table.concat(tokens, " "))
  744.  
  745.         elseif token == "PUSH_PARAM" then
  746.             table.insert(stack, params[value])
  747.  
  748.         else
  749.             cc_compile(token, stack)
  750.         end
  751.     end
  752. end
  753.  
  754. compile(script)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement