File size: 4,315 Bytes
224e773 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | //! Lexical analyzer for constraint DSL
use super::tokens::Token;
use crate::Result;
use hyperkitty_core::Error;
pub fn lex(input: &str) -> Result<Vec<Token>> {
let mut tokens = Vec::new();
let mut chars = input.chars().peekable();
while let Some(&ch) = chars.peek() {
match ch {
' ' | '\t' | '\n' | '\r' => {
chars.next();
}
'(' => {
tokens.push(Token::LeftParen);
chars.next();
}
')' => {
tokens.push(Token::RightParen);
chars.next();
}
'{' => {
tokens.push(Token::LeftBrace);
chars.next();
}
'}' => {
tokens.push(Token::RightBrace);
chars.next();
}
';' => {
tokens.push(Token::Semicolon);
chars.next();
}
',' => {
tokens.push(Token::Comma);
chars.next();
}
'=' => {
chars.next();
if chars.peek() == Some(&'=') {
tokens.push(Token::Equals);
chars.next();
} else {
tokens.push(Token::Equals);
}
}
'<' => {
chars.next();
if chars.peek() == Some(&'=') {
tokens.push(Token::LessThanEq);
chars.next();
}
}
'"' => {
chars.next();
let mut s = String::new();
while let Some(&c) = chars.peek() {
if c == '"' {
chars.next();
break;
}
s.push(c);
chars.next();
}
tokens.push(Token::String(s));
}
c if c.is_ascii_digit() => {
let mut num = String::new();
while let Some(&c) = chars.peek() {
if c.is_ascii_digit() {
num.push(c);
chars.next();
} else {
break;
}
}
if let Ok(n) = num.parse::<i64>() {
tokens.push(Token::Number(n));
}
}
c if c.is_alphabetic() || c == '_' => {
let mut ident = String::new();
while let Some(&c) = chars.peek() {
if c.is_alphanumeric() || c == '_' {
ident.push(c);
chars.next();
} else {
break;
}
}
let token = match ident.as_str() {
"require" => Token::Require,
"balance" => Token::Balance,
"invariant" => Token::Invariant,
"entropy" => Token::Entropy,
"proof" => Token::Proof,
"runtime" => Token::Runtime,
"identity" => Token::Identity,
"phase" => Token::Phase,
"otherwise" => Token::Otherwise,
"reject" => Token::Reject,
_ => Token::Identifier(ident),
};
tokens.push(token);
}
_ => {
chars.next();
}
}
}
tokens.push(Token::Eof);
Ok(tokens)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn lex_keywords() {
let tokens = lex("require balance").unwrap();
assert_eq!(tokens[0], Token::Require);
assert_eq!(tokens[1], Token::Balance);
}
#[test]
fn lex_number() {
let tokens = lex("42").unwrap();
assert_eq!(tokens[0], Token::Number(42));
}
#[test]
fn lex_string() {
let tokens = lex("\"hello\"").unwrap();
assert!(matches!(tokens[0], Token::String(ref s) if s == "hello"));
}
}
|