use super::functions::*;
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct AnnotatedToken {
pub token: Token,
pub annotations: Vec<TokenAnnotation>,
}
#[allow(dead_code)]
impl AnnotatedToken {
pub fn plain(token: Token) -> Self {
Self {
token,
annotations: Vec::new(),
}
}
pub fn annotate(&mut self, ann: TokenAnnotation) {
self.annotations.push(ann);
}
pub fn has_annotation(&self, ann: &TokenAnnotation) -> bool {
self.annotations.contains(ann)
}
pub fn is_binding_site(&self) -> bool {
self.annotations
.iter()
.any(|a| matches!(a, TokenAnnotation::BindingSite(_)))
}
pub fn is_use_site(&self) -> bool {
self.annotations
.iter()
.any(|a| matches!(a, TokenAnnotation::UseSite(_)))
}
pub fn resolved_name(&self) -> Option<&str> {
self.annotations.iter().find_map(|a| {
if let TokenAnnotation::ResolvedName(s) = a {
Some(s.as_str())
} else {
None
}
})
}
}
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TokenRole {
DeclStart,
TacticKeyword,
TypeLevel,
TermLevel,
Punctuation,
Name,
Literal,
Eof,
Error,
}
#[derive(Debug, Clone, Default)]
pub struct TokenStats {
pub total: usize,
pub identifiers: usize,
pub keywords: usize,
pub nat_literals: usize,
pub string_literals: usize,
pub operators: usize,
pub errors: usize,
pub doc_comments: usize,
}
impl TokenStats {
pub fn compute(tokens: &[Token]) -> Self {
let mut stats = Self {
total: tokens.len(),
..Default::default()
};
for tok in tokens {
match &tok.kind {
TokenKind::Ident(_) => stats.identifiers += 1,
TokenKind::Nat(_) => stats.nat_literals += 1,
TokenKind::String(_) => stats.string_literals += 1,
TokenKind::Error(_) => stats.errors += 1,
TokenKind::DocComment(_) => stats.doc_comments += 1,
k if Token {
kind: k.clone(),
span: tok.span.clone(),
}
.is_keyword() =>
{
stats.keywords += 1
}
k if is_arithmetic_op(k) || is_comparison_op(k) || is_logical_op(k) => {
stats.operators += 1;
}
_ => {}
}
}
stats
}
pub fn has_errors(&self) -> bool {
self.errors > 0
}
}
pub struct TokenStream {
tokens: Vec<Token>,
pos: usize,
}
impl TokenStream {
pub fn new(tokens: Vec<Token>) -> Self {
Self { tokens, pos: 0 }
}
pub fn peek(&self) -> Option<&Token> {
self.tokens.get(self.pos)
}
pub fn peek_ahead(&self, n: usize) -> Option<&Token> {
self.tokens.get(self.pos + n)
}
pub fn current_kind(&self) -> Option<&TokenKind> {
self.tokens.get(self.pos).map(|t| &t.kind)
}
pub fn at_keyword(&self, kw: &TokenKind) -> bool {
self.tokens
.get(self.pos)
.is_some_and(|t| std::mem::discriminant(&t.kind) == std::mem::discriminant(kw))
}
pub fn at_ident(&self, name: &str) -> bool {
matches!(
self.tokens.get(self.pos).map(| t | & t.kind), Some(TokenKind::Ident(s)) if s
== name
)
}
pub fn is_eof(&self) -> bool {
self.pos >= self.tokens.len()
|| matches!(
self.tokens.get(self.pos).map(|t| &t.kind),
Some(TokenKind::Eof)
)
}
#[allow(clippy::should_implement_trait)]
pub fn next(&mut self) -> Option<&Token> {
if self.pos < self.tokens.len() {
let tok = &self.tokens[self.pos];
self.pos += 1;
Some(tok)
} else {
None
}
}
pub fn consume_if(&mut self, kind: &TokenKind) -> bool {
if self.tokens.get(self.pos).is_some_and(|t| &t.kind == kind) {
self.pos += 1;
true
} else {
false
}
}
pub fn expect(&mut self, kind: &TokenKind) -> Result<&Token, String> {
match self.tokens.get(self.pos) {
Some(t) if &t.kind == kind => {
self.pos += 1;
Ok(&self.tokens[self.pos - 1])
}
Some(t) => Err(format!(
"Expected {} but got {} at line {}",
kind, t.kind, t.span.line
)),
None => Err(format!("Expected {} but got end of file", kind)),
}
}
pub fn expect_ident(&mut self) -> Result<String, String> {
match self.tokens.get(self.pos) {
Some(Token {
kind: TokenKind::Ident(s),
..
}) => {
let name = s.clone();
self.pos += 1;
Ok(name)
}
Some(t) => Err(format!(
"Expected identifier but got {} at line {}",
t.kind, t.span.line
)),
None => Err("Expected identifier but got end of file".to_string()),
}
}
pub fn snapshot(&self) -> usize {
self.pos
}
pub fn restore(&mut self, snap: usize) {
self.pos = snap;
}
pub fn remaining(&self) -> usize {
self.tokens.len().saturating_sub(self.pos)
}
pub fn current_span(&self) -> Span {
self.tokens
.get(self.pos)
.map(|t| t.span.clone())
.unwrap_or_else(|| Span::new(0, 0, 1, 1))
}
pub fn skip_until(&mut self, kinds: &[TokenKind]) {
while let Some(t) = self.tokens.get(self.pos) {
if kinds
.iter()
.any(|k| std::mem::discriminant(&t.kind) == std::mem::discriminant(k))
{
break;
}
self.pos += 1;
}
}
pub fn drain_remaining(&mut self) -> Vec<Token> {
let rest = self.tokens[self.pos..].to_vec();
self.pos = self.tokens.len();
rest
}
pub fn total_len(&self) -> usize {
self.tokens.len()
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Token {
pub kind: TokenKind,
pub span: Span,
}
impl Token {
pub fn new(kind: TokenKind, span: Span) -> Self {
Self { kind, span }
}
pub fn is_ident(&self) -> bool {
matches!(self.kind, TokenKind::Ident(_))
}
pub fn as_ident(&self) -> Option<&str> {
match &self.kind {
TokenKind::Ident(s) => Some(s),
_ => None,
}
}
pub fn is_keyword(&self) -> bool {
matches!(
self.kind,
TokenKind::Axiom
| TokenKind::Definition
| TokenKind::Theorem
| TokenKind::Lemma
| TokenKind::Fun
| TokenKind::Forall
| TokenKind::Let
| TokenKind::In
| TokenKind::If
| TokenKind::Then
| TokenKind::Else
| TokenKind::Match
| TokenKind::With
| TokenKind::Do
| TokenKind::Have
| TokenKind::Suffices
| TokenKind::Show
| TokenKind::Where
| TokenKind::From
| TokenKind::By
| TokenKind::Return
)
}
}
#[allow(dead_code)]
pub struct TokenCursor<'a> {
tokens: &'a [Token],
pos: usize,
checkpoints: Vec<usize>,
}
#[allow(dead_code)]
impl<'a> TokenCursor<'a> {
pub fn new(tokens: &'a [Token]) -> Self {
Self {
tokens,
pos: 0,
checkpoints: Vec::new(),
}
}
pub fn peek(&self) -> Option<&'a Token> {
self.tokens.get(self.pos)
}
pub fn peek_n(&self, n: usize) -> Option<&'a Token> {
self.tokens.get(self.pos + n)
}
pub fn advance(&mut self) -> Option<&'a Token> {
if self.pos < self.tokens.len() {
let tok = &self.tokens[self.pos];
self.pos += 1;
Some(tok)
} else {
None
}
}
pub fn position(&self) -> usize {
self.pos
}
pub fn remaining(&self) -> usize {
self.tokens.len().saturating_sub(self.pos)
}
pub fn is_eof(&self) -> bool {
self.pos >= self.tokens.len()
}
pub fn save(&mut self) {
self.checkpoints.push(self.pos);
}
pub fn restore(&mut self) {
if let Some(saved) = self.checkpoints.pop() {
self.pos = saved;
}
}
pub fn commit(&mut self) {
self.checkpoints.pop();
}
pub fn try_consume(&mut self, kind: &TokenKind) -> bool {
if self.tokens.get(self.pos).is_some_and(|t| &t.kind == kind) {
self.pos += 1;
true
} else {
false
}
}
pub fn expect_kind(&mut self, kind: &TokenKind) -> Result<&'a Token, String> {
match self.tokens.get(self.pos) {
Some(t) if &t.kind == kind => {
self.pos += 1;
Ok(&self.tokens[self.pos - 1])
}
Some(t) => Err(format!("expected {:?} got {:?}", kind, t.kind)),
None => Err(format!("expected {:?} got EOF", kind)),
}
}
pub fn remaining_slice(&self) -> &'a [Token] {
&self.tokens[self.pos..]
}
pub fn collect_until<F: Fn(&Token) -> bool>(&mut self, pred: F) -> Vec<&'a Token> {
let mut result = Vec::new();
while let Some(tok) = self.tokens.get(self.pos) {
if pred(tok) {
break;
}
result.push(tok);
self.pos += 1;
}
result
}
pub fn skip_while<F: Fn(&Token) -> bool>(&mut self, pred: F) {
while self.tokens.get(self.pos).is_some_and(&pred) {
self.pos += 1;
}
}
}
#[allow(dead_code)]
pub struct TokenKindSet {
kinds: Vec<std::mem::Discriminant<TokenKind>>,
}
#[allow(dead_code)]
impl TokenKindSet {
pub fn new() -> Self {
Self { kinds: Vec::new() }
}
pub fn add(&mut self, kind: &TokenKind) {
let d = std::mem::discriminant(kind);
if !self.kinds.contains(&d) {
self.kinds.push(d);
}
}
pub fn contains(&self, kind: &TokenKind) -> bool {
self.kinds.contains(&std::mem::discriminant(kind))
}
pub fn len(&self) -> usize {
self.kinds.len()
}
pub fn is_empty(&self) -> bool {
self.kinds.is_empty()
}
}
#[allow(dead_code)]
pub struct TokenRange {
pub tokens: Vec<Token>,
pub span: Span,
}
#[allow(dead_code)]
impl TokenRange {
pub fn from_tokens(tokens: Vec<Token>) -> Option<Self> {
if tokens.is_empty() {
return None;
}
let first = tokens
.first()
.expect("tokens non-empty per is_empty check above");
let last = tokens
.last()
.expect("tokens non-empty per is_empty check above");
let span = first.span.merge(&last.span);
Some(Self { tokens, span })
}
pub fn len(&self) -> usize {
self.tokens.len()
}
pub fn is_empty(&self) -> bool {
self.tokens.is_empty()
}
pub fn get(&self, i: usize) -> Option<&Token> {
self.tokens.get(i)
}
pub fn iter(&self) -> std::slice::Iter<'_, Token> {
self.tokens.iter()
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum TokenKind {
Axiom,
Definition,
Theorem,
Lemma,
Opaque,
Inductive,
Structure,
Class,
Instance,
Namespace,
Section,
Variable,
Variables,
Parameter,
Parameters,
Constant,
Constants,
End,
Import,
Export,
Open,
Attribute,
Return,
Type,
Prop,
Sort,
Fun,
Forall,
Let,
In,
If,
Then,
Else,
Match,
With,
Do,
Have,
Suffices,
Show,
Where,
From,
By,
Arrow,
FatArrow,
And,
Or,
Not,
Iff,
Exists,
Eq,
Ne,
Lt,
Le,
Gt,
Ge,
LParen,
RParen,
LBrace,
RBrace,
LBracket,
RBracket,
LAngle,
RAngle,
Comma,
Colon,
Semicolon,
Dot,
DotDot,
Bar,
At,
Hash,
Plus,
Minus,
Star,
Slash,
Percent,
Caret,
Assign,
AndAnd,
OrOr,
BangEq,
Bang,
LeftArrow,
Ident(String),
Nat(u64),
Float(f64),
String(String),
Char(char),
DocComment(String),
InterpolatedString(Vec<StringPart>),
Underscore,
Question,
Eof,
Error(String),
}
#[allow(dead_code)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TokenAnnotation {
BindingSite(String),
UseSite(String),
ResolvedName(String),
TacticStart,
TacticEnd,
ImplicitHole,
TypeSeparator,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Span {
pub start: usize,
pub end: usize,
pub line: usize,
pub column: usize,
}
impl Span {
pub fn new(start: usize, end: usize, line: usize, column: usize) -> Self {
Self {
start,
end,
line,
column,
}
}
pub fn merge(&self, other: &Span) -> Span {
Span {
start: self.start.min(other.start),
end: self.end.max(other.end),
line: self.line,
column: self.column,
}
}
}
#[allow(dead_code)]
pub struct TokenPairMatcher;
#[allow(dead_code)]
impl TokenPairMatcher {
pub fn closing_of(kind: &TokenKind) -> Option<TokenKind> {
match kind {
TokenKind::LParen => Some(TokenKind::RParen),
TokenKind::LBrace => Some(TokenKind::RBrace),
TokenKind::LBracket => Some(TokenKind::RBracket),
TokenKind::LAngle => Some(TokenKind::RAngle),
_ => None,
}
}
pub fn opening_of(kind: &TokenKind) -> Option<TokenKind> {
match kind {
TokenKind::RParen => Some(TokenKind::LParen),
TokenKind::RBrace => Some(TokenKind::LBrace),
TokenKind::RBracket => Some(TokenKind::LBracket),
TokenKind::RAngle => Some(TokenKind::LAngle),
_ => None,
}
}
pub fn find_closing(tokens: &[Token], start: usize) -> Option<usize> {
let opener = &tokens.get(start)?.kind;
let closer = Self::closing_of(opener)?;
let mut depth = 0usize;
for (i, tok) in tokens[start..].iter().enumerate() {
if std::mem::discriminant(&tok.kind) == std::mem::discriminant(opener) {
depth += 1;
} else if std::mem::discriminant(&tok.kind) == std::mem::discriminant(&closer) {
depth -= 1;
if depth == 0 {
return Some(start + i);
}
}
}
None
}
pub fn extract_group(tokens: &[Token], start: usize) -> Option<&[Token]> {
let end = Self::find_closing(tokens, start)?;
Some(&tokens[start + 1..end])
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct ContextualToken {
pub token: Token,
pub prev: Option<Token>,
pub next: Option<Token>,
}
#[allow(dead_code)]
impl ContextualToken {
pub fn new(token: Token, prev: Option<Token>, next: Option<Token>) -> Self {
Self { token, prev, next }
}
pub fn from_slice(tokens: &[Token]) -> Vec<ContextualToken> {
tokens
.iter()
.enumerate()
.map(|(i, tok)| {
let prev = if i > 0 {
Some(tokens[i - 1].clone())
} else {
None
};
let next = tokens.get(i + 1).cloned();
ContextualToken::new(tok.clone(), prev, next)
})
.collect()
}
pub fn is_line_start(&self) -> bool {
self.token.span.column == 1
}
pub fn same_line_as_prev(&self) -> bool {
self.prev
.as_ref()
.is_some_and(|p| p.span.line == self.token.span.line)
}
pub fn same_line_as_next(&self) -> bool {
self.next
.as_ref()
.is_some_and(|n| n.span.line == self.token.span.line)
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum StringPart {
Literal(String),
Interpolation(Vec<Token>),
}
#[allow(dead_code)]
pub struct TokenBuffer {
tokens: Vec<Token>,
}
#[allow(dead_code)]
impl TokenBuffer {
pub fn new() -> Self {
Self { tokens: Vec::new() }
}
pub fn with_capacity(cap: usize) -> Self {
Self {
tokens: Vec::with_capacity(cap),
}
}
pub fn push(&mut self, token: Token) {
self.tokens.push(token);
}
pub fn get(&self, i: usize) -> Option<&Token> {
self.tokens.get(i)
}
pub fn len(&self) -> usize {
self.tokens.len()
}
pub fn is_empty(&self) -> bool {
self.tokens.is_empty()
}
pub fn into_tokens(self) -> Vec<Token> {
self.tokens
}
pub fn into_stream(self) -> TokenStream {
TokenStream::new(self.tokens)
}
pub fn extend_from(&mut self, other: TokenBuffer) {
self.tokens.extend(other.tokens);
}
pub fn truncate(&mut self, n: usize) {
self.tokens.truncate(n);
}
pub fn last(&self) -> Option<&Token> {
self.tokens.last()
}
pub fn retain<F: FnMut(&Token) -> bool>(&mut self, pred: F) {
self.tokens.retain(pred);
}
pub fn split_off(&mut self, mid: usize) -> TokenBuffer {
TokenBuffer {
tokens: self.tokens.split_off(mid),
}
}
pub fn count_kind(&self, kind: &TokenKind) -> usize {
count_tokens(&self.tokens, kind)
}
pub fn find_first<F: Fn(&Token) -> bool>(&self, pred: F) -> Option<usize> {
self.tokens.iter().position(pred)
}
pub fn slice(&self, start: usize, end: usize) -> &[Token] {
&self.tokens[start..end.min(self.tokens.len())]
}
}