2020-02-20 15:41:03 +03:00
|
|
|
module main
|
|
|
|
|
|
|
|
import os
|
2020-11-03 03:04:14 +03:00
|
|
|
import rand
|
|
|
|
import term
|
|
|
|
import v.pref
|
2020-02-20 15:41:03 +03:00
|
|
|
|
|
|
|
const (
|
|
|
|
too_long_line_length = 100
|
2020-11-03 03:04:14 +03:00
|
|
|
term_colors = term.can_show_color_on_stderr()
|
2020-02-20 15:41:03 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
fn main() {
|
2020-11-18 20:28:28 +03:00
|
|
|
files_paths := if '-all' in os.args { md_file_paths() } else { os.args[1..] }
|
2020-10-20 21:14:56 +03:00
|
|
|
mut warnings := 0
|
2020-02-20 15:41:03 +03:00
|
|
|
mut errors := 0
|
2020-11-03 03:04:14 +03:00
|
|
|
mut oks := 0
|
|
|
|
mut all_md_files := []MDFile{}
|
2020-02-20 15:41:03 +03:00
|
|
|
for file_path in files_paths {
|
2020-03-20 18:41:18 +03:00
|
|
|
real_path := os.real_path(file_path)
|
2020-02-20 15:41:03 +03:00
|
|
|
lines := os.read_lines(real_path) or {
|
2020-10-20 21:14:56 +03:00
|
|
|
println('"$file_path" does not exist')
|
|
|
|
warnings++
|
2020-02-20 15:41:03 +03:00
|
|
|
continue
|
|
|
|
}
|
2020-11-03 03:04:14 +03:00
|
|
|
mut mdfile := MDFile{
|
|
|
|
path: file_path
|
|
|
|
}
|
2020-02-20 15:41:03 +03:00
|
|
|
for i, line in lines {
|
|
|
|
if line.len > too_long_line_length {
|
2020-11-18 20:28:28 +03:00
|
|
|
if mdfile.state == .vexample {
|
|
|
|
println(wline(file_path, i, line.len, 'long V example line'))
|
|
|
|
warnings++
|
|
|
|
} else if mdfile.state == .codeblock {
|
|
|
|
println(wline(file_path, i, line.len, 'long code block line'))
|
|
|
|
warnings++
|
|
|
|
} else if line.starts_with('|') {
|
2020-11-03 03:04:14 +03:00
|
|
|
println(wline(file_path, i, line.len, 'long table'))
|
2020-10-20 21:14:56 +03:00
|
|
|
warnings++
|
|
|
|
} else if line.contains('https') {
|
2020-11-03 03:04:14 +03:00
|
|
|
println(wline(file_path, i, line.len, 'long link'))
|
2020-10-20 21:14:56 +03:00
|
|
|
warnings++
|
|
|
|
} else {
|
2020-11-03 03:04:14 +03:00
|
|
|
eprintln(eline(file_path, i, line.len, 'line too long'))
|
2020-10-20 21:14:56 +03:00
|
|
|
errors++
|
|
|
|
}
|
2020-02-20 15:41:03 +03:00
|
|
|
}
|
2020-11-03 03:04:14 +03:00
|
|
|
mdfile.parse_line(i, line)
|
2020-02-20 15:41:03 +03:00
|
|
|
}
|
2020-11-03 03:04:14 +03:00
|
|
|
all_md_files << mdfile
|
|
|
|
}
|
|
|
|
for mut mdfile in all_md_files {
|
|
|
|
new_errors, new_oks := mdfile.check_examples()
|
|
|
|
errors += new_errors
|
|
|
|
oks += new_oks
|
2020-02-20 15:41:03 +03:00
|
|
|
}
|
2020-11-03 03:04:14 +03:00
|
|
|
// println('all_md_files: $all_md_files')
|
|
|
|
if warnings > 0 || errors > 0 || oks > 0 {
|
|
|
|
println('\nWarnings: $warnings | Errors: $errors | OKs: $oks')
|
2020-10-20 21:14:56 +03:00
|
|
|
}
|
2020-02-20 15:41:03 +03:00
|
|
|
if errors > 0 {
|
|
|
|
exit(1)
|
|
|
|
}
|
|
|
|
}
|
2020-11-03 03:04:14 +03:00
|
|
|
|
2020-11-18 20:28:28 +03:00
|
|
|
fn md_file_paths() []string {
|
|
|
|
mut files_to_check := []string{}
|
|
|
|
md_files := os.walk_ext('.', '.md')
|
|
|
|
for file in md_files {
|
|
|
|
if file.starts_with('./thirdparty') {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
files_to_check << file
|
|
|
|
}
|
|
|
|
return files_to_check
|
|
|
|
}
|
|
|
|
|
2020-11-03 03:04:14 +03:00
|
|
|
fn ftext(s string, cb fn (string) string) string {
|
|
|
|
if term_colors {
|
|
|
|
return cb(s)
|
|
|
|
}
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
|
|
|
|
fn btext(s string) string {
|
|
|
|
return ftext(s, term.bold)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn mtext(s string) string {
|
|
|
|
return ftext(s, term.magenta)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn rtext(s string) string {
|
|
|
|
return ftext(s, term.red)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn wline(file_path string, lnumber int, column int, message string) string {
|
|
|
|
return btext('$file_path:${lnumber + 1}:${column + 1}:') + btext(mtext(' warn:')) + rtext(' $message')
|
|
|
|
}
|
|
|
|
|
|
|
|
fn eline(file_path string, lnumber int, column int, message string) string {
|
|
|
|
return btext('$file_path:${lnumber + 1}:${column + 1}:') + btext(rtext(' error: $message'))
|
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
|
|
|
default_command = 'compile'
|
|
|
|
)
|
|
|
|
|
|
|
|
struct VCodeExample {
|
|
|
|
mut:
|
|
|
|
text []string
|
|
|
|
command string
|
|
|
|
sline int
|
|
|
|
eline int
|
|
|
|
}
|
|
|
|
|
|
|
|
enum MDFileParserState {
|
|
|
|
markdown
|
|
|
|
vexample
|
2020-11-18 20:28:28 +03:00
|
|
|
codeblock
|
2020-11-03 03:04:14 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
struct MDFile {
|
|
|
|
path string
|
|
|
|
mut:
|
|
|
|
examples []VCodeExample
|
|
|
|
current VCodeExample
|
|
|
|
state MDFileParserState = .markdown
|
|
|
|
}
|
|
|
|
|
|
|
|
fn (mut f MDFile) parse_line(lnumber int, line string) {
|
|
|
|
if line.starts_with('```v') {
|
|
|
|
if f.state == .markdown {
|
|
|
|
f.state = .vexample
|
|
|
|
mut command := line.replace('```v', '').trim_space()
|
|
|
|
if command == '' {
|
|
|
|
command = default_command
|
|
|
|
}
|
|
|
|
f.current = VCodeExample{
|
|
|
|
sline: lnumber
|
|
|
|
command: command
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
2020-11-18 20:28:28 +03:00
|
|
|
if line.starts_with('```') {
|
|
|
|
match f.state {
|
|
|
|
.vexample {
|
|
|
|
f.state = .markdown
|
|
|
|
f.current.eline = lnumber
|
|
|
|
f.examples << f.current
|
|
|
|
f.current = VCodeExample{}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
.codeblock {
|
|
|
|
f.state = .markdown
|
|
|
|
return
|
|
|
|
}
|
|
|
|
.markdown {
|
|
|
|
f.state = .codeblock
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2020-11-03 03:04:14 +03:00
|
|
|
}
|
|
|
|
if f.state == .vexample {
|
|
|
|
f.current.text << line
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn (mut f MDFile) dump() {
|
|
|
|
for e in f.examples {
|
|
|
|
eprintln('f.path: $f.path | example: $e')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn (mut f MDFile) check_examples() (int, int) {
|
|
|
|
mut errors := 0
|
|
|
|
mut oks := 0
|
|
|
|
vexe := pref.vexe_path()
|
|
|
|
for e in f.examples {
|
|
|
|
if e.command == 'ignore' {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if e.command == 'wip' {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
fname := os.base(f.path).replace('.md', '_md')
|
|
|
|
uid := rand.ulid()
|
|
|
|
vfile := os.join_path(os.temp_dir(), 'check_${fname}_example_${e.sline}__${e.eline}__${uid}.v')
|
|
|
|
mut should_cleanup_vfile := true
|
|
|
|
// eprintln('>>> checking example $vfile ...')
|
2020-12-06 00:54:41 +03:00
|
|
|
vcontent := e.text.join('\n') + '\n'
|
2020-12-04 21:34:05 +03:00
|
|
|
os.write_file(vfile, vcontent) or { panic(err) }
|
2020-11-03 03:04:14 +03:00
|
|
|
mut acommands := e.command.split(' ')
|
2020-12-06 00:54:41 +03:00
|
|
|
nofmt := 'nofmt' in acommands
|
2020-11-03 03:04:14 +03:00
|
|
|
for command in acommands {
|
|
|
|
match command {
|
|
|
|
'compile' {
|
2020-12-04 21:34:05 +03:00
|
|
|
res := os.system('"$vexe" -w -Wfatal-errors -o x.c $vfile')
|
2020-11-03 03:04:14 +03:00
|
|
|
os.rm('x.c') or { }
|
2020-12-06 00:54:41 +03:00
|
|
|
fmt_res := if nofmt { 0 } else { os.system('"$vexe" fmt -verify $vfile') }
|
|
|
|
if res != 0 || fmt_res != 0 {
|
|
|
|
if res != 0 {
|
|
|
|
eprintln(eline(f.path, e.sline, 0, 'example failed to compile'))
|
|
|
|
}
|
|
|
|
if fmt_res != 0 {
|
|
|
|
eprintln(eline(f.path, e.sline, 0, 'example is not formatted'))
|
|
|
|
}
|
2020-11-03 03:04:14 +03:00
|
|
|
eprintln(vcontent)
|
|
|
|
should_cleanup_vfile = false
|
|
|
|
errors++
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
oks++
|
|
|
|
}
|
2020-11-14 21:02:10 +03:00
|
|
|
'live' {
|
2020-12-04 21:34:05 +03:00
|
|
|
res := os.system('"$vexe" -w -Wfatal-errors -live -o x.c $vfile')
|
2020-12-06 00:54:41 +03:00
|
|
|
fmt_res := if nofmt { 0 } else { os.system('"$vexe" fmt -verify $vfile') }
|
|
|
|
if res != 0 || fmt_res != 0 {
|
|
|
|
if res != 0 {
|
|
|
|
eprintln(eline(f.path, e.sline, 0, 'example failed to compile with -live'))
|
|
|
|
}
|
|
|
|
if fmt_res != 0 {
|
|
|
|
eprintln(eline(f.path, e.sline, 0, 'example is not formatted'))
|
|
|
|
}
|
2020-11-14 21:02:10 +03:00
|
|
|
eprintln(vcontent)
|
|
|
|
should_cleanup_vfile = false
|
|
|
|
errors++
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
oks++
|
|
|
|
}
|
2020-11-03 03:04:14 +03:00
|
|
|
'failcompile' {
|
2020-12-04 21:34:05 +03:00
|
|
|
res := os.system('"$vexe" -w -Wfatal-errors -o x.c $vfile')
|
2020-11-03 03:04:14 +03:00
|
|
|
os.rm('x.c') or { }
|
|
|
|
if res == 0 {
|
|
|
|
eprintln(eline(f.path, e.sline, 0, '`failcompile` example compiled'))
|
|
|
|
eprintln(vcontent)
|
|
|
|
should_cleanup_vfile = false
|
|
|
|
errors++
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
oks++
|
|
|
|
}
|
|
|
|
'oksyntax' {
|
2020-12-04 21:34:05 +03:00
|
|
|
res := os.system('"$vexe" -w -Wfatal-errors -check-syntax $vfile')
|
2020-12-06 00:54:41 +03:00
|
|
|
fmt_res := if nofmt { 0 } else { os.system('"$vexe" fmt -verify $vfile') }
|
|
|
|
if res != 0 || fmt_res != 0 {
|
|
|
|
if res != 0 {
|
|
|
|
eprintln(eline(f.path, e.sline, 0, '`oksyntax` example with invalid syntax'))
|
|
|
|
}
|
|
|
|
if fmt_res != 0 {
|
|
|
|
eprintln(eline(f.path, e.sline, 0, '`oksyntax` example is not formatted'))
|
|
|
|
}
|
2020-11-03 03:04:14 +03:00
|
|
|
eprintln(vcontent)
|
|
|
|
should_cleanup_vfile = false
|
|
|
|
errors++
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
oks++
|
|
|
|
}
|
|
|
|
'badsyntax' {
|
2020-12-04 21:34:05 +03:00
|
|
|
res := os.system('"$vexe" -w -Wfatal-errors -check-syntax $vfile')
|
2020-11-03 03:04:14 +03:00
|
|
|
if res == 0 {
|
|
|
|
eprintln(eline(f.path, e.sline, 0, '`badsyntax` example can be parsed fine'))
|
|
|
|
eprintln(vcontent)
|
|
|
|
should_cleanup_vfile = false
|
|
|
|
errors++
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
oks++
|
|
|
|
}
|
2020-12-06 00:54:41 +03:00
|
|
|
'nofmt' {}
|
2020-11-03 03:04:14 +03:00
|
|
|
else {
|
|
|
|
eprintln(eline(f.path, e.sline, 0, 'unrecognized command: "$command", use one of: wip/ignore/compile/failcompile/oksyntax/badsyntax'))
|
|
|
|
should_cleanup_vfile = false
|
|
|
|
errors++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if should_cleanup_vfile {
|
2020-12-04 21:34:05 +03:00
|
|
|
os.rm(vfile) or { panic(err) }
|
2020-11-03 03:04:14 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return errors, oks
|
|
|
|
}
|