use crate::Config;
use crate::lexer::Symbol;
macro_rules! expect {
($value:expr, $expected:pat) => {
match $value {
$expected => {},
symbol => return Err(ParserError::UnexpectedSymbol(symbol.clone())),
}
};
($value:expr, $expected:pat, $then:block) => {
match $value {
$expected => $then,
symbol => return Err(ParserError::UnexpectedSymbol(symbol.clone())),
}
}
}
#[derive(Debug)]
pub enum ParserError {
UnexpectedSymbol(Symbol),
ExpectedSymbol,
ExpectedSymbolAfter(Symbol),
}
impl std::fmt::Display for ParserError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
impl std::error::Error for ParserError {
fn description(&self) -> &str {
use ParserError::*;
match self {
UnexpectedSymbol(_) => "Unexpected symbol",
ExpectedSymbol => "Expected symbol",
ExpectedSymbolAfter(_) => "Expected symbol after",
}
}
}
#[derive(Debug, Clone)]
pub enum Type {
Integer(i64),
Float(f64),
String(String),
Boolean(bool),
Array(Vec<Type>),
Dict(Config),
Null,
}
#[derive(Debug, Clone)]
pub struct Expression {
pub key: String,
pub value: Type,
}
impl Expression {
pub fn new(key: impl AsRef<str>, value: Type) -> Self {
Self {
key: key.as_ref().to_string(),
value,
}
}
}
impl PartialEq for Expression {
fn eq(&self, other: &Self) -> bool {
self.key == other.key
}
}
impl Eq for Expression {}
impl std::hash::Hash for Expression {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.key.hash(state);
}
}
#[derive(Debug, Default)]
pub struct Parser {
symbols: Vec<Symbol>,
cursor: isize,
expressions: Config,
}
impl Parser {
pub fn new(symbols: Vec<Symbol>) -> Self {
Self {
symbols,
cursor: -1,
..Default::default()
}
}
pub fn parse(&mut self) -> Result<Config, ParserError> {
while self.peek().is_some() {
let expression = self.expression()?;
self.expressions.set(expression);
}
Ok(self.expressions.clone())
}
fn next(&mut self) -> Option<Symbol> {
self.consume();
self.symbols.get(self.cursor as usize).cloned()
}
fn peek(&self) -> Option<Symbol> {
self.symbols.get((self.cursor + 1) as usize).cloned()
}
fn consume(&mut self) {
self.cursor += 1;
}
fn expression(&mut self) -> Result<Expression, ParserError> {
let symbol = self.next().unwrap();
expect!(symbol, Symbol::Identifier(ref key), {
let symbol = self.next().ok_or(ParserError::ExpectedSymbolAfter(symbol.clone()))?;
expect!(symbol, Symbol::Assignment);
self.peek().ok_or(ParserError::ExpectedSymbolAfter(symbol.clone()))?;
Ok(Expression::new(key, self.value()?))
})
}
fn value(&mut self) -> Result<Type, ParserError> {
use Symbol::*;
let symbol = self.peek().ok_or(ParserError::ExpectedSymbol)?;
let value = match symbol {
Integer(number) => Type::Integer(number),
Float(number) => Type::Float(number),
String(string) => Type::String(string),
Boolean(state) => Type::Boolean(state),
Single('[') => Type::Array(self.array()?),
Single('{') => Type::Dict(self.dict()?),
Null => Type::Null,
_ => return Err(ParserError::UnexpectedSymbol(symbol.clone())),
};
if !matches!(value, Type::Array(_) | Type::Dict(_)) {
self.consume();
}
Ok(value)
}
fn array(&mut self) -> Result<Vec<Type>, ParserError> {
let mut array: Vec<Type> = Vec::new();
let symbol = self.next().ok_or(ParserError::ExpectedSymbol)?;
expect!(symbol, Symbol::Single('['));
loop {
let symbol = self.peek().ok_or(ParserError::ExpectedSymbolAfter(self.symbols[self.cursor as usize].clone()))?;
if let Symbol::Single(']') = symbol {
self.consume();
break;
}
if !array.is_empty() {
expect!(symbol, Symbol::Single(','));
self.consume();
}
array.push(self.value()?);
}
Ok(array)
}
fn dict(&mut self) -> Result<Config, ParserError> {
let mut dict = Config::default();
let symbol = self.next().ok_or(ParserError::ExpectedSymbol)?;
expect!(symbol, Symbol::Single('{'));
loop {
let symbol = self.peek().ok_or(ParserError::ExpectedSymbol)?;
if let Symbol::Single('}') = symbol {
self.consume();
break;
}
dict.set(self.expression()?);
}
Ok(dict)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::lexer::Lexer;
#[test]
fn parser_parse() -> Result<(), Box<dyn std::error::Error>> {
let buffer = br#"
# where is our windows? :O
windows_root = "C:\\"
# is it updated? :D
updated = true
# what is it version? :v
version = 99999"#;
let mut lexer = Lexer::new(buffer.to_vec(), Default::default());
let symbols = lexer.lex()?;
let mut parser = Parser::new(symbols);
let expressions = parser.parse()?;
assert_eq!(expressions.0.len(), 3);
assert!(matches!(expressions.get("windows_root"), Some(Type::String(ref string)) if string == "C:\\"));
assert!(matches!(expressions.get("updated"), Some(Type::Boolean(true))));
assert!(matches!(expressions.get("version"), Some(Type::Integer(99999))));
Ok(())
}
#[test]
fn parser_parse_array() -> Result<(), Box<dyn std::error::Error>> {
let buffer = b"boolean_byte = [false, true, false, false, false, false, false, true]";
let mut lexer = Lexer::new(buffer.to_vec(), Default::default());
let symbols = lexer.lex()?;
let mut parser = Parser::new(symbols);
let expressions = parser.parse()?;
assert_eq!(expressions.0.len(), 1);
assert!(matches!(expressions.get("boolean_byte"), Some(Type::Array(ref array)) if array.len() == 8));
Ok(())
}
#[test]
fn parser_parse_2d_array() -> Result<(), Box<dyn std::error::Error>> {
let buffer = b"matrix = [[1, 0], [0, 1]]";
let mut lexer = Lexer::new(buffer.to_vec(), Default::default());
let symbols = lexer.lex()?;
let mut parser = Parser::new(symbols);
let expressions = parser.parse()?;
assert_eq!(expressions.0.len(), 1);
assert!(matches!(expressions.get("matrix"), Some(Type::Array(ref array)) if array.len() == 2));
assert!(matches!(expressions.get("matrix"), Some(Type::Array(ref array))
if matches!(array[0], Type::Array(ref array)
if array.len() == 2)));
assert!(matches!(expressions.get("matrix"), Some(Type::Array(ref array))
if matches!(array[1], Type::Array(ref array)
if array.len() == 2)));
Ok(())
}
#[test]
fn parser_parse_dict() -> Result<(), Box<dyn std::error::Error>> {
let buffer = b"jsobject = {\n prototype = null\n banana = \"'b' + 'a' + + 'a' + 'a'\"\n enemy_confused = true\n}";
let mut lexer = Lexer::new(buffer.to_vec(), Default::default());
let symbols = lexer.lex()?;
let mut parser = Parser::new(symbols);
let expressions = parser.parse()?;
assert_eq!(expressions.0.len(), 1);
if let Type::Dict(dict) = expressions.get("jsobject").unwrap() {
assert_eq!(dict.0.len(), 2);
assert!(dict.get("prototype").is_none());
assert!(matches!(dict.get("banana"), Some(Type::String(ref string)) if string == "'b' + 'a' + + 'a' + 'a'"));
assert!(matches!(dict.get("enemy_confused"), Some(Type::Boolean(true))));
} else {
panic!("panik");
};
Ok(())
}
#[test]
fn parser_parse_nested_dict() -> Result<(), Box<dyn std::error::Error>> {
let buffer = br#"
audio_output = {
type = "shout"
name = "my cool stream"
host = "localhost"
port = 80
mount = "/var/lib/mpd/http.ogg"
password = "p4ssw0rd"
quality = 5.0
bitrate = 128
format = {
samples = 48_000
bits = 16
channels = 2
}
encoding = "mp3"
}"#;
let mut lexer = Lexer::new(buffer.to_vec(), Default::default());
let symbols = lexer.lex()?;
let mut parser = Parser::new(symbols);
let expressions = parser.parse()?;
assert_eq!(expressions.0.len(), 1);
if let Type::Dict(dict) = expressions.get("audio_output").unwrap() {
assert_eq!(dict.0.len(), 10);
if let Type::Dict(dict) = dict.get("format").unwrap() {
assert_eq!(dict.0.len(), 3);
}
}
Ok(())
}
}