Skip to content

Commit

Permalink
添加一个简单的Lint, 以检查生成逻辑时的常见问题
Browse files Browse the repository at this point in the history
- 修复逻辑分割器对空字符串的解析错误
  • Loading branch information
A4-Tacks committed Feb 19, 2024
1 parent ed43e65 commit 680ee8f
Show file tree
Hide file tree
Showing 8 changed files with 589 additions and 15 deletions.
14 changes: 12 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "mindustry_logic_bang_lang"
version = "0.14.17"
version = "0.14.18"
edition = "2021"

authors = ["A4-Tacks <[email protected]>"]
Expand All @@ -19,13 +19,15 @@ members = [
"./tools/tag_code",
"./tools/utils",
"./tools/var_utils",
"./tools/logic_lint",
]

[dependencies]
tag_code = { path = "./tools/tag_code", version = "*" }
display_source = { path = "./tools/display_source", version = "*" }
parser = { path = "./tools/parser", version = "*" }
syntax = { path = "./tools/syntax", version = "*" }
logic_lint = { path = "./tools/logic_lint", version = "*" }

[profile.release]
strip = true
Expand Down
36 changes: 26 additions & 10 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ use parser::{
},
};
use tag_code::TagCodes;
use logic_lint::Source;

/// 带有错误前缀, 并且文本为红色的eprintln
macro_rules! err {
Expand Down Expand Up @@ -62,6 +63,7 @@ pub const HELP_MSG: &str = concat_lines! {
"\t", "r: compile MdtLogicCode to MdtBangLang";
"\t", "R: compile MdtLogicCode to MdtBangLang (Builded TagDown)";
"\t", "C: compile MdtTagCode to MdtLogicCode";
"\t", "l: lint MdtLogicCode";
;
"input from stdin";
"output to stdout";
Expand Down Expand Up @@ -98,7 +100,7 @@ fn main() {
);
let mut src = read_stdin();
for mode in modes {
src = mode.compile(&src)
src = mode.compile(src)
}
println!("{src}")
}
Expand All @@ -111,33 +113,34 @@ enum CompileMode {
MdtLogicToMdtTagCode { tag_down: bool },
MdtLogicToBang { tag_down: bool },
MdtTagCodeToMdtLogic,
LintLogic,
}
impl CompileMode {
fn compile(&self, src: &str) -> String {
fn compile(&self, src: String) -> String {
match *self {
Self::BangToMdtLogic => {
let ast = build_ast(src);
let ast = build_ast(&src);
let mut meta = compile_ast(ast);
build_tag_down(&mut meta);
let logic_lines = meta.tag_codes_mut().compile().unwrap();
logic_lines.join("\n")
},
Self::BangToASTDebug => {
let ast = build_ast(src);
let ast = build_ast(&src);
format!("{ast:#?}")
},
Self::BangToASTDisplay => {
let ast = build_ast(src);
let ast = build_ast(&src);
display_ast(&ast)
},
Self::BangToMdtTagCode { tag_down } => {
let ast = build_ast(src);
let ast = build_ast(&src);
let mut meta = compile_ast(ast);
if tag_down { build_tag_down(&mut meta); }
meta.tag_codes().to_string()
},
Self::MdtLogicToMdtTagCode { tag_down } => {
match TagCodes::from_str(src) {
match TagCodes::from_str(&src) {
Ok(mut lines) => {
if tag_down {
lines.build_tagdown().unwrap();
Expand All @@ -152,7 +155,7 @@ impl CompileMode {
}
},
Self::MdtLogicToBang { tag_down } => {
match TagCodes::from_str(src) {
match TagCodes::from_str(&src) {
Ok(mut lines) => {
if tag_down {
lines.build_tagdown().unwrap();
Expand Down Expand Up @@ -183,12 +186,17 @@ impl CompileMode {
}
},
Self::MdtTagCodeToMdtLogic => {
let tag_codes = TagCodes::from_tag_lines(src);
let tag_codes = TagCodes::from_tag_lines(&src);
let mut meta = CompileMeta::with_tag_codes(tag_codes);
build_tag_down(&mut meta);
let logic_lines = meta.tag_codes_mut().compile().unwrap();
logic_lines.join("\n")
},
Self::LintLogic => {
let linter = Source::from_str(&src);
linter.show_lints();
src
},
}
}
}
Expand All @@ -207,6 +215,7 @@ impl TryFrom<char> for CompileMode {
'r' => Self::MdtLogicToBang { tag_down: false },
'R' => Self::MdtLogicToBang { tag_down: true },
'C' => Self::MdtTagCodeToMdtLogic,
'l' => Self::LintLogic,
mode => return Err(mode),
})
}
Expand Down Expand Up @@ -337,7 +346,14 @@ fn unwrap_parse_err(result: ParseResult<'_>, src: &str) -> Expand {
let [loc] = get_locations(src, [location]);
let view = &src[
location
..src.len().min(location+MAX_INVALID_TOKEN_VIEW)
..
src.len().min(
src[location..]
.char_indices()
.map(|(i, _ch)| location+i)
.take(MAX_INVALID_TOKEN_VIEW+1)
.last()
.unwrap_or(location))
];
err!(
"在位置 {:?} 处找到无效的令牌: {:?}",
Expand Down
11 changes: 11 additions & 0 deletions tools/logic_lint/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "logic_lint"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
lazy-regex = "3.0.2"
tag_code = { path = "../tag_code", version = "*" }
var_utils = { path = "../var_utils", version = "*" }
147 changes: 147 additions & 0 deletions tools/logic_lint/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
pub mod lints;

use core::fmt;
use std::{borrow::Cow, ops::Deref};

use crate::lints::{ShowLint, Lint};
use tag_code::mdt_logic_split_unwraped;

const LIGHT_ARGS_BEGIN: &str = "\x1b[7m";
const LIGHT_ARGS_END: &str = "\x1b[27m";

#[derive(Debug)]
pub struct Line<'a> {
args: Vec<Var<'a>>,
}
impl<'a> Line<'a> {
pub fn from_line(lineno: usize, s: &'a str) -> Self {
let logic_args = mdt_logic_split_unwraped(s);
assert_ne!(
logic_args.len(), 0,
"line args count by zero ({})", lineno);
let args = logic_args
.into_iter()
.enumerate()
.map(|(i, arg)| Var::new(lineno, i, arg))
.collect();

Self { args }
}

pub fn hint_args(&self, hints: &[usize]) -> Vec<Cow<'_, str>> {
self.args().into_iter()
.enumerate()
.map(|(i, arg)| if hints.contains(&i) {
format!(
"{}{}{}",
LIGHT_ARGS_BEGIN,
arg.value(),
LIGHT_ARGS_END,
).into()
} else {
arg.value().into()
})
.collect()
}

pub fn lint(&'a self, src: &'a Source<'_>) -> Vec<lints::Lint> {
lints::lint(src, self)
}

pub fn lineno(&self) -> usize {
self.args.first().unwrap().lineno
}

pub fn args(&self) -> &[Var<'_>] {
self.args.as_ref()
}
}

#[derive(Debug)]
pub struct Source<'a> {
lines: Vec<Line<'a>>,
}
impl<'a> Source<'a> {
pub fn from_str(s: &'a str) -> Self {
let lines = s.lines().enumerate()
.map(|(lineno, line)| Line::from_line(lineno, line))
.collect();

Self {
lines,
}
}

/// 返回指定行周围的行, 而不包括指定行
pub fn view_lines(
&self,
lineno: usize,
rng: (usize, usize),
) -> (&[Line<'_>], &[Line<'_>]) {
let (head, tail) = (
&self.lines[..lineno],
&self.lines[lineno+1..],
);
let head = &head[
(head.len().checked_sub(rng.0).unwrap_or_default())..];
let tail = &tail[..rng.1.min(tail.len())];
(head, tail)
}

pub fn lint(&self) -> Vec<lints::Lint> {
self.lines.iter()
.map(|line| line.lint(self))
.flatten()
.collect()
}

pub fn lines(&self) -> &[Line<'_>] {
self.lines.as_ref()
}

pub fn show_lints(&self) {
struct LintFmtter<'a>(&'a Source<'a>, &'a Lint<'a>);
impl fmt::Display for LintFmtter<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.1.show_lint(self.0, f)
}
}
for lint in self.lint() {
let fmtter = LintFmtter(self, &lint);
eprintln!("{}", fmtter)
}
}
}

#[derive(Debug)]
pub struct Var<'a> {
lineno: usize,
arg_idx: usize,
value: &'a str,
}

impl<'a> Deref for Var<'a> {
type Target = str;

fn deref(&self) -> &Self::Target {
self.value()
}
}

impl<'a> Var<'a> {
pub fn new(lineno: usize, arg_idx: usize, value: &'a str) -> Self {
Self { lineno, arg_idx, value }
}

pub fn value(&self) -> &str {
self.value
}

pub fn arg_idx(&self) -> usize {
self.arg_idx
}

pub fn lineno(&self) -> usize {
self.lineno
}
}
Loading

0 comments on commit 680ee8f

Please sign in to comment.