#[cfg(test)]
mod tests;
use std::fs::read_to_string;
use lexer::tokenize;
use token::{Token, TokenType};
use std::slice::Iter;
mod lexer;
mod token;
#[derive(Debug, PartialEq)]
pub enum GsblError {
FailedToParseFile(String),
FailedToParseArray(String),
MalformedCode(String),
}
#[derive(Debug, PartialEq, Clone)]
enum GsblValueType {
String,
Int,
Float,
Bool,
Table,
Array,
}
#[derive(Debug, PartialEq, Clone)]
enum GsblValue {
String(String),
Int(i32),
Float(f32),
Bool(bool),
Table(GsblTable),
Array(Vec<GsblValue>)
}
#[derive(Debug, PartialEq, Clone)]
struct Data {
name: String,
value: GsblValue,
value_type: GsblValueType
}
#[derive(Debug, PartialEq, Clone)]
pub struct GsblTable { data: Vec<Data>
}
impl GsblTable {
fn expect<'a>(tokens: &'a mut Iter<Token>, token_type: TokenType) -> Result<&'a Token, GsblError> {
let token = tokens.next().unwrap();
if token.token_type != token_type {
return Err(GsblError::MalformedCode(format!("Expected token {:?}, but found {:?}", token_type, token.token_type)));
}
Ok(token)
}
fn parse_table<'a>(
tokens: &'a mut Iter<Token>,
mut token: &'a Token
) -> Result<GsblTable, GsblError> {
let mut table_data = Vec::new();
while token.token_type != TokenType::CloseBrace
&& token.token_type != TokenType::EndOfFile {
if token.token_type == TokenType::Identifier {
table_data.push(Self::parse_variable(tokens, &token)?);
}
token = tokens.next().unwrap();
}
if token.token_type == TokenType::EndOfFile {
return Err(GsblError::MalformedCode("Unterminated table".to_string()));
}
Ok(GsblTable {data: table_data})
}
fn parse_array<'a>(
tokens: &'a mut Iter<Token>,
mut token: &'a Token
) -> Result<Vec<GsblValue>, GsblError> {
let mut array_data = Vec::new();
let mut array_type = TokenType::Unknown;
while token.token_type != TokenType::CloseBracket
&& token.token_type != TokenType::EndOfFile {
if array_type == TokenType::Unknown {
array_type = token.token_type.clone();
}
if array_type != token.token_type && token.token_type != TokenType::Comma {
return Err(GsblError::MalformedCode("Multiple data types inside array".to_string()));
}
array_data.push(match token.token_type {
TokenType::String => {
GsblValue::String(token.value.clone())
},
TokenType::Int => {
GsblValue::Int(token.value.parse::<i32>().unwrap())
},
TokenType::Float => {
GsblValue::Float(token.value.parse::<f32>().unwrap())
},
TokenType::Bool => {
GsblValue::Bool(token.value == "true")
},
TokenType::OpenBracket => {
GsblValue::Array(Self::parse_array(tokens, token)?) },
TokenType::Comma => {
token = tokens.next().unwrap();
continue;
}
_ => { return Err(GsblError::FailedToParseArray(format!("Unknown token inside array declaration: {:?}", token.token_type))); }
});
token = tokens.next().unwrap();
}
if token.token_type == TokenType::EndOfFile {
return Err(GsblError::MalformedCode("Unterminated array".to_string()));
}
Ok(array_data)
}
fn parse_variable<'a>(
tokens: &'a mut Iter<Token>,
mut token: &'a Token
) -> Result<Data, GsblError> {
let var_name = token.value.clone();
Self::expect(tokens, TokenType::Equals)?;
token = tokens.next().unwrap();
let value_type: GsblValueType;
let data = Data {
name: var_name,
value: match token.token_type {
TokenType::String => {
value_type = GsblValueType::String;
GsblValue::String(token.value.clone())
},
TokenType::Int => {
value_type = GsblValueType::Int;
GsblValue::Int(token.value.parse::<i32>().unwrap())
},
TokenType::Float => {
value_type = GsblValueType::Float;
GsblValue::Float(token.value.parse::<f32>().unwrap())
},
TokenType::Bool => {
value_type = GsblValueType::Bool;
GsblValue::Bool(token.value == "true")
},
TokenType::OpenBrace => {
token = tokens.next().unwrap();
value_type = GsblValueType::Table;
GsblValue::Table(Self::parse_table(tokens, token)?)
},
TokenType::OpenBracket => {
token = tokens.next().unwrap();
value_type = GsblValueType::Array;
GsblValue::Array(Self::parse_array(tokens, token)?)
}
_ => { return Ok(Data { value: GsblValue::Int(0), value_type: GsblValueType::Int, name: "coolplaceholder".to_string()}); }
},
value_type
};
Ok(data)
}
pub fn parse_file(file_path: &str) -> Result<Self, GsblError> {
let tokens = tokenize(&read_to_string(file_path).unwrap());
if !tokens.is_ok() {
return Err(tokens.unwrap_err());
}
let tokens = tokens.unwrap();
let mut tokens = tokens.iter();
let mut table: GsblTable = GsblTable { data: Vec::new() };
while let Some(token) = tokens.next() {
if token.token_type == TokenType::EndOfFile {
break;
}
if token.token_type == TokenType::MultilineComment || token.token_type == TokenType::Comment {
continue;
}
if token.token_type == TokenType::OpenBrace {
table = Self::parse_table(&mut tokens, token)?;
break;
}
}
Ok(table)
}
fn get_value<T>(&self, var_name: &str, value_type: GsblValueType, map_fn: impl Fn(&GsblValue) -> Option<T>) -> Option<T> {
for e in &self.data {
if e.name == var_name && e.value_type == value_type {
return map_fn(&e.value);
}
}
None
}
fn get_array<T>(&self, var_name: &str, map_fn: impl Fn(&GsblValue) -> Option<T>) -> Option<Vec<T>> {
for e in &self.data {
if e.name == var_name && e.value_type == GsblValueType::Array {
if let GsblValue::Array(a) = &e.value {
return Some(
a.iter()
.filter_map(|value| map_fn(value))
.collect()
);
}
}
}
None
}
pub fn get_table(&self, var_name: &str) -> Option<GsblTable> {
self.get_value(var_name, GsblValueType::Table, |v| {
if let GsblValue::Table(t) = v {
Some((*t).clone())
} else {
None
}
})
}
pub fn get_string(&self, var_name: &str) -> Option<String> {
self.get_value(var_name, GsblValueType::String, |v| {
if let GsblValue::String(s) = v {
Some((*s).clone())
} else {
None
}
})
}
pub fn get_string_array(&self, var_name: &str) -> Option<Vec<String>> {
self.get_array(var_name, |v| {
if let GsblValue::String(s) = v {
Some((*s).clone())
} else {
None
}
})
}
pub fn get_int(&self, var_name: &str) -> Option<i32> {
self.get_value(var_name, GsblValueType::Int, |v| {
if let GsblValue::Int(i) = v {
Some(*i)
} else {
None
}
})
}
pub fn get_int_array(&self, var_name: &str) -> Option<Vec<i32>> {
self.get_array(var_name, |v| {
if let GsblValue::Int(i) = v {
Some(*i)
} else {
None
}
})
}
pub fn get_float(&self, var_name: &str) -> Option<f32> {
self.get_value(var_name, GsblValueType::Float, |v| {
if let GsblValue::Float(f) = v {
Some(*f)
} else {
None
}
})
}
pub fn get_float_array(&self, var_name: &str) -> Option<Vec<f32>> {
self.get_array(var_name, |v| {
if let GsblValue::Float(f) = v {
Some(*f)
} else {
None
}
})
}
pub fn get_bool(&self, var_name: &str) -> Option<bool> {
self.get_value(var_name, GsblValueType::Bool, |v| {
if let GsblValue::Bool(b) = v {
Some(*b)
} else {
None
}
})
}
pub fn get_bool_array(&self, var_name: &str) -> Option<Vec<bool>> {
self.get_array(var_name, |v| {
if let GsblValue::Bool(b) = v {
Some(*b)
} else {
None
}
})
}
}