Bug 1429298 - Part 3: Use macro for path parser. r=emilio
☠☠ backed out by e03480382807 ☠ ☠
authorBoris Chiou <boris.chiou@gmail.com>
Wed, 22 Aug 2018 01:20:21 +0000
changeset 490483 761e9bb54adbc9f8164a5d83c8d9483b93e8f1fb
parent 490482 0b9ec0d707b5d23a37986a9818230dec1932a2f3
child 490484 196fc7b48b84303aaf9b6d8354d3ea9ff9fe5ef0
push id1815
push userffxbld-merge
push dateMon, 15 Oct 2018 10:40:45 +0000
treeherdermozilla-release@18d4c09e9378 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio
bugs1429298
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1429298 - Part 3: Use macro for path parser. r=emilio There are a lot of duplicates, so we use macro to refine them. Depends on D2963 Differential Revision: https://phabricator.services.mozilla.com/D2966
servo/components/style/values/specified/motion.rs
--- a/servo/components/style/values/specified/motion.rs
+++ b/servo/components/style/values/specified/motion.rs
@@ -65,16 +65,44 @@ impl Parse for OffsetPath {
 }
 
 /// SVG Path parser.
 struct PathParser<'a> {
     chars: Peekable<Chars<'a>>,
     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> {
     /// Parse a sub-path.
     fn parse_subpath(&mut self) -> Result<(), ()> {
         // Handle "moveto" Command first. If there is no "moveto", this is not a valid sub-path
         // (i.e. not a valid moveto-drawto-command-group).
         self.parse_moveto()?;
 
         // Handle other commands.
@@ -82,56 +110,40 @@ impl<'a> PathParser<'a> {
             skip_wsp(&mut self.chars);
             if self.chars.peek().map_or(true, |m| *m == 'M' || *m == 'm') {
                 break;
             }
 
             match self.chars.next() {
                 Some(command) => {
                     let abs = command.is_uppercase();
-                    match command {
-                        'Z' | 'z' => {
-                            // Note: A "closepath" coulbe be followed immediately by "moveto" or
-                            // any other command, so we don't break this loop.
-                            self.path.push(PathCommand::ClosePath);
-                        },
-                        'L' | 'l' => {
-                            skip_wsp(&mut self.chars);
-                            self.parse_lineto(abs)?;
-                        },
-                        '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(()),
+                    macro_rules! parse_command {
+                        ( $($($p:pat)|+ => $parse_func:ident,)* ) => {
+                            match command {
+                                $(
+                                    $($p)|+ => {
+                                        skip_wsp(&mut self.chars);
+                                        self.$parse_func(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.
             }
         }
         Ok(())
     }
 
     /// Parse "moveto" command.
@@ -153,179 +165,82 @@ impl<'a> PathParser<'a> {
         }
         skip_comma_wsp(&mut self.chars);
 
         // If a moveto is followed by multiple pairs of coordinates, the subsequent
         // pairs are treated as implicit lineto commands.
         self.parse_lineto(absolute)
     }
 
+    /// Parse "closepath" command.
+    fn parse_closepath(&mut self, _absolute: bool) -> Result<(), ()> {
+        self.path.push(PathCommand::ClosePath);
+        Ok(())
+    }
+
     /// Parse "lineto" command.
     fn parse_lineto(&mut self, absolute: bool) -> Result<(), ()> {
-        loop {
-            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_arguments!(self, absolute, LineTo, [ point => parse_coord ])
     }
 
     /// Parse horizontal "lineto" command.
     fn parse_h_lineto(&mut self, absolute: bool) -> Result<(), ()> {
-        loop {
-            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_arguments!(self, absolute, HorizontalLineTo, [ x => parse_number ])
     }
 
     /// Parse vertical "lineto" command.
     fn parse_v_lineto(&mut self, absolute: bool) -> Result<(), ()> {
-        loop {
-            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_arguments!(self, absolute, VerticalLineTo, [ y => parse_number ])
     }
 
     /// Parse cubic Bézier curve command.
     fn parse_curveto(&mut self, absolute: bool) -> Result<(), ()> {
-        loop {
-            let control1 = parse_coord(&mut self.chars)?;
-            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_arguments!(self, absolute, CurveTo, [
+            control1 => parse_coord, control2 => parse_coord, point => parse_coord
+        ])
     }
 
     /// Parse smooth "curveto" command.
     fn parse_smooth_curveto(&mut self, absolute: bool) -> Result<(), ()> {
-        loop {
-            let control2 = parse_coord(&mut self.chars)?;
-            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_arguments!(self, absolute, SmoothCurveTo, [
+            control2 => parse_coord, point => parse_coord
+        ])
     }
 
     /// Parse quadratic Bézier curve command.
     fn parse_quadratic_bezier_curveto(&mut self, absolute: bool) -> Result<(), ()> {
-        loop {
-            let control1 = parse_coord(&mut self.chars)?;
-            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_arguments!(self, absolute, QuadBezierCurveTo, [
+            control1 => parse_coord, point => parse_coord
+        ])
     }
 
     /// Parse smooth quadratic Bézier curveto command.
     fn parse_smooth_quadratic_bezier_curveto(&mut self, absolute: bool) -> Result<(), ()> {
-        loop {
-            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_arguments!(self, absolute, SmoothQuadBezierCurveTo, [ point => parse_coord ])
     }
 
     /// Parse elliptical arc curve command.
     fn parse_elliprical_arc(&mut self, absolute: bool) -> Result<(), ()> {
         // Parse a flag whose value is '0' or '1'; otherwise, return Err(()).
         let parse_flag = |iter: &mut Peekable<Chars>| -> Result<bool, ()> {
             let value = match iter.peek() {
                 Some(c) if *c == '0' || *c == '1' => *c == '1',
                 _ => return Err(()),
             };
             iter.next();
             Ok(value)
         };
-
-        loop {
-            let rx = parse_number(&mut self.chars)?;
-            skip_comma_wsp(&mut self.chars);
-            let ry = parse_number(&mut self.chars)?;
-            skip_comma_wsp(&mut self.chars);
-            let angle = parse_number(&mut self.chars)?;
-            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(())
+        parse_arguments!(self, absolute, EllipticalArc, [
+            rx => parse_number,
+            ry => parse_number,
+            angle => parse_number,
+            large_arc_flag => parse_flag,
+            sweep_flag => parse_flag,
+            point => parse_coord
+        ])
     }
 }
 
 /// The SVG path data.
 ///
 /// https://www.w3.org/TR/SVG11/paths.html#PathData
 #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue)]
 pub struct SVGPathData(Box<[PathCommand]>);