Skip to content

Commit

Permalink
Merge pull request #6 from TheNathannator/arson
Browse files Browse the repository at this point in the history
dtacheck: Use Arson for DTA parsing
  • Loading branch information
DarkRTA authored Nov 7, 2024
2 parents 121bea1 + b0710fa commit 50f56c9
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 597 deletions.
3 changes: 1 addition & 2 deletions crates/dtacheck/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
arson = { git = "https://github.com/hmxmilohax/arson", version = "0.1.0", rev = "c0c102c86f3734d41623b2fd4bfc78bf74302500" }
clap = { version = "4.4.12", features = ["derive"] }
codespan-reporting = "0.11.1"
derive_more = "0.99.17"
logos = "0.13.0"
97 changes: 0 additions & 97 deletions crates/dtacheck/src/lexer.rs

This file was deleted.

2 changes: 0 additions & 2 deletions crates/dtacheck/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
pub mod lexer;
pub mod linter;
pub mod parser;
130 changes: 45 additions & 85 deletions crates/dtacheck/src/linter.rs
Original file line number Diff line number Diff line change
@@ -1,44 +1,54 @@
use std::collections::HashMap;
use std::ops::Range;

use arson::parse::parser as arson_parse;
use arson_parse::{Expression, ExpressionKind};
use codespan_reporting::diagnostic::Diagnostic;
use codespan_reporting::diagnostic::Label;

use crate::lexer::Token;
use crate::lexer::TokenKind;
use crate::parser::Node;
use crate::parser::NodeKind;

pub trait Lint {
fn to_codespan(&self, id: usize) -> Diagnostic<usize>;
}

pub fn lint_file(
ast: &[Node],
tokens: &[Token],
funcs: &Function,
) -> Vec<Box<dyn Lint>> {
impl<'src> Lint for arson_parse::ParseError<'src> {
fn to_codespan(&self, id: usize) -> Diagnostic<usize> {
self.to_diagnostic(id)
}
}

pub fn lint_file(ast: &[Expression], funcs: &Function) -> Vec<Box<dyn Lint>> {
let mut lints = Vec::new();
lint_node(&mut lints, ast, funcs);
lint_preprocs(&mut lints, tokens);
lints
}

fn lint_node(lints: &mut Vec<Box<dyn Lint>>, ast: &[Node], funcs: &Function) {
fn lint_node(
lints: &mut Vec<Box<dyn Lint>>,
ast: &[Expression],
funcs: &Function,
) {
for node in ast {
match &node.kind {
NodeKind::Array(array)
| NodeKind::Prop(array)
| NodeKind::Define(_, array) => lint_node(lints, array, funcs),
NodeKind::Stmt(array) => {
ExpressionKind::Array(array) | ExpressionKind::Property(array) => {
lint_node(lints, &array, funcs)
}
ExpressionKind::Define(_, array) => {
lint_node(lints, &array.exprs, funcs)
}
ExpressionKind::Command(array) => {
lint_node(lints, array, funcs);

let has_preprocessor_directive =
array.iter().any(Node::is_preproc);
let has_preprocessor_directive = array.iter().any(|e| {
matches!(e.kind, ExpressionKind::Conditional { .. })
});

if !has_preprocessor_directive {
lint_fn_args(lints, array, node.span.clone(), funcs);
lint_switch_fallthrough(lints, array, node.span.clone());
lint_fn_args(lints, array, node.location.clone(), funcs);
lint_switch_fallthrough(
lints,
array,
node.location.clone(),
);
}
}
_ => (),
Expand Down Expand Up @@ -89,11 +99,15 @@ impl Default for Function {
}

impl Function {
pub fn lookup(&self, stmt: &[Node]) -> (&Function, usize) {
pub fn lookup(&self, stmt: &[Expression]) -> (&Function, usize) {
self.lookup_inner(stmt, 0)
}

fn lookup_inner(&self, stmt: &[Node], depth: usize) -> (&Function, usize) {
fn lookup_inner(
&self,
stmt: &[Expression],
depth: usize,
) -> (&Function, usize) {
if self.children.is_empty() {
return (self, depth);
};
Expand All @@ -102,7 +116,7 @@ impl Function {
return (self, depth);
};

let NodeKind::Symbol(ref sym) = node.kind else {
let ExpressionKind::Symbol(sym) = node.kind else {
return (self, depth);
};

Expand Down Expand Up @@ -132,7 +146,7 @@ impl Function {

fn lint_fn_args(
lints: &mut Vec<Box<dyn Lint>>,
stmt: &[Node],
stmt: &[Expression],
span: Range<usize>,
funcs: &Function,
) {
Expand All @@ -145,74 +159,20 @@ fn lint_fn_args(
}
}

fn generate_function_name(stmt: &[Node]) -> String {
fn generate_function_name(stmt: &[Expression]) -> String {
let list: Vec<&str> = stmt
.iter()
.map(|x| match &x.kind {
NodeKind::Symbol(sym) => Some(sym),
.map(|x| match x.kind {
ExpressionKind::Symbol(sym) => Some(sym),
_ => None,
})
.take_while(Option::is_some)
.map(|x| x.unwrap().as_str())
.map(|x| x.unwrap())
.collect();

list.join(" ")
}

// preprocesor directives
enum PreProcLint {
Unmatched(Range<usize>),
Extra(Range<usize>),
}

impl Lint for PreProcLint {
fn to_codespan(&self, id: usize) -> Diagnostic<usize> {
match self {
Self::Unmatched(s) => Diagnostic::error()
.with_message("unmatched preprocessing directive")
.with_labels(vec![Label::primary(id, s.clone())]),
Self::Extra(s) => Diagnostic::error()
.with_message("extraneous preprocessing directive")
.with_labels(vec![Label::primary(id, s.clone())]),
}
}
}

fn lint_preprocs(lints: &mut Vec<Box<dyn Lint>>, tokens: &[Token]) {
let mut directive_stack: Vec<(Range<usize>, bool)> = Vec::new();
for token in tokens {
match token.kind {
TokenKind::IfNDef | TokenKind::IfDef => {
directive_stack.push((token.span.clone(), false));
}
TokenKind::Else => {
if let Some(entry) = directive_stack.pop() {
if entry.1 {
lints.push(Box::new(PreProcLint::Extra(
token.span.clone(),
)));
}
directive_stack.push((token.span.clone(), true));
} else {
lints
.push(Box::new(PreProcLint::Extra(token.span.clone())));
}
}
TokenKind::EndIf => {
if directive_stack.pop().is_none() {
lints
.push(Box::new(PreProcLint::Extra(token.span.clone())));
}
}
_ => (),
}
}

for lint in directive_stack {
lints.push(Box::new(PreProcLint::Unmatched(lint.0)));
}
}

// switch fallthough

struct SwitchFallthroughLint(Range<usize>, Range<usize>);
Expand All @@ -231,14 +191,14 @@ impl Lint for SwitchFallthroughLint {

fn lint_switch_fallthrough(
lints: &mut Vec<Box<dyn Lint>>,
stmt: &[Node],
stmt: &[Expression],
span: Range<usize>,
) {
if stmt.is_empty() {
return;
}

let NodeKind::Symbol(ref sym) = stmt[0].kind else {
let ExpressionKind::Symbol(sym) = stmt[0].kind else {
return;
};

Expand All @@ -250,7 +210,7 @@ fn lint_switch_fallthrough(
return;
};

if last_node.kind.is_array() {
if matches!(last_node.kind, ExpressionKind::Array(_)) {
let pos = span.end - 1;
lints.push(Box::new(SwitchFallthroughLint(span, pos..pos)))
}
Expand Down
13 changes: 7 additions & 6 deletions crates/dtacheck/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@ use std::fs;
use std::path::Path;
use std::path::PathBuf;

use arson::parse::lexer;
use arson::parse::parser;
use clap::Parser as ClapParser;
use codespan_reporting::files::SimpleFiles;
use codespan_reporting::term;
use codespan_reporting::term::termcolor::ColorChoice;
use codespan_reporting::term::termcolor::StandardStream;
use codespan_reporting::term::Chars;
use dtacheck::lexer;
use dtacheck::linter::lint_file;
use dtacheck::linter::Function;
use dtacheck::linter::Lint;
use dtacheck::parser;

#[derive(ClapParser)]
struct Args {
Expand Down Expand Up @@ -54,7 +54,10 @@ fn main() {
let file_id = files.add(args.file.to_str().unwrap(), &data);

let tokens = lexer::lex(&data);
let (ast, diagnostics) = parser::parse(&tokens);
let (ast, diagnostics) = match parser::parse(tokens) {
Ok(ast) => (ast, Vec::new()),
Err(errors) => (Vec::new(), errors),
};

let writer = StandardStream::stderr(ColorChoice::Auto);
let config = codespan_reporting::term::Config {
Expand All @@ -71,9 +74,7 @@ fn main() {
);
}

let Ok(ast) = ast else { return };

for diag in lint_file(&ast, &tokens, &funcs) {
for diag in lint_file(&ast, &funcs) {
let _ = term::emit(
&mut writer.lock(),
&config,
Expand Down
Loading

0 comments on commit 50f56c9

Please sign in to comment.