<commit_before>//! Encode `Primitive` nodes to HTML

use super::{attr_itemtype_str, attr_slot, concat, elem, EncodeContext, ToHtml};
use html_escape::encode_safe;
use node_dispatch::dispatch_primitive;
use stencila_schema::*;

impl ToHtml for Primitive {
    fn to_html(&self, context: &EncodeContext) -> String {
        // Call `array_to_html` to avoid `Vec<Primitive>.to_html()` for arrays
        if let Primitive::Array(array) = self {
            return array_to_html(array, context);
        }

        dispatch_primitive!(self, to_html, context)
    }
}

/// Encode an atomic primitive to HTML
macro_rules! atomic_to_html {
    ($type:ty) => {
        impl ToHtml for $type {
            fn to_html(&self, _context: &EncodeContext) -> String {
                elem(
                    "span",
                    &[attr_itemtype_str(stringify!($type))],
                    &self.to_string(),
                )
            }
        }
    };
}
atomic_to_html!(Null);
atomic_to_html!(Boolean);
atomic_to_html!(Integer);
atomic_to_html!(Number);

/// Encode a `String` to HTML
///
/// This is the only `Node` type that is NOT represented by an element
/// (with an `itemtype` attribute, which in this case would be `https://schema.org/Text`).
/// This reduces the size of the generated HTML (whole page and in patches), but is also
/// useful in applying [`Operation`]s in the `web` module because it allows discrimination
/// between strings and other node types.
///
/// The string is escaped so that the generated HTML can be safely interpolated within HTML.
impl ToHtml for String {
    fn to_html(&self, _context: &EncodeContext) -> String {
        encode_safe(self).to_string()
    }
}

// Encode an `Array` to the HTML semantically equivalent `<ol>`.
//
// We can't implement `ToHtml` for `Array` as that conflicts with `impl ToHtml for `Vec<Primitive>`.
// Instead this function is used to provide necessary structure for patching to work on arrays. Note that
// arrays have special handling in the `../web/patches` TypeScript so changes made to the HTML structure
// will need concomitant changes there.
#[allow(clippy::ptr_arg)]
pub fn array_to_html(array: &Array, context: &EncodeContext) -> String {
    let items = concat(array, |item| elem("li", &[], &item.to_html(context)));
    elem(
        "stencila-array",
        &[attr_itemtype_str("Array")],
        &elem("ol", &[attr_slot("items")], &items),
    )
}

/// Encode an `Object` to the HTML semantically equivalent `<dl>`.
///
/// Note that objects have special handling in the `../web/patches` TypeScript so changes made to
/// the HTML structure will need concomitant changes there.
impl ToHtml for Object {
    fn to_html(&self, context: &EncodeContext) -> String {
        let pairs = self
            .iter()
            .map(|(key, value)| {
                [
                    elem("dt", &[], &key.to_html(context)),
                    elem("dd", &[], &value.to_html(context)),
                ]
                .concat()
            })
            .collect::<Vec<String>>()
            .concat();
        elem(
            "stencila-object",
            &[attr_itemtype_str("Object")],
            &elem("dl", &[attr_slot("pairs")], &pairs),
        )
    }
}
<commit_msg>fix(HTML codec): Encode `Array` and `Object` nodes as `<span>`s when inline<commit_after>//! Encode `Primitive` nodes to HTML

use super::{attr_itemtype_str, attr_slot, concat, elem, EncodeContext, ToHtml};
use html_escape::encode_safe;
use node_dispatch::dispatch_primitive;
use stencila_schema::*;

impl ToHtml for Primitive {
    fn to_html(&self, context: &EncodeContext) -> String {
        // Call `array_to_html` to avoid `Vec<Primitive>.to_html()` for arrays
        if let Primitive::Array(array) = self {
            return array_to_html(array, context);
        }

        dispatch_primitive!(self, to_html, context)
    }
}

/// Encode an atomic primitive to HTML
macro_rules! atomic_to_html {
    ($type:ty) => {
        impl ToHtml for $type {
            fn to_html(&self, _context: &EncodeContext) -> String {
                elem(
                    "span",
                    &[attr_itemtype_str(stringify!($type))],
                    &self.to_string(),
                )
            }
        }
    };
}
atomic_to_html!(Null);
atomic_to_html!(Boolean);
atomic_to_html!(Integer);
atomic_to_html!(Number);

/// Encode a `String` to HTML
///
/// This is the only `Node` type that is NOT represented by an element
/// (with an `itemtype` attribute, which in this case would be `https://schema.org/Text`).
/// This reduces the size of the generated HTML (whole page and in patches), but is also
/// useful in applying [`Operation`]s in the `web` module because it allows discrimination
/// between strings and other node types.
///
/// The string is escaped so that the generated HTML can be safely interpolated within HTML.
impl ToHtml for String {
    fn to_html(&self, _context: &EncodeContext) -> String {
        encode_safe(self).to_string()
    }
}

// Encode an `Array` to the HTML semantically equivalent `<ol>`, or if inline, to a `<span>`.
//
// We can't implement `ToHtml` for `Array` as that conflicts with `impl ToHtml for `Vec<Primitive>`.
// Instead this function is used to provide necessary structure for patching to work on arrays. Note that
// arrays have special handling in the `../web/patches` TypeScript so changes made to the HTML structure
// will need concomitant changes there.
#[allow(clippy::ptr_arg)]
pub fn array_to_html(array: &Array, context: &EncodeContext) -> String {
    let (container_tag, item_tag) = match context.inline {
        true => ("span", "span"),
        false => ("ol", "li"),
    };
    let items = concat(array, |item| elem(item_tag, &[], &item.to_html(context)));
    elem(
        "stencila-array",
        &[attr_itemtype_str("Array")],
        &elem(container_tag, &[attr_slot("items")], &items),
    )
}

/// Encode an `Object` to the HTML semantically equivalent `<dl>`, or if inline, to a `<span>`.
///
/// Note that objects have special handling in the `../web/patches` TypeScript so changes made to
/// the HTML structure will need concomitant changes there.
impl ToHtml for Object {
    fn to_html(&self, context: &EncodeContext) -> String {
        let (container_tag, key_tag, value_tag) = match context.inline {
            true => ("span", "span", "span"),
            false => ("dl", "dt", "dd"),
        };
        let pairs = self
            .iter()
            .map(|(key, value)| {
                [
                    elem(key_tag, &[], &key.to_html(context)),
                    elem(value_tag, &[], &value.to_html(context)),
                ]
                .concat()
            })
            .collect::<Vec<String>>()
            .concat();
        elem(
            "stencila-object",
            &[attr_itemtype_str("Object")],
            &elem(container_tag, &[attr_slot("pairs")], &pairs),
        )
    }
}
<|endoftext|>


<commit_before>/* This file is part of Grust, GObject introspection bindings for Rust
 *
 * Copyright (C) 2013  Mikhail Zabaluev <mikhail.zabaluev@gmail.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301  USA
 */

extern mod grust (name="grust", vers="0.1");
extern mod gio (name="grust-Gio", vers="2.0");

use core::result::{Result,Ok};
use core::comm::{Port,stream};
use core::task::TaskResult;
use core::util;
// use core::timer::recv_timeout;
// use core::uv_global_loop;
use grust::eventloop::EventLoop;

// Test timeout in milliseconds
static TEST_TIMEOUT: uint = 3000u;

fn spawn_with_future(func: ~fn()) -> Port<TaskResult> {
    let mut (port, _) = stream::<TaskResult>();
    let mut task = task::task();
    do task.future_result |p| { port = p; };
    task.spawn(func);
    port
}

fn tcase(test: ~fn()) {
    grust::init();

    let port = spawn_with_future(test);

    // recv_timeout is broken, see https://github.com/mozilla/rust/issues/6089
    match Some(port.recv()) /* recv_timeout(&uv_global_loop::get(), TEST_TIMEOUT, &port) */ {
        Some(task::Success) => {}
        Some(task::Failure) => {
            fail!(~"test failed");
        }
        None => {
            error!("test timed out");
            unsafe { libc::abort(); }
        }
    }
}

fn tcase_result(test: ~fn() -> Result<(),()>) {
    do tcase {
        let result = test();
        assert!(result == Ok(()));
    }
}

#[test]
fn simple() {
    do tcase {
        let fobj = gio::file_new_for_path("/dev/null");
        let f = fobj.interface() as &gio::File;
        assert!(f.get_path().to_str() == ~"/dev/null");
    }
}

#[test]
fn as_interface() {
    do tcase {
        do gio::file_new_for_path("/dev/null").as_interface |fobj| {
            let f = fobj as &gio::File;
            assert!(f.get_path().to_str() == ~"/dev/null");
        };
    }
}

#[test]
fn new_ref() {
    do tcase {
        let fobj = gio::file_new_for_path("/dev/null");
        let gobj = fobj.interface().new_ref();
        let g = gobj.interface() as &gio::File;
        assert!(g.get_path().to_str() == ~"/dev/null");
    }
}

#[test]
fn clone() {
    do tcase {
        let fobj = gio::file_new_for_path("/dev/null");
        do fobj.clone().as_interface |gobj| {
            let g = gobj as &gio::File;
            assert!(g.get_path().to_str() == ~"/dev/null");
        };
    }
}

#[test]
fn off_task() {
    do tcase_result {
        let f = ~gio::file_new_for_path("/dev/null");
        do task::try {
            let f = f.interface() as &gio::File;
            let p = f.get_path();
            assert!(p.to_str() == ~"/dev/null");
        }
    }
}

#[test]
fn off_task_as_interface() {
    do tcase_result {
        let fobj = ~gio::file_new_for_path("/dev/null");
        do task::try {
            do fobj.as_interface |fifa| {
                let f = fifa as &gio::File;
                let p = f.get_path();
                assert!(p.to_str() == ~"/dev/null");
            };
        }
    }
}

#[test]
fn async() {
    do tcase {
        let fobj = gio::file_new_for_path("/dev/null");
        let f = fobj.interface() as &gio::File;
        let el = EventLoop::new();
        let elo = ~el.clone();
        do f.read_async(0, None) |obj, res| {
            let f: &gio::File = obj.cast::<gio::raw::GFile>()
                                as &gio::File;
            let in = f.read_finish(res);
            util::ignore(in);
            elo.quit();
        };
        el.run();
    }
}

#[test]
#[ignore]  // See https://github.com/mzabaluev/grust/issues/4
fn async_off_task() {
    do tcase {
        let fobj = ~gio::file_new_for_path("/dev/null");
        let el = EventLoop::new();
        let elo = ~el.clone();
        do spawn {
            let f = fobj.interface() as &gio::File;
            let elo2 = ~elo.clone();
            do f.read_async(0, None) |obj, res| {
                let fifa: &gio::interfaces::File = obj.cast();
                let f = fifa as &gio::File;
                f.read_finish(res);
                elo2.quit();
            };
        }
        el.run();
    }
}

#[test]
fn async_off_stack() {
    do tcase {
        let fobj = ~gio::file_new_for_path("/dev/null");
        let el = EventLoop::new();
        let elo = ~el.clone();
        do task::spawn_sched(task::ThreadPerCore) {
            let f = fobj.interface() as &gio::File;
            let elo2 = ~elo.clone();
            do f.read_async(0, None) |obj, res| {
                let f = obj.cast::<gio::raw::GFile>() as &gio::File;
                f.read_finish(res);
                elo2.quit();
            };
        };
        el.run();
    }
}
<commit_msg>Revert "Backported the tests to Rust state of master"<commit_after>/* This file is part of Grust, GObject introspection bindings for Rust
 *
 * Copyright (C) 2013  Mikhail Zabaluev <mikhail.zabaluev@gmail.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301  USA
 */

extern mod grust (name="grust", vers="0.1");
extern mod gio (name="grust-Gio", vers="2.0");

use std::result::{Result,Ok};
use std::comm::{Port,stream};
use std::task::TaskResult;
use std::util;
// use std::timer::recv_timeout;
// use std::uv_global_loop;
use grust::eventloop::EventLoop;

// Test timeout in milliseconds
static TEST_TIMEOUT: uint = 3000u;

fn spawn_with_future(func: ~fn()) -> Port<TaskResult> {
    let mut (port, _) = stream::<TaskResult>();
    let mut task = task::task();
    do task.future_result |p| { port = p; };
    task.spawn(func);
    port
}

fn tcase(test: ~fn()) {
    grust::init();

    let port = spawn_with_future(test);

    // recv_timeout is broken, see https://github.com/mozilla/rust/issues/6089
    match Some(port.recv()) /* recv_timeout(&uv_global_loop::get(), TEST_TIMEOUT, &port) */ {
        Some(task::Success) => {}
        Some(task::Failure) => {
            fail!(~"test failed");
        }
        None => {
            error!("test timed out");
            unsafe { libc::abort(); }
        }
    }
}

fn tcase_result(test: ~fn() -> Result<(),()>) {
    do tcase {
        let result = test();
        assert!(result == Ok(()));
    }
}

#[test]
fn simple() {
    do tcase {
        let fobj = gio::file_new_for_path("/dev/null");
        let f = fobj.interface() as &gio::File;
        assert!(f.get_path().to_str() == ~"/dev/null");
    }
}

#[test]
fn as_interface() {
    do tcase {
        do gio::file_new_for_path("/dev/null").as_interface |fobj| {
            let f = fobj as &gio::File;
            assert!(f.get_path().to_str() == ~"/dev/null");
        };
    }
}

#[test]
fn new_ref() {
    do tcase {
        let fobj = gio::file_new_for_path("/dev/null");
        let gobj = fobj.interface().new_ref();
        let g = gobj.interface() as &gio::File;
        assert!(g.get_path().to_str() == ~"/dev/null");
    }
}

#[test]
fn clone() {
    do tcase {
        let fobj = gio::file_new_for_path("/dev/null");
        do fobj.clone().as_interface |gobj| {
            let g = gobj as &gio::File;
            assert!(g.get_path().to_str() == ~"/dev/null");
        };
    }
}

#[test]
fn off_task() {
    do tcase_result {
        let f = ~gio::file_new_for_path("/dev/null");
        do task::try {
            let f = f.interface() as &gio::File;
            let p = f.get_path();
            assert!(p.to_str() == ~"/dev/null");
        }
    }
}

#[test]
fn off_task_as_interface() {
    do tcase_result {
        let fobj = ~gio::file_new_for_path("/dev/null");
        do task::try {
            do fobj.as_interface |fifa| {
                let f = fifa as &gio::File;
                let p = f.get_path();
                assert!(p.to_str() == ~"/dev/null");
            };
        }
    }
}

#[test]
fn async() {
    do tcase {
        let fobj = gio::file_new_for_path("/dev/null");
        let f = fobj.interface() as &gio::File;
        let el = EventLoop::new();
        let elo = ~el.clone();
        do f.read_async(0, None) |obj, res| {
            let f: &gio::File = obj.cast::<gio::raw::GFile>()
                                as &gio::File;
            let in = f.read_finish(res);
            util::ignore(in);
            elo.quit();
        };
        el.run();
    }
}

#[test]
#[ignore]  // See https://github.com/mzabaluev/grust/issues/4
fn async_off_task() {
    do tcase {
        let fobj = ~gio::file_new_for_path("/dev/null");
        let el = EventLoop::new();
        let elo = ~el.clone();
        do spawn {
            let f = fobj.interface() as &gio::File;
            let elo2 = ~elo.clone();
            do f.read_async(0, None) |obj, res| {
                let fifa: &gio::interfaces::File = obj.cast();
                let f = fifa as &gio::File;
                f.read_finish(res);
                elo2.quit();
            };
        }
        el.run();
    }
}

#[test]
fn async_off_stack() {
    do tcase {
        let fobj = ~gio::file_new_for_path("/dev/null");
        let el = EventLoop::new();
        let elo = ~el.clone();
        do task::spawn_sched(task::ThreadPerCore) {
            let f = fobj.interface() as &gio::File;
            let elo2 = ~elo.clone();
            do f.read_async(0, None) |obj, res| {
                let f = obj.cast::<gio::raw::GFile>() as &gio::File;
                f.read_finish(res);
                elo2.quit();
            };
        };
        el.run();
    }
}
<|endoftext|>


<commit_before>#![crate_id = "seax-svm"]
#![crate_type="lib"]
#![feature(box_syntax)]
#![allow(dead_code)]

/// Contains the Seax Virtual Machine (SVM) and miscellaneous
/// support code.
pub mod svm {

    use svm::slist::List;
    use svm::slist::Stack;

    /// Singly-linked list and stack implementations.
    pub mod slist {

        use svm::slist::List::{Cons,Nil};
        use std::fmt::Show;

        /// A stack implementation wrapping a List<T>
        pub struct Stack<T> {
            /// The head item of the list.
            head: Box<List<T>>
        }

        impl<T> Stack<T> {
            /// Push an item to the top of the stack, returning a new stack
            pub fn push(self, it: T) -> Stack<T> {
                Stack { head: Box::new(self.head.prepend(it)) }
            }

            /// Peak at the top item of the stack.
            ///
            /// Returns Some<T> if there is an item on top of the stack,
            /// and None if the stack is empty.
            pub fn peek(&self) -> Option<&T> {
                match *self.head {
                    Nil => None,
                    Cons(ref it,_) => Some(it)
                }
            }

            /// Returns an empty stack.
            pub fn empty() -> Stack<T> {
                Stack { head: box Nil }
            }

            /// Wraps a list into a stack.
            pub fn new(l: List<T>) -> Stack<T> {
                Stack { head: box l }
            }
        }

        /// Singly-linked cons list.
        ///
        /// This is used internally to represent list primitives in the machine.
        pub enum List<T> {
            Cons(T, Box<List<T>>),
            Nil,
        }

        /// Public implementation for List.
        impl<T> List<T> {

            /// Creates a new empty list
            pub fn new() -> List<T> {
                Nil
            }

            /// Prepends the given item to the list
            pub fn prepend(self, it: T) -> List<T> {
                Cons(it, box self)
            }

            /// Returns the length of the list
            pub fn length (&self) -> i32 {
                match *self {
                    Cons(_, ref tail) => 1 + tail.length(),
                    Nil => 0
                }
            }
        }

        impl<T> List<T> where T: Show {
            /// Return a string representation of the list
            fn to_string(&self) -> String {
                match *self {
                    Cons(ref head, ref tail) => format!("({:?}, {})", head, tail.to_string()),
                    Nil => format!("nil")
                }
            }

        }

       macro_rules! list(
            ( $e:expr, $($rest:expr),+ ) => ( Cons($e, Box::new(list!( $( $rest ),+ )) ));
            ( $e:expr ) => ( Cons($e, Box::new(Nil)) );
            () => ( @Empty )
        );

        #[cfg(test)]
        mod tests {
            use super::{List, Stack};
            use super::List::{Cons,Nil};

            #[test]
            fn test_list_length() {
                let l: List<i32> = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
                assert_eq!(l.length(), 3);
            }

            #[test]
            fn test_list_to_string() {
                let l: List<i32> = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
                assert_eq!(l.to_string(), "(1i32, (2i32, (3i32, nil)))");
            }

            #[test]
            fn test_stack_peek() {
                let s: Stack<i32> = Stack::new(Cons(1, Box::new(Cons(2, Box::new(Nil)))));
                assert_eq!(s.peek(), Some(&1));
            }

            #[test]
            fn test_stack_push() {
                let mut s: Stack<i32> = Stack::empty();
                assert_eq!(s.peek(), None);
                s = s.push(1);
                assert_eq!(s.peek(), Some(&1));
                s = s.push(6);
                assert_eq!(s.peek(), Some(&6));
            }

            #[test]
            fn test_stack_pop() {
                let mut s: Stack<i32> = Stack::empty();
                assert_eq!(s.peek(), None);
                s = s.push(1);
                assert_eq!(s.peek(), Some(&1));
                s = s.push(6);
                assert_eq!(s.peek(), Some(&6));
            }

            #[test]
            fn test_list_macro() {
                let l: List<i32> = list!(1i32, 2i32, 3i32);
                assert_eq!(l.to_string(), "(1i32, (2i32, (3i32, nil)))")
            }
        }
    }

    /// SVM item types
    pub enum SVMCell {
        Atom,
        ListCell(Box<List<SVMCell>>)
    }

    pub enum Atom {
        AtomUInt(usize),
        AtomSInt(isize),
        AtomFloat(f64),
        AtomStr(String)
    }

    /// SVM instruction types
    pub enum SVMInstruction {
        InstNIL,
        InstLDC(Atom),
        InstLD,
        InstLDF,
        InstJOIN,
        InstAP,
        InstRET,
        InstDUM,
        InstRAP
    }

    /// Represents a SVM machine state
    pub struct State {
        stack: Stack<SVMCell>,
        env: Stack<SVMCell>,
        control: Stack<SVMCell>,
        dump: Stack<SVMCell>
    }

    impl State {

        /// Creates a new empty state
        fn new() -> State {
            State {
                stack: Stack::empty(),
                env: Stack::empty(),
                control: Stack::empty(),
                dump: Stack::empty()
            }
        }

        pub fn eval(self, inst: SVMInstruction) -> State {
            match inst {
                SVMInstruction::InstNIL => State {
                    stack: self.stack.push(SVMCell::ListCell(box List::new())),
                    env: self.env,
                    control: self.control,
                    dump: self.dump
                },
                _ => { unimplemented!() }
            }
        }

    }
}
<commit_msg>Add documentation on SVM instruction set<commit_after>#![crate_id = "seax-svm"]
#![crate_type="lib"]
#![feature(box_syntax)]
#![allow(dead_code)]

/// Contains the Seax Virtual Machine (SVM) and miscellaneous
/// support code.
pub mod svm {

    use svm::slist::List;
    use svm::slist::Stack;

    /// Singly-linked list and stack implementations.
    pub mod slist {

        use svm::slist::List::{Cons,Nil};
        use std::fmt::Show;

        /// A stack implementation wrapping a List<T>
        pub struct Stack<T> {
            /// The head item of the list.
            head: Box<List<T>>
        }

        impl<T> Stack<T> {
            /// Push an item to the top of the stack, returning a new stack
            pub fn push(self, it: T) -> Stack<T> {
                Stack { head: Box::new(self.head.prepend(it)) }
            }

            /// Peak at the top item of the stack.
            ///
            /// Returns Some<T> if there is an item on top of the stack,
            /// and None if the stack is empty.
            pub fn peek(&self) -> Option<&T> {
                match *self.head {
                    Nil => None,
                    Cons(ref it,_) => Some(it)
                }
            }

            /// Returns an empty stack.
            pub fn empty() -> Stack<T> {
                Stack { head: box Nil }
            }

            /// Wraps a list into a stack.
            pub fn new(l: List<T>) -> Stack<T> {
                Stack { head: box l }
            }
        }

        /// Singly-linked cons list.
        ///
        /// This is used internally to represent list primitives in the machine.
        pub enum List<T> {
            Cons(T, Box<List<T>>),
            Nil,
        }

        /// Public implementation for List.
        impl<T> List<T> {

            /// Creates a new empty list
            pub fn new() -> List<T> {
                Nil
            }

            /// Prepends the given item to the list
            pub fn prepend(self, it: T) -> List<T> {
                Cons(it, box self)
            }

            /// Returns the length of the list
            pub fn length (&self) -> i32 {
                match *self {
                    Cons(_, ref tail) => 1 + tail.length(),
                    Nil => 0
                }
            }
        }

        impl<T> List<T> where T: Show {
            /// Return a string representation of the list
            fn to_string(&self) -> String {
                match *self {
                    Cons(ref head, ref tail) => format!("({:?}, {})", head, tail.to_string()),
                    Nil => format!("nil")
                }
            }

        }

       macro_rules! list(
            ( $e:expr, $($rest:expr),+ ) => ( Cons($e, Box::new(list!( $( $rest ),+ )) ));
            ( $e:expr ) => ( Cons($e, Box::new(Nil)) );
            () => ( @Empty )
        );

        #[cfg(test)]
        mod tests {
            use super::{List, Stack};
            use super::List::{Cons,Nil};

            #[test]
            fn test_list_length() {
                let l: List<i32> = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
                assert_eq!(l.length(), 3);
            }

            #[test]
            fn test_list_to_string() {
                let l: List<i32> = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
                assert_eq!(l.to_string(), "(1i32, (2i32, (3i32, nil)))");
            }

            #[test]
            fn test_stack_peek() {
                let s: Stack<i32> = Stack::new(Cons(1, Box::new(Cons(2, Box::new(Nil)))));
                assert_eq!(s.peek(), Some(&1));
            }

            #[test]
            fn test_stack_push() {
                let mut s: Stack<i32> = Stack::empty();
                assert_eq!(s.peek(), None);
                s = s.push(1);
                assert_eq!(s.peek(), Some(&1));
                s = s.push(6);
                assert_eq!(s.peek(), Some(&6));
            }

            #[test]
            fn test_stack_pop() {
                let mut s: Stack<i32> = Stack::empty();
                assert_eq!(s.peek(), None);
                s = s.push(1);
                assert_eq!(s.peek(), Some(&1));
                s = s.push(6);
                assert_eq!(s.peek(), Some(&6));
            }

            #[test]
            fn test_list_macro() {
                let l: List<i32> = list!(1i32, 2i32, 3i32);
                assert_eq!(l.to_string(), "(1i32, (2i32, (3i32, nil)))")
            }
        }
    }

    /// SVM item types
    pub enum SVMCell {
        Atom,
        ListCell(Box<List<SVMCell>>)
    }

    pub enum Atom {
        AtomUInt(usize),
        AtomSInt(isize),
        AtomFloat(f64),
        AtomStr(String)
    }

    /// SVM instruction types
    pub enum SVMInstruction {
        /// `nil`
        ///
        /// Pushes an empty list (nil) onto the stack
        InstNIL,
        /// `ldc`: `L`oa`d` `C`onstant. Loads a constant (atom)
        InstLDC(Atom),
        /// `ld`: `L`oa`d`. Pushes a variable onto the stack.
        ///
        /// The variable is indicated by the argument, a pair.
        /// The pair's `car` specifies the level, the `cdr` the position.
        /// So `(1 . 3)` gives the current function's (level 1) third parameter.
        InstLD,
        /// `ldf`: `L`oa`d` `F`unction.
        ///
        ///  Takes one list argument representing a function and constructs
        ///  a closure (a pair containing the function and the current
        ///  environment) and pushes that onto the stack.
        InstLDF,
        /// `join`
        ///
        /// Pops a list reference from the dump and makes this the new value
        /// of `C`. This instruction occurs at the end of both alternatives of
        ///  a `sel`.
        InstJOIN,
        /// `ap`: `Ap`ply.
        ///
        /// Pops a closure and a list of parameter values from the stack.
        /// The closure is applied to the parameters by installing its
        /// environment as the current one, pushing the parameter list
        /// in front of that, clearing the stack, and setting `C` to the
        /// closure's function pointer. The previous values of `S`, `E`,
        ///  and the next value of `C` are saved on the dump.
        InstAP,
        /// `ret`: `Ret`urn.
        ///
        /// Pops one return value from the stack, restores
        /// `S`, `E`, and `C` from the dump, and pushes
        /// the return value onto the now-current stack.
        InstRET,
        /// `dum`: `Dum`my.
        ///
        /// Pops a dummy environment (an empty list) onto the `E` stack.
        InstDUM,
        /// `rap`: `R`ecursive `Ap`ply.
        /// Works like `ap`, only that it replaces an occurrence of a
        /// dummy environment with the current one, thus making recursive
        ///  functions possible.
        InstRAP,
        /// `sel`: `Sel`ect
        ///
        /// Expects two list arguments, and pops a value from the stack.
        /// The first list is executed if the popped value was non-nil,
        /// the second list otherwise. Before one of these list pointers
        ///  is made the new `C`, a pointer to the instruction following
        ///  `sel` is saved on the dump.
        InstSEL
        // TODO:
    }

    /// Represents a SVM machine state
    pub struct State {
        stack: Stack<SVMCell>,
        env: Stack<SVMCell>,
        control: Stack<SVMCell>,
        dump: Stack<SVMCell>
    }

    impl State {

        /// Creates a new empty state
        fn new() -> State {
            State {
                stack: Stack::empty(),
                env: Stack::empty(),
                control: Stack::empty(),
                dump: Stack::empty()
            }
        }

        pub fn eval(self, inst: SVMInstruction) -> State {
            match inst {
                SVMInstruction::InstNIL => State {
                    stack: self.stack.push(SVMCell::ListCell(box List::new())),
                    env: self.env,
                    control: self.control,
                    dump: self.dump
                },
                _ => { unimplemented!() }
            }
        }

    }
}
<|endoftext|>


<commit_before>#![feature(box_syntax)]
#[test]
fn it_works() {
}

pub mod svm {
    mod slist {

        use svm::slist::List::{Cons,Nil};
        use std::fmt::Show;

        /// Singly-linked cons list.
        ///
        /// This is used internally to represent list primitives in the machine.
        #[deriving(Show)]
        pub enum List<T> {
            Cons(T, Box<List<T>>),
            Nil,
        }

        /// Public implementation for List.
        impl<T> List<T> {

            /// Creates a new empty list
            fn new() -> List<T> {
                Nil
            }

            /// Prepends the given item to the list
            fn prepend(self, it: T) -> List<T> {
                Cons(it, box self)
            }

            /// Returns the length of the list
            fn length (&self) -> i32 {
                match *self {
                    Cons(_, ref tail) => 1 + tail.length(),
                    Nil => 0
                }
            }
        }

        impl<T> List<T> where T: Show {
            /// Return a string representation of the list
            fn to_string(&self) -> String {
                match *self {
                    Cons(ref head, ref tail) => format!("({:?}, {})", head, tail.to_string()),
                    Nil => format!("nil")
                }

            }
        }

        #[cfg(test)]
        mod tests {
            use super::List;
            use super::List::{Cons,Nil};

            #[test]
            fn test_length() {
                let l: List<i32> = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
                assert_eq!(l.length(), 3);
            }

            #[test]
            fn test_to_string() {
                let l: List<i32> = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
                assert_eq!(l.to_string(), "(1i32, (2i32, (3i32, nil)))");
            }
        }
    }

    enum Exp {
        Number(i32)
    }

    struct Engine {
        stack: Vec<Exp>
    }
}
<commit_msg>Stack implementation has working push() and peek(), is immutable<commit_after>#![feature(box_syntax)]
#[test]
fn it_works() {
}

pub mod svm {
    use svm::slist::List;

    mod slist {
        use svm::slist::List::{Cons,Nil};
        use std::fmt::Show;

        pub struct Stack<T> {
            head: Box<List<T>>
        }

        impl<T> Stack<T> {
            fn push(mut self, it: T) -> Stack<T> {
                Stack { head: Box::new(self.head.prepend(it)) }
            }

            fn peek(&self) -> Option<&T> {
                match *self.head {
                    Nil => None,
                    Cons(ref it,_) => Some(it)
                }
            }

            fn empty() -> Stack<T> {
                Stack { head: box Nil }
            }

            fn new(l: List<T>) -> Stack<T> {
                Stack { head: box l }
            }
        }

        /// Singly-linked cons list.
        ///
        /// This is used internally to represent list primitives in the machine.
        #[deriving(Show)]
        pub enum List<T> {
            Cons(T, Box<List<T>>),
            Nil,
        }

        /// Public implementation for List.
        impl<T> List<T> {

            /// Creates a new empty list
            pub fn new() -> List<T> {
                Nil
            }

            /// Prepends the given item to the list
            pub fn prepend(self, it: T) -> List<T> {
                Cons(it, box self)
            }

            /// Returns the length of the list
            pub fn length (&self) -> i32 {
                match *self {
                    Cons(_, ref tail) => 1 + tail.length(),
                    Nil => 0
                }
            }
        }

        impl<T> List<T> where T: Show {
            /// Return a string representation of the list
            fn to_string(&self) -> String {
                match *self {
                    Cons(ref head, ref tail) => format!("({:?}, {})", head, tail.to_string()),
                    Nil => format!("nil")
                }

            }
        }

        #[cfg(test)]
        mod tests {
            use super::{List, Stack};
            use super::List::{Cons,Nil};

            #[test]
            fn test_list_length() {
                let l: List<i32> = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
                assert_eq!(l.length(), 3);
            }

            #[test]
            fn test_list_to_string() {
                let l: List<i32> = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
                assert_eq!(l.to_string(), "(1i32, (2i32, (3i32, nil)))");
            }

            #[test]
            fn test_stack_peek() {
                let s: Stack<i32> = Stack::new(Cons(1, Box::new(Cons(2, Box::new(Nil)))));
                assert_eq!(s.peek(), Some(&1));
            }

            #[test]
            fn test_stack_push() {
                let mut s: Stack<i32> = Stack::empty();
                assert_eq!(s.peek(), None);
                s = s.push(1);
                assert_eq!(s.peek(), Some(&1));
                s = s.push(6);
                assert_eq!(s.peek(), Some(&6));
            }
        }
    }

    enum Exp {
        Number(i32)
    }

    struct Engine {
        stack: List<Exp>
    }
}
<|endoftext|>


<commit_before>#[cfg(feature = "std")]
#[doc(hidden)]
#[macro_export]
macro_rules! forward_to_deserialize_method {
    ($func:ident($($arg:ty),*)) => {
        #[inline]
        fn $func<__V>(self, $(_: $arg,)* visitor: __V) -> ::std::result::Result<__V::Value, Self::Error>
            where __V: $crate::de::Visitor<'de>
        {
            self.deserialize(visitor)
        }
    };
}

#[cfg(not(feature = "std"))]
#[doc(hidden)]
#[macro_export]
macro_rules! forward_to_deserialize_method {
    ($func:ident($($arg:ty),*)) => {
        #[inline]
        fn $func<__V>(self, $(_: $arg,)* visitor: __V) -> ::core::result::Result<__V::Value, Self::Error>
            where __V: $crate::de::Visitor<'de>
        {
            self.deserialize(visitor)
        }
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! forward_to_deserialize_helper {
    (bool) => {
        forward_to_deserialize_method!{deserialize_bool()}
    };
    (u8) => {
        forward_to_deserialize_method!{deserialize_u8()}
    };
    (u16) => {
        forward_to_deserialize_method!{deserialize_u16()}
    };
    (u32) => {
        forward_to_deserialize_method!{deserialize_u32()}
    };
    (u64) => {
        forward_to_deserialize_method!{deserialize_u64()}
    };
    (i8) => {
        forward_to_deserialize_method!{deserialize_i8()}
    };
    (i16) => {
        forward_to_deserialize_method!{deserialize_i16()}
    };
    (i32) => {
        forward_to_deserialize_method!{deserialize_i32()}
    };
    (i64) => {
        forward_to_deserialize_method!{deserialize_i64()}
    };
    (f32) => {
        forward_to_deserialize_method!{deserialize_f32()}
    };
    (f64) => {
        forward_to_deserialize_method!{deserialize_f64()}
    };
    (char) => {
        forward_to_deserialize_method!{deserialize_char()}
    };
    (str) => {
        forward_to_deserialize_method!{deserialize_str()}
    };
    (string) => {
        forward_to_deserialize_method!{deserialize_string()}
    };
    (unit) => {
        forward_to_deserialize_method!{deserialize_unit()}
    };
    (option) => {
        forward_to_deserialize_method!{deserialize_option()}
    };
    (seq) => {
        forward_to_deserialize_method!{deserialize_seq()}
    };
    (seq_fixed_size) => {
        forward_to_deserialize_method!{deserialize_seq_fixed_size(usize)}
    };
    (bytes) => {
        forward_to_deserialize_method!{deserialize_bytes()}
    };
    (byte_buf) => {
        forward_to_deserialize_method!{deserialize_byte_buf()}
    };
    (map) => {
        forward_to_deserialize_method!{deserialize_map()}
    };
    (unit_struct) => {
        forward_to_deserialize_method!{deserialize_unit_struct(&'static str)}
    };
    (newtype_struct) => {
        forward_to_deserialize_method!{deserialize_newtype_struct(&'static str)}
    };
    (tuple_struct) => {
        forward_to_deserialize_method!{deserialize_tuple_struct(&'static str, usize)}
    };
    (struct) => {
        forward_to_deserialize_method!{deserialize_struct(&'static str, &'static [&'static str])}
    };
    (struct_field) => {
        forward_to_deserialize_method!{deserialize_struct_field()}
    };
    (tuple) => {
        forward_to_deserialize_method!{deserialize_tuple(usize)}
    };
    (enum) => {
        forward_to_deserialize_method!{deserialize_enum(&'static str, &'static [&'static str])}
    };
    (ignored_any) => {
        forward_to_deserialize_method!{deserialize_ignored_any()}
    };
}

// Super explicit first paragraph because this shows up at the top level and
// trips up people who are just looking for basic Serialize / Deserialize
// documentation.
//
/// Helper macro when implementing the `Deserializer` part of a new data format
/// for Serde.
///
/// Some `Deserializer` implementations for self-describing formats do not care
/// what hint the `Visitor` gives them, they just want to blindly call the
/// `Visitor` method corresponding to the data they can tell is in the input.
/// This requires repetitive implementations of all the `Deserializer` trait
/// methods.
///
/// ```rust
/// # #[macro_use] extern crate serde;
/// # use serde::de::{value, Deserializer, Visitor};
/// # pub struct MyDeserializer;
/// # impl<'de> Deserializer<'de> for MyDeserializer {
/// #     type Error = value::Error;
/// #     fn deserialize<V>(self, _: V) -> Result<V::Value, Self::Error>
/// #         where V: Visitor<'de>
/// #     { unimplemented!() }
/// #
/// #[inline]
/// fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value, Self::Error>
///     where V: Visitor<'de>
/// {
///     self.deserialize(visitor)
/// }
/// #     forward_to_deserialize! {
/// #         u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string unit option
/// #         seq seq_fixed_size bytes byte_buf map unit_struct newtype_struct
/// #         tuple_struct struct struct_field tuple enum ignored_any
/// #     }
/// # }
/// # fn main() {}
/// ```
///
/// The `forward_to_deserialize!` macro implements these simple forwarding
/// methods so that they forward directly to `Deserializer::deserialize`. You
/// can choose which methods to forward.
///
/// ```rust
/// # #[macro_use] extern crate serde;
/// # use serde::de::{value, Deserializer, Visitor};
/// # pub struct MyDeserializer;
/// impl<'de> Deserializer<'de> for MyDeserializer {
/// #   type Error = value::Error;
///     fn deserialize<V>(self, visitor: V) -> Result<V::Value, Self::Error>
///         where V: Visitor<'de>
///     {
///         /* ... */
/// #       let _ = visitor;
/// #       unimplemented!()
///     }
///
///     forward_to_deserialize! {
///         bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string unit option
///         seq seq_fixed_size bytes byte_buf map unit_struct newtype_struct
///         tuple_struct struct struct_field tuple enum ignored_any
///     }
/// }
/// # fn main() {}
/// ```
#[macro_export]
macro_rules! forward_to_deserialize {
    ($($func:ident)*) => {
        $(forward_to_deserialize_helper!{$func})*
    };
}

/// Seralize the `$value` that implements Display as a string,
/// when that string is statically known to never have more than
/// a constant `$MAX_LEN` bytes.
///
/// Panics if the Display impl tries to write more than `$MAX_LEN` bytes.
#[cfg(feature = "std")]
// Not exported
macro_rules! serialize_display_bounded_length {
    ($value: expr, $MAX_LEN: expr, $serializer: expr) => {
        {
            use std::io::Write;
            let mut buffer: [u8; $MAX_LEN] = unsafe { ::std::mem::uninitialized() };
            let remaining_len;
            {
                let mut remaining = &mut buffer[..];
                write!(remaining, "{}", $value).unwrap();
                remaining_len = remaining.len()
            }
            let written_len = buffer.len() - remaining_len;
            let written = &buffer[..written_len];

            // write! only provides std::fmt::Formatter to Display implementations,
            // which has methods write_str and write_char but no method to write arbitrary bytes.
            // Therefore, `written` is well-formed in UTF-8.
            let written_str = unsafe {
                ::std::str::from_utf8_unchecked(written)
            };
            $serializer.serialize_str(written_str)
        }
    }
}
<commit_msg>Add a note about assuming the deserializer lifetime name<commit_after>#[cfg(feature = "std")]
#[doc(hidden)]
#[macro_export]
macro_rules! forward_to_deserialize_method {
    ($func:ident($($arg:ty),*)) => {
        #[inline]
        fn $func<__V>(self, $(_: $arg,)* visitor: __V) -> ::std::result::Result<__V::Value, Self::Error>
            where __V: $crate::de::Visitor<'de>
        {
            self.deserialize(visitor)
        }
    };
}

#[cfg(not(feature = "std"))]
#[doc(hidden)]
#[macro_export]
macro_rules! forward_to_deserialize_method {
    ($func:ident($($arg:ty),*)) => {
        #[inline]
        fn $func<__V>(self, $(_: $arg,)* visitor: __V) -> ::core::result::Result<__V::Value, Self::Error>
            where __V: $crate::de::Visitor<'de>
        {
            self.deserialize(visitor)
        }
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! forward_to_deserialize_helper {
    (bool) => {
        forward_to_deserialize_method!{deserialize_bool()}
    };
    (u8) => {
        forward_to_deserialize_method!{deserialize_u8()}
    };
    (u16) => {
        forward_to_deserialize_method!{deserialize_u16()}
    };
    (u32) => {
        forward_to_deserialize_method!{deserialize_u32()}
    };
    (u64) => {
        forward_to_deserialize_method!{deserialize_u64()}
    };
    (i8) => {
        forward_to_deserialize_method!{deserialize_i8()}
    };
    (i16) => {
        forward_to_deserialize_method!{deserialize_i16()}
    };
    (i32) => {
        forward_to_deserialize_method!{deserialize_i32()}
    };
    (i64) => {
        forward_to_deserialize_method!{deserialize_i64()}
    };
    (f32) => {
        forward_to_deserialize_method!{deserialize_f32()}
    };
    (f64) => {
        forward_to_deserialize_method!{deserialize_f64()}
    };
    (char) => {
        forward_to_deserialize_method!{deserialize_char()}
    };
    (str) => {
        forward_to_deserialize_method!{deserialize_str()}
    };
    (string) => {
        forward_to_deserialize_method!{deserialize_string()}
    };
    (unit) => {
        forward_to_deserialize_method!{deserialize_unit()}
    };
    (option) => {
        forward_to_deserialize_method!{deserialize_option()}
    };
    (seq) => {
        forward_to_deserialize_method!{deserialize_seq()}
    };
    (seq_fixed_size) => {
        forward_to_deserialize_method!{deserialize_seq_fixed_size(usize)}
    };
    (bytes) => {
        forward_to_deserialize_method!{deserialize_bytes()}
    };
    (byte_buf) => {
        forward_to_deserialize_method!{deserialize_byte_buf()}
    };
    (map) => {
        forward_to_deserialize_method!{deserialize_map()}
    };
    (unit_struct) => {
        forward_to_deserialize_method!{deserialize_unit_struct(&'static str)}
    };
    (newtype_struct) => {
        forward_to_deserialize_method!{deserialize_newtype_struct(&'static str)}
    };
    (tuple_struct) => {
        forward_to_deserialize_method!{deserialize_tuple_struct(&'static str, usize)}
    };
    (struct) => {
        forward_to_deserialize_method!{deserialize_struct(&'static str, &'static [&'static str])}
    };
    (struct_field) => {
        forward_to_deserialize_method!{deserialize_struct_field()}
    };
    (tuple) => {
        forward_to_deserialize_method!{deserialize_tuple(usize)}
    };
    (enum) => {
        forward_to_deserialize_method!{deserialize_enum(&'static str, &'static [&'static str])}
    };
    (ignored_any) => {
        forward_to_deserialize_method!{deserialize_ignored_any()}
    };
}

// Super explicit first paragraph because this shows up at the top level and
// trips up people who are just looking for basic Serialize / Deserialize
// documentation.
//
/// Helper macro when implementing the `Deserializer` part of a new data format
/// for Serde.
///
/// Some `Deserializer` implementations for self-describing formats do not care
/// what hint the `Visitor` gives them, they just want to blindly call the
/// `Visitor` method corresponding to the data they can tell is in the input.
/// This requires repetitive implementations of all the `Deserializer` trait
/// methods.
///
/// ```rust
/// # #[macro_use] extern crate serde;
/// # use serde::de::{value, Deserializer, Visitor};
/// # pub struct MyDeserializer;
/// # impl<'de> Deserializer<'de> for MyDeserializer {
/// #     type Error = value::Error;
/// #     fn deserialize<V>(self, _: V) -> Result<V::Value, Self::Error>
/// #         where V: Visitor<'de>
/// #     { unimplemented!() }
/// #
/// #[inline]
/// fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value, Self::Error>
///     where V: Visitor<'de>
/// {
///     self.deserialize(visitor)
/// }
/// #     forward_to_deserialize! {
/// #         u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string unit option
/// #         seq seq_fixed_size bytes byte_buf map unit_struct newtype_struct
/// #         tuple_struct struct struct_field tuple enum ignored_any
/// #     }
/// # }
/// # fn main() {}
/// ```
///
/// The `forward_to_deserialize!` macro implements these simple forwarding
/// methods so that they forward directly to `Deserializer::deserialize`. You
/// can choose which methods to forward.
///
/// ```rust
/// # #[macro_use] extern crate serde;
/// # use serde::de::{value, Deserializer, Visitor};
/// # pub struct MyDeserializer;
/// impl<'de> Deserializer<'de> for MyDeserializer {
/// #   type Error = value::Error;
///     fn deserialize<V>(self, visitor: V) -> Result<V::Value, Self::Error>
///         where V: Visitor<'de>
///     {
///         /* ... */
/// #       let _ = visitor;
/// #       unimplemented!()
///     }
///
///     forward_to_deserialize! {
///         bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string unit option
///         seq seq_fixed_size bytes byte_buf map unit_struct newtype_struct
///         tuple_struct struct struct_field tuple enum ignored_any
///     }
/// }
/// # fn main() {}
/// ```
///
/// The macro assumes the convention that your `Deserializer` lifetime parameter
/// is called `'de`. It will not work if the `Deserializer` lifetime parameter
/// is called something different.
#[macro_export]
macro_rules! forward_to_deserialize {
    ($($func:ident)*) => {
        $(forward_to_deserialize_helper!{$func})*
    };
}

/// Seralize the `$value` that implements Display as a string,
/// when that string is statically known to never have more than
/// a constant `$MAX_LEN` bytes.
///
/// Panics if the Display impl tries to write more than `$MAX_LEN` bytes.
#[cfg(feature = "std")]
// Not exported
macro_rules! serialize_display_bounded_length {
    ($value: expr, $MAX_LEN: expr, $serializer: expr) => {
        {
            use std::io::Write;
            let mut buffer: [u8; $MAX_LEN] = unsafe { ::std::mem::uninitialized() };
            let remaining_len;
            {
                let mut remaining = &mut buffer[..];
                write!(remaining, "{}", $value).unwrap();
                remaining_len = remaining.len()
            }
            let written_len = buffer.len() - remaining_len;
            let written = &buffer[..written_len];

            // write! only provides std::fmt::Formatter to Display implementations,
            // which has methods write_str and write_char but no method to write arbitrary bytes.
            // Therefore, `written` is well-formed in UTF-8.
            let written_str = unsafe {
                ::std::str::from_utf8_unchecked(written)
            };
            $serializer.serialize_str(written_str)
        }
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Wrapper around Gecko's CSS error reporting mechanism.

#![allow(unsafe_code)]

use cssparser::{SourceLocation, ParseError as CssParseError, Token, BasicParseError};
use cssparser::CowRcStr;
use selectors::parser::SelectorParseError;
use std::ptr;
use style::error_reporting::{ParseErrorReporter, ContextualParseError};
use style::gecko_bindings::bindings::{Gecko_CreateCSSErrorReporter, Gecko_DestroyCSSErrorReporter};
use style::gecko_bindings::bindings::Gecko_ReportUnexpectedCSSError;
use style::gecko_bindings::structs::{Loader, ServoStyleSheet, nsIURI};
use style::gecko_bindings::structs::ErrorReporter as GeckoErrorReporter;
use style::gecko_bindings::structs::URLExtraData as RawUrlExtraData;
use style::gecko_bindings::sugar::refptr::RefPtr;
use style::stylesheets::UrlExtraData;
use style_traits::{ParseError, StyleParseError, PropertyDeclarationParseError, ValueParseError};

/// Wrapper around an instance of Gecko's CSS error reporter.
pub struct ErrorReporter(*mut GeckoErrorReporter);

impl ErrorReporter {
    /// Create a new instance of the Gecko error reporter.
    pub fn new(sheet: *mut ServoStyleSheet,
               loader: *mut Loader,
               url: *mut RawUrlExtraData) -> ErrorReporter {
        unsafe {
            let url = RefPtr::from_ptr_ref(&url);
            ErrorReporter(Gecko_CreateCSSErrorReporter(sheet, loader, url.mBaseURI.raw::<nsIURI>()))
        }
    }
}

impl Drop for ErrorReporter {
    fn drop(&mut self) {
        unsafe {
            Gecko_DestroyCSSErrorReporter(self.0);
        }
    }
}

enum ErrorString<'a> {
    Snippet(CowRcStr<'a>),
    Ident(CowRcStr<'a>),
    UnexpectedToken(Token<'a>),
}

impl<'a> ErrorString<'a> {
    fn into_str(self) -> String {
        match self {
            ErrorString::Snippet(s) => s.as_ref().to_owned(),
            ErrorString::Ident(i) => escape_css_ident(&i),
            ErrorString::UnexpectedToken(t) => token_to_str(t),
        }
    }
}

// This is identical to the behaviour of cssparser::serialize_identifier, except that
// it uses numerical escapes for a larger set of characters.
fn escape_css_ident(ident: &str) -> String {
    // The relevant parts of the CSS grammar are:
    //   ident    ([-]?{nmstart}|[-][-]){nmchar}*
    //   nmstart  [_a-z]|{nonascii}|{escape}
    //   nmchar   [_a-z0-9-]|{nonascii}|{escape}
    //   nonascii [^\0-\177]
    //   escape   {unicode}|\\[^\n\r\f0-9a-f]
    //   unicode  \\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?
    // from http://www.w3.org/TR/CSS21/syndata.html#tokenization but
    // modified for idents by
    // http://dev.w3.org/csswg/cssom/#serialize-an-identifier and
    // http://dev.w3.org/csswg/css-syntax/#would-start-an-identifier
    if ident.is_empty() {
        return ident.into()
    }

    let mut escaped = String::with_capacity(ident.len());

    // A leading dash does not need to be escaped as long as it is not the
    // *only* character in the identifier.
    let mut iter = ident.chars().peekable();
    if iter.peek() == Some(&'-') {
        if ident.len() == 1 {
            return "\\-".into();
        }

        escaped.push('-');
        // Skip the first character.
        let _ = iter.next();
    }

    // Escape a digit at the start (including after a dash),
    // numerically.  If we didn't escape it numerically, it would get
    // interpreted as a numeric escape for the wrong character.
    if iter.peek().map_or(false, |&c| '0' <= c && c <= '9') {
        let ch = iter.next().unwrap();
        escaped.push_str(&format!("\\{:x} ", ch as u32));
    }

    while let Some(ch) = iter.next() {
        if ch == '\0' {
            escaped.push_str("\u{FFFD}");
        } else if ch < (0x20 as char) || (0x7f as char <= ch && ch < (0xA0 as char)) {
            // Escape U+0000 through U+001F and U+007F through U+009F numerically.
            escaped.push_str(&format!("\\{:x} ", ch as u32));
        } else {
            // Escape ASCII non-identifier printables as a backslash plus
            // the character.
            if (ch < (0x7F as char)) &&
                ch != '_' && ch != '-' &&
                (ch < '0' || '9' < ch) &&
                (ch < 'A' || 'Z' < ch) &&
                (ch < 'a' || 'z' < ch)
            {
                escaped.push('\\');
            }
            escaped.push(ch);
        }
    }

    escaped
}

// This is identical to the behaviour of cssparser::CssStringWriter, except that
// the characters between 0x7F and 0xA0 as numerically escaped as well.
fn escape_css_string(s: &str) -> String {
    let mut escaped = String::new();
    for ch in s.chars() {
        if ch < ' ' || (ch >= (0x7F as char) && ch < (0xA0 as char)) {
            escaped.push_str(&format!("\\{:x} ", ch as u32));
        } else {
            if ch == '"' || ch == '\'' || ch == '\\' {
                // Escape backslash and quote characters symbolically.
                // It's not technically necessary to escape the quote
                // character that isn't being used to delimit the string,
                // but we do it anyway because that makes testing simpler.
                escaped.push('\\');
            }
            escaped.push(ch);
        }
    }
    escaped
}

fn token_to_str<'a>(t: Token<'a>) -> String {
    match t {
        Token::Ident(i) => escape_css_ident(&i),
        Token::AtKeyword(kw) => format!("@{}", escape_css_ident(&kw)),
        Token::Hash(h) | Token::IDHash(h) => format!("#{}", escape_css_ident(&h)),
        Token::QuotedString(s) => format!("'{}'", escape_css_string(&s)),
        Token::UnquotedUrl(u) => format!("'{}'", escape_css_string(&u)),
        Token::Delim(d) => d.to_string(),
        Token::Number { int_value: Some(i), .. } => i.to_string(),
        Token::Number { value, .. } => value.to_string(),
        Token::Percentage { int_value: Some(i), .. } => i.to_string(),
        Token::Percentage { unit_value, .. } => unit_value.to_string(),
        Token::Dimension { int_value: Some(i), ref unit, .. } =>
            format!("{}{}", i.to_string(), escape_css_ident(&unit.to_string())),
        Token::Dimension { value, ref unit, .. } =>
            format!("{}{}", value.to_string(), escape_css_ident(&unit.to_string())),
        Token::WhiteSpace(_) => "whitespace".into(),
        Token::Comment(_) => "comment".into(),
        Token::Colon => ":".into(),
        Token::Semicolon => ";".into(),
        Token::Comma => ",".into(),
        Token::IncludeMatch => "~=".into(),
        Token::DashMatch => "|=".into(),
        Token::PrefixMatch => "^=".into(),
        Token::SuffixMatch => "$=".into(),
        Token::SubstringMatch => "*=".into(),
        Token::Column => "||".into(),
        Token::CDO => "<!--".into(),
        Token::CDC => "-->".into(),
        Token::Function(f) => format!("{}(", escape_css_ident(&f)),
        Token::ParenthesisBlock => "(".into(),
        Token::SquareBracketBlock => "[".into(),
        Token::CurlyBracketBlock => "{".into(),
        Token::BadUrl(url) => format!("url('{}", escape_css_string(&url)).into(),
        Token::BadString(s) => format!("'{}", escape_css_string(&s)).into(),
        Token::CloseParenthesis => "unmatched close parenthesis".into(),
        Token::CloseSquareBracket => "unmatched close square bracket".into(),
        Token::CloseCurlyBracket => "unmatched close curly bracket".into(),
    }
}

enum Action {
    Nothing,
    Skip,
    Drop,
}

trait ErrorHelpers<'a> {
    fn error_data(self) -> (CowRcStr<'a>, ParseError<'a>);
    fn error_params(self) -> (ErrorString<'a>, Option<ErrorString<'a>>);
    fn to_gecko_message(&self) -> (Option<&'static [u8]>, &'static [u8], Action);
}

fn extract_error_param<'a>(err: ParseError<'a>) -> Option<ErrorString<'a>> {
    Some(match err {
        CssParseError::Basic(BasicParseError::UnexpectedToken(t)) =>
            ErrorString::UnexpectedToken(t),

        CssParseError::Basic(BasicParseError::AtRuleInvalid(i)) =>
            ErrorString::Snippet(format!("@{}", escape_css_ident(&i)).into()),

        CssParseError::Custom(SelectorParseError::Custom(
            StyleParseError::PropertyDeclaration(
                PropertyDeclarationParseError::InvalidValue(property, None)))) =>
            ErrorString::Snippet(property),

        CssParseError::Custom(SelectorParseError::UnexpectedIdent(ident)) =>
            ErrorString::Ident(ident),

        CssParseError::Custom(SelectorParseError::ExpectedNamespace(namespace)) =>
            ErrorString::Ident(namespace),

        CssParseError::Custom(SelectorParseError::Custom(
            StyleParseError::PropertyDeclaration(
                PropertyDeclarationParseError::UnknownProperty(property)))) =>
            ErrorString::Ident(property),

        CssParseError::Custom(SelectorParseError::Custom(
            StyleParseError::UnexpectedTokenWithinNamespace(token))) =>
            ErrorString::UnexpectedToken(token),

        _ => return None,
    })
}

fn extract_value_error_param<'a>(err: ValueParseError<'a>) -> ErrorString<'a> {
    match err {
        ValueParseError::InvalidColor(t) => ErrorString::UnexpectedToken(t),
    }
}

/// If an error parameter is present in the given error, return it. Additionally return
/// a second parameter if it exists, for use in the prefix for the eventual error message.
fn extract_error_params<'a>(err: ParseError<'a>) -> Option<(ErrorString<'a>, Option<ErrorString<'a>>)> {
    match err {
        CssParseError::Custom(SelectorParseError::Custom(
            StyleParseError::PropertyDeclaration(
                PropertyDeclarationParseError::InvalidValue(property, Some(e))))) =>
            Some((ErrorString::Snippet(property.into()), Some(extract_value_error_param(e)))),

        err => extract_error_param(err).map(|e| (e, None)),
    }
}

impl<'a> ErrorHelpers<'a> for ContextualParseError<'a> {
    fn error_data(self) -> (CowRcStr<'a>, ParseError<'a>) {
        match self {
            ContextualParseError::UnsupportedPropertyDeclaration(s, err) |
            ContextualParseError::UnsupportedFontFaceDescriptor(s, err) |
            ContextualParseError::UnsupportedFontFeatureValuesDescriptor(s, err) |
            ContextualParseError::InvalidKeyframeRule(s, err) |
            ContextualParseError::InvalidFontFeatureValuesRule(s, err) |
            ContextualParseError::UnsupportedKeyframePropertyDeclaration(s, err) |
            ContextualParseError::InvalidRule(s, err) |
            ContextualParseError::UnsupportedRule(s, err) |
            ContextualParseError::UnsupportedViewportDescriptorDeclaration(s, err) |
            ContextualParseError::UnsupportedCounterStyleDescriptorDeclaration(s, err) =>
                (s.into(), err),
            ContextualParseError::InvalidCounterStyleWithoutSymbols(s) |
            ContextualParseError::InvalidCounterStyleNotEnoughSymbols(s) =>
                (s.into(), StyleParseError::UnspecifiedError.into()),
            ContextualParseError::InvalidCounterStyleWithoutAdditiveSymbols |
            ContextualParseError::InvalidCounterStyleExtendsWithSymbols |
            ContextualParseError::InvalidCounterStyleExtendsWithAdditiveSymbols =>
                ("".into(), StyleParseError::UnspecifiedError.into())
        }
    }

    fn error_params(self) -> (ErrorString<'a>, Option<ErrorString<'a>>) {
        let (s, error) = self.error_data();
        extract_error_params(error).unwrap_or((ErrorString::Snippet(s), None))
    }

    fn to_gecko_message(&self) -> (Option<&'static [u8]>, &'static [u8], Action) {
        let (msg, action): (&[u8], Action) = match *self {
            ContextualParseError::UnsupportedPropertyDeclaration(
                _, CssParseError::Basic(BasicParseError::UnexpectedToken(_))) |
            ContextualParseError::UnsupportedPropertyDeclaration(
                _, CssParseError::Basic(BasicParseError::AtRuleInvalid(_))) =>
                (b"PEParseDeclarationDeclExpected\0", Action::Skip),
            ContextualParseError::UnsupportedPropertyDeclaration(
                _, CssParseError::Custom(SelectorParseError::Custom(
                    StyleParseError::PropertyDeclaration(
                        PropertyDeclarationParseError::InvalidValue(_, ref err))))) => {
                let prefix = match *err {
                    Some(ValueParseError::InvalidColor(_)) => Some(&b"PEColorNotColor\0"[..]),
                    _ => None,
                };
                return (prefix, b"PEValueParsingError\0", Action::Drop);
            }
            ContextualParseError::UnsupportedPropertyDeclaration(..) =>
                (b"PEUnknownProperty\0", Action::Drop),
            ContextualParseError::UnsupportedFontFaceDescriptor(..) =>
                (b"PEUnknwnFontDesc\0", Action::Skip),
            ContextualParseError::InvalidKeyframeRule(..) =>
                (b"PEKeyframeBadName\0", Action::Nothing),
            ContextualParseError::UnsupportedKeyframePropertyDeclaration(..) =>
                (b"PEBadSelectorKeyframeRuleIgnored\0", Action::Nothing),
            ContextualParseError::InvalidRule(
                _, CssParseError::Custom(SelectorParseError::ExpectedNamespace(_))) =>
                (b"PEUnknownNamespacePrefix\0", Action::Nothing),
            ContextualParseError::InvalidRule(
                _, CssParseError::Custom(SelectorParseError::Custom(
                StyleParseError::UnexpectedTokenWithinNamespace(_)))) =>
                (b"PEAtNSUnexpected\0", Action::Nothing),
            ContextualParseError::InvalidRule(..) =>
                (b"PEBadSelectorRSIgnored\0", Action::Nothing),
            ContextualParseError::UnsupportedRule(..) =>
                (b"PEDeclDropped\0", Action::Nothing),
            ContextualParseError::UnsupportedViewportDescriptorDeclaration(..) |
            ContextualParseError::UnsupportedCounterStyleDescriptorDeclaration(..) |
            ContextualParseError::InvalidCounterStyleWithoutSymbols(..) |
            ContextualParseError::InvalidCounterStyleNotEnoughSymbols(..) |
            ContextualParseError::InvalidCounterStyleWithoutAdditiveSymbols |
            ContextualParseError::InvalidCounterStyleExtendsWithSymbols |
            ContextualParseError::InvalidCounterStyleExtendsWithAdditiveSymbols |
            ContextualParseError::UnsupportedFontFeatureValuesDescriptor(..) |
            ContextualParseError::InvalidFontFeatureValuesRule(..) =>
                (b"PEUnknownAtRule\0", Action::Skip),
        };
        (None, msg, action)
    }
}

impl ParseErrorReporter for ErrorReporter {
    fn report_error(&self,
                    _url: &UrlExtraData,
                    location: SourceLocation,
                    error: ContextualParseError) {
        let (pre, name, action) = error.to_gecko_message();
        let suffix = match action {
            Action::Nothing => ptr::null(),
            Action::Skip => b"PEDeclSkipped\0".as_ptr(),
            Action::Drop => b"PEDeclDropped\0".as_ptr(),
        };
        let (param, pre_param) = error.error_params();
        let param = param.into_str();
        let pre_param = pre_param.map(|p| p.into_str());
        let pre_param_ptr = pre_param.as_ref().map_or(ptr::null(), |p| p.as_ptr());
        // The CSS source text is unused and will be removed in bug 1381188.
        let source = "";
        unsafe {
            Gecko_ReportUnexpectedCSSError(self.0,
                                           name.as_ptr() as *const _,
                                           param.as_ptr() as *const _,
                                           param.len() as u32,
                                           pre.map_or(ptr::null(), |p| p.as_ptr()) as *const _,
                                           pre_param_ptr as *const _,
                                           pre_param.as_ref().map_or(0, |p| p.len()) as u32,
                                           suffix as *const _,
                                           source.as_ptr() as *const _,
                                           source.len() as u32,
                                           location.line,
                                           location.column);
        }
    }
}
<commit_msg>Auto merge of #18135 - jdm:lesstring, r=emilio<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Wrapper around Gecko's CSS error reporting mechanism.

#![allow(unsafe_code)]

use cssparser::{SourceLocation, ParseError as CssParseError, Token, BasicParseError};
use cssparser::CowRcStr;
use selectors::parser::SelectorParseError;
use std::ptr;
use style::error_reporting::{ParseErrorReporter, ContextualParseError};
use style::gecko_bindings::bindings::{Gecko_CreateCSSErrorReporter, Gecko_DestroyCSSErrorReporter};
use style::gecko_bindings::bindings::Gecko_ReportUnexpectedCSSError;
use style::gecko_bindings::structs::{Loader, ServoStyleSheet, nsIURI};
use style::gecko_bindings::structs::ErrorReporter as GeckoErrorReporter;
use style::gecko_bindings::structs::URLExtraData as RawUrlExtraData;
use style::gecko_bindings::sugar::refptr::RefPtr;
use style::stylesheets::UrlExtraData;
use style_traits::{ParseError, StyleParseError, PropertyDeclarationParseError, ValueParseError};

/// Wrapper around an instance of Gecko's CSS error reporter.
pub struct ErrorReporter(*mut GeckoErrorReporter);

impl ErrorReporter {
    /// Create a new instance of the Gecko error reporter.
    pub fn new(sheet: *mut ServoStyleSheet,
               loader: *mut Loader,
               url: *mut RawUrlExtraData) -> ErrorReporter {
        unsafe {
            let url = RefPtr::from_ptr_ref(&url);
            ErrorReporter(Gecko_CreateCSSErrorReporter(sheet, loader, url.mBaseURI.raw::<nsIURI>()))
        }
    }
}

impl Drop for ErrorReporter {
    fn drop(&mut self) {
        unsafe {
            Gecko_DestroyCSSErrorReporter(self.0);
        }
    }
}

enum ErrorString<'a> {
    Snippet(CowRcStr<'a>),
    Ident(CowRcStr<'a>),
    UnexpectedToken(Token<'a>),
}

impl<'a> ErrorString<'a> {
    fn into_str(self) -> CowRcStr<'a> {
        match self {
            ErrorString::Snippet(s) => s,
            ErrorString::Ident(i) => escape_css_ident(&i).into(),
            ErrorString::UnexpectedToken(t) => token_to_str(t).into(),
        }
    }
}

// This is identical to the behaviour of cssparser::serialize_identifier, except that
// it uses numerical escapes for a larger set of characters.
fn escape_css_ident(ident: &str) -> String {
    // The relevant parts of the CSS grammar are:
    //   ident    ([-]?{nmstart}|[-][-]){nmchar}*
    //   nmstart  [_a-z]|{nonascii}|{escape}
    //   nmchar   [_a-z0-9-]|{nonascii}|{escape}
    //   nonascii [^\0-\177]
    //   escape   {unicode}|\\[^\n\r\f0-9a-f]
    //   unicode  \\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?
    // from http://www.w3.org/TR/CSS21/syndata.html#tokenization but
    // modified for idents by
    // http://dev.w3.org/csswg/cssom/#serialize-an-identifier and
    // http://dev.w3.org/csswg/css-syntax/#would-start-an-identifier
    if ident.is_empty() {
        return ident.into()
    }

    let mut escaped = String::with_capacity(ident.len());

    // A leading dash does not need to be escaped as long as it is not the
    // *only* character in the identifier.
    let mut iter = ident.chars().peekable();
    if iter.peek() == Some(&'-') {
        if ident.len() == 1 {
            return "\\-".into();
        }

        escaped.push('-');
        // Skip the first character.
        let _ = iter.next();
    }

    // Escape a digit at the start (including after a dash),
    // numerically.  If we didn't escape it numerically, it would get
    // interpreted as a numeric escape for the wrong character.
    if iter.peek().map_or(false, |&c| '0' <= c && c <= '9') {
        let ch = iter.next().unwrap();
        escaped.push_str(&format!("\\{:x} ", ch as u32));
    }

    while let Some(ch) = iter.next() {
        if ch == '\0' {
            escaped.push_str("\u{FFFD}");
        } else if ch < (0x20 as char) || (0x7f as char <= ch && ch < (0xA0 as char)) {
            // Escape U+0000 through U+001F and U+007F through U+009F numerically.
            escaped.push_str(&format!("\\{:x} ", ch as u32));
        } else {
            // Escape ASCII non-identifier printables as a backslash plus
            // the character.
            if (ch < (0x7F as char)) &&
                ch != '_' && ch != '-' &&
                (ch < '0' || '9' < ch) &&
                (ch < 'A' || 'Z' < ch) &&
                (ch < 'a' || 'z' < ch)
            {
                escaped.push('\\');
            }
            escaped.push(ch);
        }
    }

    escaped
}

// This is identical to the behaviour of cssparser::CssStringWriter, except that
// the characters between 0x7F and 0xA0 as numerically escaped as well.
fn escape_css_string(s: &str) -> String {
    let mut escaped = String::new();
    for ch in s.chars() {
        if ch < ' ' || (ch >= (0x7F as char) && ch < (0xA0 as char)) {
            escaped.push_str(&format!("\\{:x} ", ch as u32));
        } else {
            if ch == '"' || ch == '\'' || ch == '\\' {
                // Escape backslash and quote characters symbolically.
                // It's not technically necessary to escape the quote
                // character that isn't being used to delimit the string,
                // but we do it anyway because that makes testing simpler.
                escaped.push('\\');
            }
            escaped.push(ch);
        }
    }
    escaped
}

fn token_to_str<'a>(t: Token<'a>) -> String {
    match t {
        Token::Ident(i) => escape_css_ident(&i),
        Token::AtKeyword(kw) => format!("@{}", escape_css_ident(&kw)),
        Token::Hash(h) | Token::IDHash(h) => format!("#{}", escape_css_ident(&h)),
        Token::QuotedString(s) => format!("'{}'", escape_css_string(&s)),
        Token::UnquotedUrl(u) => format!("'{}'", escape_css_string(&u)),
        Token::Delim(d) => d.to_string(),
        Token::Number { int_value: Some(i), .. } => i.to_string(),
        Token::Number { value, .. } => value.to_string(),
        Token::Percentage { int_value: Some(i), .. } => i.to_string(),
        Token::Percentage { unit_value, .. } => unit_value.to_string(),
        Token::Dimension { int_value: Some(i), ref unit, .. } =>
            format!("{}{}", i, escape_css_ident(&*unit)),
        Token::Dimension { value, ref unit, .. } =>
            format!("{}{}", value, escape_css_ident(&*unit)),
        Token::WhiteSpace(_) => "whitespace".into(),
        Token::Comment(_) => "comment".into(),
        Token::Colon => ":".into(),
        Token::Semicolon => ";".into(),
        Token::Comma => ",".into(),
        Token::IncludeMatch => "~=".into(),
        Token::DashMatch => "|=".into(),
        Token::PrefixMatch => "^=".into(),
        Token::SuffixMatch => "$=".into(),
        Token::SubstringMatch => "*=".into(),
        Token::Column => "||".into(),
        Token::CDO => "<!--".into(),
        Token::CDC => "-->".into(),
        Token::Function(f) => format!("{}(", escape_css_ident(&f)),
        Token::ParenthesisBlock => "(".into(),
        Token::SquareBracketBlock => "[".into(),
        Token::CurlyBracketBlock => "{".into(),
        Token::BadUrl(url) => format!("url('{}", escape_css_string(&url)).into(),
        Token::BadString(s) => format!("'{}", escape_css_string(&s)).into(),
        Token::CloseParenthesis => "unmatched close parenthesis".into(),
        Token::CloseSquareBracket => "unmatched close square bracket".into(),
        Token::CloseCurlyBracket => "unmatched close curly bracket".into(),
    }
}

enum Action {
    Nothing,
    Skip,
    Drop,
}

trait ErrorHelpers<'a> {
    fn error_data(self) -> (CowRcStr<'a>, ParseError<'a>);
    fn error_params(self) -> (ErrorString<'a>, Option<ErrorString<'a>>);
    fn to_gecko_message(&self) -> (Option<&'static [u8]>, &'static [u8], Action);
}

fn extract_error_param<'a>(err: ParseError<'a>) -> Option<ErrorString<'a>> {
    Some(match err {
        CssParseError::Basic(BasicParseError::UnexpectedToken(t)) =>
            ErrorString::UnexpectedToken(t),

        CssParseError::Basic(BasicParseError::AtRuleInvalid(i)) =>
            ErrorString::Snippet(format!("@{}", escape_css_ident(&i)).into()),

        CssParseError::Custom(SelectorParseError::Custom(
            StyleParseError::PropertyDeclaration(
                PropertyDeclarationParseError::InvalidValue(property, None)))) =>
            ErrorString::Snippet(property),

        CssParseError::Custom(SelectorParseError::UnexpectedIdent(ident)) =>
            ErrorString::Ident(ident),

        CssParseError::Custom(SelectorParseError::ExpectedNamespace(namespace)) =>
            ErrorString::Ident(namespace),

        CssParseError::Custom(SelectorParseError::Custom(
            StyleParseError::PropertyDeclaration(
                PropertyDeclarationParseError::UnknownProperty(property)))) =>
            ErrorString::Ident(property),

        CssParseError::Custom(SelectorParseError::Custom(
            StyleParseError::UnexpectedTokenWithinNamespace(token))) =>
            ErrorString::UnexpectedToken(token),

        _ => return None,
    })
}

fn extract_value_error_param<'a>(err: ValueParseError<'a>) -> ErrorString<'a> {
    match err {
        ValueParseError::InvalidColor(t) => ErrorString::UnexpectedToken(t),
    }
}

/// If an error parameter is present in the given error, return it. Additionally return
/// a second parameter if it exists, for use in the prefix for the eventual error message.
fn extract_error_params<'a>(err: ParseError<'a>) -> Option<(ErrorString<'a>, Option<ErrorString<'a>>)> {
    match err {
        CssParseError::Custom(SelectorParseError::Custom(
            StyleParseError::PropertyDeclaration(
                PropertyDeclarationParseError::InvalidValue(property, Some(e))))) =>
            Some((ErrorString::Snippet(property.into()), Some(extract_value_error_param(e)))),

        err => extract_error_param(err).map(|e| (e, None)),
    }
}

impl<'a> ErrorHelpers<'a> for ContextualParseError<'a> {
    fn error_data(self) -> (CowRcStr<'a>, ParseError<'a>) {
        match self {
            ContextualParseError::UnsupportedPropertyDeclaration(s, err) |
            ContextualParseError::UnsupportedFontFaceDescriptor(s, err) |
            ContextualParseError::UnsupportedFontFeatureValuesDescriptor(s, err) |
            ContextualParseError::InvalidKeyframeRule(s, err) |
            ContextualParseError::InvalidFontFeatureValuesRule(s, err) |
            ContextualParseError::UnsupportedKeyframePropertyDeclaration(s, err) |
            ContextualParseError::InvalidRule(s, err) |
            ContextualParseError::UnsupportedRule(s, err) |
            ContextualParseError::UnsupportedViewportDescriptorDeclaration(s, err) |
            ContextualParseError::UnsupportedCounterStyleDescriptorDeclaration(s, err) =>
                (s.into(), err),
            ContextualParseError::InvalidCounterStyleWithoutSymbols(s) |
            ContextualParseError::InvalidCounterStyleNotEnoughSymbols(s) =>
                (s.into(), StyleParseError::UnspecifiedError.into()),
            ContextualParseError::InvalidCounterStyleWithoutAdditiveSymbols |
            ContextualParseError::InvalidCounterStyleExtendsWithSymbols |
            ContextualParseError::InvalidCounterStyleExtendsWithAdditiveSymbols =>
                ("".into(), StyleParseError::UnspecifiedError.into())
        }
    }

    fn error_params(self) -> (ErrorString<'a>, Option<ErrorString<'a>>) {
        let (s, error) = self.error_data();
        extract_error_params(error).unwrap_or((ErrorString::Snippet(s), None))
    }

    fn to_gecko_message(&self) -> (Option<&'static [u8]>, &'static [u8], Action) {
        let (msg, action): (&[u8], Action) = match *self {
            ContextualParseError::UnsupportedPropertyDeclaration(
                _, CssParseError::Basic(BasicParseError::UnexpectedToken(_))) |
            ContextualParseError::UnsupportedPropertyDeclaration(
                _, CssParseError::Basic(BasicParseError::AtRuleInvalid(_))) =>
                (b"PEParseDeclarationDeclExpected\0", Action::Skip),
            ContextualParseError::UnsupportedPropertyDeclaration(
                _, CssParseError::Custom(SelectorParseError::Custom(
                    StyleParseError::PropertyDeclaration(
                        PropertyDeclarationParseError::InvalidValue(_, ref err))))) => {
                let prefix = match *err {
                    Some(ValueParseError::InvalidColor(_)) => Some(&b"PEColorNotColor\0"[..]),
                    _ => None,
                };
                return (prefix, b"PEValueParsingError\0", Action::Drop);
            }
            ContextualParseError::UnsupportedPropertyDeclaration(..) =>
                (b"PEUnknownProperty\0", Action::Drop),
            ContextualParseError::UnsupportedFontFaceDescriptor(..) =>
                (b"PEUnknwnFontDesc\0", Action::Skip),
            ContextualParseError::InvalidKeyframeRule(..) =>
                (b"PEKeyframeBadName\0", Action::Nothing),
            ContextualParseError::UnsupportedKeyframePropertyDeclaration(..) =>
                (b"PEBadSelectorKeyframeRuleIgnored\0", Action::Nothing),
            ContextualParseError::InvalidRule(
                _, CssParseError::Custom(SelectorParseError::ExpectedNamespace(_))) =>
                (b"PEUnknownNamespacePrefix\0", Action::Nothing),
            ContextualParseError::InvalidRule(
                _, CssParseError::Custom(SelectorParseError::Custom(
                StyleParseError::UnexpectedTokenWithinNamespace(_)))) =>
                (b"PEAtNSUnexpected\0", Action::Nothing),
            ContextualParseError::InvalidRule(..) =>
                (b"PEBadSelectorRSIgnored\0", Action::Nothing),
            ContextualParseError::UnsupportedRule(..) =>
                (b"PEDeclDropped\0", Action::Nothing),
            ContextualParseError::UnsupportedViewportDescriptorDeclaration(..) |
            ContextualParseError::UnsupportedCounterStyleDescriptorDeclaration(..) |
            ContextualParseError::InvalidCounterStyleWithoutSymbols(..) |
            ContextualParseError::InvalidCounterStyleNotEnoughSymbols(..) |
            ContextualParseError::InvalidCounterStyleWithoutAdditiveSymbols |
            ContextualParseError::InvalidCounterStyleExtendsWithSymbols |
            ContextualParseError::InvalidCounterStyleExtendsWithAdditiveSymbols |
            ContextualParseError::UnsupportedFontFeatureValuesDescriptor(..) |
            ContextualParseError::InvalidFontFeatureValuesRule(..) =>
                (b"PEUnknownAtRule\0", Action::Skip),
        };
        (None, msg, action)
    }
}

impl ParseErrorReporter for ErrorReporter {
    fn report_error(&self,
                    _url: &UrlExtraData,
                    location: SourceLocation,
                    error: ContextualParseError) {
        let (pre, name, action) = error.to_gecko_message();
        let suffix = match action {
            Action::Nothing => ptr::null(),
            Action::Skip => b"PEDeclSkipped\0".as_ptr(),
            Action::Drop => b"PEDeclDropped\0".as_ptr(),
        };
        let (param, pre_param) = error.error_params();
        let param = param.into_str();
        let pre_param = pre_param.map(|p| p.into_str());
        let pre_param_ptr = pre_param.as_ref().map_or(ptr::null(), |p| p.as_ptr());
        // The CSS source text is unused and will be removed in bug 1381188.
        let source = "";
        unsafe {
            Gecko_ReportUnexpectedCSSError(self.0,
                                           name.as_ptr() as *const _,
                                           param.as_ptr() as *const _,
                                           param.len() as u32,
                                           pre.map_or(ptr::null(), |p| p.as_ptr()) as *const _,
                                           pre_param_ptr as *const _,
                                           pre_param.as_ref().map_or(0, |p| p.len()) as u32,
                                           suffix as *const _,
                                           source.as_ptr() as *const _,
                                           source.len() as u32,
                                           location.line,
                                           location.column);
        }
    }
}
<|endoftext|>