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:
Vendored
+147
@@ -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));
|
||||
}
|
||||
}
|
||||
Vendored
+24
@@ -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 }
|
||||
}
|
||||
}
|
||||
Vendored
+112
@@ -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(())
|
||||
}
|
||||
Reference in New Issue
Block a user