tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }