mirror of
https://github.com/Noratrieb/mono-fmt.git
synced 2026-01-17 00:35:05 +01:00
parser kind of works
This commit is contained in:
parent
cbd6af9844
commit
5d24f703fa
3 changed files with 108 additions and 66 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
use std::str::Chars;
|
use std::str::Chars;
|
||||||
|
|
||||||
use parser::{Error, FmtSpec};
|
use parser::{Error, FmtSpec};
|
||||||
use peekmore::{PeekMoreIterator, PeekMore};
|
use peekmore::{PeekMore, PeekMoreIterator};
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use proc_macro2::Span;
|
use proc_macro2::Span;
|
||||||
use quote::{quote, ToTokens};
|
use quote::{quote, ToTokens};
|
||||||
|
|
@ -14,8 +14,6 @@ use syn::{
|
||||||
|
|
||||||
mod parser;
|
mod parser;
|
||||||
|
|
||||||
// TODO: Rewrite using state machine please
|
|
||||||
|
|
||||||
struct Input {
|
struct Input {
|
||||||
format_str: String,
|
format_str: String,
|
||||||
str_span: Span,
|
str_span: Span,
|
||||||
|
|
@ -163,9 +161,13 @@ pub fn format_args(tokens: TokenStream) -> TokenStream {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use peekmore::PeekMore;
|
||||||
use syn::Expr;
|
use syn::Expr;
|
||||||
|
|
||||||
use crate::FmtPart;
|
use crate::{
|
||||||
|
parser::{Align, Alignment, Argument, FmtSpec, FmtType},
|
||||||
|
FmtPart,
|
||||||
|
};
|
||||||
|
|
||||||
fn fake_expr() -> Expr {
|
fn fake_expr() -> Expr {
|
||||||
syn::parse_str("1").unwrap()
|
syn::parse_str("1").unwrap()
|
||||||
|
|
@ -177,10 +179,59 @@ mod tests {
|
||||||
|
|
||||||
fn run_test(string: &str, expr_count: usize) -> Vec<FmtPart> {
|
fn run_test(string: &str, expr_count: usize) -> Vec<FmtPart> {
|
||||||
let fmt = super::Formatter {
|
let fmt = super::Formatter {
|
||||||
string: string.chars().peekable(),
|
string: string.chars().peekmore(),
|
||||||
exprs: fake_exprs(expr_count).into_iter(),
|
exprs: fake_exprs(expr_count).into_iter(),
|
||||||
fmt_parts: Vec::new(),
|
fmt_parts: Vec::new(),
|
||||||
};
|
};
|
||||||
fmt.parse().unwrap()
|
fmt.parse().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn empty() {
|
||||||
|
let parts = run_test("{}", 1);
|
||||||
|
assert_eq!(
|
||||||
|
parts,
|
||||||
|
vec![FmtPart::Spec(
|
||||||
|
FmtSpec {
|
||||||
|
..FmtSpec::default()
|
||||||
|
},
|
||||||
|
fake_expr()
|
||||||
|
)]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn debug() {
|
||||||
|
let parts = run_test("{:?}", 1);
|
||||||
|
assert_eq!(
|
||||||
|
parts,
|
||||||
|
vec![FmtPart::Spec(
|
||||||
|
FmtSpec {
|
||||||
|
kind: FmtType::Debug,
|
||||||
|
..FmtSpec::default()
|
||||||
|
},
|
||||||
|
fake_expr()
|
||||||
|
)]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn many() {
|
||||||
|
let parts = run_test("{uwu:-<?}", 1);
|
||||||
|
assert_eq!(
|
||||||
|
parts,
|
||||||
|
vec![FmtPart::Spec(
|
||||||
|
FmtSpec {
|
||||||
|
arg: Argument::Keyword("uwu".to_string()),
|
||||||
|
align: Some(Align {
|
||||||
|
kind: Alignment::Left,
|
||||||
|
fill: Some('-'),
|
||||||
|
}),
|
||||||
|
kind: FmtType::Debug,
|
||||||
|
..FmtSpec::default()
|
||||||
|
},
|
||||||
|
fake_expr()
|
||||||
|
)]
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,8 +35,8 @@ impl Alignment {
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct Align {
|
pub struct Align {
|
||||||
kind: Alignment,
|
pub kind: Alignment,
|
||||||
fill: Option<char>,
|
pub fill: Option<char>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Default)]
|
#[derive(Debug, PartialEq, Default)]
|
||||||
|
|
@ -58,21 +58,21 @@ pub enum FmtType {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
enum Precision {
|
pub enum Precision {
|
||||||
Num(usize),
|
Num(usize),
|
||||||
Asterisk,
|
Asterisk,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Default)]
|
#[derive(Debug, PartialEq, Default)]
|
||||||
pub struct FmtSpec {
|
pub struct FmtSpec {
|
||||||
arg: Argument,
|
pub arg: Argument,
|
||||||
align: Option<Align>,
|
pub align: Option<Align>,
|
||||||
sign: Option<char>,
|
pub sign: Option<char>,
|
||||||
alternate: bool,
|
pub alternate: bool,
|
||||||
zero: bool,
|
pub zero: bool,
|
||||||
width: Option<usize>,
|
pub width: Option<usize>,
|
||||||
precision: Option<Precision>,
|
pub precision: Option<Precision>,
|
||||||
kind: FmtType,
|
pub kind: FmtType,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FmtSpecParser<'a, 'b> {
|
pub struct FmtSpecParser<'a, 'b> {
|
||||||
|
|
@ -88,11 +88,6 @@ enum State {
|
||||||
Argument,
|
Argument,
|
||||||
// : here
|
// : here
|
||||||
Align,
|
Align,
|
||||||
Sign,
|
|
||||||
Alternate,
|
|
||||||
Zero,
|
|
||||||
Width,
|
|
||||||
Precision,
|
|
||||||
Done,
|
Done,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -171,6 +166,7 @@ impl<'a, 'b> FmtSpecParser<'a, 'b> {
|
||||||
if self.argument.arg != Argument::Positional {
|
if self.argument.arg != Argument::Positional {
|
||||||
self.expect(':')?;
|
self.expect(':')?;
|
||||||
}
|
}
|
||||||
|
self.eat(':');
|
||||||
}
|
}
|
||||||
State::Argument => match self
|
State::Argument => match self
|
||||||
.peek()
|
.peek()
|
||||||
|
|
@ -185,8 +181,9 @@ impl<'a, 'b> FmtSpecParser<'a, 'b> {
|
||||||
self.state = State::Align;
|
self.state = State::Align;
|
||||||
}
|
}
|
||||||
other => {
|
other => {
|
||||||
// peek2
|
if let Some(c @ ('>' | '^' | '<')) = self.chars.peek_nth(1).copied() {
|
||||||
if let Some(c @ ('>' | '^' | '<')) = self.peek() {
|
self.next(); // fill
|
||||||
|
self.next(); // align
|
||||||
self.argument.align = Some(Align {
|
self.argument.align = Some(Align {
|
||||||
kind: Alignment::from_char(c).unwrap(),
|
kind: Alignment::from_char(c).unwrap(),
|
||||||
fill: Some(other),
|
fill: Some(other),
|
||||||
|
|
@ -201,30 +198,22 @@ impl<'a, 'b> FmtSpecParser<'a, 'b> {
|
||||||
self.next();
|
self.next();
|
||||||
self.argument.sign = Some(c);
|
self.argument.sign = Some(c);
|
||||||
}
|
}
|
||||||
self.state = State::Sign;
|
|
||||||
}
|
|
||||||
State::Sign => {
|
|
||||||
if self.eat('#') {
|
if self.eat('#') {
|
||||||
self.argument.alternate = true;
|
self.argument.alternate = true;
|
||||||
}
|
}
|
||||||
self.state = State::Alternate;
|
|
||||||
}
|
|
||||||
State::Alternate => {
|
|
||||||
if self.eat('0') {
|
if self.eat('0') {
|
||||||
self.argument.zero = true;
|
self.argument.zero = true;
|
||||||
}
|
}
|
||||||
self.state = State::Zero;
|
|
||||||
}
|
|
||||||
State::Zero => {
|
|
||||||
if let Some(width) = self.eat_until(|c| !c.is_ascii_digit()) {
|
if let Some(width) = self.eat_until(|c| !c.is_ascii_digit()) {
|
||||||
let width = width
|
let width = width
|
||||||
.parse()
|
.parse()
|
||||||
.map_err(|_| Error::new("width specified too long".to_string()))?;
|
.map_err(|_| Error::new("width specified too long".to_string()))?;
|
||||||
self.argument.width = Some(width);
|
self.argument.width = Some(width);
|
||||||
}
|
}
|
||||||
self.state = State::Width;
|
|
||||||
}
|
|
||||||
State::Width => {
|
|
||||||
if self.eat('.') {
|
if self.eat('.') {
|
||||||
if let Some(precision) = self.eat_until(|c| c != '*' && !c.is_ascii_digit()) {
|
if let Some(precision) = self.eat_until(|c| c != '*' && !c.is_ascii_digit()) {
|
||||||
let precision = if precision == "*" {
|
let precision = if precision == "*" {
|
||||||
|
|
@ -237,34 +226,36 @@ impl<'a, 'b> FmtSpecParser<'a, 'b> {
|
||||||
self.argument.precision = Some(precision);
|
self.argument.precision = Some(precision);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.state = State::Precision;
|
|
||||||
}
|
match self.next() {
|
||||||
State::Precision => match self.next() {
|
Some('?') => {
|
||||||
Some('?') => {
|
self.argument.kind = FmtType::Debug;
|
||||||
self.argument.kind = FmtType::Debug;
|
|
||||||
self.expect('}')?;
|
|
||||||
}
|
|
||||||
Some('x') => {
|
|
||||||
self.expect('?')?;
|
|
||||||
self.argument.kind = FmtType::LowerHex;
|
|
||||||
self.expect('}')?;
|
|
||||||
}
|
|
||||||
Some('X') => {
|
|
||||||
self.expect('?')?;
|
|
||||||
self.argument.kind = FmtType::UpperHex;
|
|
||||||
self.expect('}')?;
|
|
||||||
}
|
|
||||||
Some('}') | None => {}
|
|
||||||
Some(other) => {
|
|
||||||
if let Some(kind) = self.eat_until(|c| c == '}') {
|
|
||||||
self.argument.kind = FmtType::Other(format!("{other}{kind}"));
|
|
||||||
self.expect('}')?;
|
|
||||||
} else {
|
|
||||||
self.argument.kind = FmtType::Default;
|
|
||||||
self.expect('}')?;
|
self.expect('}')?;
|
||||||
}
|
}
|
||||||
|
Some('x') => {
|
||||||
|
self.expect('?')?;
|
||||||
|
self.argument.kind = FmtType::LowerHex;
|
||||||
|
self.expect('}')?;
|
||||||
|
}
|
||||||
|
Some('X') => {
|
||||||
|
self.expect('?')?;
|
||||||
|
self.argument.kind = FmtType::UpperHex;
|
||||||
|
self.expect('}')?;
|
||||||
|
}
|
||||||
|
Some('}') | None => {}
|
||||||
|
Some(other) => {
|
||||||
|
if let Some(kind) = self.eat_until(|c| c == '}') {
|
||||||
|
self.argument.kind = FmtType::Other(format!("{other}{kind}"));
|
||||||
|
self.expect('}')?;
|
||||||
|
} else {
|
||||||
|
self.argument.kind = FmtType::Default;
|
||||||
|
self.expect('}')?;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
self.state = State::Done;
|
||||||
|
}
|
||||||
State::Done => unreachable!(),
|
State::Done => unreachable!(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
12
src/lib.rs
12
src/lib.rs
|
|
@ -121,19 +121,19 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn display() {
|
fn display() {
|
||||||
//let result = format!("{}", "uwu");
|
let result = format!("{}", "uwu");
|
||||||
//assert_eq!(result, "uwu");
|
assert_eq!(result, "uwu");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn display_with_strings() {
|
fn display_with_strings() {
|
||||||
//let result = format!("oow{} omg", "uwu");
|
let result = format!("oow{} omg", "uwu");
|
||||||
//assert_eq!(result, "oowuwu omg");
|
assert_eq!(result, "oowuwu omg");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn debug() {
|
fn debug() {
|
||||||
//let result = format!("test {:?} hello", "uwu");
|
let result = format!("test {:?} hello", "uwu");
|
||||||
//assert_eq!(result, r#"test "uwu" hello"#);
|
assert_eq!(result, r#"test "uwu" hello"#);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue