parse_function.rs (4465B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 use std::str::CharIndices; 6 7 // support arguments like '4', 'ab', '4.0', '>=10.14', '*123' 8 fn acceptable_arg_character(c: char) -> bool { 9 c.is_alphanumeric() || c == '.' || c == '-' || c == '<' || c == '>' || c == '=' || c == '*' 10 } 11 12 // A crappy parser for parsing strings like "translate(1, 3) blahblah" 13 // Returns a tuple with three components: 14 // - First component is the function name (e.g. "translate") 15 // - Second component is the list of arguments (e.g. vec!["1", "3"]) 16 // - Third component is the rest of the string "blahblah" 17 pub fn parse_function(s: &str) -> (&str, Vec<&str>, &str) { 18 // XXX: This is not particularly easy to read. Sorry. 19 struct Parser<'a> { 20 itr: CharIndices<'a>, 21 start: usize, 22 o: Option<(usize, char)>, 23 } 24 impl<'a> Parser<'a> { 25 fn skip_whitespace(&mut self) { 26 while let Some(k) = self.o { 27 if !k.1.is_whitespace() { 28 break; 29 } 30 self.start = k.0 + k.1.len_utf8(); 31 self.o = self.itr.next(); 32 } 33 } 34 } 35 let mut c = s.char_indices(); 36 let o = c.next(); 37 let mut p = Parser { 38 itr: c, 39 start: 0, 40 o, 41 }; 42 43 p.skip_whitespace(); 44 45 let mut end = p.start; 46 while let Some(k) = p.o { 47 if !k.1.is_alphabetic() && k.1 != '_' && k.1 != '-' { 48 break; 49 } 50 end = k.0 + k.1.len_utf8(); 51 p.o = p.itr.next(); 52 } 53 54 let name = &s[p.start .. end]; 55 let mut args = Vec::new(); 56 57 p.skip_whitespace(); 58 59 if let Some(k) = p.o { 60 if k.1 != '(' { 61 return (name, args, &s[p.start ..]); 62 } 63 p.start = k.0 + k.1.len_utf8(); 64 p.o = p.itr.next(); 65 } 66 67 loop { 68 p.skip_whitespace(); 69 70 let mut end = p.start; 71 let mut brackets: Vec<char> = Vec::new(); 72 while let Some(k) = p.o { 73 let prev_bracket_count = brackets.len(); 74 match k.1 { 75 '[' | '(' => brackets.push(k.1), 76 ']' | ')' => { 77 let open_bracket = match k.1 { 78 ']' => '[', 79 ')' => '(', 80 _ => panic!(), 81 }; 82 match brackets.pop() { 83 // Allow final closing ) for command invocation after args 84 None if k.1 == ')' => break, 85 Some(bracket) if bracket == open_bracket => {} 86 _ => panic!("Unexpected closing bracket {}", k.1), 87 } 88 } 89 _ => {} 90 } 91 92 let not_in_bracket = brackets.is_empty() && prev_bracket_count == 0; 93 if !acceptable_arg_character(k.1) && not_in_bracket { 94 break; 95 } 96 end = k.0 + k.1.len_utf8(); 97 p.o = p.itr.next(); 98 } 99 100 args.push(&s[p.start .. end]); 101 102 p.skip_whitespace(); 103 104 if let Some(k) = p.o { 105 p.start = k.0 + k.1.len_utf8(); 106 p.o = p.itr.next(); 107 // unless we find a comma we're done 108 if k.1 != ',' { 109 if k.1 != ')' { 110 panic!("Unexpected closing character: {}", k.1); 111 } 112 break; 113 } 114 } else { 115 break; 116 } 117 } 118 (name, args, &s[p.start ..]) 119 } 120 121 #[test] 122 fn test() { 123 assert_eq!(parse_function("rotate(40)").0, "rotate"); 124 assert_eq!(parse_function(" rotate(40)").0, "rotate"); 125 assert_eq!(parse_function(" rotate (40)").0, "rotate"); 126 assert_eq!(parse_function(" rotate ( 40 )").1[0], "40"); 127 assert_eq!(parse_function("rotate(-40.0)").1[0], "-40.0"); 128 assert_eq!(parse_function("drop-shadow(0, [1, 2, 3, 4], 5)").1[0], "0"); 129 assert_eq!(parse_function("drop-shadow(0, [1, 2, 3, 4], 5)").1[1], "[1, 2, 3, 4]"); 130 assert_eq!(parse_function("drop-shadow(0, [1, 2, 3, 4], 5)").1[2], "5"); 131 assert_eq!(parse_function("drop-shadow(0, [1, 2, [3, 4]], 5)").1[1], "[1, 2, [3, 4]]"); 132 assert_eq!(parse_function("func(nest([1, 2]), [3, 4])").1[0], "nest([1, 2])"); 133 assert_eq!(parse_function("func(nest([1, 2]), [nest(3), nest(4)])").1[1], "[nest(3), nest(4)]"); 134 }