use std::iter::Peekable;
use std::str::Chars;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum BuildProfile {
Enabled(String),
Disabled(String),
}
impl std::fmt::Display for BuildProfile {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
BuildProfile::Enabled(s) => f.write_str(s),
BuildProfile::Disabled(s) => write!(f, "!{}", s),
}
}
}
impl std::str::FromStr for BuildProfile {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Some(s) = s.strip_prefix('!') {
Ok(BuildProfile::Disabled(s.to_string()))
} else {
Ok(BuildProfile::Enabled(s.to_string()))
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum VersionConstraint {
LessThan, LessThanEqual, Equal, GreaterThan, GreaterThanEqual, }
impl std::str::FromStr for VersionConstraint {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
">=" => Ok(VersionConstraint::GreaterThanEqual),
"<=" => Ok(VersionConstraint::LessThanEqual),
"=" => Ok(VersionConstraint::Equal),
">>" => Ok(VersionConstraint::GreaterThan),
"<<" => Ok(VersionConstraint::LessThan),
_ => Err(format!("Invalid version constraint: {}", s)),
}
}
}
impl std::fmt::Display for VersionConstraint {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
VersionConstraint::GreaterThanEqual => f.write_str(">="),
VersionConstraint::LessThanEqual => f.write_str("<="),
VersionConstraint::Equal => f.write_str("="),
VersionConstraint::GreaterThan => f.write_str(">>"),
VersionConstraint::LessThan => f.write_str("<<"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[allow(non_camel_case_types)]
#[repr(u16)]
pub enum SyntaxKind {
IDENT = 0, COLON, PIPE,
COMMA, L_PARENS, R_PARENS, L_BRACKET, R_BRACKET, NOT, L_ANGLE, R_ANGLE, EQUAL, WHITESPACE, NEWLINE, COMMENT, DOLLAR, L_CURLY,
R_CURLY,
ERROR, ROOT, ENTRY, RELATION, ARCHQUAL, VERSION, CONSTRAINT, ARCHITECTURES,
PROFILES,
SUBSTVAR,
}
impl From<SyntaxKind> for rowan::SyntaxKind {
fn from(kind: SyntaxKind) -> Self {
Self(kind as u16)
}
}
pub struct Lexer<'a> {
input: Peekable<Chars<'a>>,
}
impl<'a> Lexer<'a> {
pub fn new(input: &'a str) -> Self {
Lexer {
input: input.chars().peekable(),
}
}
fn is_whitespace(c: char) -> bool {
c == ' ' || c == '\t' || c == '\r'
}
fn is_valid_ident_char(c: char) -> bool {
c.is_ascii_alphanumeric() || c == '-' || c == '.' || c == '+' || c == '~'
}
fn read_while<F>(&mut self, predicate: F) -> String
where
F: Fn(char) -> bool,
{
let mut result = String::new();
while let Some(&c) = self.input.peek() {
if predicate(c) {
result.push(c);
self.input.next();
} else {
break;
}
}
result
}
fn next_token(&mut self) -> Option<(SyntaxKind, String)> {
if let Some(&c) = self.input.peek() {
match c {
':' => {
self.input.next();
Some((SyntaxKind::COLON, ":".to_owned()))
}
'|' => {
self.input.next();
Some((SyntaxKind::PIPE, "|".to_owned()))
}
',' => {
self.input.next();
Some((SyntaxKind::COMMA, ",".to_owned()))
}
'(' => {
self.input.next();
Some((SyntaxKind::L_PARENS, "(".to_owned()))
}
')' => {
self.input.next();
Some((SyntaxKind::R_PARENS, ")".to_owned()))
}
'[' => {
self.input.next();
Some((SyntaxKind::L_BRACKET, "[".to_owned()))
}
']' => {
self.input.next();
Some((SyntaxKind::R_BRACKET, "]".to_owned()))
}
'!' => {
self.input.next();
Some((SyntaxKind::NOT, "!".to_owned()))
}
'$' => {
self.input.next();
Some((SyntaxKind::DOLLAR, "$".to_owned()))
}
'{' => {
self.input.next();
Some((SyntaxKind::L_CURLY, "{".to_owned()))
}
'}' => {
self.input.next();
Some((SyntaxKind::R_CURLY, "}".to_owned()))
}
'<' => {
self.input.next();
Some((SyntaxKind::L_ANGLE, "<".to_owned()))
}
'>' => {
self.input.next();
Some((SyntaxKind::R_ANGLE, ">".to_owned()))
}
'=' => {
self.input.next();
Some((SyntaxKind::EQUAL, "=".to_owned()))
}
'\n' => {
self.input.next();
Some((SyntaxKind::NEWLINE, "\n".to_owned()))
}
_ if Self::is_whitespace(c) => {
let whitespace = self.read_while(Self::is_whitespace);
Some((SyntaxKind::WHITESPACE, whitespace))
}
'#' => {
self.input.next();
let comment = self.read_while(|c| c != '\n' && c != '\r');
Some((SyntaxKind::COMMENT, format!("#{}", comment)))
}
_ if Self::is_valid_ident_char(c) => {
let key = self.read_while(Self::is_valid_ident_char);
Some((SyntaxKind::IDENT, key))
}
_ => {
self.input.next();
Some((SyntaxKind::ERROR, c.to_string()))
}
}
} else {
None
}
}
}
impl Iterator for Lexer<'_> {
type Item = (SyntaxKind, String);
fn next(&mut self) -> Option<Self::Item> {
self.next_token()
}
}
pub(crate) fn lex(input: &str) -> Vec<(SyntaxKind, String)> {
let mut lexer = Lexer::new(input);
lexer.by_ref().collect::<Vec<_>>()
}