feat: add extended language validators and fix tokenizer crash bugs

Expand nimcheck with validators and tokenizers for C, C++, C#, Go, Rust,
Ruby, CSS, SQL, Markdown, Dockerfile, Makefile, Kotlin, Lua, Swift,
TypeScript, and XML. Register all flavors in the validator factory and
improve auto-detection scoring for the new languages.

Fix infinite tokenizer loops that caused OOM kills: closeBracket now
advances position, finishTokenizeStep guards stalled tokenization, and
Jinja/JS tokenizers no longer double-advance on brackets.

Fix block-balance false positives in Lua (for/do) and Ruby (postfix
unless), SQL trailing-comma detection across whitespace, and Makefile
tab literals in test fixtures.
This commit is contained in:
2026-07-15 06:49:45 +02:00
parent df2b327a5d
commit 5a7c24f936
109 changed files with 8811 additions and 121 deletions
+147
View File
@@ -0,0 +1,147 @@
use std::collections::{HashMap, HashSet, BTreeMap};
use std::rc::Rc;
use std::cell::RefCell;
use std::sync::{Arc, Mutex};
use std::marker::PhantomData;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Status {
Active,
Inactive,
Pending,
Banned,
}
#[derive(Debug)]
pub struct TreeNode<T> {
pub value: T,
pub left: Option<Box<TreeNode<T>>>,
pub right: Option<Box<TreeNode<T>>>,
}
impl<T: Ord> TreeNode<T> {
pub fn insert(&mut self, value: T) {
let target = if value <= self.value {
&mut self.left
} else {
&mut self.right
};
match target {
Some(node) => node.insert(value),
None => *target = Some(Box::new(TreeNode {
value,
left: None,
right: None,
})),
}
}
pub fn contains(&self, value: &T) -> bool {
&self.value == value
|| self.left.as_ref().map_or(false, |n| n.contains(value))
|| self.right.as_ref().map_or(false, |n| n.contains(value))
}
}
pub trait Monoid {
fn empty() -> Self;
fn combine(&self, other: &Self) -> Self;
}
impl Monoid for i32 {
fn empty() -> Self { 0 }
fn combine(&self, other: &Self) -> Self { self + other }
}
impl Monoid for String {
fn empty() -> Self { String::new() }
fn combine(&self, other: &Self) -> Self { format!("{}{}", self, other) }
}
pub fn reduce<T: Monoid>(items: &[T]) -> T {
items.iter().fold(T::empty(), |acc, x| acc.combine(x))
}
macro_rules! vec_of_strings {
($($x:expr),*) => (vec![$($x.to_string()),*]);
}
pub struct Lazy<T, F = fn() -> T>
where
F: Fn() -> T,
{
value: Option<T>,
factory: F,
}
impl<T, F: Fn() -> T> Lazy<T, F> {
pub fn new(factory: F) -> Self {
Self {
value: None,
factory,
}
}
pub fn get(&mut self) -> &T {
self.value.get_or_insert_with(&self.factory)
}
}
pub async fn parallel_map<T, U, F>(items: Vec<T>, f: F) -> Vec<U>
where
T: Send + 'static,
U: Send + 'static,
F: Fn(T) -> U + Send + Sync + 'static,
{
use tokio::task;
let mut handles = Vec::new();
for item in items {
handles.push(task::spawn(async move { f(item) }));
}
let mut results = Vec::new();
for handle in handles {
results.push(handle.await.unwrap());
}
results
}
pub struct CustomIterator<T> {
items: Vec<T>,
index: usize,
}
impl<T: Clone> Iterator for CustomIterator<T> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
if self.index < self.items.len() {
let item = self.items[self.index].clone();
self.index += 1;
Some(item)
} else {
None
}
}
}
const DEFAULT_PORT: u16 = 8080;
static APP_NAME: &str = "nimcheck-rs";
thread_local! {
static CACHE: RefCell<HashMap<String, String>> = RefCell::new(HashMap::new());
}
unsafe impl Send for User {}
unsafe impl Sync for User {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_insert() {
let mut root = TreeNode { value: 5, left: None, right: None };
root.insert(3);
root.insert(7);
assert!(root.contains(&3));
assert!(root.contains(&7));
}
}
+24
View File
@@ -0,0 +1,24 @@
fn main() {
let x = 42
let y = vec![1, 2,
println!("{}", x
fn missing_type(x) -> i32 {
x
}
match x {
1 => println!("one"),
2 => println!("two"),
}
struct Point {
x: i32,
y: i32,
}
impl Point {
fn new(x: i32, y: i32 -> Self {
Self { x, y }
}
}
+112
View File
@@ -0,0 +1,112 @@
use std::collections::HashMap;
use std::fmt::{Debug, Display};
use std::str::FromStr;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct User {
pub id: u64,
pub name: String,
pub email: String,
pub active: bool,
pub tags: Vec<String>,
}
impl User {
pub fn new(id: u64, name: &str, email: &str) -> Self {
Self {
id,
name: name.to_string(),
email: email.to_string(),
active: true,
tags: Vec::new(),
}
}
pub fn deactivate(&mut self) {
self.active = false;
}
pub fn add_tag(&mut self, tag: &str) {
self.tags.push(tag.to_string());
}
}
impl Display for User {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} <{}> [{}]", self.name, self.email, self.id)
}
}
pub enum ApiError {
NotFound(String),
Unauthorized,
Internal(String),
}
impl Debug for ApiError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ApiError::NotFound(msg) => write!(f, "NotFound({})", msg),
ApiError::Unauthorized => write!(f, "Unauthorized"),
ApiError::Internal(msg) => write!(f, "Internal({})", msg),
}
}
}
pub type Result<T> = std::result::Result<T, ApiError>;
pub trait Repository<T> {
fn find_by_id(&self, id: u64) -> Result<T>;
fn save(&mut self, item: T) -> Result<()>;
fn delete(&mut self, id: u64) -> Result<()>;
}
pub struct UserRepository {
users: HashMap<u64, User>,
}
impl UserRepository {
pub fn new() -> Self {
Self {
users: HashMap::new(),
}
}
}
impl Repository<User> for UserRepository {
fn find_by_id(&self, id: u64) -> Result<User> {
self.users
.get(&id)
.cloned()
.ok_or_else(|| ApiError::NotFound(format!("User {}", id)))
}
fn save(&mut self, user: User) -> Result<()> {
let id = user.id;
self.users.insert(id, user);
Ok(())
}
fn delete(&mut self, id: u64) -> Result<()> {
self.users.remove(&id);
Ok(())
}
}
fn process_users(repo: &UserRepository) -> Result<Vec<String>> {
let names: Vec<String> = repo
.users
.values()
.filter(|u| u.active)
.map(|u| u.name.clone())
.collect();
Ok(names)
}
fn main() -> Result<()> {
let mut repo = UserRepository::new();
repo.save(User::new(1, "Alice", "alice@example.com"))?;
let names = process_users(&repo)?;
println!("{:?}", names);
Ok(())
}