mirror of
https://github.com/leafo/moonscript.git
synced 2025-01-09 00:04:22 +00:00
create a basic lint tool
This commit is contained in:
parent
0726982099
commit
cc534a01f3
14
bin/moonc
14
bin/moonc
@ -9,8 +9,8 @@ local dump_tree = require"moonscript.dump".tree
|
|||||||
local alt_getopt = require "alt_getopt"
|
local alt_getopt = require "alt_getopt"
|
||||||
local lfs = require "lfs"
|
local lfs = require "lfs"
|
||||||
|
|
||||||
local opts, ind = alt_getopt.get_opts(arg, "vhwt:pTXb", {
|
local opts, ind = alt_getopt.get_opts(arg, "lvhwt:pTXb", {
|
||||||
print = "p", tree = "T", version = "v", help = "h"
|
print = "p", tree = "T", version = "v", help = "h", lint = "l"
|
||||||
})
|
})
|
||||||
|
|
||||||
local read_stdin = arg[1] == "--"
|
local read_stdin = arg[1] == "--"
|
||||||
@ -25,6 +25,7 @@ local help = [[Usage: %s [options] files...
|
|||||||
-p Write output to standard out
|
-p Write output to standard out
|
||||||
-T Write parse tree instead of code (to stdout)
|
-T Write parse tree instead of code (to stdout)
|
||||||
-X Write line rewrite map instead of code (to stdout)
|
-X Write line rewrite map instead of code (to stdout)
|
||||||
|
-l Perform lint on the file instead of compiling
|
||||||
-b Dump parse and compile time (doesn't write output)
|
-b Dump parse and compile time (doesn't write output)
|
||||||
-v Print version
|
-v Print version
|
||||||
|
|
||||||
@ -377,6 +378,15 @@ if opts.w then
|
|||||||
end
|
end
|
||||||
|
|
||||||
print "\nQuitting..."
|
print "\nQuitting..."
|
||||||
|
elseif opts.l then
|
||||||
|
for _, fname in pairs(files) do
|
||||||
|
lint = require "moonscript.cmd.lint"
|
||||||
|
local res = lint.lint_file(fname)
|
||||||
|
if res then
|
||||||
|
print(res)
|
||||||
|
print()
|
||||||
|
end
|
||||||
|
end
|
||||||
else
|
else
|
||||||
for _, fname in ipairs(files) do
|
for _, fname in ipairs(files) do
|
||||||
local success, err = compile_and_write(fname, target_dir..convert_path(fname))
|
local success, err = compile_and_write(fname, target_dir..convert_path(fname))
|
||||||
|
145
moonscript/cmd/lint.lua
Normal file
145
moonscript/cmd/lint.lua
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
local insert
|
||||||
|
do
|
||||||
|
local _obj_0 = table
|
||||||
|
insert = _obj_0.insert
|
||||||
|
end
|
||||||
|
local Set
|
||||||
|
do
|
||||||
|
local _obj_0 = require("moonscript.data")
|
||||||
|
Set = _obj_0.Set
|
||||||
|
end
|
||||||
|
local Block
|
||||||
|
do
|
||||||
|
local _obj_0 = require("moonscript.compile")
|
||||||
|
Block = _obj_0.Block
|
||||||
|
end
|
||||||
|
local whitelist_globals = Set({
|
||||||
|
"print"
|
||||||
|
})
|
||||||
|
local LinterBlock
|
||||||
|
do
|
||||||
|
local _parent_0 = Block
|
||||||
|
local _base_0 = {
|
||||||
|
block = function(self, ...)
|
||||||
|
do
|
||||||
|
local _with_0 = _parent_0.block(self, ...)
|
||||||
|
_with_0.value_compilers = self.value_compilers
|
||||||
|
return _with_0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
}
|
||||||
|
_base_0.__index = _base_0
|
||||||
|
setmetatable(_base_0, _parent_0.__base)
|
||||||
|
local _class_0 = setmetatable({
|
||||||
|
__init = function(self, lint_errors, ...)
|
||||||
|
if lint_errors == nil then
|
||||||
|
lint_errors = { }
|
||||||
|
end
|
||||||
|
self.lint_errors = lint_errors
|
||||||
|
_parent_0.__init(self, ...)
|
||||||
|
local vc = self.value_compilers
|
||||||
|
self.value_compilers = setmetatable({
|
||||||
|
raw_value = function(block, name)
|
||||||
|
if name:match("^[%w_]+$") and not block:has_name(name) and not whitelist_globals[name] then
|
||||||
|
local stm = block.current_stms[block.current_stm_i]
|
||||||
|
insert(self.lint_errors, {
|
||||||
|
"accessing global " .. tostring(name),
|
||||||
|
stm[-1]
|
||||||
|
})
|
||||||
|
end
|
||||||
|
return vc.raw_value(block, name)
|
||||||
|
end
|
||||||
|
}, {
|
||||||
|
__index = vc
|
||||||
|
})
|
||||||
|
end,
|
||||||
|
__base = _base_0,
|
||||||
|
__name = "LinterBlock",
|
||||||
|
__parent = _parent_0
|
||||||
|
}, {
|
||||||
|
__index = function(cls, name)
|
||||||
|
local val = rawget(_base_0, name)
|
||||||
|
if val == nil then
|
||||||
|
return _parent_0[name]
|
||||||
|
else
|
||||||
|
return val
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
__call = function(cls, ...)
|
||||||
|
local _self_0 = setmetatable({}, _base_0)
|
||||||
|
cls.__init(_self_0, ...)
|
||||||
|
return _self_0
|
||||||
|
end
|
||||||
|
})
|
||||||
|
_base_0.__class = _class_0
|
||||||
|
if _parent_0.__inherited then
|
||||||
|
_parent_0.__inherited(_parent_0, _class_0)
|
||||||
|
end
|
||||||
|
LinterBlock = _class_0
|
||||||
|
end
|
||||||
|
local format_lint
|
||||||
|
format_lint = function(errors, code, header)
|
||||||
|
if not (next(errors)) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local pos_to_line, get_line
|
||||||
|
do
|
||||||
|
local _obj_0 = require("moonscript.util")
|
||||||
|
pos_to_line, get_line = _obj_0.pos_to_line, _obj_0.get_line
|
||||||
|
end
|
||||||
|
local formatted
|
||||||
|
do
|
||||||
|
local _accum_0 = { }
|
||||||
|
local _len_0 = 1
|
||||||
|
for _index_0 = 1, #errors do
|
||||||
|
local _des_0 = errors[_index_0]
|
||||||
|
local msg, pos
|
||||||
|
msg, pos = _des_0[1], _des_0[2]
|
||||||
|
if pos then
|
||||||
|
local line = pos_to_line(code, pos)
|
||||||
|
msg = "line " .. tostring(line) .. ": " .. tostring(msg)
|
||||||
|
local line_text = "> " .. get_line(code, line)
|
||||||
|
local sep_len = math.max(#msg, #line_text)
|
||||||
|
_accum_0[_len_0] = table.concat({
|
||||||
|
msg,
|
||||||
|
("="):rep(sep_len),
|
||||||
|
line_text
|
||||||
|
}, "\n")
|
||||||
|
else
|
||||||
|
_accum_0[_len_0] = msg
|
||||||
|
end
|
||||||
|
_len_0 = _len_0 + 1
|
||||||
|
end
|
||||||
|
formatted = _accum_0
|
||||||
|
end
|
||||||
|
if header then
|
||||||
|
table.insert(formatted, 1, header)
|
||||||
|
end
|
||||||
|
return table.concat(formatted, "\n\n")
|
||||||
|
end
|
||||||
|
local lint_code
|
||||||
|
lint_code = function(code, name)
|
||||||
|
if name == nil then
|
||||||
|
name = "string input"
|
||||||
|
end
|
||||||
|
local parse = require("moonscript.parse")
|
||||||
|
local tree, err = parse.string(code)
|
||||||
|
if not (tree) then
|
||||||
|
return nil, err
|
||||||
|
end
|
||||||
|
local scope = LinterBlock()
|
||||||
|
scope:stms(tree)
|
||||||
|
return format_lint(scope.lint_errors, code, name)
|
||||||
|
end
|
||||||
|
local lint_file
|
||||||
|
lint_file = function(fname)
|
||||||
|
local f, err = io.open(fname)
|
||||||
|
if not (f) then
|
||||||
|
return nil, err
|
||||||
|
end
|
||||||
|
return lint_code(f:read("*a"), fname)
|
||||||
|
end
|
||||||
|
return {
|
||||||
|
lint_code = lint_code,
|
||||||
|
lint_file = lint_file
|
||||||
|
}
|
72
moonscript/cmd/lint.moon
Normal file
72
moonscript/cmd/lint.moon
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
|
||||||
|
import insert from table
|
||||||
|
import Set from require "moonscript.data"
|
||||||
|
import Block from require "moonscript.compile"
|
||||||
|
|
||||||
|
-- globals allowed to be referenced
|
||||||
|
whitelist_globals = Set {
|
||||||
|
"print"
|
||||||
|
}
|
||||||
|
|
||||||
|
class LinterBlock extends Block
|
||||||
|
new: (@lint_errors={}, ...) =>
|
||||||
|
super ...
|
||||||
|
|
||||||
|
vc = @value_compilers
|
||||||
|
@value_compilers = setmetatable {
|
||||||
|
raw_value: (block, name) ->
|
||||||
|
|
||||||
|
if name\match("^[%w_]+$") and not block\has_name(name) and not whitelist_globals[name]
|
||||||
|
stm = block.current_stms[block.current_stm_i]
|
||||||
|
insert @lint_errors, {
|
||||||
|
"accessing global #{name}"
|
||||||
|
stm[-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
vc.raw_value block, name
|
||||||
|
}, __index: vc
|
||||||
|
|
||||||
|
block: (...) =>
|
||||||
|
with super ...
|
||||||
|
.value_compilers = @value_compilers
|
||||||
|
|
||||||
|
format_lint = (errors, code, header) ->
|
||||||
|
return unless next errors
|
||||||
|
|
||||||
|
import pos_to_line, get_line from require "moonscript.util"
|
||||||
|
formatted = for {msg, pos} in *errors
|
||||||
|
if pos
|
||||||
|
line = pos_to_line code, pos
|
||||||
|
msg = "line #{line}: #{msg}"
|
||||||
|
line_text = "> " .. get_line code, line
|
||||||
|
|
||||||
|
sep_len = math.max #msg, #line_text
|
||||||
|
table.concat {
|
||||||
|
msg
|
||||||
|
"="\rep sep_len
|
||||||
|
line_text
|
||||||
|
}, "\n"
|
||||||
|
|
||||||
|
else
|
||||||
|
msg
|
||||||
|
|
||||||
|
table.insert formatted, 1, header if header
|
||||||
|
table.concat formatted, "\n\n"
|
||||||
|
|
||||||
|
|
||||||
|
lint_code = (code, name="string input") ->
|
||||||
|
parse = require "moonscript.parse"
|
||||||
|
tree, err = parse.string code
|
||||||
|
return nil, err unless tree
|
||||||
|
|
||||||
|
scope = LinterBlock!
|
||||||
|
scope\stms tree
|
||||||
|
format_lint scope.lint_errors, code, name
|
||||||
|
|
||||||
|
lint_file = (fname) ->
|
||||||
|
f, err = io.open fname
|
||||||
|
return nil, err unless f
|
||||||
|
lint_code f\read("*a"), fname
|
||||||
|
|
||||||
|
|
||||||
|
{ :lint_code, :lint_file }
|
@ -271,6 +271,7 @@ do
|
|||||||
footer = "end",
|
footer = "end",
|
||||||
export_all = false,
|
export_all = false,
|
||||||
export_proper = false,
|
export_proper = false,
|
||||||
|
value_compilers = value_compilers,
|
||||||
__tostring = function(self)
|
__tostring = function(self)
|
||||||
local h
|
local h
|
||||||
if "string" == type(self.header) then
|
if "string" == type(self.header) then
|
||||||
@ -459,7 +460,7 @@ do
|
|||||||
end,
|
end,
|
||||||
is_value = function(self, node)
|
is_value = function(self, node)
|
||||||
local t = ntype(node)
|
local t = ntype(node)
|
||||||
return value_compilers[t] ~= nil or t == "value"
|
return self.value_compilers[t] ~= nil or t == "value"
|
||||||
end,
|
end,
|
||||||
name = function(self, node, ...)
|
name = function(self, node, ...)
|
||||||
return self:value(node, ...)
|
return self:value(node, ...)
|
||||||
@ -472,7 +473,7 @@ do
|
|||||||
else
|
else
|
||||||
action = node[1]
|
action = node[1]
|
||||||
end
|
end
|
||||||
local fn = value_compilers[action]
|
local fn = self.value_compilers[action]
|
||||||
if not fn then
|
if not fn then
|
||||||
error("Failed to compile value: " .. dump.value(node))
|
error("Failed to compile value: " .. dump.value(node))
|
||||||
end
|
end
|
||||||
|
@ -161,6 +161,8 @@ class Block
|
|||||||
export_all: false
|
export_all: false
|
||||||
export_proper: false
|
export_proper: false
|
||||||
|
|
||||||
|
value_compilers: value_compilers
|
||||||
|
|
||||||
__tostring: =>
|
__tostring: =>
|
||||||
h = if "string" == type @header
|
h = if "string" == type @header
|
||||||
@header
|
@header
|
||||||
@ -316,7 +318,7 @@ class Block
|
|||||||
|
|
||||||
is_value: (node) =>
|
is_value: (node) =>
|
||||||
t = ntype node
|
t = ntype node
|
||||||
value_compilers[t] != nil or t == "value"
|
@value_compilers[t] != nil or t == "value"
|
||||||
|
|
||||||
-- line wise compile functions
|
-- line wise compile functions
|
||||||
name: (node, ...) => @value node, ...
|
name: (node, ...) => @value node, ...
|
||||||
@ -328,7 +330,7 @@ class Block
|
|||||||
else
|
else
|
||||||
node[1]
|
node[1]
|
||||||
|
|
||||||
fn = value_compilers[action]
|
fn = @value_compilers[action]
|
||||||
error "Failed to compile value: "..dump.value node if not fn
|
error "Failed to compile value: "..dump.value node if not fn
|
||||||
|
|
||||||
out = fn self, node, ...
|
out = fn self, node, ...
|
||||||
|
Loading…
Reference in New Issue
Block a user