Initial commit
This commit is contained in:
		
						commit
						c847863034
					
				
							
								
								
									
										7
									
								
								bon.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								bon.lua
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					local next_unique_id = 0
 | 
				
			||||||
 | 
					local function unique_name()
 | 
				
			||||||
 | 
					    next_unique_id = next_unique_id + 1
 | 
				
			||||||
 | 
					    return "__" .. "KY" .. next_unique_id
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return {unique_name = unique_name}
 | 
				
			||||||
							
								
								
									
										81
									
								
								des.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								des.lua
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,81 @@
 | 
				
			|||||||
 | 
					local unique_name = require"bon".unique_name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function pass_destandardify(chu)
 | 
				
			||||||
 | 
					    local function is_builtin_method(c)
 | 
				
			||||||
 | 
					        if c.type ~= 'dot' then
 | 
				
			||||||
 | 
					            return false
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if c.a.et and c.a.et.type == 'list' and c.b == 'pop' then
 | 
				
			||||||
 | 
					            return {change_stmt = function(stmt)
 | 
				
			||||||
 | 
					                assert(stmt.type == 'call')
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                local temp = {et = stmt.what.a.et, name = unique_name()}
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                return {
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        type = 'let',
 | 
				
			||||||
 | 
					                        var = temp,
 | 
				
			||||||
 | 
					                        expr = stmt.what.a
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        type = 'assign',
 | 
				
			||||||
 | 
					                        dest = {
 | 
				
			||||||
 | 
					                            type = 'index',
 | 
				
			||||||
 | 
					                            what = {type = 'var', which = temp, et = temp.et},
 | 
				
			||||||
 | 
					                            idx = {type = 'lengthof', sub = {type = 'var', which = temp, et = temp.et}, et = {type = 'integer'}}
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                        src = {type = 'null', et = {type = 'null'}}
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            end}
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        return false
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    if chu.type == "chunk" then
 | 
				
			||||||
 | 
					        for k, t in pairs(chu.types) do
 | 
				
			||||||
 | 
					            for _, m in pairs(t.members) do
 | 
				
			||||||
 | 
					                if m.type == "field" then
 | 
				
			||||||
 | 
					                    -- Do nothing.
 | 
				
			||||||
 | 
					                elseif m.type == "static" then
 | 
				
			||||||
 | 
					                    pass_destandardify(m.value)
 | 
				
			||||||
 | 
					                else
 | 
				
			||||||
 | 
					                    error("Invalid AST")
 | 
				
			||||||
 | 
					                end
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        local stmtIdx = 1
 | 
				
			||||||
 | 
					        while stmtIdx <= #chu.stmts do
 | 
				
			||||||
 | 
					            local stmt = chu.stmts[stmtIdx]
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            local builtinmethod = stmt.type == 'call' and is_builtin_method(stmt.what)
 | 
				
			||||||
 | 
					            if builtinmethod then
 | 
				
			||||||
 | 
					                table.remove(chu.stmts, stmtIdx)
 | 
				
			||||||
 | 
					                for p, o in ipairs(builtinmethod.change_stmt(stmt)) do
 | 
				
			||||||
 | 
					                    table.insert(chu.stmts, stmtIdx + p - 1, o)
 | 
				
			||||||
 | 
					                end
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					                pass_destandardify(stmt)
 | 
				
			||||||
 | 
					                stmtIdx = stmtIdx + 1
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					    elseif chu.type == 'function' then
 | 
				
			||||||
 | 
					        pass_destandardify(chu.chunk)
 | 
				
			||||||
 | 
					    elseif chu.type == 'if' then
 | 
				
			||||||
 | 
					        for i = 1, #chu do
 | 
				
			||||||
 | 
					            pass_destandardify(chu[i].chu)
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					        if chu.elsa then
 | 
				
			||||||
 | 
					            pass_destandardify(chu.elsa)
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					    elseif chu.type == 'fori' then
 | 
				
			||||||
 | 
					        pass_destandardify(chu.chu)
 | 
				
			||||||
 | 
					    elseif chu.type == 'loop' then
 | 
				
			||||||
 | 
					        pass_destandardify(chu.chu)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return pass_destandardify
 | 
				
			||||||
							
								
								
									
										224
									
								
								gen.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										224
									
								
								gen.lua
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,224 @@
 | 
				
			|||||||
 | 
					return function(ast, out)
 | 
				
			||||||
 | 
					    local function compile(chu)
 | 
				
			||||||
 | 
					        if chu.type == "chunk" then
 | 
				
			||||||
 | 
					            if chu.toplevel then
 | 
				
			||||||
 | 
					                for k, import in pairs(chu.imports) do
 | 
				
			||||||
 | 
					                    out("local %s = require(%q)\n", import[#import], table.concat(import, "."))
 | 
				
			||||||
 | 
					                end
 | 
				
			||||||
 | 
					                for k, var in pairs(chu.vars) do
 | 
				
			||||||
 | 
					                    out("local %s\n", k)
 | 
				
			||||||
 | 
					                end
 | 
				
			||||||
 | 
					                for k, expr in pairs(chu.vars) do
 | 
				
			||||||
 | 
					                    if expr then
 | 
				
			||||||
 | 
					                        out("%s=", k)
 | 
				
			||||||
 | 
					                        compile(expr)
 | 
				
			||||||
 | 
					                        out(";")
 | 
				
			||||||
 | 
					                    end
 | 
				
			||||||
 | 
					                end
 | 
				
			||||||
 | 
					                for k, t in pairs(chu.types) do
 | 
				
			||||||
 | 
					                    out("local %s={}%s.__index=%s;setmetatable(%s, %s)", k, k, k, k, k)
 | 
				
			||||||
 | 
					                    for _, m in pairs(t.members) do
 | 
				
			||||||
 | 
					                        if m.type == "field" then
 | 
				
			||||||
 | 
					                            -- Do nothing.
 | 
				
			||||||
 | 
					                        elseif m.type == "static" then
 | 
				
			||||||
 | 
					                            out("%s.%s=", k, m.name)
 | 
				
			||||||
 | 
					                            compile(m.value)
 | 
				
			||||||
 | 
					                            out(";")
 | 
				
			||||||
 | 
					                        else
 | 
				
			||||||
 | 
					                            error("Invalid AST")
 | 
				
			||||||
 | 
					                        end
 | 
				
			||||||
 | 
					                    end
 | 
				
			||||||
 | 
					                end
 | 
				
			||||||
 | 
					                out("return{")
 | 
				
			||||||
 | 
					                for k,v in pairs(chu.vars) do
 | 
				
			||||||
 | 
					                    out("%s=%s,", k, k)
 | 
				
			||||||
 | 
					                end
 | 
				
			||||||
 | 
					                for k,v in pairs(chu.types) do
 | 
				
			||||||
 | 
					                    out("%s=%s,", k, k)
 | 
				
			||||||
 | 
					                end
 | 
				
			||||||
 | 
					                out("}")
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					                for k, stmt in ipairs(chu.stmts) do
 | 
				
			||||||
 | 
					                    compile(stmt)
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    if stmt.type == 'call' then
 | 
				
			||||||
 | 
					                        out(";") --Safety delimiter.
 | 
				
			||||||
 | 
					                    end
 | 
				
			||||||
 | 
					                end
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					        elseif chu.type == "function" then
 | 
				
			||||||
 | 
					            out("function(%s%s)", chu.is_constructor and "_," or "", table.concat(chu.args, ","))
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if SAFE_MODE then
 | 
				
			||||||
 | 
					                for k, argname in pairs(chu.args) do
 | 
				
			||||||
 | 
					                    local et = chu.et.args[k]
 | 
				
			||||||
 | 
					                    if et then
 | 
				
			||||||
 | 
					                        local checks = {}
 | 
				
			||||||
 | 
					                        if et.type == 'integer' or et.type == 'number' or et.type == 'string' or et.type == 'boolean' then
 | 
				
			||||||
 | 
					                            if et.type == 'integer' then
 | 
				
			||||||
 | 
					                                table.insert(checks, 'type(' .. argname .. ')=="number"')
 | 
				
			||||||
 | 
					                                table.insert(checks, argname .. '%1==0')
 | 
				
			||||||
 | 
					                            else
 | 
				
			||||||
 | 
					                                table.insert(checks, 'type(' .. argname .. ')=="'..et.type..'"')
 | 
				
			||||||
 | 
					                            end
 | 
				
			||||||
 | 
					                        elseif et.type == 'list' then
 | 
				
			||||||
 | 
					                            table.insert(checks, 'type(' .. argname .. ')=="table"')
 | 
				
			||||||
 | 
					                        end
 | 
				
			||||||
 | 
					                        if #checks > 0 then
 | 
				
			||||||
 | 
					                            out("assert(%s, %q);", table.concat(checks, " and "), string.format("Invalid argument %q", argname))
 | 
				
			||||||
 | 
					                        end
 | 
				
			||||||
 | 
					                    end
 | 
				
			||||||
 | 
					                end
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if chu.is_constructor then
 | 
				
			||||||
 | 
					                out("local self=setmetatable({}, %s);", chu.et.ret.name)
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            compile(chu.chunk)
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if chu.is_constructor then
 | 
				
			||||||
 | 
					                out("return self;")
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            out("end")
 | 
				
			||||||
 | 
					        elseif chu.type == "let" then
 | 
				
			||||||
 | 
					            out("local %s", chu.var.name)
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if chu.expr then
 | 
				
			||||||
 | 
					                out("=")
 | 
				
			||||||
 | 
					                compile(chu.expr)
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            out(";")
 | 
				
			||||||
 | 
					        elseif chu.type == "num" then
 | 
				
			||||||
 | 
					            out("%g", chu.value)
 | 
				
			||||||
 | 
					        elseif chu.type == "var" then
 | 
				
			||||||
 | 
					            out("%s", chu.which.name)
 | 
				
			||||||
 | 
					        elseif chu.type == "binop" then
 | 
				
			||||||
 | 
					            local paren
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            paren = chu.a.type == "binop" and chu.a.level > chu.level
 | 
				
			||||||
 | 
					            if paren then out("(") end
 | 
				
			||||||
 | 
					            compile(chu.a)
 | 
				
			||||||
 | 
					            if paren then out(")") end
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            local op = chu.op
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if op == "**" then
 | 
				
			||||||
 | 
					                op = "^"
 | 
				
			||||||
 | 
					            elseif op == "/" and (not chu.a.et or chu.a.et.type == 'integer') and (not chu.b.et or chu.b.et.type == 'integer') then
 | 
				
			||||||
 | 
					                op = "//"
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            out("%s", op)
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            paren = chu.b.type == "binop" and chu.b.level > chu.level
 | 
				
			||||||
 | 
					            if paren then out("(") end
 | 
				
			||||||
 | 
					            compile(chu.b)
 | 
				
			||||||
 | 
					            if paren then out(")") end
 | 
				
			||||||
 | 
					        elseif chu.type == "lengthof" then
 | 
				
			||||||
 | 
					            out("#(")
 | 
				
			||||||
 | 
					            compile(chu.sub)
 | 
				
			||||||
 | 
					            out(")")
 | 
				
			||||||
 | 
					        elseif chu.type == "dot" then
 | 
				
			||||||
 | 
					            compile(chu.a)
 | 
				
			||||||
 | 
					            out("%s%s", chu.colon and ":" or ".", chu.b)
 | 
				
			||||||
 | 
					        elseif chu.type == "index" then
 | 
				
			||||||
 | 
					            out("(")
 | 
				
			||||||
 | 
					            compile(chu.what)
 | 
				
			||||||
 | 
					            out(")[")
 | 
				
			||||||
 | 
					            compile(chu.idx)
 | 
				
			||||||
 | 
					            out("]")
 | 
				
			||||||
 | 
					        elseif chu.type == "call" then
 | 
				
			||||||
 | 
					            local guard = chu.type == 'binop'
 | 
				
			||||||
 | 
					            if guard then
 | 
				
			||||||
 | 
					                out("(")
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					            compile(chu.what)
 | 
				
			||||||
 | 
					            if guard then
 | 
				
			||||||
 | 
					                out(")")
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					            out("(")
 | 
				
			||||||
 | 
					            for k, v in pairs(chu.args) do
 | 
				
			||||||
 | 
					                compile(v)
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                if k < #chu.args then
 | 
				
			||||||
 | 
					                    out(",")
 | 
				
			||||||
 | 
					                end
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					            out(")")
 | 
				
			||||||
 | 
					        elseif chu.type == "dict" then
 | 
				
			||||||
 | 
					            out("{")
 | 
				
			||||||
 | 
					            for k, v in pairs(chu.mappings) do
 | 
				
			||||||
 | 
					                out("[")
 | 
				
			||||||
 | 
					                compile(k)
 | 
				
			||||||
 | 
					                out("]=")
 | 
				
			||||||
 | 
					                compile(v)
 | 
				
			||||||
 | 
					                out(",")
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					            out("}")
 | 
				
			||||||
 | 
					        elseif chu.type == "list" then
 | 
				
			||||||
 | 
					            out("{")
 | 
				
			||||||
 | 
					            for k, v in pairs(chu.values) do
 | 
				
			||||||
 | 
					                out("[%i]=", k)
 | 
				
			||||||
 | 
					                compile(v)
 | 
				
			||||||
 | 
					                out(",")
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					            out("}")
 | 
				
			||||||
 | 
					        elseif chu.type == "string" then
 | 
				
			||||||
 | 
					            out("%q", chu.value)
 | 
				
			||||||
 | 
					        elseif chu.type == "null" then
 | 
				
			||||||
 | 
					            out("nil")
 | 
				
			||||||
 | 
					        elseif chu.type == "return" then
 | 
				
			||||||
 | 
					            out("return ")
 | 
				
			||||||
 | 
					            compile(chu.expr)
 | 
				
			||||||
 | 
					            out(";")
 | 
				
			||||||
 | 
					        elseif chu.type == "if" then
 | 
				
			||||||
 | 
					            out("if ")
 | 
				
			||||||
 | 
					            compile(chu[1].pred)
 | 
				
			||||||
 | 
					            out(" then ")
 | 
				
			||||||
 | 
					            compile(chu[1].chu)
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            for i = 2, #chu do
 | 
				
			||||||
 | 
					                out("elseif ")
 | 
				
			||||||
 | 
					                compile(chu[i].pred)
 | 
				
			||||||
 | 
					                out("then ")
 | 
				
			||||||
 | 
					                compile(chu[i].chu)
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if chu.elsa then
 | 
				
			||||||
 | 
					                out("else ")
 | 
				
			||||||
 | 
					                compile(chu.elsa)
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            out("end;")
 | 
				
			||||||
 | 
					        elseif chu.type == "fori" then
 | 
				
			||||||
 | 
					            out("for " .. chu.varname .. "=")
 | 
				
			||||||
 | 
					            compile(chu.from)
 | 
				
			||||||
 | 
					            out(",")
 | 
				
			||||||
 | 
					            compile(chu.to)
 | 
				
			||||||
 | 
					            out("-1 do ")
 | 
				
			||||||
 | 
					            compile(chu.chu)
 | 
				
			||||||
 | 
					            out("end ")
 | 
				
			||||||
 | 
					        elseif chu.type == "loop" then
 | 
				
			||||||
 | 
					            out("while true do ")
 | 
				
			||||||
 | 
					            compile(chu.chu)
 | 
				
			||||||
 | 
					            out("end ")
 | 
				
			||||||
 | 
					        elseif chu.type == "break" then
 | 
				
			||||||
 | 
					            out("break ")
 | 
				
			||||||
 | 
					        elseif chu.type == "noop" then
 | 
				
			||||||
 | 
					        elseif chu.type == "exprstat" then
 | 
				
			||||||
 | 
					            compile(chu.expr)
 | 
				
			||||||
 | 
					            out(";")
 | 
				
			||||||
 | 
					        elseif chu.type == "assign" then
 | 
				
			||||||
 | 
					            compile(chu.dest)
 | 
				
			||||||
 | 
					            out("=")
 | 
				
			||||||
 | 
					            compile(chu.src)
 | 
				
			||||||
 | 
					            out(";")
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            error(string.format("Invalid AST node (type %q)", chu.type))
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    return compile(ast)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
							
								
								
									
										215
									
								
								lex.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										215
									
								
								lex.lua
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,215 @@
 | 
				
			|||||||
 | 
					--[[
 | 
				
			||||||
 | 
					    Titty has two main design goals:
 | 
				
			||||||
 | 
					    1. Should look natural from the outside, Lua code's PoV, and
 | 
				
			||||||
 | 
					    2. Should compile to Lua code that also appears natural from within
 | 
				
			||||||
 | 
					    The second is less important than the first.
 | 
				
			||||||
 | 
					]]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local inspect = require"inspect".inspect
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function codepoints(str)
 | 
				
			||||||
 | 
					    local it, state, v1, v2 = utf8.codes(str)
 | 
				
			||||||
 | 
					    local idx = 0
 | 
				
			||||||
 | 
					    return function()
 | 
				
			||||||
 | 
					        if not it then return nil end
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        v1, v2 = it(state, v1)
 | 
				
			||||||
 | 
					        if v1 then
 | 
				
			||||||
 | 
					            local r = idx
 | 
				
			||||||
 | 
					            idx = idx + 1
 | 
				
			||||||
 | 
					            return r, v2
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            it = nil
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function is_digit(cp)
 | 
				
			||||||
 | 
					    return cp and (cp >= 48 and cp <= 57)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function is_ident_start(cp)
 | 
				
			||||||
 | 
					    return cp and ((cp >= 65 and cp <= 90) or (cp >= 97 and cp <= 122) or cp == 95)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function is_ident_nonstart(cp)
 | 
				
			||||||
 | 
					    return cp and (is_ident_start(cp) or is_digit(cp))
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function forall(tbl, f, startI)
 | 
				
			||||||
 | 
					    startI = startI or 1
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    for k, v in pairs(tbl) do
 | 
				
			||||||
 | 
					        if not f(v) then
 | 
				
			||||||
 | 
					            return false
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    return true
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function is_whitespace(cp)
 | 
				
			||||||
 | 
					    return cp and (cp == 32 or cp == 10 or cp == 9)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function clear(tbl)
 | 
				
			||||||
 | 
					    for k in pairs(tbl) do
 | 
				
			||||||
 | 
					        tbl[k] = nil
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function lex(cpgetraw)
 | 
				
			||||||
 | 
					    local row, column, idx = 0, 0, 0
 | 
				
			||||||
 | 
					    local pull
 | 
				
			||||||
 | 
					    local function cpget()
 | 
				
			||||||
 | 
					        if pull then
 | 
				
			||||||
 | 
					            local ret = pull
 | 
				
			||||||
 | 
					            pull = nil
 | 
				
			||||||
 | 
					            return ret
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            local cp
 | 
				
			||||||
 | 
					            idx, cp = cpgetraw()
 | 
				
			||||||
 | 
					            column = column + 1
 | 
				
			||||||
 | 
					            if cp == 10 then
 | 
				
			||||||
 | 
					                column = 0
 | 
				
			||||||
 | 
					                row = row + 1
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					            return cp
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    local buf = {}
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    return function()
 | 
				
			||||||
 | 
					        local cp
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        while true do
 | 
				
			||||||
 | 
					            cp = cpget()
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if not is_whitespace(cp) then
 | 
				
			||||||
 | 
					                break
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if not cp then
 | 
				
			||||||
 | 
					            return nil
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        local rowStart, columnStart = row, column
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if cp == 40 then
 | 
				
			||||||
 | 
					            return "(", "(", rowStart, columnStart
 | 
				
			||||||
 | 
					        elseif cp == 41 then
 | 
				
			||||||
 | 
					            return ")", ")", rowStart, columnStart
 | 
				
			||||||
 | 
					        elseif cp == 123 then
 | 
				
			||||||
 | 
					            return "{", "{", rowStart, columnStart
 | 
				
			||||||
 | 
					        elseif cp == 125 then
 | 
				
			||||||
 | 
					            return "}", "}", rowStart, columnStart
 | 
				
			||||||
 | 
					        elseif cp == 44 then
 | 
				
			||||||
 | 
					            return ",", ",", rowStart, columnStart
 | 
				
			||||||
 | 
					        elseif cp == 46 then
 | 
				
			||||||
 | 
					            return ".", ".", rowStart, columnStart
 | 
				
			||||||
 | 
					        elseif cp == 58 then
 | 
				
			||||||
 | 
					            return ":", ":", rowStart, columnStart
 | 
				
			||||||
 | 
					        elseif cp == 61 then
 | 
				
			||||||
 | 
					            local after = cpget()
 | 
				
			||||||
 | 
					            if after == 61 then
 | 
				
			||||||
 | 
					                return "==", "==", rowStart, columnStart
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					                pull = after
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					            return "=", "=", rowStart, columnStart
 | 
				
			||||||
 | 
					        elseif cp == 43 then
 | 
				
			||||||
 | 
					            return "+", "+", rowStart, columnStart
 | 
				
			||||||
 | 
					        elseif cp == 45 then
 | 
				
			||||||
 | 
					            return "-", "-", rowStart, columnStart
 | 
				
			||||||
 | 
					        elseif cp == 42 then
 | 
				
			||||||
 | 
					            local after = cpget()
 | 
				
			||||||
 | 
					            if after == 42 then
 | 
				
			||||||
 | 
					                return "**", "**", rowStart, columnStart
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					                pull = after
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					            return "*", "*", rowStart, columnStart
 | 
				
			||||||
 | 
					        elseif cp == 47 then
 | 
				
			||||||
 | 
					            return "/", "/", rowStart, columnStart
 | 
				
			||||||
 | 
					        elseif cp == 37 then
 | 
				
			||||||
 | 
					            return "%", "%", rowStart, columnStart
 | 
				
			||||||
 | 
					        elseif cp == 35 then
 | 
				
			||||||
 | 
					            return "#", "#", rowStart, columnStart
 | 
				
			||||||
 | 
					        elseif cp == 91 then
 | 
				
			||||||
 | 
					            return "[", "[", rowStart, columnStart
 | 
				
			||||||
 | 
					        elseif cp == 93 then
 | 
				
			||||||
 | 
					            return "]", "]", rowStart, columnStart
 | 
				
			||||||
 | 
					        elseif cp == 39 then
 | 
				
			||||||
 | 
					            while true do
 | 
				
			||||||
 | 
					                cp = cpget()
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                if cp ~= 39 then
 | 
				
			||||||
 | 
					                    buf[#buf + 1] = cp
 | 
				
			||||||
 | 
					                else
 | 
				
			||||||
 | 
					                    break
 | 
				
			||||||
 | 
					                end
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            local ret = utf8.char(table.unpack(buf))
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            clear(buf)
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            return "string", ret, rowStart, columnStart
 | 
				
			||||||
 | 
					        elseif is_ident_start(cp) then
 | 
				
			||||||
 | 
					            buf[1] = cp
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            while true do
 | 
				
			||||||
 | 
					                cp = cpget()
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                if is_ident_nonstart(cp) then
 | 
				
			||||||
 | 
					                    buf[#buf + 1] = cp
 | 
				
			||||||
 | 
					                else
 | 
				
			||||||
 | 
					                    pull = cp
 | 
				
			||||||
 | 
					                    break
 | 
				
			||||||
 | 
					                end
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            local ret = utf8.char(table.unpack(buf))
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            clear(buf)
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if ret == "func" or ret == "end" or ret == "if"
 | 
				
			||||||
 | 
					                or ret == "while" or ret == "do" or ret == "then"
 | 
				
			||||||
 | 
					                or ret == "elseif" or ret == "type" or ret == "interf"
 | 
				
			||||||
 | 
					                or ret == "let" or ret == "return" or ret == "else"
 | 
				
			||||||
 | 
					                or ret == "for" or ret == "import" or ret == "constr"
 | 
				
			||||||
 | 
					                or ret == "loop" or ret == "break" or ret == "nil" then
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                return ret, ret, rowStart, columnStart
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            return "id", ret, rowStart, columnStart
 | 
				
			||||||
 | 
					        elseif is_digit(cp) then
 | 
				
			||||||
 | 
					            buf[1] = cp
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            local dotFound = false
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            while true do
 | 
				
			||||||
 | 
					                cp = cpget()
 | 
				
			||||||
 | 
					                if is_digit(cp) then
 | 
				
			||||||
 | 
					                    buf[#buf + 1] = cp
 | 
				
			||||||
 | 
					                elseif cp == 46 and not dotFound then
 | 
				
			||||||
 | 
					                    dotFound = true
 | 
				
			||||||
 | 
					                    buf[#buf + 1] = cp
 | 
				
			||||||
 | 
					                else
 | 
				
			||||||
 | 
					                    pull = cp
 | 
				
			||||||
 | 
					                    break
 | 
				
			||||||
 | 
					                end
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            local ret = utf8.char(table.unpack(buf))
 | 
				
			||||||
 | 
					            clear(buf)
 | 
				
			||||||
 | 
					            return "num", ret, rowStart, columnStart
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            error(string.format("%i:%i unknown character %q (code point %i)", row, column, utf8.char(cp), cp))
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return {lex = lex, codepoints = codepoints}
 | 
				
			||||||
							
								
								
									
										16
									
								
								main.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								main.lua
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					local Lexer = require"lex"
 | 
				
			||||||
 | 
					local Parser = require"parse"
 | 
				
			||||||
 | 
					local Des = require"des"
 | 
				
			||||||
 | 
					local Gen = require"gen"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local tokens = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					for tt, ts, tr, tc in Lexer.lex(Lexer.codepoints(io.open(arg[1], "r"):read("*a"))) do
 | 
				
			||||||
 | 
					    tokens[#tokens + 1] = {tt, ts, tr, tc}
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local ast = Parser(tokens, false)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Des(ast)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Gen(ast, function(str, ...) io.write(string.format(str, ...)) end)
 | 
				
			||||||
							
								
								
									
										696
									
								
								parse.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										696
									
								
								parse.lua
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,696 @@
 | 
				
			|||||||
 | 
					local unique_name = require"bon".unique_name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function do_everything(Tokens, Skeleton)
 | 
				
			||||||
 | 
					    local TokensIdx = 1
 | 
				
			||||||
 | 
					    local IdxStack = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    local function gettoken()
 | 
				
			||||||
 | 
					        local r = Tokens[TokensIdx]
 | 
				
			||||||
 | 
					        if r then
 | 
				
			||||||
 | 
					            TokensIdx = TokensIdx + 1
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					        return table.unpack(r)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    local function peektoken()
 | 
				
			||||||
 | 
					        local r = Tokens[TokensIdx]
 | 
				
			||||||
 | 
					        if r then
 | 
				
			||||||
 | 
					            return table.unpack(Tokens[TokensIdx])
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    local function maybe(t)
 | 
				
			||||||
 | 
					        local a, b, c, d = peektoken()
 | 
				
			||||||
 | 
					        if a == t then return gettoken() end
 | 
				
			||||||
 | 
					        return nil
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    local function pushlexi()
 | 
				
			||||||
 | 
					        table.insert(IdxStack, TokensIdx)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    local function poplexi()
 | 
				
			||||||
 | 
					        TokensIdx = table.remove(IdxStack)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    local function forgetlexi()
 | 
				
			||||||
 | 
					        table.remove(IdxStack)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    local function expect(t)
 | 
				
			||||||
 | 
					        local a, b, c, d = gettoken()
 | 
				
			||||||
 | 
					        if t ~= a then
 | 
				
			||||||
 | 
					            error(string.format("%i:%i expected %s, got %s", c, d, t, a))
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					        return a, b, c, d
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    local parse_chunk
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    local function new_parse_context(parent)
 | 
				
			||||||
 | 
					        return setmetatable({["the parent"] = parent}, {
 | 
				
			||||||
 | 
					            __index = function(self, key)
 | 
				
			||||||
 | 
					                while true do
 | 
				
			||||||
 | 
					                    local r = rawget(self, key)
 | 
				
			||||||
 | 
					                    if r then
 | 
				
			||||||
 | 
					                        return r
 | 
				
			||||||
 | 
					                    end
 | 
				
			||||||
 | 
					                    self = rawget(self, "the parent")
 | 
				
			||||||
 | 
					                    if not self then
 | 
				
			||||||
 | 
					                        return
 | 
				
			||||||
 | 
					                    end
 | 
				
			||||||
 | 
					                end
 | 
				
			||||||
 | 
					            end,
 | 
				
			||||||
 | 
					            __newindex = function(self, key, value)
 | 
				
			||||||
 | 
					                local z = self
 | 
				
			||||||
 | 
					                local r = nil
 | 
				
			||||||
 | 
					                while true do
 | 
				
			||||||
 | 
					                    r = rawget(z, key)
 | 
				
			||||||
 | 
					                    if r then break end
 | 
				
			||||||
 | 
					                    z = rawget(z, "the parent")
 | 
				
			||||||
 | 
					                    if not z then break end
 | 
				
			||||||
 | 
					                end
 | 
				
			||||||
 | 
					                rawset(z or self, key, value)
 | 
				
			||||||
 | 
					            end,
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    local ParseContext, ParseContextTypes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    local function push_parse_context()
 | 
				
			||||||
 | 
					        ParseContext = new_parse_context(ParseContext)
 | 
				
			||||||
 | 
					        ParseContextTypes = new_parse_context(ParseContextTypes)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    local function pop_parse_context()
 | 
				
			||||||
 | 
					        ParseContext = rawget(ParseContext, "the parent")
 | 
				
			||||||
 | 
					        ParseContextTypes = rawget(ParseContextTypes, "the parent")
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    local Types = {}
 | 
				
			||||||
 | 
					    function Types.castable(this, to)
 | 
				
			||||||
 | 
					        return not to or (this and this.type == to.type)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    function Types.is_number(this)
 | 
				
			||||||
 | 
					        return not this or this.type == 'number' or this.type == 'integer'
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    function Types.can_do_arith(this)
 | 
				
			||||||
 | 
					        return Types.is_number(this)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    function Types.is_callable(this)
 | 
				
			||||||
 | 
					        return not this or this.type == 'function'
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    function Types.is_indexable(this)
 | 
				
			||||||
 | 
					        return not this or this.type == 'list'
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    function Types.has_field(this, field)
 | 
				
			||||||
 | 
					        if not this then
 | 
				
			||||||
 | 
					            return true, nil
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if this.type == 'list' and field == 'pop' then
 | 
				
			||||||
 | 
					            return true, {type = 'function', ret = false, args = {this}}
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if this.type ~= 'structure' then
 | 
				
			||||||
 | 
					            return false, nil
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        for k, v in ipairs(this.members) do
 | 
				
			||||||
 | 
					            if v.name == field then
 | 
				
			||||||
 | 
					                return true, v.et
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        return false, nil
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    local function parse_et()
 | 
				
			||||||
 | 
					        local A
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if peektoken() == "id" then
 | 
				
			||||||
 | 
					            local name = select(2, gettoken())
 | 
				
			||||||
 | 
					            if name == "number" then
 | 
				
			||||||
 | 
					                A = {type = 'number'}
 | 
				
			||||||
 | 
					            elseif name == "integer" then
 | 
				
			||||||
 | 
					                A = {type = 'integer'}
 | 
				
			||||||
 | 
					            elseif name == "string" then
 | 
				
			||||||
 | 
					                A = {type = 'string'}
 | 
				
			||||||
 | 
					            elseif name == "boolean" then
 | 
				
			||||||
 | 
					                A = {type = 'boolean'}
 | 
				
			||||||
 | 
					            elseif name == "list" then
 | 
				
			||||||
 | 
					                A = {type = 'list'}
 | 
				
			||||||
 | 
					            elseif name == "dict" then
 | 
				
			||||||
 | 
					                A = {type = 'dict'}
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					                A = ParseContextTypes[name]
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if not A then
 | 
				
			||||||
 | 
					            error("Invalid type description")
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if maybe("[") then
 | 
				
			||||||
 | 
					            A.parametrization = {parse_et()}
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            expect("]")
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        return A
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    local parse_function
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    local function parse_function_arg()
 | 
				
			||||||
 | 
					        local name = select(2, expect("id"))
 | 
				
			||||||
 | 
					        local et
 | 
				
			||||||
 | 
					        if maybe(":") then
 | 
				
			||||||
 | 
					            et = parse_et()
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					        return name, et
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    local function parse_type()
 | 
				
			||||||
 | 
					        expect("type")
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        local name = select(2, expect("id"))
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        local ret = {type = 'structure', members = {}, name = name}
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        while true do
 | 
				
			||||||
 | 
					            if maybe("end") then
 | 
				
			||||||
 | 
					                break
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if peektoken() == "constr" or peektoken() == "func" then
 | 
				
			||||||
 | 
					                local isConstr = gettoken() == "constr"
 | 
				
			||||||
 | 
					                local fname
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                if isConstr then
 | 
				
			||||||
 | 
					                    fname = "__call"
 | 
				
			||||||
 | 
					                else
 | 
				
			||||||
 | 
					                    fname = select(2, expect("id"))
 | 
				
			||||||
 | 
					                end
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                local argnames = {}
 | 
				
			||||||
 | 
					                local argets = {}
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                if not isConstr then
 | 
				
			||||||
 | 
					                    table.insert(argnames, "self")
 | 
				
			||||||
 | 
					                    table.insert(argets, ret)
 | 
				
			||||||
 | 
					                end
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                push_parse_context()
 | 
				
			||||||
 | 
					                rawset(ParseContext, "self", {et = ret, name = "self"})
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                if maybe("(") then
 | 
				
			||||||
 | 
					                    if not maybe(")") then
 | 
				
			||||||
 | 
					                        
 | 
				
			||||||
 | 
					                        while true do
 | 
				
			||||||
 | 
					                            local name, et = parse_function_arg()
 | 
				
			||||||
 | 
					                            
 | 
				
			||||||
 | 
					                            table.insert(argnames, name)
 | 
				
			||||||
 | 
					                            argets[#argnames] = et
 | 
				
			||||||
 | 
					                            
 | 
				
			||||||
 | 
					                            rawset(ParseContext, name, {et = et, name = name})
 | 
				
			||||||
 | 
					                            
 | 
				
			||||||
 | 
					                            if maybe(")") then
 | 
				
			||||||
 | 
					                                break
 | 
				
			||||||
 | 
					                            else
 | 
				
			||||||
 | 
					                                expect(",")
 | 
				
			||||||
 | 
					                            end
 | 
				
			||||||
 | 
					                        end
 | 
				
			||||||
 | 
					                    end
 | 
				
			||||||
 | 
					                end
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                local fret
 | 
				
			||||||
 | 
					                if isConstr then
 | 
				
			||||||
 | 
					                    fret = ret
 | 
				
			||||||
 | 
					                else
 | 
				
			||||||
 | 
					                    if maybe(":") then
 | 
				
			||||||
 | 
					                        fret = parse_et()
 | 
				
			||||||
 | 
					                    end
 | 
				
			||||||
 | 
					                end
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                local chu = parse_chunk(false)
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                local et = {type = "function", args = argets, ret = fret}
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                table.insert(ret.members, {
 | 
				
			||||||
 | 
					                    type = "static",
 | 
				
			||||||
 | 
					                    name = fname,
 | 
				
			||||||
 | 
					                    et = et,
 | 
				
			||||||
 | 
					                    value = {type = "function", et = et, args = argnames, chunk = chu, is_constructor = isConstr},
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                pop_parse_context()
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                expect("end")
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					                local membname = select(2, expect("id"))
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                expect(":")
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                local et = parse_et()
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                table.insert(ret.members, {type = "field", name = membname, et = et})
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        return name, ret
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    parse_function = function()
 | 
				
			||||||
 | 
					        expect("func")
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        local name = select(2, maybe("id"))
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        push_parse_context()
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        local argnames = {}
 | 
				
			||||||
 | 
					        local argets = {}
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if maybe("(") then
 | 
				
			||||||
 | 
					            if not maybe(")") then
 | 
				
			||||||
 | 
					                while true do
 | 
				
			||||||
 | 
					                    local name, et = parse_function_arg()
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    table.insert(argnames, name)
 | 
				
			||||||
 | 
					                    argets[#argnames] = et
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    rawset(ParseContext, name, {et = et, name = name})
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    if maybe(")") then
 | 
				
			||||||
 | 
					                        break
 | 
				
			||||||
 | 
					                    else
 | 
				
			||||||
 | 
					                        expect(",")
 | 
				
			||||||
 | 
					                    end
 | 
				
			||||||
 | 
					                end
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        local retet
 | 
				
			||||||
 | 
					        if maybe(":") then
 | 
				
			||||||
 | 
					            retet = parse_et()
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        rawset(ParseContext, "the function name", name)
 | 
				
			||||||
 | 
					        rawset(ParseContextTypes, "the function return type", retet)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        local ret = {
 | 
				
			||||||
 | 
					            type = "function",
 | 
				
			||||||
 | 
					            et = {type = "function", args = argets, ret = retet},
 | 
				
			||||||
 | 
					            chunk = parse_chunk(false),
 | 
				
			||||||
 | 
					            args = argnames
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        pop_parse_context()
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        expect("end")
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        return ret, name
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    local active_stmt_list_stack = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    BINOP_LEVELS = {["+"] = 3, ["-"] = 3, ["=="] = 4, ["/"] = 2, ["*"] = 2, ["%"] = 2, ["**"] = 1}
 | 
				
			||||||
 | 
					    local function parse_expr(level)
 | 
				
			||||||
 | 
					        level = level or 4
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if level == 4 then
 | 
				
			||||||
 | 
					            local A = parse_expr(level - 1)
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if peektoken() == "==" then
 | 
				
			||||||
 | 
					                local op = gettoken()
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                local B = parse_expr(level - 1)
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                A = {type = "binop", level = level, et = {type = 'boolean'}, op = op, a = A, b = B}
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            return A
 | 
				
			||||||
 | 
					        elseif level == 3 then
 | 
				
			||||||
 | 
					            local A = parse_expr(level - 1)
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if peektoken() == "+" or peektoken() == "-" then
 | 
				
			||||||
 | 
					                local op = gettoken()
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                local B = parse_expr(level - 1)
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                if not Types.can_do_arith(A.et) or not Types.can_do_arith(B.et) then
 | 
				
			||||||
 | 
					                    error(op .. " operator not supported for non-number types")
 | 
				
			||||||
 | 
					                end
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                A = {type = "binop", level = level, et = A.et, op = op, a = A, b = B}
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            return A
 | 
				
			||||||
 | 
					        elseif level == 2 then
 | 
				
			||||||
 | 
					            local A = parse_expr(level - 1)
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if peektoken() == "*" or peektoken() == "/" or peektoken() == "%" then
 | 
				
			||||||
 | 
					                local op = gettoken()
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                local B = parse_expr(level - 1)
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                if not Types.can_do_arith(A.et) or not Types.can_do_arith(B.et) then
 | 
				
			||||||
 | 
					                    error(op .. " operator not supported for non-number types")
 | 
				
			||||||
 | 
					                end
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                A = {type = "binop", level = level, et = A.et, op = op, a = A, b = B}
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            return A
 | 
				
			||||||
 | 
					        elseif level == 1 then
 | 
				
			||||||
 | 
					            local A = parse_expr(level - 1)
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if peektoken() == "**" then
 | 
				
			||||||
 | 
					                local op = gettoken()
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                local B = parse_expr(level - 1)
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                if not Types.can_do_arith(A.et) or not Types.can_do_arith(B.et) then
 | 
				
			||||||
 | 
					                    error(op .. " operator not supported for non-number types")
 | 
				
			||||||
 | 
					                end
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                A = {type = "binop", level = level, et = A.et, op = op, a = A, b = B}
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            return A
 | 
				
			||||||
 | 
					        elseif level == 0 then
 | 
				
			||||||
 | 
					            local A
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if maybe("#") then
 | 
				
			||||||
 | 
					                A = {type = 'lengthof', et = {type = 'integer'}, sub = parse_expr(level)}
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                if A.sub.et and A.sub.et.type ~= 'list' then
 | 
				
			||||||
 | 
					                    error("# requires a list")
 | 
				
			||||||
 | 
					                end
 | 
				
			||||||
 | 
					            elseif maybe("(") then
 | 
				
			||||||
 | 
					                A = parse_expr()
 | 
				
			||||||
 | 
					                expect(")")
 | 
				
			||||||
 | 
					            elseif peektoken() == "func" then
 | 
				
			||||||
 | 
					                A = parse_function()
 | 
				
			||||||
 | 
					            elseif peektoken() == "num" then
 | 
				
			||||||
 | 
					                local lit = select(2, gettoken())
 | 
				
			||||||
 | 
					                A = {type = "num", et = lit:find("%.") and {type = 'number'} or {type = 'integer'}, value = tonumber(lit)}
 | 
				
			||||||
 | 
					            elseif peektoken() == "id" then
 | 
				
			||||||
 | 
					                local name = select(2, gettoken())
 | 
				
			||||||
 | 
					                local v = ParseContext[name]
 | 
				
			||||||
 | 
					                if not v then
 | 
				
			||||||
 | 
					                    error(string.format("No value with name %s exists.", name))
 | 
				
			||||||
 | 
					                end
 | 
				
			||||||
 | 
					                A = {type = "var", which = v, et = v.et}
 | 
				
			||||||
 | 
					            elseif maybe("{") then
 | 
				
			||||||
 | 
					                A = {type = "dict", et = {type = "dict", parametrization = {}}, mappings = {}}
 | 
				
			||||||
 | 
					                expect("}")
 | 
				
			||||||
 | 
					            elseif maybe("[") then
 | 
				
			||||||
 | 
					                A = {type = "list", et = {type = "list", parametrization = {}}, values = {}}
 | 
				
			||||||
 | 
					                expect("]")
 | 
				
			||||||
 | 
					            elseif peektoken() == "string" then
 | 
				
			||||||
 | 
					                A = {type = "string", et = {type = 'string'}, value = select(2, gettoken())}
 | 
				
			||||||
 | 
					            elseif maybe("nil") then
 | 
				
			||||||
 | 
					                A = {type = 'null', et = {type = 'null'}}
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					                error("Unexpected token " .. select(2, peektoken()))
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            while peektoken() == "[" or peektoken() == "(" or peektoken() == "." do
 | 
				
			||||||
 | 
					                if maybe("[") then
 | 
				
			||||||
 | 
					                    if A.et and A.et.type ~= 'list' and A.et.type ~= 'dict' and A.et.type ~= 'string' then
 | 
				
			||||||
 | 
					                        error("Indexed value is not a list nor dict.")
 | 
				
			||||||
 | 
					                    end
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    local newet
 | 
				
			||||||
 | 
					                    if A.et.type == 'list' then
 | 
				
			||||||
 | 
					                        newet = A.et.parametrization and A.et.parametrization[1]
 | 
				
			||||||
 | 
					                    elseif A.et.type == 'dict' then
 | 
				
			||||||
 | 
					                        newet = A.et.parametrization and A.et.parametrization[2]
 | 
				
			||||||
 | 
					                    elseif A.et.type == 'string' then
 | 
				
			||||||
 | 
					                        newet = A.et
 | 
				
			||||||
 | 
					                    else
 | 
				
			||||||
 | 
					                        error()
 | 
				
			||||||
 | 
					                    end
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    local idx = parse_expr()
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    if A.et.type == 'string' then
 | 
				
			||||||
 | 
					                        local temp = {name = unique_name(), et = nil}
 | 
				
			||||||
 | 
					                        
 | 
				
			||||||
 | 
					                        table.insert(active_stmt_list_stack[#active_stmt_list_stack], {
 | 
				
			||||||
 | 
					                            type = 'let',
 | 
				
			||||||
 | 
					                            var = temp,
 | 
				
			||||||
 | 
					                            expr = {type = 'binop', level = BINOP_LEVELS['+'], op = '+', a = idx, b = {type = 'num', value = 1, et = {type = 'integer'}}}
 | 
				
			||||||
 | 
					                        })
 | 
				
			||||||
 | 
					                        
 | 
				
			||||||
 | 
					                        A = {
 | 
				
			||||||
 | 
					                            type = 'call',
 | 
				
			||||||
 | 
					                            what = {type = 'dot', colon = true, a = A, b = 'sub', et = nil},
 | 
				
			||||||
 | 
					                            args = {
 | 
				
			||||||
 | 
					                                {type = 'var', which = temp, et = temp.et},
 | 
				
			||||||
 | 
					                                {type = 'var', which = temp, et = temp.et}
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    else
 | 
				
			||||||
 | 
					                        if A.et.type == 'list' then
 | 
				
			||||||
 | 
					                            -- Switch to 1-based indexing
 | 
				
			||||||
 | 
					                            idx = {type = 'binop', level = BINOP_LEVELS['+'], op = '+', a = idx, b = {type = 'num', value = 1, et = {type = 'integer'}}}
 | 
				
			||||||
 | 
					                        end
 | 
				
			||||||
 | 
					                        
 | 
				
			||||||
 | 
					                        A = {type = 'index', what = A, idx = idx, et = newet}
 | 
				
			||||||
 | 
					                    end
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    expect("]")
 | 
				
			||||||
 | 
					                elseif maybe("(") then
 | 
				
			||||||
 | 
					                    if not Types.is_callable(A.et) then
 | 
				
			||||||
 | 
					                        error("Called value is not a function.")
 | 
				
			||||||
 | 
					                    end
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    local args = {}
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    if not maybe(")") then
 | 
				
			||||||
 | 
					                        while true do
 | 
				
			||||||
 | 
					                            table.insert(args, parse_expr())
 | 
				
			||||||
 | 
					                            
 | 
				
			||||||
 | 
					                            if maybe(")") then break end
 | 
				
			||||||
 | 
					                            
 | 
				
			||||||
 | 
					                            expect(",")
 | 
				
			||||||
 | 
					                        end
 | 
				
			||||||
 | 
					                    end
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    A = {type = 'call', what = A, et = A.et and A.et.ret or nil, args = args}
 | 
				
			||||||
 | 
					                elseif maybe(".") then
 | 
				
			||||||
 | 
					                    local B = select(2, expect("id"))
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    local has_field, field_type = Types.has_field(A.et, B)
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    if not has_field then
 | 
				
			||||||
 | 
					                        error(string.format("No such field %s", B))
 | 
				
			||||||
 | 
					                    end
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    A = {type = 'dot', a = A, b = B, et = field_type}
 | 
				
			||||||
 | 
					                end
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            return A
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        error("Invalid expression")
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    local function parse_stmt()
 | 
				
			||||||
 | 
					        if maybe("let") then
 | 
				
			||||||
 | 
					            local name = select(2, expect("id"))
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            local et
 | 
				
			||||||
 | 
					            if maybe(":") then
 | 
				
			||||||
 | 
					                et = parse_et()
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            local expr
 | 
				
			||||||
 | 
					            if maybe("=") then
 | 
				
			||||||
 | 
					                expr = parse_expr()
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if not et and not expr then
 | 
				
			||||||
 | 
					                error(string.format("Cannot create name %s without a given type", name))
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if not et then
 | 
				
			||||||
 | 
					                et = expr.et
 | 
				
			||||||
 | 
					            elseif expr and not Types.castable(expr.et, et) then
 | 
				
			||||||
 | 
					                error(string.format("Cannot cast expression to type %s", et.type))
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            local newvar = {et = et, name = name}
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            ParseContext[name] = newvar
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            return {type = "let", var = newvar, expr = expr}
 | 
				
			||||||
 | 
					        elseif maybe("if") then
 | 
				
			||||||
 | 
					            local ret = {type = "if"}
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            while true do
 | 
				
			||||||
 | 
					                local expr = parse_expr()
 | 
				
			||||||
 | 
					                if not et and not Types.castable(expr.et, {type = 'boolean'}) then
 | 
				
			||||||
 | 
					                    error("If predicate must be castable to boolean")
 | 
				
			||||||
 | 
					                end
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                expect("then")
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                local chu = parse_chunk(false)
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                table.insert(ret, {pred = expr, chu = chu})
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                if maybe("else") then
 | 
				
			||||||
 | 
					                    ret.elsa = parse_chunk(false)
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    expect("end")
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    break
 | 
				
			||||||
 | 
					                end
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                if maybe("end") then
 | 
				
			||||||
 | 
					                    break
 | 
				
			||||||
 | 
					                end
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                expect("elseif")
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            return ret
 | 
				
			||||||
 | 
					        elseif maybe("for") then
 | 
				
			||||||
 | 
					            local varname = select(2, expect("id"))
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            expect("=")
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            local from = parse_expr()
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            expect(",")
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            local to = parse_expr()
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            expect("do")
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            push_parse_context()
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            rawset(ParseContext, varname, {et = {type = 'integer'}, name = varname})
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            local chu = parse_chunk(false)
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            pop_parse_context()
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            expect("end")
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            return {type = "fori", varname = varname, from = from, to = to, chu = chu}
 | 
				
			||||||
 | 
					        elseif maybe("loop") then
 | 
				
			||||||
 | 
					            push_parse_context()
 | 
				
			||||||
 | 
					            local chu = parse_chunk(false)
 | 
				
			||||||
 | 
					            pop_parse_context()
 | 
				
			||||||
 | 
					            expect("end")
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            return {type = "loop", chu = chu}
 | 
				
			||||||
 | 
					        elseif maybe("break") then
 | 
				
			||||||
 | 
					            return {type = "break"}
 | 
				
			||||||
 | 
					        elseif maybe("return") then
 | 
				
			||||||
 | 
					            local expr = parse_expr()
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if expr.et and not Types.castable(expr.et, ParseContextTypes["the function return type"]) then
 | 
				
			||||||
 | 
					                error(string.format("Function %s returns different type.", ParseContext["the function name"]))
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            return {type = "return", expr = expr}
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            local e = parse_expr()
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if maybe("=") then
 | 
				
			||||||
 | 
					                local src = parse_expr()
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                if not Types.castable(src.et, e.et) then
 | 
				
			||||||
 | 
					                    error("Cannot cast for assignment")
 | 
				
			||||||
 | 
					                end
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                return {type = "assign", dest = e, src = src}
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					                if e.type ~= "call" then
 | 
				
			||||||
 | 
					                    error("Expected statement.")
 | 
				
			||||||
 | 
					                end
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                return e
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        error(string.format("Unexpected token %s", (select(2, peektoken()))))
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    local SAFE_MODE = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    parse_chunk = function(toplevel)
 | 
				
			||||||
 | 
					        push_parse_context()
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        local ast = {type = "chunk", types = {}, vars = {}, stmts = {}, imports = {}, toplevel = toplevel}
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        table.insert(active_stmt_list_stack, ast.stmts)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        while true do
 | 
				
			||||||
 | 
					            local tt, ts, tr, tc = peektoken()
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if tt == "end" or tt == "else" or tt == "elseif" then
 | 
				
			||||||
 | 
					                if toplevel then
 | 
				
			||||||
 | 
					                    error("Unexpected end in top-level chunk")
 | 
				
			||||||
 | 
					                end
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                break
 | 
				
			||||||
 | 
					            elseif tt == nil then
 | 
				
			||||||
 | 
					                if not toplevel then
 | 
				
			||||||
 | 
					                    error("Unexpected EOF in non top-level chunk")
 | 
				
			||||||
 | 
					                end
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                break
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if toplevel and tt == "import" then
 | 
				
			||||||
 | 
					                gettoken()
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                local path = {}
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                while true do
 | 
				
			||||||
 | 
					                    local part = select(2, expect("id"))
 | 
				
			||||||
 | 
					                    table.insert(path, part)
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    if not maybe(".") then
 | 
				
			||||||
 | 
					                        break
 | 
				
			||||||
 | 
					                    end
 | 
				
			||||||
 | 
					                end
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                rawset(ParseContext, path[#path], {et = nil, name = path[#path]})
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                table.insert(ast.imports, path)
 | 
				
			||||||
 | 
					            elseif tt == "type" then
 | 
				
			||||||
 | 
					                local name, t = parse_type()
 | 
				
			||||||
 | 
					                ast.types[name] = t
 | 
				
			||||||
 | 
					                rawset(ParseContextTypes, name, t)
 | 
				
			||||||
 | 
					            elseif tt == "func" then
 | 
				
			||||||
 | 
					                local expr, name = parse_function()
 | 
				
			||||||
 | 
					                ast.vars[name] = expr
 | 
				
			||||||
 | 
					                rawset(ParseContext, name, {et = expr.et, name = name})
 | 
				
			||||||
 | 
					            elseif not toplevel then
 | 
				
			||||||
 | 
					                table.insert(ast.stmts, parse_stmt())
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					                error(string.format("Unexpected %s in top-level chunk", tt))
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        table.remove(active_stmt_list_stack)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        pop_parse_context()
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        return ast
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    return parse_chunk(true)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--local chu = parse_chunk(true)
 | 
				
			||||||
 | 
					--pass_destandardify(chu)
 | 
				
			||||||
 | 
					--compile(chu)
 | 
				
			||||||
 | 
					--print()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return do_everything
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user