style: Use macro for path parser.

There are a lot of duplicates, so we use macro to refine them.

Depends on D2963

Differential Revision: https://phabricator.services.mozilla.com/D2966
This commit is contained in:
Boris Chiou 2018-08-22 01:20:21 +00:00 committed by Emilio Cobos Álvarez
parent dce2e2927f
commit b85c734c41
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C

View file

@ -70,6 +70,34 @@ struct PathParser<'a> {
path: Vec<PathCommand>, path: Vec<PathCommand>,
} }
macro_rules! parse_arguments {
(
$parser:ident,
$abs:ident,
$enum:ident,
[ $para:ident => $func:ident $(, $other_para:ident => $other_func:ident)* ]
) => {
{
loop {
let $para = $func(&mut $parser.chars)?;
$(
skip_comma_wsp(&mut $parser.chars);
let $other_para = $other_func(&mut $parser.chars)?;
)*
$parser.path.push(PathCommand::$enum { $para $(, $other_para)*, $abs });
// End of string or the next character is a possible new command.
if !skip_wsp(&mut $parser.chars) ||
$parser.chars.peek().map_or(true, |c| c.is_ascii_alphabetic()) {
break;
}
skip_comma_wsp(&mut $parser.chars);
}
Ok(())
}
}
}
impl<'a> PathParser<'a> { impl<'a> PathParser<'a> {
/// Parse a sub-path. /// Parse a sub-path.
fn parse_subpath(&mut self) -> Result<(), ()> { fn parse_subpath(&mut self) -> Result<(), ()> {
@ -87,46 +115,30 @@ impl<'a> PathParser<'a> {
match self.chars.next() { match self.chars.next() {
Some(command) => { Some(command) => {
let abs = command.is_uppercase(); let abs = command.is_uppercase();
match command { macro_rules! parse_command {
'Z' | 'z' => { ( $($($p:pat)|+ => $parse_func:ident,)* ) => {
// Note: A "closepath" coulbe be followed immediately by "moveto" or match command {
// any other command, so we don't break this loop. $(
self.path.push(PathCommand::ClosePath); $($p)|+ => {
}, skip_wsp(&mut self.chars);
'L' | 'l' => { self.$parse_func(abs)?;
skip_wsp(&mut self.chars); },
self.parse_lineto(abs)?; )*
}, _ => return Err(()),
'H' | 'h' => { }
skip_wsp(&mut self.chars); }
self.parse_h_lineto(abs)?;
},
'V' | 'v' => {
skip_wsp(&mut self.chars);
self.parse_v_lineto(abs)?;
},
'C' | 'c' => {
skip_wsp(&mut self.chars);
self.parse_curveto(abs)?;
},
'S' | 's' => {
skip_wsp(&mut self.chars);
self.parse_smooth_curveto(abs)?;
},
'Q' | 'q' => {
skip_wsp(&mut self.chars);
self.parse_quadratic_bezier_curveto(abs)?;
},
'T' | 't' => {
skip_wsp(&mut self.chars);
self.parse_smooth_quadratic_bezier_curveto(abs)?;
},
'A' | 'a' => {
skip_wsp(&mut self.chars);
self.parse_elliprical_arc(abs)?;
},
_ => return Err(()),
} }
parse_command!(
'Z' | 'z' => parse_closepath,
'L' | 'l' => parse_lineto,
'H' | 'h' => parse_h_lineto,
'V' | 'v' => parse_v_lineto,
'C' | 'c' => parse_curveto,
'S' | 's' => parse_smooth_curveto,
'Q' | 'q' => parse_quadratic_bezier_curveto,
'T' | 't' => parse_smooth_quadratic_bezier_curveto,
'A' | 'a' => parse_elliprical_arc,
);
}, },
_ => break, // no more commands. _ => break, // no more commands.
} }
@ -158,128 +170,51 @@ impl<'a> PathParser<'a> {
self.parse_lineto(absolute) self.parse_lineto(absolute)
} }
/// Parse "closepath" command.
fn parse_closepath(&mut self, _absolute: bool) -> Result<(), ()> {
self.path.push(PathCommand::ClosePath);
Ok(())
}
/// Parse "lineto" command. /// Parse "lineto" command.
fn parse_lineto(&mut self, absolute: bool) -> Result<(), ()> { fn parse_lineto(&mut self, absolute: bool) -> Result<(), ()> {
loop { parse_arguments!(self, absolute, LineTo, [ point => parse_coord ])
let point = parse_coord(&mut self.chars)?;
self.path.push(PathCommand::LineTo { point, absolute });
// End of string or the next character is a possible new command.
if !skip_wsp(&mut self.chars) ||
self.chars.peek().map_or(true, |c| c.is_ascii_alphabetic()) {
break;
}
skip_comma_wsp(&mut self.chars);
}
Ok(())
} }
/// Parse horizontal "lineto" command. /// Parse horizontal "lineto" command.
fn parse_h_lineto(&mut self, absolute: bool) -> Result<(), ()> { fn parse_h_lineto(&mut self, absolute: bool) -> Result<(), ()> {
loop { parse_arguments!(self, absolute, HorizontalLineTo, [ x => parse_number ])
let x = parse_number(&mut self.chars)?;
self.path.push(PathCommand::HorizontalLineTo { x, absolute });
// End of string or the next character is a possible new command.
if !skip_wsp(&mut self.chars) ||
self.chars.peek().map_or(true, |c| c.is_ascii_alphabetic()) {
break;
}
skip_comma_wsp(&mut self.chars);
}
Ok(())
} }
/// Parse vertical "lineto" command. /// Parse vertical "lineto" command.
fn parse_v_lineto(&mut self, absolute: bool) -> Result<(), ()> { fn parse_v_lineto(&mut self, absolute: bool) -> Result<(), ()> {
loop { parse_arguments!(self, absolute, VerticalLineTo, [ y => parse_number ])
let y = parse_number(&mut self.chars)?;
self.path.push(PathCommand::VerticalLineTo { y, absolute });
// End of string or the next character is a possible new command.
if !skip_wsp(&mut self.chars) ||
self.chars.peek().map_or(true, |c| c.is_ascii_alphabetic()) {
break;
}
skip_comma_wsp(&mut self.chars);
}
Ok(())
} }
/// Parse cubic Bézier curve command. /// Parse cubic Bézier curve command.
fn parse_curveto(&mut self, absolute: bool) -> Result<(), ()> { fn parse_curveto(&mut self, absolute: bool) -> Result<(), ()> {
loop { parse_arguments!(self, absolute, CurveTo, [
let control1 = parse_coord(&mut self.chars)?; control1 => parse_coord, control2 => parse_coord, point => parse_coord
skip_comma_wsp(&mut self.chars); ])
let control2 = parse_coord(&mut self.chars)?;
skip_comma_wsp(&mut self.chars);
let point = parse_coord(&mut self.chars)?;
self.path.push(PathCommand::CurveTo { control1, control2, point, absolute });
// End of string or the next character is a possible new command.
if !skip_wsp(&mut self.chars) ||
self.chars.peek().map_or(true, |c| c.is_ascii_alphabetic()) {
break;
}
skip_comma_wsp(&mut self.chars);
}
Ok(())
} }
/// Parse smooth "curveto" command. /// Parse smooth "curveto" command.
fn parse_smooth_curveto(&mut self, absolute: bool) -> Result<(), ()> { fn parse_smooth_curveto(&mut self, absolute: bool) -> Result<(), ()> {
loop { parse_arguments!(self, absolute, SmoothCurveTo, [
let control2 = parse_coord(&mut self.chars)?; control2 => parse_coord, point => parse_coord
skip_comma_wsp(&mut self.chars); ])
let point = parse_coord(&mut self.chars)?;
self.path.push(PathCommand::SmoothCurveTo { control2, point, absolute });
// End of string or the next character is a possible new command.
if !skip_wsp(&mut self.chars) ||
self.chars.peek().map_or(true, |c| c.is_ascii_alphabetic()) {
break;
}
skip_comma_wsp(&mut self.chars);
}
Ok(())
} }
/// Parse quadratic Bézier curve command. /// Parse quadratic Bézier curve command.
fn parse_quadratic_bezier_curveto(&mut self, absolute: bool) -> Result<(), ()> { fn parse_quadratic_bezier_curveto(&mut self, absolute: bool) -> Result<(), ()> {
loop { parse_arguments!(self, absolute, QuadBezierCurveTo, [
let control1 = parse_coord(&mut self.chars)?; control1 => parse_coord, point => parse_coord
skip_comma_wsp(&mut self.chars); ])
let point = parse_coord(&mut self.chars)?;
self.path.push(PathCommand::QuadBezierCurveTo { control1, point, absolute });
// End of string or the next character is a possible new command.
if !skip_wsp(&mut self.chars) ||
self.chars.peek().map_or(true, |c| c.is_ascii_alphabetic()) {
break;
}
skip_comma_wsp(&mut self.chars);
}
Ok(())
} }
/// Parse smooth quadratic Bézier curveto command. /// Parse smooth quadratic Bézier curveto command.
fn parse_smooth_quadratic_bezier_curveto(&mut self, absolute: bool) -> Result<(), ()> { fn parse_smooth_quadratic_bezier_curveto(&mut self, absolute: bool) -> Result<(), ()> {
loop { parse_arguments!(self, absolute, SmoothQuadBezierCurveTo, [ point => parse_coord ])
let point = parse_coord(&mut self.chars)?;
self.path.push(PathCommand::SmoothQuadBezierCurveTo { point, absolute });
// End of string or the next character is a possible new command.
if !skip_wsp(&mut self.chars) ||
self.chars.peek().map_or(true, |c| c.is_ascii_alphabetic()) {
break;
}
skip_comma_wsp(&mut self.chars);
}
Ok(())
} }
/// Parse elliptical arc curve command. /// Parse elliptical arc curve command.
@ -293,34 +228,14 @@ impl<'a> PathParser<'a> {
iter.next(); iter.next();
Ok(value) Ok(value)
}; };
parse_arguments!(self, absolute, EllipticalArc, [
loop { rx => parse_number,
let rx = parse_number(&mut self.chars)?; ry => parse_number,
skip_comma_wsp(&mut self.chars); angle => parse_number,
let ry = parse_number(&mut self.chars)?; large_arc_flag => parse_flag,
skip_comma_wsp(&mut self.chars); sweep_flag => parse_flag,
let angle = parse_number(&mut self.chars)?; point => parse_coord
skip_comma_wsp(&mut self.chars); ])
let large_arc_flag = parse_flag(&mut self.chars)?;
skip_comma_wsp(&mut self.chars);
let sweep_flag = parse_flag(&mut self.chars)?;
skip_comma_wsp(&mut self.chars);
let point = parse_coord(&mut self.chars)?;
self.path.push(
PathCommand::EllipticalArc {
rx, ry, angle, large_arc_flag, sweep_flag, point, absolute
}
);
// End of string or the next character is a possible new command.
if !skip_wsp(&mut self.chars) ||
self.chars.peek().map_or(true, |c| c.is_ascii_alphabetic()) {
break;
}
skip_comma_wsp(&mut self.chars);
}
Ok(())
} }
} }