add formatter, move Token and Class types out to main library binary to make the validator and formatter less dependent on the lexer

This commit is contained in:
abbie 2024-01-19 00:07:59 +00:00
parent 1995706a8b
commit ed882263a8
Signed by: threeoh6000
GPG key ID: 801FE4AD456E922C
11 changed files with 135 additions and 26 deletions

2
Cargo.lock generated
View file

@ -4,4 +4,4 @@ version = 3
[[package]] [[package]]
name = "frostwalker" name = "frostwalker"
version = "0.0.5" version = "0.0.6"

View file

@ -1,6 +1,6 @@
[package] [package]
name = "frostwalker" name = "frostwalker"
version = "0.0.5" version = "0.0.6"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View file

@ -1,7 +1,7 @@
# Frostwalker # Frostwalker
*Frostwalker is not stable nor ready for dependency development yet. Unless you plan to implement your own formatter, please wait for 0.1.0 to release. 0.0 versions are placeholders and will not be tagged.* *Frostwalker is still in development and should be considered alpha level software. The parser stack is complete but evaluation is still ongoing.*
A TOML-like configuration language parser for Rust. At present, the crate has a lexer and an incomplete validator with unit tests written for common cases. A formatter will eventually be added. A TOML-like configuration language parser for Rust. The crate features a parser stack made up of a lexer, validator, and formatter which all have unit tests and integration tests.
## Justification ## Justification
The Rust crate `toml` pulls in 8 other crates to do its job, including `serde`. While this may not be irksome to basically most Rust developers, I'm used to using severely underpowered Intel hardware so low compile times is a big focus for me so a crate made by me that requires only the standard library compared to one that requires other dependencies is preferrable. The Rust crate `toml` pulls in 8 other crates to do its job, including `serde`. While this may not be irksome to basically most Rust developers, I'm used to using severely underpowered Intel hardware so low compile times is a big focus for me so a crate made by me that requires only the standard library compared to one that requires other dependencies is preferrable.

41
src/formatter.rs Normal file
View file

@ -0,0 +1,41 @@
use super::{Token, Class};
use std::collections::HashMap;
pub fn format(tree: Vec<Token>) -> HashMap<String, String> {
let mut output = HashMap::new();
let mut current_key = "".to_string();
let mut current_index = 0;
let mut i = 0;
while i < tree.len() {
if tree[i].class == Class::IDENTIFIER {
current_key = tree[i].value.clone().unwrap_or("".to_string());
i = i + 1;
continue;
}
if tree[i].class == Class::LITERAL && current_key != "".to_string() {
output.insert(current_key, tree[i].value.clone().unwrap_or("".to_string()));
current_key = "".to_string();
i = i + 1;
continue;
}
if tree[i].class == Class::SEPARATOR && current_key != "".to_string() && tree[i].value.clone().unwrap_or("".to_string()) == "[".to_string() {
current_index = 0;
loop {
i = i + 1;
output.insert(format!("{}[{}]", current_key, current_index), tree[i].value.clone().unwrap_or("".to_string()));
current_index = current_index + 1;
i = i + 1;
if tree[i].class == Class::SEPARATOR && tree[i].value.clone().unwrap_or("".to_string()) == ",".to_string() {
continue;
} else {
break;
}
}
}
i = i + 1;
}
return output;
}

60
src/formatter_tests.rs Normal file
View file

@ -0,0 +1,60 @@
use super::{Token, Class};
use super::formatter;
#[test]
fn single_key() {
let id = Token { class: Class::IDENTIFIER, value: Some("key".to_string()) };
let op = Token { class: Class::EQUALS, value: None };
let strn = Token { class: Class::LITERAL, value: Some("value".to_string()) };
let manual_tree = vec![id, op, strn];
let tree = formatter::format(manual_tree);
assert_eq!(format!("{:?}", tree), "{\"key\": \"value\"}");
}
#[test]
fn single_key_escaped_double_quote() {
let id = Token { class: Class::IDENTIFIER, value: Some("key".to_string()) };
let op = Token { class: Class::EQUALS, value: None };
let strn = Token { class: Class::LITERAL, value: Some("\"value".to_string()) };
let manual_tree = vec![id, op, strn];
let tree = formatter::format(manual_tree);
assert_eq!(format!("{:?}", tree), "{\"key\": \"\\\"value\"}");
}
#[test]
fn double_key() {
let id = Token { class: Class::IDENTIFIER, value: Some("key".to_string()) };
let op = Token { class: Class::EQUALS, value: None };
let strn = Token { class: Class::LITERAL, value: Some("value".to_string()) };
let nl = Token { class: Class::NEWLINE, value: None };
let id2 = Token { class: Class::IDENTIFIER, value: Some("key2".to_string()) };
let op2 = Token { class: Class::EQUALS, value: None };
let strn2 = Token { class: Class::LITERAL, value: Some("value2".to_string()) };
let manual_tree = vec![id, op, strn, nl, id2, op2, strn2];
let tree = formatter::format(manual_tree);
if !(tree.get("key").unwrap() == "value") || !(tree.get("key2").unwrap() == "value2") {
println!("{:?}", tree);
panic!();
}
}
#[test]
fn array() {
let id = Token { class: Class::IDENTIFIER, value: Some("key".to_string()) };
let op = Token { class: Class::EQUALS, value: None };
let t1 = Token { class: Class::SEPARATOR, value: Some("[".to_string()) };
let strn = Token { class: Class::LITERAL, value: Some("value".to_string()) };
let t2 = Token { class: Class::SEPARATOR, value: Some(",".to_string()) };
let strn2 = Token { class: Class::LITERAL, value: Some("6".to_string()) };
let t3 = Token { class: Class::SEPARATOR, value: Some("]".to_string()) };
let manual_tree = vec![id, op, t1, strn, t2, strn2, t3];
let tree = formatter::format(manual_tree);
if !(tree.get("key[0]").unwrap() == "value") || !(tree.get("key[1]").unwrap() == "6") {
println!("{:?}", tree);
panic!();
}
}

View file

@ -1,21 +1,4 @@
#[derive(Debug)] use super::{Token, Class};
#[derive(PartialEq)]
pub struct Token {
pub class: Class,
pub value: Option<String>,
}
#[derive(Debug)]
#[derive(PartialEq)]
pub enum Class {
IDENTIFIER,
SEPARATOR,
EQUALS,
LITERAL,
NEWLINE,
BOOLEAN,
UNKNOWN,
}
pub fn tokenise(source: &str) -> Vec<Token> { pub fn tokenise(source: &str) -> Vec<Token> {
return tokenize(source); return tokenize(source);

View file

@ -1,4 +1,5 @@
use super::lexer::{self, Token, Class}; use super::{Token, Class};
use super::lexer;
#[test] #[test]
fn single_key() { fn single_key() {

View file

@ -1,8 +1,32 @@
#[derive(Debug)]
#[derive(PartialEq)]
pub struct Token {
pub class: Class,
pub value: Option<String>,
}
#[derive(Debug)]
#[derive(PartialEq)]
pub enum Class {
IDENTIFIER,
SEPARATOR,
EQUALS,
LITERAL,
NEWLINE,
BOOLEAN,
UNKNOWN,
}
pub mod lexer; pub mod lexer;
pub mod validator; pub mod validator;
pub mod formatter;
#[cfg(test)] #[cfg(test)]
mod lexer_tests; mod lexer_tests;
#[cfg(test)] #[cfg(test)]
mod validator_tests; mod validator_tests;
#[cfg(test)]
mod formatter_tests;

View file

@ -1,4 +1,4 @@
use super::lexer::{Token, Class}; use super::{Token, Class};
#[derive(Debug)] #[derive(Debug)]
#[derive(PartialEq)] #[derive(PartialEq)]

View file

@ -1,5 +1,5 @@
use super::validator; use super::validator;
use super::lexer::{Token, Class}; use super::{Token, Class};
#[test] #[test]
fn single_key() { fn single_key() {

View file

@ -1,4 +1,4 @@
use frostwalker::lexer::{self, Token, Class}; use frostwalker::lexer;
use frostwalker::validator; use frostwalker::validator;
#[test] #[test]