#![allow(unsafe_code)]
pub mod table;
use std::fmt;
use cxx::UniquePtr;
#[cxx::bridge(namespace = "ts_grammar")]
mod ffi {
unsafe extern "C++" {
include!("ts-grammar/cxx/facade.h");
type TokenizerInfo;
type Compiler;
type Compiled;
type Matcher;
fn tokenizer_info_from_json(json: &str) -> Result<UniquePtr<TokenizerInfo>>;
fn tokenizer_vocab_size(info: &TokenizerInfo) -> i32;
fn new_compiler(info: &TokenizerInfo, max_threads: i32) -> Result<UniquePtr<Compiler>>;
fn compile_json_schema(
compiler: &Compiler,
schema: &str,
any_whitespace: bool,
) -> Result<UniquePtr<Compiled>>;
fn compile_regex(compiler: &Compiler, regex: &str) -> Result<UniquePtr<Compiled>>;
fn compile_ebnf(compiler: &Compiler, ebnf: &str) -> Result<UniquePtr<Compiled>>;
fn compile_structural_tag(compiler: &Compiler, json: &str) -> Result<UniquePtr<Compiled>>;
fn compiled_memory_size(compiled: &Compiled) -> usize;
fn new_matcher(compiled: &Compiled, max_rollback_tokens: i32)
-> Result<UniquePtr<Matcher>>;
fn matcher_accept_token(matcher: Pin<&mut Matcher>, token: i32) -> bool;
fn matcher_fill_bitmask(
matcher: Pin<&mut Matcher>,
data: usize,
rows: i64,
width: i64,
row: i32,
) -> Result<()>;
fn matcher_traverse_draft_tree(
matcher: Pin<&mut Matcher>,
next_node: &[i64],
next_sibling: &[i64],
draft_tokens: &[i64],
bitmask: usize,
width: i64,
) -> Result<()>;
fn matcher_rollback(matcher: Pin<&mut Matcher>, num_tokens: i32) -> Result<()>;
fn matcher_is_terminated(matcher: &Matcher) -> bool;
fn matcher_stop_token_ids(matcher: &Matcher) -> Vec<i32>;
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Error(pub String);
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
impl std::error::Error for Error {}
impl From<cxx::Exception> for Error {
fn from(error: cxx::Exception) -> Self {
Self(error.what().to_owned())
}
}
pub struct TokenizerInfo(UniquePtr<ffi::TokenizerInfo>);
unsafe impl Send for TokenizerInfo {}
unsafe impl Sync for TokenizerInfo {}
impl TokenizerInfo {
pub fn from_json(json: &str) -> Result<Self, Error> {
Ok(Self(ffi::tokenizer_info_from_json(json)?))
}
pub fn vocab_size(&self) -> i32 {
ffi::tokenizer_vocab_size(&self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum GrammarKind {
JsonSchema,
Regex,
Ebnf,
StructuralTag,
}
pub struct Compiler(UniquePtr<ffi::Compiler>);
unsafe impl Send for Compiler {}
unsafe impl Sync for Compiler {}
impl Compiler {
pub fn new(info: &TokenizerInfo, max_threads: i32) -> Result<Self, Error> {
Ok(Self(ffi::new_compiler(&info.0, max_threads)?))
}
pub fn compile(
&self,
kind: GrammarKind,
source: &str,
any_whitespace: bool,
) -> Result<CompiledGrammar, Error> {
let compiled = match kind {
GrammarKind::JsonSchema => ffi::compile_json_schema(&self.0, source, any_whitespace),
GrammarKind::Regex => ffi::compile_regex(&self.0, source),
GrammarKind::Ebnf => ffi::compile_ebnf(&self.0, source),
GrammarKind::StructuralTag => ffi::compile_structural_tag(&self.0, source),
}?;
Ok(CompiledGrammar(compiled))
}
}
pub struct CompiledGrammar(UniquePtr<ffi::Compiled>);
unsafe impl Send for CompiledGrammar {}
unsafe impl Sync for CompiledGrammar {}
impl CompiledGrammar {
pub fn memory_size_bytes(&self) -> usize {
ffi::compiled_memory_size(&self.0)
}
pub fn matcher(&self, max_rollback_tokens: i32) -> Result<Matcher, Error> {
Ok(Matcher(ffi::new_matcher(&self.0, max_rollback_tokens)?))
}
}
pub struct Matcher(UniquePtr<ffi::Matcher>);
unsafe impl Send for Matcher {}
impl Matcher {
pub fn accept_token(&mut self, token: i32) -> bool {
ffi::matcher_accept_token(self.0.pin_mut(), token)
}
pub unsafe fn fill_bitmask(
&mut self,
data: usize,
rows: i64,
width: i64,
row: i32,
) -> Result<(), Error> {
Ok(ffi::matcher_fill_bitmask(
self.0.pin_mut(),
data,
rows,
width,
row,
)?)
}
pub unsafe fn traverse_draft_tree(
&mut self,
next_node: &[i64],
next_sibling: &[i64],
draft_tokens: &[i64],
bitmask: usize,
width: i64,
) -> Result<(), Error> {
Ok(ffi::matcher_traverse_draft_tree(
self.0.pin_mut(),
next_node,
next_sibling,
draft_tokens,
bitmask,
width,
)?)
}
pub fn rollback(&mut self, num_tokens: i32) -> Result<(), Error> {
Ok(ffi::matcher_rollback(self.0.pin_mut(), num_tokens)?)
}
pub fn is_terminated(&self) -> bool {
ffi::matcher_is_terminated(&self.0)
}
pub fn stop_token_ids(&self) -> Vec<i32> {
ffi::matcher_stop_token_ids(&self.0)
}
}