finish trying to write the new perser

This commit is contained in:
nora 2022-09-12 22:27:38 +02:00
parent 34155bcd48
commit a96b97f5de
4 changed files with 142 additions and 27 deletions

View file

@ -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(),

View file

@ -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(())
}
}