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 lfs = require "lfs"
|
||||
|
||||
local opts, ind = alt_getopt.get_opts(arg, "vhwt:pTXb", {
|
||||
print = "p", tree = "T", version = "v", help = "h"
|
||||
local opts, ind = alt_getopt.get_opts(arg, "lvhwt:pTXb", {
|
||||
print = "p", tree = "T", version = "v", help = "h", lint = "l"
|
||||
})
|
||||
|
||||
local read_stdin = arg[1] == "--"
|
||||
@ -25,6 +25,7 @@ local help = [[Usage: %s [options] files...
|
||||
-p Write output to standard out
|
||||
-T Write parse tree 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)
|
||||
-v Print version
|
||||
|
||||
@ -377,6 +378,15 @@ if opts.w then
|
||||
end
|
||||
|
||||
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
|
||||
for _, fname in ipairs(files) do
|
||||
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",
|
||||
export_all = false,
|
||||
export_proper = false,
|
||||
value_compilers = value_compilers,
|
||||
__tostring = function(self)
|
||||
local h
|
||||
if "string" == type(self.header) then
|
||||
@ -459,7 +460,7 @@ do
|
||||
end,
|
||||
is_value = function(self, node)
|
||||
local t = ntype(node)
|
||||
return value_compilers[t] ~= nil or t == "value"
|
||||
return self.value_compilers[t] ~= nil or t == "value"
|
||||
end,
|
||||
name = function(self, node, ...)
|
||||
return self:value(node, ...)
|
||||
@ -472,7 +473,7 @@ do
|
||||
else
|
||||
action = node[1]
|
||||
end
|
||||
local fn = value_compilers[action]
|
||||
local fn = self.value_compilers[action]
|
||||
if not fn then
|
||||
error("Failed to compile value: " .. dump.value(node))
|
||||
end
|
||||
|
@ -161,6 +161,8 @@ class Block
|
||||
export_all: false
|
||||
export_proper: false
|
||||
|
||||
value_compilers: value_compilers
|
||||
|
||||
__tostring: =>
|
||||
h = if "string" == type @header
|
||||
@header
|
||||
@ -316,7 +318,7 @@ class Block
|
||||
|
||||
is_value: (node) =>
|
||||
t = ntype node
|
||||
value_compilers[t] != nil or t == "value"
|
||||
@value_compilers[t] != nil or t == "value"
|
||||
|
||||
-- line wise compile functions
|
||||
name: (node, ...) => @value node, ...
|
||||
@ -328,7 +330,7 @@ class Block
|
||||
else
|
||||
node[1]
|
||||
|
||||
fn = value_compilers[action]
|
||||
fn = @value_compilers[action]
|
||||
error "Failed to compile value: "..dump.value node if not fn
|
||||
|
||||
out = fn self, node, ...
|
||||
|
Loading…
Reference in New Issue
Block a user