#[derive(Clone,Copy,Debug,PartialEq)]
pub enum TokenType {
AmpersandAmpersand,
BarBar,
BoolLiteral(bool),
Colon,
Comma,
EqualsEquals,
Else,
EOF,
ForAll,
Fun,
Identifier,
If,
IntLiteral,
LeftAngle,
LeftAngleEquals,
LeftBrace,
LeftCurly,
LeftSquare,
LongRightArrow,
Minus,
Percent,
Plus,
RecFun,
RightAngle,
RightAngleEquals,
RightArrow,
RightBrace,
RightCurly,
RightSlash,
RightSquare,
Shreak,
ShreakEquals,
Star,
Uint,
WhiteSpace
}
#[derive(Clone,Copy,Debug,PartialEq)]
pub struct Token {
pub kind : TokenType,
pub offset: usize,
pub len: usize
}
impl Token {
pub fn relocate(&mut self, offset: usize) {
self.offset = offset;
}
}
pub const EOF : Token = Token{kind: TokenType::EOF,offset:0,len:0};
const ELSE : &[char] = &['e','l','s','e'];
const FALSE : &[char] = &['f','a','l','s','e'];
const FORALL : &[char] = &['f','o','r','a','l','l'];
const FUN : &[char] = &['f','u','n'];
const IF : &[char] = &['i','f'];
const RECFUN : &[char] = &['r','e','c','f','u','n'];
const TRUE : &[char] = &['t','r','u','e'];
const UINT : &[char] = &['u','i','n','t'];
fn scan(input: &[char]) -> Token {
if let Ok(tok) = scan_whitespace(input) { return tok; }
if let Ok(tok) = scan_line_comment(input) { return tok; }
if let Ok(tok) = scan_triple_operators(input) { return tok; }
if let Ok(tok) = scan_double_operators(input) { return tok; }
if let Ok(tok) = scan_single_operators(input) { return tok; }
if let Ok(tok) = scan_int_literal(input) { return tok; }
if let Ok(tok) = scan_keyword(input) { return tok; }
if let Ok(tok) = scan_identifier(input) { return tok; }
panic!("unexpected character: {}", input[0]);
}
fn scan_single_operators(input: &[char]) -> Result<Token,()> {
if input.is_empty() {
Err(())
} else {
let t = match input[0] {
':' => TokenType::Colon,
',' => TokenType::Comma,
'<' => TokenType::LeftAngle,
'(' => TokenType::LeftBrace,
'{' => TokenType::LeftCurly,
'[' => TokenType::LeftSquare,
'-' => TokenType::Minus,
'%' => TokenType::Percent,
'+' => TokenType::Plus,
'>' => TokenType::RightAngle,
')' => TokenType::RightBrace,
'}' => TokenType::RightCurly,
'/' => TokenType::RightSlash,
']' => TokenType::RightSquare,
'!' => TokenType::Shreak,
'*' => TokenType::Star,
_ => { return Err(()); }
};
Ok(Token{kind:t, offset:0, len:1})
}
}
fn scan_double_operators(input: &[char]) -> Result<Token,()> {
if input.len() <= 1 {
Err(())
} else {
let t = match (input[0], input[1]) {
('&','&') => TokenType::AmpersandAmpersand,
('|','|') => TokenType::BarBar,
('=','=') => TokenType::EqualsEquals,
('<','=') => TokenType::LeftAngleEquals,
('-','>') => TokenType::RightArrow,
('>','=') => TokenType::RightAngleEquals,
('!','=') => TokenType::ShreakEquals,
_ => { return Err(()); }
};
Ok(Token{kind:t, offset:0, len:2})
}
}
fn scan_triple_operators(input: &[char]) -> Result<Token,()> {
if input.len() <= 2 {
Err(())
} else {
let t = match (input[0], input[1], input[2]) {
('=','=','>') => TokenType::LongRightArrow,
_ => { return Err(()); }
};
Ok(Token{kind:t, offset:0, len:3})
}
}
fn scan_int_literal(input: &[char]) -> Result<Token,()> {
let mut i = 0;
while i < input.len() && input[i].is_ascii_digit() {
i += 1;
}
if i != 0 {
Ok(Token{kind: TokenType::IntLiteral, offset: 0, len: i})
} else {
Err(())
}
}
fn scan_identifier(input: &[char]) -> Result<Token,()> {
if !input.is_empty() && is_identifier_start(input[0]) {
let mut i = 0;
while i < input.len() && is_identifier_middle(input[i]) {
i += 1;
}
if i != 0 {
return Ok(Token{kind: TokenType::Identifier, offset: 0, len: i});
}
}
Err(())
}
fn scan_keyword(input: &[char]) -> Result<Token,()> {
let ident = scan_identifier(input)?;
let t = match &input[0..ident.len] {
ELSE => TokenType::Else,
FALSE => TokenType::BoolLiteral(false),
FORALL => TokenType::ForAll,
IF => TokenType::If,
FUN => TokenType::Fun,
RECFUN => TokenType::RecFun,
TRUE => TokenType::BoolLiteral(true),
UINT => TokenType::Uint,
_ => { return Err(()); }
};
Ok(Token{kind:t,offset:0,len:ident.len})
}
fn scan_line_comment(input: &[char]) -> Result<Token,()> {
if input.len() < 2 || input[0] != '/' || input[1] != '/' {
Err(())
} else {
let mut i = 2;
while i < input.len() && input[i] != '\n' {
i += 1;
}
Ok(Token{kind: TokenType::WhiteSpace, offset:0, len: i})
}
}
fn scan_whitespace(input: &[char]) -> Result<Token,()> {
let mut i = 0;
while i < input.len() && is_whitespace(input[i]) {
i += 1;
}
if i >= 1 {
Ok(Token{kind: TokenType::WhiteSpace, offset: 0, len: i})
} else {
Err(())
}
}
fn is_identifier_start(c: char) -> bool {
c.is_ascii_alphabetic() || c == '_'
}
fn is_identifier_middle(c: char) -> bool {
c.is_ascii_digit() || is_identifier_start(c)
}
fn is_whitespace(c: char) -> bool {
c == ' ' || c == '\t' || c == '\n'
}
pub struct Lexer {
chars: Vec<char>,
offset: usize
}
impl Lexer {
pub fn new(content: &str) -> Self {
let chars = content.chars().collect();
Self{chars, offset: 0}
}
pub fn lookahead(&mut self, mut n: usize) -> Token {
let mut tok = Self::lookahead_from(&self.chars,self.offset);
while n > 0 {
tok = Self::lookahead_from(&self.chars,tok.offset + tok.len);
n -= 1;
}
tok
}
pub fn matches(&mut self, kind: TokenType) -> bool {
let lookahead = self.lookahead(0);
if lookahead.kind == kind {
self.accept(&lookahead);
true
} else {
false
}
}
pub fn match_any(&mut self, tokens: &[TokenType]) -> Option<Token> {
let lookahead = self.lookahead(0);
for t in tokens {
if lookahead.kind == *t {
return Some(self.expect(*t))
}
}
None
}
pub fn to_string(&self, token: &Token) -> String {
let n = token.offset;
let slice = &self.chars[n..n+token.len];
slice.iter().collect()
}
pub fn accept(&mut self, token: &Token) {
self.skip_whitespace();
assert!(self.lookahead(0) == *token);
assert!(self.offset == token.offset);
self.offset += token.len
}
pub fn expect(&mut self, kind: TokenType) -> Token {
let tok = self.lookahead(0);
if tok.kind != kind {
panic!("unexpected token encountered {tok:?}");
}
self.accept(&tok);
tok
}
fn skip_whitespace(&mut self) {
while self.offset < self.chars.len() {
let tok = scan(&self.chars[self.offset..]);
if tok.kind == TokenType::WhiteSpace {
self.offset += tok.len;
} else {
break;
}
}
}
fn lookahead_from(chars: &[char], offset: usize) -> Token {
if offset >= chars.len() {
EOF
} else {
let slice = &chars[offset..];
let mut tok = scan(slice);
if tok.kind == TokenType::WhiteSpace {
Self::lookahead_from(chars, offset + tok.len)
} else {
tok.relocate(offset);
tok
}
}
}
}