diff --git a/spec/translator_spec.lua b/spec/translator_spec.lua index 60622ffd..a9084948 100644 --- a/spec/translator_spec.lua +++ b/spec/translator_spec.lua @@ -9,9 +9,13 @@ local util = require "pallene.util" local execution_tests = require "spec.execution_tests" -local function compile(filename, pallene_code) +local function compile(filename, pallene_code, preserve_columns) assert(util.set_file_contents(filename, pallene_code)) - local cmd = string.format("pallenec %s --emit-lua", util.shell_quote(filename)) + local flag = "--emit-lua" + if preserve_columns then + flag = "--emit-lua-preserve-columns" + end + local cmd = string.format("pallenec %s " .. flag, util.shell_quote(filename)) local ok, _, _, error_message = util.outputs_of_execute(cmd) return ok, error_message end @@ -32,8 +36,8 @@ end) -- Translation tests -------------------- -local function assert_translation(pallene_code, expected) - assert(compile("__translation_test__.pln", pallene_code)) +local function assert_translation(pallene_code, expected, preserve_columns) + assert(compile("__translation_test__.pln", pallene_code, preserve_columns)) local contents = assert(util.get_file_contents("__translation_test__.lua")) -- The introduction of math.ln in Pallene to workaround single param math.log requires emitted -- Lua code to handle this as well. The current workaround injects "math.ln = math.log; " at @@ -483,6 +487,7 @@ local m: module = {} local xs: {any} = {10, "hello", 3.14} local function f(x: any, y: any): any + return nil as nil end return m @@ -492,12 +497,37 @@ local m = {} local xs = {10, "hello", 3.14} local function f(x, y) + return nil end return m ]]) end) + it("Remove any type annotation (preserving columns)", function () + assert_translation( +[[ +local m: module = {} +local xs: {any} = {10, "hello", 3.14} + +local function f(x: any, y: any): any + return nil as nil +end + +return m +]], +[[ +local m = {} +local xs = {10, "hello", 3.14} + +local function f(x , y ) ]] .. "\n" .. [[ + return nil ]] .. "\n" .. [[ +end + +return m +]], true) + end) + it("Remove function shapes", function () assert_translation( [[ diff --git a/src/pallene/driver.lua b/src/pallene/driver.lua index df4d9713..de6221d9 100644 --- a/src/pallene/driver.lua +++ b/src/pallene/driver.lua @@ -149,17 +149,31 @@ end local function compile_pln_to_lua(input_ext, output_ext, input_file_name, base_name) assert(input_ext == "pln") + local preserve_columns = false + if output_ext == "lua_pc" then + preserve_columns = true + output_ext = "lua" + end + local input, err = driver.load_input(input_file_name) if not input then return false, { err } end - local prog_ast, errs = driver.compile_internal(input_file_name, input, "checker") + -- Perform compilation steps up to (including) variable verifications. + local prog_ast, errs = driver.compile_internal(input_file_name, input, "uninitialized") + if not prog_ast then + return false, errs + end + + -- Redo the compilation, this time stopping after the syntax checker to have + -- an AST that we can translate to lua. + prog_ast, errs = driver.compile_internal(input_file_name, input, "checker") if not prog_ast then return false, errs end - local translation = translator.translate(input, prog_ast) + local translation = translator.translate(input, prog_ast, preserve_columns) assert(util.set_file_contents(base_name .. "." .. output_ext, translation)) return true, {} @@ -180,7 +194,7 @@ function driver.compile(argv0, opt_level, input_ext, output_ext, input_file_name local mod_name = string.gsub(output_base_name, "/", "_") - if output_ext == "lua" then + if output_ext == "lua" or output_ext == "lua_pc" then return compile_pln_to_lua(input_ext, output_ext, input_file_name, output_base_name) else local first_step = step_index[input_ext] or error("invalid extension") diff --git a/src/pallene/pallenec.lua b/src/pallene/pallenec.lua index 8038509c..939cb950 100644 --- a/src/pallene/pallenec.lua +++ b/src/pallene/pallenec.lua @@ -28,6 +28,10 @@ do p:mutex( p:flag("--emit-c", "Generate a .c file instead of an executable"), p:flag("--emit-lua", "Generate a .lua file instead of an executable"), + p:flag("--emit-lua-preserve-columns", + "Generate a .lua file instead of an executable with" + .. "\nthe positions of tokens preserved (useful with" + .. "\nLua diagnostics tool source mapping)"), p:flag("--compile-c", "Compile a .c file generated by --emit-c"), p:flag("--only-check","Check for syntax or type errors, without compiling"), p:flag("--print-ir", "Show the intermediate representation for a program") @@ -60,7 +64,7 @@ local function compile_up_to(stop_after) end local function do_check() - compile_up_to("checker") + compile_up_to("uninitialized") end local function do_print_ir() @@ -71,6 +75,7 @@ end function pallenec.main() if opts.emit_c then compile("pln", "c") elseif opts.emit_lua then compile("pln", "lua") + elseif opts.emit_lua_preserve_columns then compile("pln", "lua_pc") elseif opts.compile_c then compile("c" , "so") elseif opts.only_check then do_check() elseif opts.print_ir then do_print_ir() diff --git a/src/pallene/translator.lua b/src/pallene/translator.lua index 8b81382c..0003f18c 100644 --- a/src/pallene/translator.lua +++ b/src/pallene/translator.lua @@ -30,8 +30,13 @@ local translator = {} local Translator = util.Class() -function Translator:init(input) +function Translator:init(input, preserve_columns) self.input = input -- string + if preserve_columns then + self.eraser = " " -- insert a space for each removed character + else + self.eraser = "" -- insert nothing when removing characters + end self.last_index = 1 -- integer self.partials = {} -- list of strings return self @@ -51,7 +56,7 @@ function Translator:erase_region(start_index, stop_index) self:add_previous(start_index - 1) local region = self.input:sub(start_index, stop_index) - local partial = region:gsub("[^\n\r]", "") + local partial = region:gsub("[^\n\r]", self.eraser) table.insert(self.partials, partial) self.last_index = stop_index + 1 @@ -65,8 +70,8 @@ function Translator:prepend_compatibility_code() self.partials[1] = "math.ln = math.log; " .. self.partials[1] end -function translator.translate(input, prog_ast) - local instance = Translator.new(input) +function translator.translate(input, prog_ast, preserve_columns) + local instance = Translator.new(input, preserve_columns) -- Erase all type regions, while preserving comments -- As a sanity check, assert that the comment regions are either inside or outside the type