<commit_before>/*
 * Copyright (c) 2017 Boucher, Antoni <bouanto@zoho.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

include!("../relm-state/src/macros.rs");
<commit_msg>Temporary fix to publish the crate<commit_after>/*
 * Copyright (c) 2017 Boucher, Antoni <bouanto@zoho.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/// Connect events to sending a message.
///
/// ## Rules
/// 1. Send `$msg` to `$other_component` when the GTK+ `$event` is emitted on `$widget`.
///
/// 2. Optionally send `$msg.0` when the GTK+ `$event` is emitted on `$widget`.
/// Return `$msg.1` in the GTK+ callback.
/// This variant gives more control to the caller since it expects a `$msg` returning `(Option<MSG>,
/// ReturnValue)` where the `ReturnValue` is the value to return in the GTK+ callback.
/// Option<MSG> can be None if no message needs to be emitted.
///
/// 3. Send `$msg` when the GTK+ `$event` is emitted on `$widget`.
///
/// 4. Send `$msg` to `$widget` when the `$message` is received on `$stream`.
#[macro_export]
macro_rules! connect {
    // Connect to a GTK+ widget event, sending a message to another widget.
    ($widget:expr, $event:ident($($args:pat),*), $other_component:expr, $msg:expr) => {
        connect_stream!($widget, $event($($args),*), $other_component.stream(), $msg);
    };

    // Connect to a GTK+ widget event.
    // This variant gives more control to the caller since it expects a `$msg` returning (Option<MSG>,
    // ReturnValue) where the ReturnValue is the value to return in the GTK+ callback.
    // Option<MSG> can be None if no message needs to be emitted.
    ($relm:expr, $widget:expr, $event:ident($($args:pat),*), return $msg:expr) => {{
        connect_stream!(return $relm.stream(), $widget, $event($($args),*), $msg);
    }};

    // Connect to a GTK+ widget event.
    ($relm:expr, $widget:expr, $event:ident($($args:pat),*), $msg:expr) => {{
        let stream = $relm.stream().clone();
        let _ = $widget.$event(move |$($args),*| {
            let msg: Option<_> = $crate::IntoOption::into_option($msg);
            if let Some(msg) = msg {
                stream.emit(msg);
            }
        });
    }};

    // Connect to a message reception.
    // TODO: create another macro rule accepting multiple patterns.
    ($src_component:ident @ $message:pat, $dst_component:expr, $msg:expr) => {
        let stream = $src_component.stream().clone();
        connect_stream!(stream@$message, $dst_component.stream(), $msg);
    };
}

/// Connect events to sending a message.
/// Similar to `connect!` but wants a stream instead of a component.
///
/// ## Rules
/// 1. Send `$msg` to `$other_stream` when the GTK+ `$event` is emitted on `$widget`.
///
/// 2. Send `$msg` to `$widget` when the `$message` is received on `$stream`.
#[macro_export]
macro_rules! connect_stream {
    // Connect to a GTK+ widget event.
    // This variant gives more control to the caller since it expects a `$msg` returning (Option<MSG>,
    // ReturnValue) where the ReturnValue is the value to return in the GTK+ callback.
    // Option<MSG> can be None if no message needs to be emitted.
    (return $stream:expr, $widget:expr, $event:ident($($args:pat),*), $msg:expr) => {{
        let stream = $stream.clone();
        let _ = $widget.$event(move |$($args),*| {
            let (msg, return_value) = $crate::IntoPair::into_pair($msg);
            let msg: Option<_> = $crate::IntoOption::into_option(msg);
            if let Some(msg) = msg {
                stream.emit(msg);
            }
            return_value
        });
    }};

    // Connect to a GTK+ widget event, sending a message to another widget.
    ($widget:expr, $event:ident($($args:pat),*), $other_stream:expr, $msg:expr) => {
        let stream = $other_stream.clone();
        let _ = $widget.$event(move |$($args),*| {
            let msg: Option<_> = $crate::IntoOption::into_option($msg);
            if let Some(msg) = msg {
                stream.emit(msg);
            }
        });
    };

    // Connect to a message reception.
    // TODO: create another macro rule accepting multiple patterns.
    ($src_stream:ident @ $message:pat, $dst_stream:expr, $msg:expr) => {
        let stream = $dst_stream.clone();
        $src_stream.observe(move |msg| {
            #[allow(unreachable_patterns)]
            match msg {
                &$message =>  {
                    let msg: Option<_> = $crate::IntoOption::into_option($msg);
                    if let Some(msg) = msg {
                        stream.emit(msg);
                    }
                },
                _ => (),
            }
        });
    };
}

/// Connect an asynchronous method call to send a message.
/// The variants with `$fail_msg` will send this message when there's an error.
/// Those without this argument will ignore the error.
#[macro_export]
macro_rules! connect_async {
    ($object:expr, $async_method:ident, $relm:expr, $msg:expr) => {
        connect_async!($object, $async_method(), $relm, $msg)
    };
    ($object:expr, $async_method:ident ( $($args:expr),* ), $relm:expr, $msg:expr) => {{
        let stream = $relm.stream().clone();
        $object.$async_method($($args,)* None, move |result| {
            if let Ok(result) = result {
                stream.emit($msg(result));
            }
        });
    }};
    ($object:expr, $async_method:ident, $relm:expr, $msg:expr, $fail_msg:expr) => {
        connect_async!($object, $async_method(), $relm, $msg, $fail_msg)
    };
    ($object:expr, $async_method:ident ( $($args:expr),* ), $relm:expr, $msg:expr, $fail_msg:expr) => {{
        let event_stream = $relm.stream().clone();
        let fail_event_stream = $relm.stream().clone();
        $object.$async_method($($args,)* None, move |result| {
            match result {
                Ok(value) => event_stream.emit($msg(value)),
                Err(error) => fail_event_stream.emit($fail_msg(error)),
            }
        });
    }};
}

/// Connect an asynchronous function call to send a message.
/// The variants with `$fail_msg` will send this message when there's an error.
/// Those without this argument will ignore the error.
#[macro_export]
macro_rules! connect_async_func {
    ($class:ident :: $async_function:ident, $relm:expr, $msg:expr) => {
        connect_async_func!($class::$async_func(), $relm, $msg)
    };
    ($class:ident :: $async_func:ident ( $($args:expr),* ), $relm:expr, $msg:expr) => {{
        let stream = $relm.stream().clone();
        $class::$async_func($($args,)* None, move |result| {
            if let Ok(result) = result {
                stream.emit($msg(result));
            }
        });
    }};
    ($class:ident :: $async_func:ident, $relm:expr, $msg:expr, $fail_msg:expr) => {
        connect_async_func!($class::$async_func(), $relm, $msg, $fail_msg)
    };
    ($class:ident :: $async_func:ident ( $($args:expr),* ), $relm:expr, $msg:expr, $fail_msg:expr) => {{
        let event_stream = $relm.stream().clone();
        let fail_event_stream = $relm.stream().clone();
        $class::$async_func($($args,)* None, move |result| {
            match result {
                Ok(value) => event_stream.emit($msg(value)),
                Err(error) => fail_event_stream.emit($fail_msg(error)),
            }
        });

    }};
}

/// Like `connect_async!`, but also return a `Cancellable` to control the asynchronous request.
#[macro_export]
macro_rules! connect_async_full {
    ($object:expr, $async_method:ident, $relm:expr, $msg:expr) => {
        connect_async_full!($object, $async_method(), $relm, $msg)
    };
    ($object:expr, $async_method:ident ( $($args:expr),* ), $relm:expr, $msg:expr) => {{
        let cancellable = ::gio::Cancellable::new();
        let stream = $relm.stream().clone();
        $object.$async_method($($args,)* Some(&cancellable), move |result| {
            if let Ok(result) = result {
                stream.emit($msg(result));
            }
        });
        cancellable
    }};
    ($object:expr, $async_method:ident, $relm:expr, $msg:expr, $fail_msg:expr) => {
        connect_async_full!($object, $async_method(), $relm, $msg, $fail_msg)
    };
    ($object:expr, $async_method:ident ( $($args:expr),* ), $relm:expr, $msg:expr, $fail_msg:expr) => {{
        let cancellable = ::gio::Cancellable::new();
        let event_stream = $relm.stream().clone();
        let fail_event_stream = $relm.stream().clone();
        $object.$async_method($($args,)* Some(&cancellable), move |result| {
            match result {
                Ok(value) => event_stream.emit($msg(value)),
                Err(error) => fail_event_stream.emit($fail_msg(error)),
            }
        });

        cancellable
    }};
}

/// Like `connect_async_func!`, but also return a `Cancellable` to control the asynchronous request.
#[macro_export]
macro_rules! connect_async_func_full {
    ($class:ident :: $async_function:ident, $relm:expr, $msg:expr) => {
        connect_async!($async_func(), $relm, $msg)
    };
    ($class:ident :: $async_func:ident ( $($args:expr),* ), $relm:expr, $msg:expr) => {{
        let cancellable = ::gio::Cancellable::new();
        let stream = $relm.stream().clone();
        $class::$async_func($($args,)* Some(&cancellable), move |result| {
            if let Ok(result) = result {
                stream.emit($msg(result));
            }
        });
        cancellable
    }};
    ($class:ident :: $async_func:ident, $relm:expr, $msg:expr, $fail_msg:expr) => {
        connect_async!($async_func(), $relm, $msg, $fail_msg)
    };
    ($class:ident :: $async_func:ident ( $($args:expr),* ), $relm:expr, $msg:expr, $fail_msg:expr) => {{
        let cancellable = ::gio::Cancellable::new();
        let event_stream = $relm.stream().clone();
        let fail_event_stream = $relm.stream().clone();
        $class::$async_func($($args,)* Some(&cancellable), move |result| {
            match result {
                Ok(value) => event_stream.emit($msg(value)),
                Err(error) => fail_event_stream.emit($fail_msg(error)),
            }
        });

        cancellable
    }};
}
<|endoftext|>


<commit_before>#[cfg(test)]
macro_rules! raw_pixel_conversion_tests {
    ($name: ident <$($ty_param: ident),+> : $($component: ident),+) => {
        #[test]
        fn convert_from_f32_array() {
            raw_pixel_conversion_tests!(@float_array_test f32, $name<$($ty_param),+>: $($component),+);
        }

        #[test]
        fn convert_from_f64_array() {
            raw_pixel_conversion_tests!(@float_array_test f64, $name<$($ty_param),+>: $($component),+);
        }

        #[test]
        fn convert_from_f32_slice() {
            raw_pixel_conversion_tests!(@float_slice_test f32, $name<$($ty_param),+>: $($component),+);
        }

        #[test]
        fn convert_from_f64_slice() {
            raw_pixel_conversion_tests!(@float_slice_test f64, $name<$($ty_param),+>: $($component),+);
        }
    };

    (@float_array_test $float: ty, $name: ident <$($ty_param: ident),+> : $($component: ident),+) => {
        use ::Pixel;

        let mut counter: $float = 0.0;
        $(
            counter += 0.1;
            let $component = counter;
        )+
        let raw: [$float; <$name<$($ty_param,)+ $float> as Pixel<$float>>::CHANNELS] = [$($component),+];
        let color: $name<$($ty_param,)+ $float> = *$name::from_raw(&raw);

        assert_eq!(color, $name::new($($component),+));
    };

    (@float_slice_test $float: ty, $name: ident <$($ty_param: ident),+> : $($component: ident),+) => {
        use ::Pixel;

        let mut counter: $float = 0.0;
        $(
            counter += 0.1;
            let $component = counter;
        )+
        let raw: &[$float] = &[$($component),+];
        let color: $name<$($ty_param,)+ $float> = *$name::from_raw(raw);

        assert_eq!(color, $name::new($($component),+));
    };
}

#[cfg(test)]
macro_rules! raw_pixel_conversion_fail_tests {
    ($name: ident <$($ty_param: ident),+> : $($component: ident),+) => {
        #[test]
        #[should_panic(expected = "not enough color channels")]
        fn convert_from_short_f32_array() {
            raw_pixel_conversion_fail_tests!(@float_array_test f32, $name<$($ty_param),+>);
        }

        #[test]
        #[should_panic(expected = "not enough color channels")]
        fn convert_from_short_f64_array() {
            raw_pixel_conversion_fail_tests!(@float_array_test f64, $name<$($ty_param),+>);
        }

        #[test]
        #[should_panic(expected = "not enough color channels")]
        fn convert_from_short_f32_slice() {
            raw_pixel_conversion_fail_tests!(@float_slice_test f32, $name<$($ty_param),+>);
        }

        #[test]
        #[should_panic(expected = "not enough color channels")]
        fn convert_from_short_f64_slice() {
            raw_pixel_conversion_fail_tests!(@float_slice_test f64, $name<$($ty_param),+>);
        }
    };

    (@float_array_test $float: ty, $name: ident <$($ty_param: ident),+>) => {
        use ::Pixel;
        let raw: [$float; 1] = [0.1];
        let _: $name<$($ty_param,)+ $float> = *$name::from_raw(&raw);
    };

    (@float_slice_test $float: ty, $name: ident <$($ty_param: ident),+>) => {
        use ::Pixel;
        let raw: &[$float] = &[0.1];
        let _: $name<$($ty_param,)+ $float> = *$name::from_raw(raw);
    };
}
<commit_msg>Add pixel conversion tests with alpha channel<commit_after>#[cfg(test)]
macro_rules! raw_pixel_conversion_tests {
    ($name: ident <$($ty_param: ident),+> : $($component: ident),+) => {
        #[test]
        fn convert_from_f32_array() {
            raw_pixel_conversion_tests!(@float_array_test f32, $name<$($ty_param),+>: $($component),+);
        }

        #[test]
        fn convert_from_f64_array() {
            raw_pixel_conversion_tests!(@float_array_test f64, $name<$($ty_param),+>: $($component),+);
        }

        #[test]
        fn convert_from_f32_slice() {
            raw_pixel_conversion_tests!(@float_slice_test f32, $name<$($ty_param),+>: $($component),+);
        }

        #[test]
        fn convert_from_f64_slice() {
            raw_pixel_conversion_tests!(@float_slice_test f64, $name<$($ty_param),+>: $($component),+);
        }
    };

    (@float_array_test $float: ty, $name: ident <$($ty_param: ident),+> : $($component: ident),+) => {
        use ::Pixel;
        use ::Alpha;

        let mut counter: $float = 0.0;
        $(
            counter += 0.1;
            let $component = counter;
        )+
        let alpha = counter + 0.1;

        let raw: [$float; <$name<$($ty_param,)+ $float> as Pixel<$float>>::CHANNELS] = [$($component),+];
        let raw_plus_1: [$float; <$name<$($ty_param,)+ $float> as Pixel<$float>>::CHANNELS + 1] = [
            $($component,)+
            alpha
        ];
        let color: $name<$($ty_param,)+ $float> = *$name::from_raw(&raw);
        let color_long: $name<$($ty_param,)+ $float> = *$name::from_raw(&raw_plus_1);

        let color_alpha: Alpha<$name<$($ty_param,)+ $float>, $float> = *Alpha::<$name<$($ty_param,)+ $float>, $float>::from_raw(&raw_plus_1);

        assert_eq!(color, $name::new($($component),+));
        assert_eq!(color_long, $name::new($($component),+));

        assert_eq!(color_alpha, Alpha::<$name<$($ty_param,)+ $float>, $float>::new($($component,)+ alpha));
    };

    (@float_slice_test $float: ty, $name: ident <$($ty_param: ident),+> : $($component: ident),+) => {
        use ::Pixel;
        use ::Alpha;

        let mut counter: $float = 0.0;
        $(
            counter += 0.1;
            let $component = counter;
        )+
        let alpha = counter + 0.1;
        let extra = counter + 0.2;
        let raw: &[$float] = &[$($component),+];
        let raw_plus_1: &[$float] = &[
            $($component,)+
            alpha
        ];
        let raw_plus_2: &[$float] = &[
            $($component,)+
            alpha,
            extra
        ];
        let color: $name<$($ty_param,)+ $float> = *$name::from_raw(raw);
        let color_long: $name<$($ty_param,)+ $float> = *$name::from_raw(raw_plus_1);

        let color_alpha: Alpha<$name<$($ty_param,)+ $float>, $float> = *Alpha::<$name<$($ty_param,)+ $float>, $float>::from_raw(raw_plus_1);
        let color_alpha_long: Alpha<$name<$($ty_param,)+ $float>, $float> = *Alpha::<$name<$($ty_param,)+ $float>, $float>::from_raw(raw_plus_2);

        assert_eq!(color, $name::new($($component),+));
        assert_eq!(color_long, $name::new($($component),+));

        assert_eq!(color_alpha, Alpha::<$name<$($ty_param,)+ $float>, $float>::new($($component,)+ alpha));
        assert_eq!(color_alpha_long, Alpha::<$name<$($ty_param,)+ $float>, $float>::new($($component,)+ alpha));
    };
}

#[cfg(test)]
macro_rules! raw_pixel_conversion_fail_tests {
    ($name: ident <$($ty_param: ident),+> : $($component: ident),+) => {
        #[test]
        #[should_panic(expected = "not enough color channels")]
        fn convert_from_short_f32_array() {
            raw_pixel_conversion_fail_tests!(@float_array_test f32, $name<$($ty_param),+>);
        }

        #[test]
        #[should_panic(expected = "not enough color channels")]
        fn convert_from_short_f64_array() {
            raw_pixel_conversion_fail_tests!(@float_array_test f64, $name<$($ty_param),+>);
        }

        #[test]
        #[should_panic(expected = "not enough color channels")]
        fn convert_from_short_f32_slice() {
            raw_pixel_conversion_fail_tests!(@float_slice_test f32, $name<$($ty_param),+>);
        }

        #[test]
        #[should_panic(expected = "not enough color channels")]
        fn convert_from_short_f64_slice() {
            raw_pixel_conversion_fail_tests!(@float_slice_test f64, $name<$($ty_param),+>);
        }
    };

    (@float_array_test $float: ty, $name: ident <$($ty_param: ident),+>) => {
        use ::Pixel;
        let raw: [$float; 1] = [0.1];
        let _: $name<$($ty_param,)+ $float> = *$name::from_raw(&raw);
    };

    (@float_slice_test $float: ty, $name: ident <$($ty_param: ident),+>) => {
        use ::Pixel;
        let raw: &[$float] = &[0.1];
        let _: $name<$($ty_param,)+ $float> = *$name::from_raw(raw);
    };
}
<|endoftext|>


<commit_before>extern crate nom;
use nom::*;
use std::str;
use std::collections::HashMap;
use std::hash::Hash;

#[derive(PartialEq,Debug)]
pub enum JsonValue<'a> {
    Float(f64),
    Int(i64),
    Str(&'a str),
    Bool(bool),
    Object(HashMap<&'a str, JsonValue<'a>>),
    Vec(Vec<JsonValue<'a>>),
    Null,
}

named!(quoted_str<&str>,
       map_res!(delimited!(char!('"'),
                           escaped!(call!(alpha), '\\',
                                    is_a_bytes!(&b"\"n\\"[..])),
                           char!('"')),
                str::from_utf8));

named!(quoted_str_val<JsonValue>,
       map!(quoted_str,
            |x: &'a str| { JsonValue::Str(x) }));

named!(json_int<JsonValue>,
       map!(
           map_res!(
               map_res!(
                   digit,
                   str::from_utf8
               ),
               |x: &str| { x.parse::<i64>() }
           ),
           |x: i64| { JsonValue::Int(x) }
       ));

named!(json_float<JsonValue>,
       map!(
           map_res!(
               map_res!(
                   recognize!(separated_pair!(digit, char!('.'), digit)),
                   str::from_utf8
               ),
               |x: &str| { x.parse::<f64>() }
           ),
           |x: f64| { JsonValue::Float(x) }
       ));

named!(json_boolean<JsonValue>,
       alt!(value!(JsonValue::Bool(true), tag!("true")) |
            value!(JsonValue::Bool(false), tag!("false"))));

named!(json_null<JsonValue>,
       value!(JsonValue::Null, tag!("null")));

macro_rules! multispaced (
    ($i:expr, $submac:ident!( $($args:tt)* )) => (
        delimited!($i, opt!(multispace), $submac!($($args)*), opt!(multispace));
    );
    ($i:expr, $f:expr) => (
        multispaced!($i, call!($f));
    );
);

macro_rules! multispace_delimited (
    ($i:expr, $submac1:ident!( $($args1:tt)* ),
     $submac2:ident!( $($args2:tt)* ), $submac3:ident!( $($args3:tt)* )) => (
        delimited!($i, multispaced!($submac1!($($args1)*)),
                   $submac2!($($args2)*), multispaced!($submac3!($($args3)*)));
    );
    ($i:expr, $f:expr) => (
        multispaced!($i, call!($f));
    );
);

named!(keypair<(&str,JsonValue)>,
       separated_pair!(quoted_str,
                       multispaced!(char!(':')),
                       json_value));

fn pairs_to_map<K: Hash + Eq, V>(v: Vec<(K, V)>) -> HashMap<K, V> {
    v.into_iter().fold(HashMap::new(), |mut m, (k, v)| {
        m.insert(k, v);
        m
    })
}

named!(object_map<JsonValue>,
       multispace_delimited!(
           char!('{'),
           map!(
               separated_list!(
                   multispaced!(char!(',')),
                   keypair
               ),
               |x| { JsonValue::Object(pairs_to_map(x)) }),
           char!('}')));

named!(json_array<JsonValue>,
       multispace_delimited!(
           char!('['),
           map!(
               separated_list!(
                   multispaced!(char!(',')),
                   json_value
               ),
               JsonValue::Vec),
           char!(']')));

named!(json_value<JsonValue>,
       alt!(json_null |
            json_float |
            json_int |
            quoted_str_val |
            json_boolean |
            object_map |
            json_array));

#[test]
fn check_quoted_str() {
    let empty = &b""[..];

    assert_eq!(quoted_str(&b"\"foo\""[..]),
               IResult::Done(empty,"foo"));

    assert_eq!(quoted_str_val(&b"\"foo\""[..]),
               IResult::Done(empty,JsonValue::Str("foo")));

    assert_eq!(quoted_str(&b"\"f\\\"o\\\"o\""[..]),
               IResult::Done(empty,"f\\\"o\\\"o"));

    assert_eq!(quoted_str_val(&b"\"f\\\"o\\\"o\""[..]),
               IResult::Done(empty,JsonValue::Str("f\\\"o\\\"o")));
}

#[test]
fn check_int() {
    assert_eq!(json_int(&b"123"[..]),
               IResult::Done(&b""[..],JsonValue::Int(123)));
}

#[test]
fn check_float() {
    assert_eq!(json_float(&b"123.123"[..]),
               IResult::Done(&b""[..],JsonValue::Float(123.123)));
}

#[test]
fn check_pairs_to_map() {
    let mut res = HashMap::with_capacity(3);
    res.insert("a", JsonValue::Int(1));
    res.insert("b", JsonValue::Int(2));
    res.insert("c", JsonValue::Int(3));
    assert_eq!(res,
               pairs_to_map(vec![("a",JsonValue::Int(1)),
                                 ("b",JsonValue::Int(2)),
                                 ("c",JsonValue::Int(3))]));
}

#[test]
fn check_object_map() {
    let mut res = HashMap::new();
    res.insert("foo", JsonValue::Bool(true));
    res.insert("bar", JsonValue::Str("with\\\"quotes"));
    res.insert("baz", JsonValue::Int(123));
    res.insert("withafloat", JsonValue::Float(123.123));
    assert_eq!(object_map(&b"{\"foo\" : true,\"bar\":\n\"with\\\"quotes\",\"baz\":123,\n\"withafloat\":123.123}"[..]),
               IResult::Done(&b""[..],JsonValue::Object(res)));

    let mut res = HashMap::new();
    let mut nested_res = HashMap::new();

    nested_res.insert("nestedfoo", JsonValue::Bool(true));
    nested_res.insert("bar", JsonValue::Str("with\\\"quotes"));
    nested_res.insert("baz", JsonValue::Int(123));
    nested_res.insert("withafloat", JsonValue::Float(123.123));
    res.insert("foo", JsonValue::Object(nested_res));
    assert_eq!(object_map(&b"{\"foo\":{\"nestedfoo\":true,\"bar\":\"with\\\"quotes\",\"baz\":123,\"withafloat\":123.123}}"[..]),
               IResult::Done(&b""[..],JsonValue::Object(res)));

    let mut res2 = HashMap::new();
    res2.insert("foo", JsonValue::Bool(true));
    assert_eq!(object_map(&b"{ \"foo\" : true }"[..]),
               IResult::Done(&b""[..],JsonValue::Object(res2)));
}

#[test]
fn check_array() {

    assert_eq!(json_value(&b"[1,2,null,4]"[..]),
               IResult::Done(&b""[..],
                             JsonValue::Vec(vec![JsonValue::Int(1),
                                                 JsonValue::Int(2),
                                                 JsonValue::Null,
                                                 JsonValue::Int(4)])));
    let mut res = HashMap::new();
    res.insert("foo", JsonValue::Bool(true));
    res.insert("bar", JsonValue::Str("with\\\"quotes"));
    res.insert("baz", JsonValue::Int(123));
    res.insert("withafloat", JsonValue::Float(123.123));

    assert_eq!(json_value(&b"[{\"foo\":true,\n\"bar\":\"with\\\"quotes\"  ,\"baz\":123,\"withafloat\":123.123}]"[..]),
               IResult::Done(&b""[..],
                             JsonValue::Vec(vec![JsonValue::Object(res)])));

    assert_eq!(json_value(&b"[ \"foo\" ]"[..]),
           IResult::Done(&b""[..], JsonValue::Vec(vec![JsonValue::Str("foo")])));
}
<commit_msg>(maint) Add optional trailing command and equal separator<commit_after>extern crate nom;
use nom::*;
use std::str;
use std::collections::HashMap;
use std::hash::Hash;

#[derive(PartialEq,Debug)]
pub enum JsonValue<'a> {
    Float(f64),
    Int(i64),
    Str(&'a str),
    Bool(bool),
    Object(HashMap<&'a str, JsonValue<'a>>),
    Vec(Vec<JsonValue<'a>>),
    Null,
}

named!(quoted_str<&str>,
       map_res!(delimited!(char!('"'),
                           escaped!(call!(alpha), '\\',
                                    is_a_bytes!(&b"\"n\\"[..])),
                           char!('"')),
                str::from_utf8));

named!(quoted_str_val<JsonValue>,
       map!(quoted_str,
            |x: &'a str| { JsonValue::Str(x) }));

named!(json_int<JsonValue>,
       map!(
           map_res!(
               map_res!(
                   digit,
                   str::from_utf8
               ),
               |x: &str| { x.parse::<i64>() }
           ),
           |x: i64| { JsonValue::Int(x) }
       ));

named!(json_float<JsonValue>,
       map!(
           map_res!(
               map_res!(
                   recognize!(separated_pair!(digit, char!('.'), digit)),
                   str::from_utf8
               ),
               |x: &str| { x.parse::<f64>() }
           ),
           |x: f64| { JsonValue::Float(x) }
       ));

named!(json_boolean<JsonValue>,
       alt!(value!(JsonValue::Bool(true), tag!("true")) |
            value!(JsonValue::Bool(false), tag!("false"))));

named!(json_null<JsonValue>,
       value!(JsonValue::Null, tag!("null")));

macro_rules! multispaced (
    ($i:expr, $submac:ident!( $($args:tt)* )) => (
        delimited!($i, opt!(multispace), $submac!($($args)*), opt!(multispace));
    );
    ($i:expr, $f:expr) => (
        multispaced!($i, call!($f));
    );
);

macro_rules! multispace_delimited (
    (
        $i:expr,
        $submac1:ident!( $($args1:tt)* ),
        $submac2:ident!( $($args2:tt)* ),
        $submac3:ident!( $($args3:tt)* )
    ) => (
        delimited!($i,
                   multispaced!($submac1!($($args1)*)),
                   $submac2!($($args2)*),
                   multispaced!($submac3!($($args3)*)));
    );
    ($i:expr, $f:expr) => (
        multispaced!($i, call!($f));
    );
);

named!(keypair<(&str,JsonValue)>,
       separated_pair!(quoted_str,
                       multispaced!(alt!(char!(':') | char!('='))),
                       json_value));

fn pairs_to_map<K: Hash + Eq, V>(v: Vec<(K, V)>) -> HashMap<K, V> {
    v.into_iter().fold(HashMap::new(), |mut m, (k, v)| {
        m.insert(k, v);
        m
    })
}

named!(object_map<JsonValue>,
       multispace_delimited!(
           char!('{'),
           map!(
               terminated!(
                   separated_list!(
                       multispaced!(char!(',')),
                       keypair
                   ),
                   opt!(multispaced!(char!(',')))),
               |x| { JsonValue::Object(pairs_to_map(x)) }),
           char!('}')));

named!(json_array<JsonValue>,
       multispace_delimited!(
           char!('['),
           map!(
               terminated!(
                   separated_list!(
                       multispaced!(char!(',')),
                       json_value
                   ),
                   opt!(multispaced!(char!(',')))),
               JsonValue::Vec),
           char!(']')));

named!(json_value<JsonValue>,
       alt!(json_null |
            json_float |
            json_int |
            quoted_str_val |
            json_boolean |
            object_map |
            json_array));

#[test]
fn check_quoted_str() {
    let empty = &b""[..];

    assert_eq!(quoted_str(&b"\"foo\""[..]),
               IResult::Done(empty,"foo"));

    assert_eq!(quoted_str_val(&b"\"foo\""[..]),
               IResult::Done(empty,JsonValue::Str("foo")));

    assert_eq!(quoted_str(&b"\"f\\\"o\\\"o\""[..]),
               IResult::Done(empty,"f\\\"o\\\"o"));

    assert_eq!(quoted_str_val(&b"\"f\\\"o\\\"o\""[..]),
               IResult::Done(empty,JsonValue::Str("f\\\"o\\\"o")));
}

#[test]
fn check_int() {
    assert_eq!(json_int(&b"123"[..]),
               IResult::Done(&b""[..],JsonValue::Int(123)));
}

#[test]
fn check_float() {
    assert_eq!(json_float(&b"123.123"[..]),
               IResult::Done(&b""[..],JsonValue::Float(123.123)));
}

#[test]
fn check_pairs_to_map() {
    let mut res = HashMap::with_capacity(3);
    res.insert("a", JsonValue::Int(1));
    res.insert("b", JsonValue::Int(2));
    res.insert("c", JsonValue::Int(3));
    assert_eq!(res,
               pairs_to_map(vec![("a",JsonValue::Int(1)),
                                 ("b",JsonValue::Int(2)),
                                 ("c",JsonValue::Int(3))]));
}

#[test]
fn check_object_map() {
    let mut res = HashMap::new();
    res.insert("foo", JsonValue::Bool(true));
    res.insert("bar", JsonValue::Str("with\\\"quotes"));
    res.insert("baz", JsonValue::Int(123));
    res.insert("withafloat", JsonValue::Float(123.123));
    assert_eq!(object_map(&b"{\"foo\" : true,\"bar\"=\n\"with\\\"quotes\",\"baz\":123,\n\"withafloat\":123.123}"[..]),
               IResult::Done(&b""[..],JsonValue::Object(res)));

    let mut res = HashMap::new();
    let mut nested_res = HashMap::new();

    nested_res.insert("nestedfoo", JsonValue::Bool(true));
    nested_res.insert("bar", JsonValue::Str("with\\\"quotes"));
    nested_res.insert("baz", JsonValue::Int(123));
    nested_res.insert("withafloat", JsonValue::Float(123.123));
    res.insert("foo", JsonValue::Object(nested_res));
    assert_eq!(object_map(&b"{\"foo\":{\"nestedfoo\":true,\"bar\":\"with\\\"quotes\",\"baz\":123,\"withafloat\":123.123}}"[..]),
               IResult::Done(&b""[..],JsonValue::Object(res)));

    let mut res2 = HashMap::new();
    res2.insert("foo", JsonValue::Bool(true));
    assert_eq!(object_map(&b"{ \"foo\" : true }"[..]),
               IResult::Done(&b""[..],JsonValue::Object(res2)));
}

#[test]
fn check_array() {

    assert_eq!(json_value(&b"[1,2,null,4,]"[..]),
               IResult::Done(&b""[..],
                             JsonValue::Vec(vec![JsonValue::Int(1),
                                                 JsonValue::Int(2),
                                                 JsonValue::Null,
                                                 JsonValue::Int(4)])));
    let mut res = HashMap::new();
    res.insert("foo", JsonValue::Bool(true));
    res.insert("bar", JsonValue::Str("with\\\"quotes"));
    res.insert("baz", JsonValue::Int(123));
    res.insert("withafloat", JsonValue::Float(123.123));

    assert_eq!(json_value(&b"[{\"foo\":true,\n\"bar\":\"with\\\"quotes\"  ,\"baz\":123,\"withafloat\":123.123}]"[..]),
               IResult::Done(&b""[..],
                             JsonValue::Vec(vec![JsonValue::Object(res)])));

    assert_eq!(json_value(&b"[ \"foo\" ]"[..]),
           IResult::Done(&b""[..], JsonValue::Vec(vec![JsonValue::Str("foo")])));
}
<|endoftext|>


<commit_before>use std::str::FromStr;
use nom::{IResult, Needed};
use types::Identifier;

/// Returns `true` if the character is a newline character according to ASN.1.
///
/// Defined in X.680 §12.1.6
fn is_newline(ch: char) -> bool {
    match ch {
        // LINE FEED
        '\n' => true,

        // VERTICAL TABULATION
        '\x0B' => true,

        // FORM FEED
        '\x0C' => true,

        // CARRIAGE RETURN
        '\x0D' => true,
        _ => false,
    }
}

named!(newline<&str, &str>,
       alt!(
           tag_s!("\n") |
           tag_s!("\x0B") |
           tag_s!("\x0C") |
           tag_s!("\x0D")
       )
);

/// Returns `true` if the character is a whitespace character according to ASN.1.
///
/// Defined in X.680 §12.1.6
fn is_whitespace(ch: char) -> bool {
    match ch {
        ' ' | '\t' => true,

        // All newline characters are also whitespace
        _ if is_newline(ch) => true,
        _ => false,
    }
}

/// Takes input until it finds the end of a single-line comment. This will not consume the ending
/// character(s) of the comment.
fn take_until_single_line_comment_end(input: &str) -> IResult<&str, &str> {
    let mut offset = input.len();
    let mut chars = input.char_indices().peekable();
    loop {
        match chars.next() {
            Some((i, c)) => {
                match c {
                    '-' => {
                        match chars.peek() {
                            Some(&(_, pc)) => {
                                if pc == '-' {
                                    offset = i;
                                    break;
                                } else {
                                    // Safe to advance because we know that the peeked char is not
                                    // a dash.
                                    chars.next();
                                    continue;
                                }
                            }
                            // Got a single "-" at the end of the string. Consider it to be part of
                            // the comment.
                            None => {
                                // Normally, adding 1 to a byte offset in a `&str` could lead to an
                                // invalid access of a non-character boundary. In this case, we
                                // know that the last character was a "-", which is a single byte.
                                offset = i + 1;
                                break
                            }
                        }
                    }
                    _ if is_newline(c) => {
                        offset = i;
                        break
                    }
                    _ => continue,
                }
            }
            None => break,
        }
    }

    if offset < input.len() {
        IResult::Done(&input[offset..], &input[..offset])
    } else {
        IResult::Done("", input)
    }
}

/// Parse a single-line comment.
///
/// Defined in X.680 §12.6a
named!(single_line_comment<&str, &str>,
    delimited!(
        tag_s!("--"),
        take_until_single_line_comment_end,
        alt!(newline | tag_s!("--"))
    )
);

fn offset<T: AsRef<[u8]>>(base: T, sub: T) -> usize {
    let fst = base.as_ref().as_ptr();
    let snd = sub.as_ref().as_ptr();

    snd as usize - fst as usize
}

/// Version of `recognize!` macro that can be used for strings.
macro_rules! recognize_s (
  ($i:expr, $submac:ident!( $($args:tt)* )) => (
    {
      use ::nom::IResult;
      match $submac!($i, $($args)*) {
        IResult::Done(i,_) => {
          let index = offset($i, i);

          IResult::Done(i, &($i)[..index])
        },
        IResult::Error(e)      => return IResult::Error(e),
        IResult::Incomplete(i) => return IResult::Incomplete(i)
      }
    }
  );
  ($i:expr, $f:expr) => (
    recognize_s!($i, call!($f))
  );
);

/// Parse a multi-line comment. Multi-line comments in ASN.1 can be nested arbitrarily, so the
/// following is a valid comment:
///
/// ```notrust
/// /* Outer
///  * /* Inner */
///  * Still a comment
///  */
/// ```
///
/// Multi-line comments must be closed. If the end of the file is reached before the closing tag is
/// found, this is an error.
///
/// Defined in X.680 §12.6b
fn multi_line_comment(input: &str) -> IResult<&str, &str> {
    let mut offset = input.len();

    let (inside_comment, _) = try_parse!(input, tag_s!("/*"));
    let mut chars = inside_comment.char_indices().peekable();

    // The previous `try_parse!` has already consumed an opening tag, so the beginning nesting
    // level is 1.
    let mut nest_level: u32 = 1;
    loop {
        let next_char = chars.next();

        match next_char {
            Some((i, c)) => {
                match c {
                    '/' => {
                        match chars.peek() {
                            Some(&(_, pc)) => {
                                if pc == '*' {
                                    nest_level += 1;
                                } else {
                                    continue;
                                }
                            }
                            // Got a single "/" at the end of the string. This is a parse error, as
                            // all multi-line comments must be closed.
                            None => {
                                return IResult::Incomplete(Needed::Unknown);
                            }
                        }
                    }
                    '*' => {
                        match chars.peek() {
                            Some(&(_, pc)) => {
                                if pc == '/' {
                                    // About to close the comment.
                                    if nest_level == 1 {
                                        offset = i;
                                        break;
                                    }
                                    nest_level -= 1;
                                } else {
                                    continue;
                                }
                            }
                            // Got a single "*" at the end of the string. This is a parse error, as
                            // all multi-line comments must be closed.
                            None => {
                                return IResult::Incomplete(Needed::Size(1));
                            }
                        }
                    }
                    _ => continue,
                }
            }
            None => break,
        }
    }

    if offset + 2 <= inside_comment.len() {
        // Consume the closing comment characters
        IResult::Done(&inside_comment[(offset + 2)..], &inside_comment[..offset])
    } else {
        IResult::Done("", inside_comment)
    }
}

/// Parse a comment.
///
/// Defined in X.680 §12.6
named!(comment<&str, &str>,
    alt!(
        single_line_comment |
        multi_line_comment
    )
);

/// Parse an integer.
///
/// Defined in X.680 §12.8
named!(number<&str, u64>,
    map_res!(
        re_find!(r"^(0|[1-9]\d*)"),
        FromStr::from_str
    )
);

/// Parse an `f64`.
///
/// Defined in X.680 §12.9
named!(realnumber<&str, f64>,
   map_res!(
       re_find!(r"^\d+(\.\d*)?([eE]-?(0|[1-9]\d*))?"),
       FromStr::from_str
   )
);

/// Parse an `Identifier`
///
/// Starts with a lowercase letter, can contain letters, digits, or hyphens, but cannot contain a
/// double hyphen or end with a hyphen.
named!(identifier<&str, Identifier>,
    map!(
        re_find!(r"^[a-z]([a-zA-Z0-9]+|-[a-zA-Z0-9])*"),
        Identifier::new));

#[cfg(test)]
mod tests {
    use super::{number, realnumber, single_line_comment, take_until_single_line_comment_end,
                newline, multi_line_comment, comment};
    use nom::IResult::{Done, Incomplete};
    use nom::{IResult, Needed};

    /// Simple way of specifying an `IResult::Done` with no remaining input.
    macro_rules! done_result (
        ($res:expr) => (
            IResult::Done("", $res)
        )
    );

    #[test]
    fn test_newline() {
        assert_eq!(newline("\n"), done_result!("\n"));
        assert_eq!(newline("\x0B"), done_result!("\x0B"));
    }

    #[test]
    fn test_take_until_single_line_comment_end() {
        assert_eq!(take_until_single_line_comment_end(" thing\nmore"), Done("\nmore", " thing"));
        assert_eq!(take_until_single_line_comment_end(" thing\n"), Done("\n", " thing"));
        assert_eq!(take_until_single_line_comment_end(" thing--"), Done("--", " thing"));
        assert_eq!(take_until_single_line_comment_end(" dash-in-middle--"),
                   Done("--", " dash-in-middle"));
        assert_eq!(take_until_single_line_comment_end(" dash-in-middle\x0C"),
                   Done("\x0C", " dash-in-middle"));

        // Empty comments
        assert_eq!(take_until_single_line_comment_end("\n"), Done("\n", ""));
        assert_eq!(take_until_single_line_comment_end("--"), Done("--", ""));

        assert_eq!(take_until_single_line_comment_end("stuff-"), done_result!("stuff-"));
        assert_eq!(take_until_single_line_comment_end("-"), done_result!("-"));
    }

    fn single_line_comment_tests<F>(parser: F)
        where F: Fn(&str) -> IResult<&str, &str> {
        assert_eq!(parser("-- thing\nmore"), Done("more", " thing"));
        assert_eq!(parser("-- thing--"), done_result!(" thing"));
        assert_eq!(parser("-- thing\n"), done_result!(" thing"));
        assert_eq!(parser("-- dash-in-middle--"), done_result!(" dash-in-middle"));
        assert_eq!(parser("-- dash-in-middle\x0C"), done_result!(" dash-in-middle"));

        // "/*" and "*/ have no special meaning in single-line comment.
        assert_eq!(single_line_comment("--/* still\nsingle"), Done("single", "/* still"));
        assert_eq!(single_line_comment("--*/* still\nsingle"), Done("single", "*/* still"));
        assert_eq!(single_line_comment("--*/ *//* still\nsingle"),
                   Done("single", "*/ *//* still"));

        assert_eq!(single_line_comment("--\n"), done_result!(""));
        assert_eq!(single_line_comment("----"), done_result!(""));
    }

    #[test]
    fn test_single_line_comment() {
        single_line_comment_tests(single_line_comment);
    }

    fn multi_line_comment_tests<F>(parser: F)
        where F: Fn(&str) -> IResult<&str, &str> {
        assert_eq!(parser("/* stuff */"), done_result!(" stuff "));
        assert_eq!(parser("/* line1\nline2 */"), done_result!(" line1\nline2 "));
        assert_eq!(parser("/**/"), done_result!(""));

        // "--" in multi-line comment has no special meaning.
        assert_eq!(parser("/* -- */"), done_result!(" -- "));

        // Incomplete
        assert_eq!(parser("/*/"), Incomplete(Needed::Unknown));
        assert_eq!(parser("/**"), Incomplete(Needed::Size(1)));

        // Nested comments
        assert_eq!(parser("/* Outer /* Inner */ Outer again */"),
                   done_result!(" Outer /* Inner */ Outer again "));
        assert_eq!(parser("/* Out /* In */ Out2 /* In2 */ Out3 */"),
                   done_result!(" Out /* In */ Out2 /* In2 */ Out3 "));
        assert_eq!(parser("/* Out /* In */ Out */ Extra */"),
                   Done(" Extra */", " Out /* In */ Out "));
    }

    #[test]
    fn test_multi_line_comment() {
        multi_line_comment_tests(multi_line_comment);
    }

    #[test]
    fn test_comment() {
        single_line_comment_tests(comment);
        multi_line_comment_tests(comment);
    }

    #[test]
    fn test_number() {
        assert_eq!(number("0"), done_result!(0));
        assert_eq!(number("1"), done_result!(1));

        // Matches the first zero, but considers the second digit to be leftover input.
        assert_eq!(number("01"), Done("1", 0));
    }

    #[test]
    fn test_realnumber() {
        assert_eq!(realnumber("1"), done_result!(1.0));
        assert_eq!(realnumber("0"), done_result!(0.0));

        // Can have a leading zero
        assert_eq!(realnumber("00"), done_result!(0.0));
        assert_eq!(realnumber("01"), done_result!(1.0));

        // Can have a trailing period without a factional part
        assert_eq!(realnumber("1."), done_result!(1.0));
        assert_eq!(realnumber("1.0"), done_result!(1.0));
        assert_eq!(realnumber("1.001"), done_result!(1.001));

        // Exponents
        assert_eq!(realnumber("1.1e3"), done_result!(1100.0));
        assert_eq!(realnumber("1.1e-3"), done_result!(0.0011));
        assert_eq!(realnumber("1.e3"), done_result!(1000.0));
        assert_eq!(realnumber("1e3"), done_result!(1000.0));
        assert_eq!(realnumber("1.1E3"), done_result!(1100.0));
        assert_eq!(realnumber("1.1E-3"), done_result!(0.0011));

        // Leading zero in exponent
        assert_eq!(realnumber("1.1E-03"), Done("3", 1.1));
        assert_eq!(realnumber("1.1E03"), Done("3", 1.1));
    }
}
<commit_msg>Remove unused macro recognize_s<commit_after>use std::str::FromStr;
use nom::{IResult, Needed};
use types::Identifier;

/// Returns `true` if the character is a newline character according to ASN.1.
///
/// Defined in X.680 §12.1.6
fn is_newline(ch: char) -> bool {
    match ch {
        // LINE FEED
        '\n' => true,

        // VERTICAL TABULATION
        '\x0B' => true,

        // FORM FEED
        '\x0C' => true,

        // CARRIAGE RETURN
        '\x0D' => true,
        _ => false,
    }
}

named!(newline<&str, &str>,
       alt!(
           tag_s!("\n") |
           tag_s!("\x0B") |
           tag_s!("\x0C") |
           tag_s!("\x0D")
       )
);

/// Returns `true` if the character is a whitespace character according to ASN.1.
///
/// Defined in X.680 §12.1.6
fn is_whitespace(ch: char) -> bool {
    match ch {
        ' ' | '\t' => true,

        // All newline characters are also whitespace
        _ if is_newline(ch) => true,
        _ => false,
    }
}

/// Takes input until it finds the end of a single-line comment. This will not consume the ending
/// character(s) of the comment.
fn take_until_single_line_comment_end(input: &str) -> IResult<&str, &str> {
    let mut offset = input.len();
    let mut chars = input.char_indices().peekable();
    loop {
        match chars.next() {
            Some((i, c)) => {
                match c {
                    '-' => {
                        match chars.peek() {
                            Some(&(_, pc)) => {
                                if pc == '-' {
                                    offset = i;
                                    break;
                                } else {
                                    // Safe to advance because we know that the peeked char is not
                                    // a dash.
                                    chars.next();
                                    continue;
                                }
                            }
                            // Got a single "-" at the end of the string. Consider it to be part of
                            // the comment.
                            None => {
                                // Normally, adding 1 to a byte offset in a `&str` could lead to an
                                // invalid access of a non-character boundary. In this case, we
                                // know that the last character was a "-", which is a single byte.
                                offset = i + 1;
                                break
                            }
                        }
                    }
                    _ if is_newline(c) => {
                        offset = i;
                        break
                    }
                    _ => continue,
                }
            }
            None => break,
        }
    }

    if offset < input.len() {
        IResult::Done(&input[offset..], &input[..offset])
    } else {
        IResult::Done("", input)
    }
}

/// Parse a single-line comment.
///
/// Defined in X.680 §12.6a
named!(single_line_comment<&str, &str>,
    delimited!(
        tag_s!("--"),
        take_until_single_line_comment_end,
        alt!(newline | tag_s!("--"))
    )
);

fn offset<T: AsRef<[u8]>>(base: T, sub: T) -> usize {
    let fst = base.as_ref().as_ptr();
    let snd = sub.as_ref().as_ptr();

    snd as usize - fst as usize
}

/// Parse a multi-line comment. Multi-line comments in ASN.1 can be nested arbitrarily, so the
/// following is a valid comment:
///
/// ```notrust
/// /* Outer
///  * /* Inner */
///  * Still a comment
///  */
/// ```
///
/// Multi-line comments must be closed. If the end of the file is reached before the closing tag is
/// found, this is an error.
///
/// Defined in X.680 §12.6b
fn multi_line_comment(input: &str) -> IResult<&str, &str> {
    let mut offset = input.len();

    let (inside_comment, _) = try_parse!(input, tag_s!("/*"));
    let mut chars = inside_comment.char_indices().peekable();

    // The previous `try_parse!` has already consumed an opening tag, so the beginning nesting
    // level is 1.
    let mut nest_level: u32 = 1;
    loop {
        let next_char = chars.next();

        match next_char {
            Some((i, c)) => {
                match c {
                    '/' => {
                        match chars.peek() {
                            Some(&(_, pc)) => {
                                if pc == '*' {
                                    nest_level += 1;
                                } else {
                                    continue;
                                }
                            }
                            // Got a single "/" at the end of the string. This is a parse error, as
                            // all multi-line comments must be closed.
                            None => {
                                return IResult::Incomplete(Needed::Unknown);
                            }
                        }
                    }
                    '*' => {
                        match chars.peek() {
                            Some(&(_, pc)) => {
                                if pc == '/' {
                                    // About to close the comment.
                                    if nest_level == 1 {
                                        offset = i;
                                        break;
                                    }
                                    nest_level -= 1;
                                } else {
                                    continue;
                                }
                            }
                            // Got a single "*" at the end of the string. This is a parse error, as
                            // all multi-line comments must be closed.
                            None => {
                                return IResult::Incomplete(Needed::Size(1));
                            }
                        }
                    }
                    _ => continue,
                }
            }
            None => break,
        }
    }

    if offset + 2 <= inside_comment.len() {
        // Consume the closing comment characters
        IResult::Done(&inside_comment[(offset + 2)..], &inside_comment[..offset])
    } else {
        IResult::Done("", inside_comment)
    }
}

/// Parse a comment.
///
/// Defined in X.680 §12.6
named!(comment<&str, &str>,
    alt!(
        single_line_comment |
        multi_line_comment
    )
);

/// Parse an integer.
///
/// Defined in X.680 §12.8
named!(number<&str, u64>,
    map_res!(
        re_find!(r"^(0|[1-9]\d*)"),
        FromStr::from_str
    )
);

/// Parse an `f64`.
///
/// Defined in X.680 §12.9
named!(realnumber<&str, f64>,
   map_res!(
       re_find!(r"^\d+(\.\d*)?([eE]-?(0|[1-9]\d*))?"),
       FromStr::from_str
   )
);

/// Parse an `Identifier`
///
/// Starts with a lowercase letter, can contain letters, digits, or hyphens, but cannot contain a
/// double hyphen or end with a hyphen.
named!(identifier<&str, Identifier>,
    map!(
        re_find!(r"^[a-z]([a-zA-Z0-9]+|-[a-zA-Z0-9])*"),
        Identifier::new));

#[cfg(test)]
mod tests {
    use super::{number, realnumber, single_line_comment, take_until_single_line_comment_end,
                newline, multi_line_comment, comment};
    use nom::IResult::{Done, Incomplete};
    use nom::{IResult, Needed};

    /// Simple way of specifying an `IResult::Done` with no remaining input.
    macro_rules! done_result (
        ($res:expr) => (
            IResult::Done("", $res)
        )
    );

    #[test]
    fn test_newline() {
        assert_eq!(newline("\n"), done_result!("\n"));
        assert_eq!(newline("\x0B"), done_result!("\x0B"));
    }

    #[test]
    fn test_take_until_single_line_comment_end() {
        assert_eq!(take_until_single_line_comment_end(" thing\nmore"), Done("\nmore", " thing"));
        assert_eq!(take_until_single_line_comment_end(" thing\n"), Done("\n", " thing"));
        assert_eq!(take_until_single_line_comment_end(" thing--"), Done("--", " thing"));
        assert_eq!(take_until_single_line_comment_end(" dash-in-middle--"),
                   Done("--", " dash-in-middle"));
        assert_eq!(take_until_single_line_comment_end(" dash-in-middle\x0C"),
                   Done("\x0C", " dash-in-middle"));

        // Empty comments
        assert_eq!(take_until_single_line_comment_end("\n"), Done("\n", ""));
        assert_eq!(take_until_single_line_comment_end("--"), Done("--", ""));

        assert_eq!(take_until_single_line_comment_end("stuff-"), done_result!("stuff-"));
        assert_eq!(take_until_single_line_comment_end("-"), done_result!("-"));
    }

    fn single_line_comment_tests<F>(parser: F)
        where F: Fn(&str) -> IResult<&str, &str> {
        assert_eq!(parser("-- thing\nmore"), Done("more", " thing"));
        assert_eq!(parser("-- thing--"), done_result!(" thing"));
        assert_eq!(parser("-- thing\n"), done_result!(" thing"));
        assert_eq!(parser("-- dash-in-middle--"), done_result!(" dash-in-middle"));
        assert_eq!(parser("-- dash-in-middle\x0C"), done_result!(" dash-in-middle"));

        // "/*" and "*/ have no special meaning in single-line comment.
        assert_eq!(single_line_comment("--/* still\nsingle"), Done("single", "/* still"));
        assert_eq!(single_line_comment("--*/* still\nsingle"), Done("single", "*/* still"));
        assert_eq!(single_line_comment("--*/ *//* still\nsingle"),
                   Done("single", "*/ *//* still"));

        assert_eq!(single_line_comment("--\n"), done_result!(""));
        assert_eq!(single_line_comment("----"), done_result!(""));
    }

    #[test]
    fn test_single_line_comment() {
        single_line_comment_tests(single_line_comment);
    }

    fn multi_line_comment_tests<F>(parser: F)
        where F: Fn(&str) -> IResult<&str, &str> {
        assert_eq!(parser("/* stuff */"), done_result!(" stuff "));
        assert_eq!(parser("/* line1\nline2 */"), done_result!(" line1\nline2 "));
        assert_eq!(parser("/**/"), done_result!(""));

        // "--" in multi-line comment has no special meaning.
        assert_eq!(parser("/* -- */"), done_result!(" -- "));

        // Incomplete
        assert_eq!(parser("/*/"), Incomplete(Needed::Unknown));
        assert_eq!(parser("/**"), Incomplete(Needed::Size(1)));

        // Nested comments
        assert_eq!(parser("/* Outer /* Inner */ Outer again */"),
                   done_result!(" Outer /* Inner */ Outer again "));
        assert_eq!(parser("/* Out /* In */ Out2 /* In2 */ Out3 */"),
                   done_result!(" Out /* In */ Out2 /* In2 */ Out3 "));
        assert_eq!(parser("/* Out /* In */ Out */ Extra */"),
                   Done(" Extra */", " Out /* In */ Out "));
    }

    #[test]
    fn test_multi_line_comment() {
        multi_line_comment_tests(multi_line_comment);
    }

    #[test]
    fn test_comment() {
        single_line_comment_tests(comment);
        multi_line_comment_tests(comment);
    }

    #[test]
    fn test_number() {
        assert_eq!(number("0"), done_result!(0));
        assert_eq!(number("1"), done_result!(1));

        // Matches the first zero, but considers the second digit to be leftover input.
        assert_eq!(number("01"), Done("1", 0));
    }

    #[test]
    fn test_realnumber() {
        assert_eq!(realnumber("1"), done_result!(1.0));
        assert_eq!(realnumber("0"), done_result!(0.0));

        // Can have a leading zero
        assert_eq!(realnumber("00"), done_result!(0.0));
        assert_eq!(realnumber("01"), done_result!(1.0));

        // Can have a trailing period without a factional part
        assert_eq!(realnumber("1."), done_result!(1.0));
        assert_eq!(realnumber("1.0"), done_result!(1.0));
        assert_eq!(realnumber("1.001"), done_result!(1.001));

        // Exponents
        assert_eq!(realnumber("1.1e3"), done_result!(1100.0));
        assert_eq!(realnumber("1.1e-3"), done_result!(0.0011));
        assert_eq!(realnumber("1.e3"), done_result!(1000.0));
        assert_eq!(realnumber("1e3"), done_result!(1000.0));
        assert_eq!(realnumber("1.1E3"), done_result!(1100.0));
        assert_eq!(realnumber("1.1E-3"), done_result!(0.0011));

        // Leading zero in exponent
        assert_eq!(realnumber("1.1E-03"), Done("3", 1.1));
        assert_eq!(realnumber("1.1E03"), Done("3", 1.1));
    }
}
<|endoftext|>


<commit_before>use atom::*;
use errors::*;
//use number::*;
use std::iter::*;
use std::io::prelude::*;

pub fn tokenize(line: &str) -> ParseResult {
    let mut chars = line.chars().peekable();
    read_tokens(&mut chars)
}

pub type ParseResult = Result<Atom, Error>;

//use std::fmt;

// impl fmt::Display for Node {
//     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
//         match *self {
//             Node(ref node) => {
//                 write!(f, "{}", node)
//             },
//             Quote(ref node) => {
//                 try!(write!(f, "'"));
//                 write!(f, "{}", node)
//             },
//             QuasiQuote(ref node) => {
//                 try!(write!(f, "`"));
//                 write!(f, "{}", node)
//             },

//             Splice(ref node) => {
//                 try!(write!(f, "~@"));
//                 write!(f, "{}", node)
//             },
//             Unquote(ref node) => {
//                 write!(f, "~{}", node)
//             }
//         }
//     }
// }

// impl fmt::Display for Node {
//     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
//         use self::Node::*;
//         match *self {
//             Atom(ref a) => {
//                 write!(f, "{}", a)
//             },
//             List(ref list) => {
//                 try!(write!(f, "("));
//                 if let Some(first) = list.first() {
//                     try!(write!(f, "{}", first));

//                     for n in list.iter().skip(1) {
//                         try!(write!(f, " {}", n));
//                     }
//                 }
//                 write!(f, ")")
//             }
//         }
//     }
// }

use std::str::Chars;

#[derive (Debug, PartialEq)]
enum Token {
    Atom(Atom),
    Open,        // (
    Close,       // )
    Quote,       // '
    QuasiQuote, // `
    Unquote,     // ~
    Splice,      // ~@
}

//use std::convert::From;

fn read_string(iter: &mut Peekable<Chars>) -> Result<Option<Token>, Error> {
    let mut s = String::new();
    loop {
        match iter.next() {
            Some('\\') => {
                if let Some(escaped) = iter.next() {
                    s.push(escaped);
                } else {
                    return Err(Error::Parser)
                }
            },
            Some('"') => {
                let t = Token::Atom(Atom::String(s));

                return Ok(Some(t))
            },
            Some(c) => s.push(c),
            None => return Err(Error::Parser)
        }
    }
}

fn read_atom(c: char, iter: &mut Peekable<Chars>) -> Result<Option<Token>,Error> {
    let mut s = String::new();
    s.push(c);
    loop {
        match iter.peek() {
            Some(&')') =>  {
                return Ok(Some(Token::Atom(Atom::parse(&s))))
            },
            _ => ()
        }

        if let Some(c) = iter.next() {
            if !c.is_whitespace() {
                s.push(c);
            } else {
                return Ok(Some(Token::Atom(Atom::parse(&s))))
            }
        } else {
            return Ok(Some(Token::Atom(Atom::parse(&s))))
        }
    }
}

fn next(iter: &mut Peekable<Chars>) -> Result<Option<Token>, Error> {
    loop {
        if let Some(c) = iter.next() {
            match c {
                '(' => return Ok(Some(Token::Open)),
                '"' => return read_string(iter),
                ')' => return Ok(Some(Token::Close)),
                '\'' => return Ok(Some(Token::Quote)),
                '`' => return Ok(Some(Token::QuasiQuote)),
                '~' => {
                    match *try!(iter.peek().ok_or(Error::Parser)) {
                        '@' => {
                            iter.next();
                            return Ok(Some(Token::Splice))
                        }
                        _ => return Ok(Some(Token::Unquote))
                    }
                },
                ';' =>  {
                    loop {
                        match iter.next() {
                            Some('\n') => break,
                            Some(_) => (),
                            None => return Ok(None)
                        }
                    }
                },
                _ if c.is_whitespace() => (),
                _ => return read_atom(c, iter)
            }
        } else {
            return Ok(None)
        }
    }
}

fn make_quote_form(f: Form, chars: &mut Peekable<Chars> ) -> ParseResult {
    let mut list = List::with_capacity(2);
    list.push(Atom::Form(f));
    list.push(try!(read_tokens(chars)));
    Ok(Atom::List(list))
}

fn read_tokens(chars: &mut Peekable<Chars>) -> ParseResult {
    let token = try!(try!(next(chars)).ok_or(Error::Parser));

    match token {
        Token::Open => {
            let mut node = List::new();

            loop {
                match *try!(chars.peek().ok_or(Error::Parser)) {
                    ')' => {
                        chars.next();
                        break
                    },
                    _ => {
                        let token = try!(read_tokens(chars));
                        node.push(token);
                    }
                }
            }

            Ok(Atom::List(node))
        },
        Token::Close => {
            Err(Error::Parser)
        },
        Token::Quote => {
            make_quote_form(Form::Quote, chars)
        },
        Token::QuasiQuote => {
            make_quote_form(Form::QuasiQuote, chars)
        }
        Token::Unquote => {
            make_quote_form(Form::Unquote, chars)
        },
        Token::Splice => {
            make_quote_form(Form::Splice, chars)
        }
        Token::Atom(x) => Ok(x),
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use atom::*;

    fn make_atom_node(s: &str) -> Atom {
        Atom::parse(s)
    }

    fn as_list(n: &Atom) -> &[Atom] {
        match *n {
            Atom::List(ref l) => l,
            _ => panic!("don't get here!")
        }
    }

    fn as_atom(n: &Atom) -> &Atom {
        n
    }

    fn atom(s: &str) -> Atom {
        Atom::parse(s)
    }

    #[test]
    fn naked_atoms() {
        assert_eq!(Atom::from(0), tokenize("0").unwrap());
        assert_eq!(Atom::from(512), tokenize("512").unwrap());
        assert_eq!(Atom::from(-512), tokenize("-512").unwrap());
        assert_eq!(Atom::from(5.0f64), tokenize("5.0").unwrap());
        assert_eq!(Atom::string("foo bar"), tokenize("\"foo bar\"").unwrap());
        assert_eq!(Atom::symbol("foo"), tokenize("foo").unwrap());
    }

    #[test]
    fn string_escaping() {
        assert_eq!(Atom::string("foo'bar"), tokenize("\"foo\\'bar\"").unwrap());
        assert_eq!(Atom::string("foo\"bar"), tokenize("\"foo\\\"bar\"").unwrap());
    }

    #[test]
    fn simple_read_tokens() {
        let x = tokenize("(+ 1 2)").unwrap();

        let l = as_list(&x);

        assert_eq!(3, l.len());

        let xx : Vec<Atom> = vec![make_atom_node("+"), make_atom_node("1"), make_atom_node("2")];

        for pair in xx.iter().zip(l.iter()) {
            assert_eq!(pair.0, pair.1);
        }
    }

    #[test]
    fn nested_read_tokens() {
        let x = tokenize("(+ 1 (* 2 2))").unwrap();

        let l = as_list(&x);

        assert_eq!( atom("+"), *as_atom(&l[0]));
        assert_eq!( atom("1"), *as_atom(&l[1]));

        let l2 = as_list(&l[2]);

        assert_eq!( 3, l.len());
        assert_eq!( atom("*"), *as_atom(&l2[0]));
        assert_eq!( atom("2"), *as_atom(&l2[1]));
        assert_eq!( atom("2"), *as_atom(&l2[2]));
    }

    #[test]
    fn subexp_token_test() {
        let x = tokenize("(+ 1 (+ 2 3) 4)").unwrap();

        let l = as_list(&x);

        assert_eq!( atom("+"), *as_atom(&l[0]));
        assert_eq!( atom("1"), *as_atom(&l[1]));

        let l2 = as_list(&l[2]);

        assert_eq!( 3, l2.len());
        assert_eq!( atom("+"), *as_atom(&l2[0]));
        assert_eq!( atom("2"), *as_atom(&l2[1]));
        assert_eq!( atom("3"), *as_atom(&l2[2]));

        assert_eq!(atom("4"), *as_atom(&l[3]));
    }

    #[test]
    #[should_panic]
    fn unmatched_bracket() {
        tokenize("(+ 1").unwrap();
    }

    #[test]
    #[should_panic]
    fn eof() {
         tokenize("").unwrap();
    }
}
<commit_msg>unused comments<commit_after>use atom::*;
use errors::*;
use std::iter::*;
use std::io::prelude::*;

pub fn tokenize(line: &str) -> ParseResult {
    let mut chars = line.chars().peekable();
    read_tokens(&mut chars)
}

pub type ParseResult = Result<Atom, Error>;

use std::str::Chars;

#[derive (Debug, PartialEq)]
enum Token {
    Atom(Atom),
    Open,        // (
    Close,       // )
    Quote,       // '
    QuasiQuote,  // `
    Unquote,     // ~
    Splice,      // ~@
}

fn read_string(iter: &mut Peekable<Chars>) -> Result<Option<Token>, Error> {
    let mut s = String::new();
    loop {
        match iter.next() {
            Some('\\') => {
                if let Some(escaped) = iter.next() {
                    s.push(escaped);
                } else {
                    return Err(Error::Parser)
                }
            },
            Some('"') => {
                let t = Token::Atom(Atom::String(s));

                return Ok(Some(t))
            },
            Some(c) => s.push(c),
            None => return Err(Error::Parser)
        }
    }
}

fn read_atom(c: char, iter: &mut Peekable<Chars>) -> Result<Option<Token>,Error> {
    let mut s = String::new();
    s.push(c);
    loop {
        if let Some(&')') = iter.peek() {
            return Ok(Some(Token::Atom(Atom::parse(&s))))
        }

        if let Some(c) = iter.next() {
            if !c.is_whitespace() {
                s.push(c);
            } else {
                return Ok(Some(Token::Atom(Atom::parse(&s))))
            }
        } else {
            return Ok(Some(Token::Atom(Atom::parse(&s))))
        }
    }
}

fn next(iter: &mut Peekable<Chars>) -> Result<Option<Token>, Error> {
    loop {
        if let Some(c) = iter.next() {
            match c {
                '(' => return Ok(Some(Token::Open)),
                '"' => return read_string(iter),
                ')' => return Ok(Some(Token::Close)),
                '\'' => return Ok(Some(Token::Quote)),
                '`' => return Ok(Some(Token::QuasiQuote)),
                '~' => {
                    match *try!(iter.peek().ok_or(Error::Parser)) {
                        '@' => {
                            iter.next();
                            return Ok(Some(Token::Splice))
                        }
                        _ => return Ok(Some(Token::Unquote))
                    }
                },
                ';' =>  {
                    loop {
                        match iter.next() {
                            Some('\n') => break,
                            Some(_) => (),
                            None => return Ok(None)
                        }
                    }
                },
                _ if c.is_whitespace() => (),
                _ => return read_atom(c, iter)
            }
        } else {
            return Ok(None)
        }
    }
}

fn make_quote_form(f: Form, chars: &mut Peekable<Chars> ) -> ParseResult {
    let mut list = List::with_capacity(2);
    list.push(Atom::Form(f));
    list.push(try!(read_tokens(chars)));
    Ok(Atom::List(list))
}

fn read_tokens(chars: &mut Peekable<Chars>) -> ParseResult {
    let token = try!(try!(next(chars)).ok_or(Error::Parser));

    match token {
        Token::Open => {
            let mut node = List::new();

            loop {
                match *try!(chars.peek().ok_or(Error::Parser)) {
                    ')' => {
                        chars.next();
                        break
                    },
                    _ => {
                        let token = try!(read_tokens(chars));
                        node.push(token);
                    }
                }
            }

            Ok(Atom::List(node))
        },
        Token::Close => {
            Err(Error::Parser)
        },
        Token::Quote => {
            make_quote_form(Form::Quote, chars)
        },
        Token::QuasiQuote => {
            make_quote_form(Form::QuasiQuote, chars)
        }
        Token::Unquote => {
            make_quote_form(Form::Unquote, chars)
        },
        Token::Splice => {
            make_quote_form(Form::Splice, chars)
        }
        Token::Atom(x) => Ok(x),
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use atom::*;

    fn make_atom_node(s: &str) -> Atom {
        Atom::parse(s)
    }

    fn as_list(n: &Atom) -> &[Atom] {
        match *n {
            Atom::List(ref l) => l,
            _ => panic!("don't get here!")
        }
    }

    fn as_atom(n: &Atom) -> &Atom {
        n
    }

    fn atom(s: &str) -> Atom {
        Atom::parse(s)
    }

    #[test]
    fn naked_atoms() {
        assert_eq!(Atom::from(0), tokenize("0").unwrap());
        assert_eq!(Atom::from(512), tokenize("512").unwrap());
        assert_eq!(Atom::from(-512), tokenize("-512").unwrap());
        assert_eq!(Atom::from(5.0f64), tokenize("5.0").unwrap());
        assert_eq!(Atom::string("foo bar"), tokenize("\"foo bar\"").unwrap());
        assert_eq!(Atom::symbol("foo"), tokenize("foo").unwrap());
    }

    #[test]
    fn string_escaping() {
        assert_eq!(Atom::string("foo'bar"), tokenize("\"foo\\'bar\"").unwrap());
        assert_eq!(Atom::string("foo\"bar"), tokenize("\"foo\\\"bar\"").unwrap());
    }

    #[test]
    fn simple_read_tokens() {
        let x = tokenize("(+ 1 2)").unwrap();

        let l = as_list(&x);

        assert_eq!(3, l.len());

        let xx : Vec<Atom> = vec![make_atom_node("+"), make_atom_node("1"), make_atom_node("2")];

        for pair in xx.iter().zip(l.iter()) {
            assert_eq!(pair.0, pair.1);
        }
    }

    #[test]
    fn nested_read_tokens() {
        let x = tokenize("(+ 1 (* 2 2))").unwrap();

        let l = as_list(&x);

        assert_eq!( atom("+"), *as_atom(&l[0]));
        assert_eq!( atom("1"), *as_atom(&l[1]));

        let l2 = as_list(&l[2]);

        assert_eq!( 3, l.len());
        assert_eq!( atom("*"), *as_atom(&l2[0]));
        assert_eq!( atom("2"), *as_atom(&l2[1]));
        assert_eq!( atom("2"), *as_atom(&l2[2]));
    }

    #[test]
    fn subexp_token_test() {
        let x = tokenize("(+ 1 (+ 2 3) 4)").unwrap();

        let l = as_list(&x);

        assert_eq!( atom("+"), *as_atom(&l[0]));
        assert_eq!( atom("1"), *as_atom(&l[1]));

        let l2 = as_list(&l[2]);

        assert_eq!( 3, l2.len());
        assert_eq!( atom("+"), *as_atom(&l2[0]));
        assert_eq!( atom("2"), *as_atom(&l2[1]));
        assert_eq!( atom("3"), *as_atom(&l2[2]));

        assert_eq!(atom("4"), *as_atom(&l[3]));
    }

    #[test]
    #[should_panic]
    fn unmatched_bracket() {
        tokenize("(+ 1").unwrap();
    }

    #[test]
    #[should_panic]
    fn eof() {
         tokenize("").unwrap();
    }
}
<|endoftext|>