use std::fmt;
#[derive(Clone, Copy, PartialEq, Eq, Default)]
pub struct Span {
pub start: usize,
pub end: usize,
pub line: u32,
pub column: u32,
}
impl Span {
pub fn new(start: usize, end: usize, line: u32, column: u32) -> Self {
Self {
start,
end,
line,
column,
}
}
pub fn dummy() -> Self {
Self::default()
}
pub fn merge(self, other: Self) -> Self {
Self {
start: self.start.min(other.start),
end: self.end.max(other.end),
line: self.line.min(other.line),
column: if self.line <= other.line {
self.column
} else {
other.column
},
}
}
pub fn len(&self) -> usize {
self.end - self.start
}
pub fn is_empty(&self) -> bool {
self.start == self.end
}
}
impl fmt::Debug for Span {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}:{}", self.line, self.column)
}
}
impl fmt::Display for Span {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}:{}", self.line, self.column)
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum TokenKind {
Module,
Use,
Const,
Var,
Type,
Init,
Action,
Invariant,
Property,
Fairness,
Func,
And,
Or,
Not,
Implies,
Iff,
All,
Any,
Choose,
In,
For,
If,
Then,
Else,
Let,
With,
Require,
Changes,
Always,
Eventually,
LeadsTo,
Enabled,
WeakFair,
StrongFair,
True,
False,
Nat,
Int,
Bool,
String_,
Set,
Seq,
Dict,
Option_,
Some_,
None_,
LParen,
RParen,
LBrace,
RBrace,
LBracket,
RBracket,
Comma,
Colon,
Semicolon,
Dot,
DotDot,
Arrow,
FatArrow,
Prime,
Pipe,
Eq,
Ne,
Lt,
Le,
Gt,
Ge,
Plus,
Minus,
Star,
Slash,
Percent,
Union,
Intersect,
Diff,
SubsetOf,
PlusPlus,
Head,
Tail,
Len,
Keys,
Values,
Powerset,
UnionAll,
Ampersand,
Assign,
Integer(i64),
StringLit(std::string::String),
Ident(std::string::String),
Comment(std::string::String),
DocComment(std::string::String),
Eof,
Error(std::string::String),
}
impl TokenKind {
pub fn is_keyword(&self) -> bool {
matches!(
self,
TokenKind::Module
| TokenKind::Use
| TokenKind::Const
| TokenKind::Var
| TokenKind::Type
| TokenKind::Init
| TokenKind::Action
| TokenKind::Invariant
| TokenKind::Property
| TokenKind::Fairness
| TokenKind::Func
| TokenKind::And
| TokenKind::Or
| TokenKind::Not
| TokenKind::Implies
| TokenKind::Iff
| TokenKind::All
| TokenKind::Any
| TokenKind::Choose
| TokenKind::In
| TokenKind::For
| TokenKind::If
| TokenKind::Then
| TokenKind::Else
| TokenKind::Let
| TokenKind::With
| TokenKind::Require
| TokenKind::Changes
| TokenKind::Always
| TokenKind::Eventually
| TokenKind::LeadsTo
| TokenKind::Enabled
| TokenKind::WeakFair
| TokenKind::StrongFair
| TokenKind::True
| TokenKind::False
| TokenKind::Nat
| TokenKind::Int
| TokenKind::Bool
| TokenKind::String_
| TokenKind::Set
| TokenKind::Seq
| TokenKind::Dict
| TokenKind::Option_
| TokenKind::Some_
| TokenKind::None_
| TokenKind::Union
| TokenKind::Intersect
| TokenKind::Diff
| TokenKind::SubsetOf
| TokenKind::Head
| TokenKind::Tail
| TokenKind::Len
| TokenKind::Keys
| TokenKind::Values
| TokenKind::Powerset
| TokenKind::UnionAll
)
}
pub fn keyword(ident: &str) -> Option<TokenKind> {
Some(match ident {
"module" => TokenKind::Module,
"use" => TokenKind::Use,
"const" => TokenKind::Const,
"var" => TokenKind::Var,
"type" => TokenKind::Type,
"init" => TokenKind::Init,
"action" => TokenKind::Action,
"invariant" => TokenKind::Invariant,
"property" => TokenKind::Property,
"fairness" => TokenKind::Fairness,
"func" => TokenKind::Func,
"and" => TokenKind::And,
"or" => TokenKind::Or,
"not" => TokenKind::Not,
"implies" => TokenKind::Implies,
"iff" => TokenKind::Iff,
"all" => TokenKind::All,
"any" => TokenKind::Any,
"fix" => TokenKind::Choose,
"in" => TokenKind::In,
"for" => TokenKind::For,
"if" => TokenKind::If,
"then" => TokenKind::Then,
"else" => TokenKind::Else,
"let" => TokenKind::Let,
"with" => TokenKind::With,
"require" => TokenKind::Require,
"changes" => TokenKind::Changes,
"always" => TokenKind::Always,
"eventually" => TokenKind::Eventually,
"leads_to" => TokenKind::LeadsTo,
"enabled" => TokenKind::Enabled,
"weak_fair" => TokenKind::WeakFair,
"strong_fair" => TokenKind::StrongFair,
"true" => TokenKind::True,
"false" => TokenKind::False,
"Nat" => TokenKind::Nat,
"Int" => TokenKind::Int,
"Bool" => TokenKind::Bool,
"String" => TokenKind::String_,
"Set" => TokenKind::Set,
"Seq" => TokenKind::Seq,
"Dict" => TokenKind::Dict,
"Option" => TokenKind::Option_,
"Some" => TokenKind::Some_,
"None" => TokenKind::None_,
"union" => TokenKind::Union,
"intersect" => TokenKind::Intersect,
"diff" => TokenKind::Diff,
"subset_of" => TokenKind::SubsetOf,
"head" => TokenKind::Head,
"tail" => TokenKind::Tail,
"len" => TokenKind::Len,
"keys" => TokenKind::Keys,
"values" => TokenKind::Values,
"powerset" => TokenKind::Powerset,
"union_all" => TokenKind::UnionAll,
_ => return None,
})
}
pub fn is_trivia(&self) -> bool {
matches!(self, TokenKind::Comment(_) | TokenKind::DocComment(_))
}
}
impl fmt::Display for TokenKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TokenKind::Module => write!(f, "module"),
TokenKind::Use => write!(f, "use"),
TokenKind::Const => write!(f, "const"),
TokenKind::Var => write!(f, "var"),
TokenKind::Type => write!(f, "type"),
TokenKind::Init => write!(f, "init"),
TokenKind::Action => write!(f, "action"),
TokenKind::Invariant => write!(f, "invariant"),
TokenKind::Property => write!(f, "property"),
TokenKind::Fairness => write!(f, "fairness"),
TokenKind::Func => write!(f, "func"),
TokenKind::And => write!(f, "and"),
TokenKind::Or => write!(f, "or"),
TokenKind::Not => write!(f, "not"),
TokenKind::Implies => write!(f, "implies"),
TokenKind::Iff => write!(f, "iff"),
TokenKind::All => write!(f, "all"),
TokenKind::Any => write!(f, "any"),
TokenKind::Choose => write!(f, "choose"),
TokenKind::In => write!(f, "in"),
TokenKind::For => write!(f, "for"),
TokenKind::If => write!(f, "if"),
TokenKind::Then => write!(f, "then"),
TokenKind::Else => write!(f, "else"),
TokenKind::Let => write!(f, "let"),
TokenKind::With => write!(f, "with"),
TokenKind::Require => write!(f, "require"),
TokenKind::Changes => write!(f, "changes"),
TokenKind::Always => write!(f, "always"),
TokenKind::Eventually => write!(f, "eventually"),
TokenKind::LeadsTo => write!(f, "leads_to"),
TokenKind::Enabled => write!(f, "enabled"),
TokenKind::WeakFair => write!(f, "weak_fair"),
TokenKind::StrongFair => write!(f, "strong_fair"),
TokenKind::True => write!(f, "true"),
TokenKind::False => write!(f, "false"),
TokenKind::Nat => write!(f, "Nat"),
TokenKind::Int => write!(f, "Int"),
TokenKind::Bool => write!(f, "Bool"),
TokenKind::String_ => write!(f, "String"),
TokenKind::Set => write!(f, "Set"),
TokenKind::Seq => write!(f, "Seq"),
TokenKind::Dict => write!(f, "Dict"),
TokenKind::Option_ => write!(f, "Option"),
TokenKind::Some_ => write!(f, "Some"),
TokenKind::None_ => write!(f, "None"),
TokenKind::LParen => write!(f, "("),
TokenKind::RParen => write!(f, ")"),
TokenKind::LBrace => write!(f, "{{"),
TokenKind::RBrace => write!(f, "}}"),
TokenKind::LBracket => write!(f, "["),
TokenKind::RBracket => write!(f, "]"),
TokenKind::Comma => write!(f, ","),
TokenKind::Colon => write!(f, ":"),
TokenKind::Semicolon => write!(f, ";"),
TokenKind::Dot => write!(f, "."),
TokenKind::DotDot => write!(f, ".."),
TokenKind::Arrow => write!(f, "->"),
TokenKind::FatArrow => write!(f, "=>"),
TokenKind::Prime => write!(f, "'"),
TokenKind::Pipe => write!(f, "|"),
TokenKind::Eq => write!(f, "=="),
TokenKind::Ne => write!(f, "!="),
TokenKind::Lt => write!(f, "<"),
TokenKind::Le => write!(f, "<="),
TokenKind::Gt => write!(f, ">"),
TokenKind::Ge => write!(f, ">="),
TokenKind::Plus => write!(f, "+"),
TokenKind::Minus => write!(f, "-"),
TokenKind::Star => write!(f, "*"),
TokenKind::Slash => write!(f, "/"),
TokenKind::Percent => write!(f, "%"),
TokenKind::Union => write!(f, "union"),
TokenKind::Intersect => write!(f, "intersect"),
TokenKind::Diff => write!(f, "diff"),
TokenKind::SubsetOf => write!(f, "subset_of"),
TokenKind::PlusPlus => write!(f, "++"),
TokenKind::Head => write!(f, "head"),
TokenKind::Tail => write!(f, "tail"),
TokenKind::Len => write!(f, "len"),
TokenKind::Keys => write!(f, "keys"),
TokenKind::Values => write!(f, "values"),
TokenKind::Powerset => write!(f, "powerset"),
TokenKind::UnionAll => write!(f, "union_all"),
TokenKind::Ampersand => write!(f, "&"),
TokenKind::Assign => write!(f, "="),
TokenKind::Integer(n) => write!(f, "{}", n),
TokenKind::StringLit(s) => write!(f, "\"{}\"", s),
TokenKind::Ident(s) => write!(f, "{}", s),
TokenKind::Comment(s) => write!(f, "// {}", s),
TokenKind::DocComment(s) => write!(f, "/// {}", s),
TokenKind::Eof => write!(f, "EOF"),
TokenKind::Error(msg) => write!(f, "ERROR: {}", msg),
}
}
}
#[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_eof(&self) -> bool {
matches!(self.kind, TokenKind::Eof)
}
pub fn is_error(&self) -> bool {
matches!(self.kind, TokenKind::Error(_))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_span_merge() {
let s1 = Span::new(0, 5, 1, 1);
let s2 = Span::new(10, 15, 1, 11);
let merged = s1.merge(s2);
assert_eq!(merged.start, 0);
assert_eq!(merged.end, 15);
}
#[test]
fn test_keyword_lookup() {
assert_eq!(TokenKind::keyword("module"), Some(TokenKind::Module));
assert_eq!(TokenKind::keyword("and"), Some(TokenKind::And));
assert_eq!(TokenKind::keyword("Nat"), Some(TokenKind::Nat));
assert_eq!(TokenKind::keyword("foo"), None);
}
#[test]
fn test_is_keyword() {
assert!(TokenKind::Module.is_keyword());
assert!(TokenKind::And.is_keyword());
assert!(!TokenKind::LParen.is_keyword());
assert!(!TokenKind::Integer(42).is_keyword());
}
}