use std::fmt::Display;
use super::{
structs::{Keyword, Lexicon, Token},
LexerBase,
};
use crate::parse_string;
pub struct SourceLexer<LB>
where
LB: LexerBase,
{
stack: Vec<Table<LB>>,
source_function: fn(&str) -> Result<LB, String>,
canonical_name_function: fn(&str) -> String,
}
struct Table<LB>
where
LB: LexerBase,
{
stream_name: String,
lexer: LB,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Error {
msg: String,
}
impl Error {
pub fn new(s: &str) -> Error {
Error { msg: s.to_string() }
}
}
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.msg)
}
}
impl<LB> SourceLexer<LB>
where
LB: LexerBase,
{
pub fn new(
stream_name: &str,
source_function: fn(&str) -> Result<LB, String>,
canonical_name_function: fn(&str) -> String,
) -> Result<Self, Error> {
match (source_function)(stream_name) {
Ok(lexer) => Ok(Self {
stack: vec![Table {
stream_name: stream_name.to_string(),
lexer: lexer,
}],
source_function,
canonical_name_function,
}),
Err(s) => {
Err(Error::new(&s))
}
}
}
}
impl<LB> LexerBase for SourceLexer<LB>
where
LB: LexerBase,
{
fn next_token(&mut self) -> super::structs::Token {
let last = self.stack.last_mut();
match last {
Some(table) => {
let token = table.lexer.next_token();
match token.term() {
Lexicon::EOT => {
self.stack.pop();
token
}
Lexicon::Keyword(k) => match k {
Keyword::Source => {
let token = table.lexer.next_token();
match token.term() {
Lexicon::String(stream_name) => {
let stream_name = match parse_string(&stream_name) {
Ok(s) => s,
Err(e) => {
return Token::create(
Lexicon::Error(e),
token.column(),
token.line(),
&token.raw(),
)
}
};
let canonical_name =
(self.canonical_name_function)(&stream_name);
for needle in &self.stack {
if needle.stream_name.eq(&canonical_name) {
return Token::create(
Lexicon::Error(
"Circular source detected, abort load"
.to_string(),
),
token.column(),
token.line(),
&token.raw(),
);
}
}
match (self.source_function)(&stream_name) {
Ok(lb) => {
let t = Table {
stream_name: canonical_name,
lexer: lb,
};
self.stack.push(t);
self.next_token()
},
Err(s) => {
Token::create(Lexicon::Error(s), token.column(), token.line(), &token.raw())
}
}
}
_ => Token::create(
Lexicon::Error(
"Expected string indicating source file or stream"
.to_string(),
),
token.column(),
token.line(),
&token.raw(),
),
}
}
_ => token,
},
_ => token,
}
}
None => Token::create(Lexicon::EOT, 0, 0, &""),
}
}
fn current_stream(&self) -> Option<String> {
let last = self.stack.last();
match last {
Some(table) => Some(table.stream_name.to_string()),
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use super::super::Lexer;
use super::super::LexerBase;
use super::*;
#[test]
fn test_keywords() {
let mut l = SourceLexer::<Lexer<&[u8]>>::new(
"foo",
|stream_name| {
if stream_name == "foo" {
let s = &mut "mainmenu config menuconfig choice endchoice menu endmenu if endif bool def_bool \
tristate def_tristate string hex int default depends on select imply visible range prompt comment".as_bytes();
Ok(Lexer::create(s))
} else {
Err("Error".to_owned())
}
},
|stream_name| stream_name.to_string(),
).unwrap();
assert_eq!(Lexicon::Keyword(Keyword::Mainmenu), l.next_token().term());
assert_eq!(Lexicon::Keyword(Keyword::Config), l.next_token().term());
assert_eq!(Lexicon::Keyword(Keyword::Menuconfig), l.next_token().term());
assert_eq!(Lexicon::Keyword(Keyword::Choice), l.next_token().term());
assert_eq!(Lexicon::Keyword(Keyword::Endchoice), l.next_token().term());
assert_eq!(Lexicon::Keyword(Keyword::Menu), l.next_token().term());
assert_eq!(Lexicon::Keyword(Keyword::Endmenu), l.next_token().term());
assert_eq!(Lexicon::Keyword(Keyword::If), l.next_token().term());
assert_eq!(Lexicon::Keyword(Keyword::Endif), l.next_token().term());
assert_eq!(Lexicon::Keyword(Keyword::Bool), l.next_token().term());
assert_eq!(Lexicon::Keyword(Keyword::DefBool), l.next_token().term());
assert_eq!(Lexicon::Keyword(Keyword::Tristate), l.next_token().term());
assert_eq!(
Lexicon::Keyword(Keyword::DefTristate),
l.next_token().term()
);
assert_eq!(Lexicon::Keyword(Keyword::String), l.next_token().term());
assert_eq!(Lexicon::Keyword(Keyword::Hex), l.next_token().term());
assert_eq!(Lexicon::Keyword(Keyword::Int), l.next_token().term());
assert_eq!(Lexicon::Keyword(Keyword::Default), l.next_token().term());
assert_eq!(Lexicon::Keyword(Keyword::Depends), l.next_token().term());
assert_eq!(Lexicon::Keyword(Keyword::On), l.next_token().term());
assert_eq!(Lexicon::Keyword(Keyword::Select), l.next_token().term());
assert_eq!(Lexicon::Keyword(Keyword::Imply), l.next_token().term());
assert_eq!(Lexicon::Keyword(Keyword::Visible), l.next_token().term());
assert_eq!(Lexicon::Keyword(Keyword::Range), l.next_token().term());
assert_eq!(Lexicon::Keyword(Keyword::Prompt), l.next_token().term());
assert_eq!(Lexicon::Keyword(Keyword::Comment), l.next_token().term());
assert_eq!(Lexicon::EOT, l.next_token().term());
}
#[test]
fn test_keywords_sub_lexer() {
let mut l = SourceLexer::<Lexer<&[u8]>>::new(
"foo",
|stream_name| {
if stream_name == "foo" {
let s = &mut "source \"bar\"".as_bytes();
Ok(Lexer::create(s))
} else if stream_name == "bar" {
let s = &mut "mainmenu config menuconfig choice endchoice menu endmenu if endif bool def_bool \
tristate def_tristate string hex int default depends on select imply visible range prompt comment".as_bytes();
Ok(Lexer::create(s))
} else {
Err("Error".to_owned())
}
},
|stream_name| stream_name.to_string(),
).unwrap();
assert_eq!(Lexicon::Keyword(Keyword::Mainmenu), l.next_token().term());
assert_eq!(Lexicon::Keyword(Keyword::Config), l.next_token().term());
assert_eq!(Lexicon::Keyword(Keyword::Menuconfig), l.next_token().term());
assert_eq!(Lexicon::Keyword(Keyword::Choice), l.next_token().term());
assert_eq!(Lexicon::Keyword(Keyword::Endchoice), l.next_token().term());
assert_eq!(Lexicon::Keyword(Keyword::Menu), l.next_token().term());
assert_eq!(Lexicon::Keyword(Keyword::Endmenu), l.next_token().term());
assert_eq!(Lexicon::Keyword(Keyword::If), l.next_token().term());
assert_eq!(Lexicon::Keyword(Keyword::Endif), l.next_token().term());
assert_eq!(Lexicon::Keyword(Keyword::Bool), l.next_token().term());
assert_eq!(Lexicon::Keyword(Keyword::DefBool), l.next_token().term());
assert_eq!(Lexicon::Keyword(Keyword::Tristate), l.next_token().term());
assert_eq!(
Lexicon::Keyword(Keyword::DefTristate),
l.next_token().term()
);
assert_eq!(Lexicon::Keyword(Keyword::String), l.next_token().term());
assert_eq!(Lexicon::Keyword(Keyword::Hex), l.next_token().term());
assert_eq!(Lexicon::Keyword(Keyword::Int), l.next_token().term());
assert_eq!(Lexicon::Keyword(Keyword::Default), l.next_token().term());
assert_eq!(Lexicon::Keyword(Keyword::Depends), l.next_token().term());
assert_eq!(Lexicon::Keyword(Keyword::On), l.next_token().term());
assert_eq!(Lexicon::Keyword(Keyword::Select), l.next_token().term());
assert_eq!(Lexicon::Keyword(Keyword::Imply), l.next_token().term());
assert_eq!(Lexicon::Keyword(Keyword::Visible), l.next_token().term());
assert_eq!(Lexicon::Keyword(Keyword::Range), l.next_token().term());
assert_eq!(Lexicon::Keyword(Keyword::Prompt), l.next_token().term());
assert_eq!(Lexicon::Keyword(Keyword::Comment), l.next_token().term());
assert_eq!(Lexicon::EOT, l.next_token().term());
}
#[test]
fn test_error_wrong_syntax() {
let mut l = SourceLexer::<Lexer<&[u8]>>::new(
"foo",
|stream_name| {
if stream_name == "foo" {
let s = &mut "source bla".as_bytes();
Ok(Lexer::create(s))
} else {
Err("Error".to_owned())
}
},
|stream_name| stream_name.to_string(),
).unwrap();
let is_error = if let Lexicon::Error(_) = l.next_token().term() {
true
} else {
false
};
assert_eq!(is_error, true);
}
#[test]
fn test_error_circular_syntax() {
let mut l = SourceLexer::<Lexer<&[u8]>>::new(
"foo",
|stream_name| {
if stream_name == "foo" {
let s = &mut "source \"bar\"".as_bytes();
Ok(Lexer::create(s))
} else if stream_name == "bar" {
let s = &mut "source \"foo\"".as_bytes();
Ok(Lexer::create(s))
} else {
Err("Error".to_owned())
}
},
|stream_name| stream_name.to_string(),
).unwrap();
let is_error = if let Lexicon::Error(_) = l.next_token().term() {
true
} else {
false
};
assert_eq!(is_error, true);
}
}