mirror of
https://github.com/Noratrieb/mono-fmt.git
synced 2026-01-14 15:25:08 +01:00
finish trying to write the new perser
This commit is contained in:
parent
34155bcd48
commit
a96b97f5de
4 changed files with 142 additions and 27 deletions
|
|
@ -65,9 +65,9 @@ impl std::fmt::Debug for FmtPart {
|
|||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Literal(arg0) => f.debug_tuple("Literal").field(arg0).finish(),
|
||||
Self::Debug(arg0) => f.debug_tuple("Debug").finish(),
|
||||
Self::Display(arg0) => f.debug_tuple("Display").finish(),
|
||||
Self::Advanced(arg0, arg1) => f.debug_tuple("Advanced").field(arg0).finish(),
|
||||
Self::Debug(_) => f.debug_tuple("Debug").finish(),
|
||||
Self::Display(_) => f.debug_tuple("Display").finish(),
|
||||
Self::Advanced(arg0, _) => f.debug_tuple("Advanced").field(arg0).finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -234,7 +234,9 @@ impl ToTokens for FmtPart {
|
|||
pub fn format_args(tokens: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(tokens as Input);
|
||||
|
||||
parser::FmtSpecParser::new(&mut input.format_str.chars().peekable()).parse();
|
||||
if false {
|
||||
parser::FmtSpecParser::new(&mut input.format_str.chars().peekable()).parse().unwrap();
|
||||
}
|
||||
|
||||
let formatter = Formatter {
|
||||
string: input.format_str.chars().peekable(),
|
||||
|
|
|
|||
|
|
@ -1,9 +1,27 @@
|
|||
use std::{iter::Peekable, str::Chars};
|
||||
|
||||
#[derive(Debug, PartialEq, Default)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Alignment {
|
||||
Left,
|
||||
Center,
|
||||
Right,
|
||||
}
|
||||
|
||||
impl Alignment {
|
||||
fn from_char(char: char) -> Result<Self, ()> {
|
||||
match char {
|
||||
'<' => Ok(Self::Left),
|
||||
'^' => Ok(Self::Center),
|
||||
'>' => Ok(Self::Right),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct Align {
|
||||
amount: usize,
|
||||
char: Option<char>,
|
||||
kind: Alignment,
|
||||
fill: Option<char>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Default)]
|
||||
|
|
@ -24,6 +42,12 @@ pub enum FmtType {
|
|||
Other(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum Precision {
|
||||
Num(usize),
|
||||
Asterisk,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Default)]
|
||||
pub struct FmtSpec {
|
||||
arg: Argument,
|
||||
|
|
@ -32,12 +56,13 @@ pub struct FmtSpec {
|
|||
alternate: bool,
|
||||
zero: bool,
|
||||
width: Option<usize>,
|
||||
precision: Option<usize>,
|
||||
precision: Option<Precision>,
|
||||
kind: FmtType,
|
||||
}
|
||||
|
||||
pub struct FmtSpecParser<'a> {
|
||||
chars: &'a mut Peekable<Chars<'a>>,
|
||||
/// The last state of the parser.
|
||||
state: State,
|
||||
argument: FmtSpec,
|
||||
}
|
||||
|
|
@ -47,13 +72,12 @@ enum State {
|
|||
Initial,
|
||||
Argument,
|
||||
// : here
|
||||
Fill,
|
||||
Align,
|
||||
Sign,
|
||||
Alternate,
|
||||
Zero,
|
||||
Width,
|
||||
Precision,
|
||||
Type,
|
||||
Done,
|
||||
}
|
||||
|
||||
|
|
@ -89,21 +113,33 @@ impl<'a> FmtSpecParser<'a> {
|
|||
false
|
||||
}
|
||||
|
||||
fn eat_until(&mut self, char: char) -> Option<String> {
|
||||
fn expect(&mut self, char: char) -> Result<(), ()> {
|
||||
if !self.eat(char) {
|
||||
return Err(());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn eat_until(&mut self, should_stop: impl Fn(char) -> bool) -> Option<String> {
|
||||
let mut string = String::new();
|
||||
let mut has_char = false;
|
||||
while self.peek() != Some(char) {
|
||||
self.next();
|
||||
string.push(char);
|
||||
// let_chains would be neat here
|
||||
while self.peek().is_some() && !should_stop(self.peek().unwrap()) {
|
||||
let next = self.next().unwrap();
|
||||
string.push(next);
|
||||
has_char = true;
|
||||
}
|
||||
has_char.then_some(string)
|
||||
}
|
||||
|
||||
fn eat_until_match(&mut self, char: char) -> Option<String> {
|
||||
self.eat_until(|c| c == char)
|
||||
}
|
||||
|
||||
fn step(&mut self) -> Result<(), ()> {
|
||||
match self.state {
|
||||
State::Initial => {
|
||||
let argument = if let Some(arg) = self.eat_until(':') {
|
||||
let argument = if let Some(arg) = self.eat_until_match(':') {
|
||||
if let Ok(num) = arg.parse() {
|
||||
Argument::PositionalExplicit(num)
|
||||
} else {
|
||||
|
|
@ -119,18 +155,94 @@ impl<'a> FmtSpecParser<'a> {
|
|||
if !self.eat(':') {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
State::Argument => todo!(),
|
||||
State::Fill => todo!(),
|
||||
State::Align => todo!(),
|
||||
State::Sign => todo!(),
|
||||
State::Zero => todo!(),
|
||||
State::Width => todo!(),
|
||||
State::Precision => todo!(),
|
||||
State::Type => todo!(),
|
||||
State::Argument => match self.next().ok_or(())? {
|
||||
c @ ('>' | '^' | '<') => {
|
||||
self.argument.align = Some(Align {
|
||||
kind: Alignment::from_char(c)?,
|
||||
fill: None,
|
||||
});
|
||||
self.state = State::Align;
|
||||
}
|
||||
other => {
|
||||
if let Some(c @ ('>' | '^' | '<')) = self.peek() {
|
||||
self.argument.align = Some(Align {
|
||||
kind: Alignment::from_char(c).unwrap(),
|
||||
fill: Some(other),
|
||||
});
|
||||
}
|
||||
|
||||
self.state = State::Align;
|
||||
}
|
||||
},
|
||||
State::Align => {
|
||||
if let Some(c @ ('+' | '-')) = self.peek() {
|
||||
self.next();
|
||||
self.argument.sign = Some(c);
|
||||
}
|
||||
self.state = State::Sign;
|
||||
}
|
||||
State::Sign => {
|
||||
if self.eat('#') {
|
||||
self.argument.alternate = true;
|
||||
}
|
||||
self.state = State::Alternate;
|
||||
}
|
||||
State::Alternate => {
|
||||
if self.eat('0') {
|
||||
self.argument.zero = true;
|
||||
}
|
||||
self.state = State::Zero;
|
||||
}
|
||||
State::Zero => {
|
||||
if let Some(width) = self.eat_until(|c| !c.is_ascii_digit()) {
|
||||
let width = width.parse().map_err(|_| ())?;
|
||||
self.argument.width = Some(width);
|
||||
}
|
||||
self.state = State::Width;
|
||||
}
|
||||
State::Width => {
|
||||
if self.eat('.') {
|
||||
if let Some(precision) = self.eat_until(|c| c != '*' && !c.is_ascii_digit()) {
|
||||
let precision = if precision == "*" {
|
||||
Precision::Asterisk
|
||||
} else {
|
||||
Precision::Num(precision.parse().map_err(|_| ())?)
|
||||
};
|
||||
self.argument.precision = Some(precision);
|
||||
}
|
||||
}
|
||||
self.state = State::Precision;
|
||||
}
|
||||
State::Precision => match self.next() {
|
||||
Some('?') => {
|
||||
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('}')?;
|
||||
}
|
||||
}
|
||||
},
|
||||
State::Done => unreachable!(),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue