<commit_before>use std::cell::Cell;

use graphics::character::CharacterCache;

use cfg::ColorScheme;

use ui::{Px, BB};
use ui::layout::{RectBB, RectBounded, ConstrainCx, Layout};
use ui::color::Scheme;
use ui::draw::{Draw, DrawCx};
use ui::event::*;
use ui::text::{self, FontFace};

pub struct Set<T> {
    bb: RectBB,
    tabs: Vec<T>,
    current: Cell<usize>
}

const TAB_WIDTH: Px = 150.0;

impl<T> Set<T> {
    pub fn new() -> Set<T> {
        Set {
            bb: RectBB::default(),
            tabs: vec![],
            current: Cell::new(0)
        }
    }

    pub fn add(&mut self, x: T) {
        self.tabs.push(x);
    }

    pub fn remove(&mut self) -> Option<T> {
        let current = self.current.get();
        if current >= self.tabs.len() {
            None
        } else {
            if current > 0 {
                self.current.set(current - 1);
            }
            Some(self.tabs.remove(current))
        }
    }
}

impl<T: Layout> RectBounded for Set<T> {
    fn rect_bb(&self) -> &RectBB { &self.bb }
    fn name(&self) -> &'static str { "<tabset>" }
    fn constrain<'a, 'b>(&'a self, (cx, bb): ConstrainCx<'b, 'a>) {
        let current = self.current.get();
        if current >= self.tabs.len() {
            return;
        }

        let tb = self.tabs[current].collect(cx);

        let height = cx.fonts.metrics(text::Regular).height * 2.0;

        cx.equal(bb.x1, tb.x1);
        cx.equal(tb.x2, bb.x2);
        cx.distance(bb.y1, tb.y1, height);
        cx.equal(tb.y2, bb.y2);
    }
}

pub trait Tab {
    fn title(&self) -> String;
}

impl<T> Draw for Set<T> where T: Layout + Tab + Draw {
    fn draw(&self, cx: &mut DrawCx) {
        let bb = self.bb();
        let current = self.current.get();
        if current < self.tabs.len() {
            let metrics = cx.fonts.metrics(text::Regular);

            // Background for all tabs.
            cx.rect(BB {
                x1: bb.x1, x2: bb.x1 + (self.tabs.len() as Px) * TAB_WIDTH,
                y1: bb.y1, y2: bb.y1 + metrics.height * 2.0
            }, ColorScheme.focus());

            for (i, tab) in self.tabs.iter().enumerate() {
                let size = text::Regular.size();
                let text = tab.title();
                let w = text::Regular.cache(&mut cx.fonts).width(size, &text) as Px;

                // Background for each tab.
                let x = bb.x1 + (i as Px) * TAB_WIDTH;
                cx.rect(BB {
                    x1: x + 1.0, x2: x + TAB_WIDTH - 1.0,
                    y1: bb.y1, y2: bb.y1 + metrics.height * 2.0 - if i == current { 2.0 } else { 1.0 }
                }, if i == current { ColorScheme.back_view() } else { ColorScheme.back_view_alt() });

                cx.text(text::Regular, [x + (TAB_WIDTH - w) / 2.0,
                                        bb.y1 + metrics.height * 0.5],
                                        ColorScheme.normal(), &text);
            }

            self.tabs[current].draw(cx);
        }
    }
}

trait SetDispatch<T, E> {
    fn dispatch(&self, _tab: &T, _ev: &E) -> bool { false }
}

impl<E, T> Dispatch<E> for Set<T> where Set<T>: SetDispatch<T, E>, T: Dispatch<E> {
    fn dispatch(&self, ev: &E) -> bool {
        let current = self.current.get();
        if current < self.tabs.len() {
            let tab = &self.tabs[current];
            tab.dispatch(ev) | SetDispatch::dispatch(self, tab, ev)
        } else {
            false
        }
    }
}

impl<T> SetDispatch<T, MouseDown> for Set<T> where T: Layout {
    fn dispatch(&self, tab: &T, ev: &MouseDown) -> bool {
        let bb = self.bb();
        let pos = [ev.x, ev.y];
        if bb.contains(pos) && !tab.bb().contains(pos) {
            let current = ((ev.x - bb.x1) / TAB_WIDTH) as usize;
            if current < self.tabs.len() {
                self.current.set(current);
                return true;
            }
        }
        false
    }
}

impl<T> SetDispatch<T, MouseUp> for Set<T> {}
impl<T> SetDispatch<T, MouseMove> for Set<T> {}
impl<T> SetDispatch<T, MouseScroll> for Set<T> {}
impl<T> SetDispatch<T, Update> for Set<T> {}
impl<'a, T> SetDispatch<T, TextInput<'a>> for Set<T> {}
impl<T> SetDispatch<T, KeyDown> for Set<T> {}
impl<T> SetDispatch<T, KeyUp> for Set<T> {}
<commit_msg>ui::tab: clean up tab bar rendering.<commit_after>use std::cell::Cell;

use graphics::character::CharacterCache;

use cfg::ColorScheme;

use ui::{Px, BB};
use ui::layout::{RectBB, RectBounded, ConstrainCx, Layout};
use ui::color::Scheme;
use ui::draw::{Draw, DrawCx};
use ui::event::*;
use ui::text::{self, FontFace};

pub struct Set<T> {
    bb: RectBB,
    tabs: Vec<T>,
    current: Cell<usize>
}

const TAB_WIDTH: Px = 150.0;

impl<T> Set<T> {
    pub fn new() -> Set<T> {
        Set {
            bb: RectBB::default(),
            tabs: vec![],
            current: Cell::new(0)
        }
    }

    pub fn add(&mut self, x: T) {
        self.tabs.push(x);
    }

    pub fn remove(&mut self) -> Option<T> {
        let current = self.current.get();
        if current >= self.tabs.len() {
            None
        } else {
            if current > 0 {
                self.current.set(current - 1);
            }
            Some(self.tabs.remove(current))
        }
    }
}

impl<T: Layout> RectBounded for Set<T> {
    fn rect_bb(&self) -> &RectBB { &self.bb }
    fn name(&self) -> &'static str { "<tabset>" }
    fn constrain<'a, 'b>(&'a self, (cx, bb): ConstrainCx<'b, 'a>) {
        let current = self.current.get();
        if current >= self.tabs.len() {
            return;
        }

        let tb = self.tabs[current].collect(cx);

        let height = cx.fonts.metrics(text::Regular).height * 2.0;

        cx.equal(bb.x1, tb.x1);
        cx.equal(tb.x2, bb.x2);
        cx.distance(bb.y1, tb.y1, height);
        cx.equal(tb.y2, bb.y2);
    }
}

pub trait Tab {
    fn title(&self) -> String;
}

impl<T> Draw for Set<T> where T: Layout + Tab + Draw {
    fn draw(&self, cx: &mut DrawCx) {
        let bb = self.bb();
        let current = self.current.get();
        if current < self.tabs.len() {
            let metrics = cx.fonts.metrics(text::Regular);

            // Background for all tabs.
            cx.rect(BB {
                x1: bb.x1, x2: bb.x1 + (self.tabs.len() as Px) * TAB_WIDTH,
                y1: bb.y1, y2: bb.y1 + metrics.height * 2.0
            }, ColorScheme.inactive());

            for (i, tab) in self.tabs.iter().enumerate() {
                let size = text::Regular.size();
                let text = tab.title();
                let w = text::Regular.cache(&mut cx.fonts).width(size, &text) as Px;

                // Background for each tab.
                let x = bb.x1 + (i as Px) * TAB_WIDTH;
                cx.rect(BB {
                    x1: x + 1.0, x2: x + TAB_WIDTH - 1.0,
                    y1: bb.y1, y2: bb.y1 + metrics.height * 2.0
                }, ColorScheme.background());

                // Focus highlight.
                if i == current {
                    let y = bb.y1 + metrics.height * 2.0 - 5.0;
                    cx.rect(BB {
                        x1: x + 3.0, x2: x + TAB_WIDTH - 3.0,
                        y1: y, y2: y + 2.0
                    }, ColorScheme.focus());
                }

                cx.text(text::Regular, [(x + (TAB_WIDTH - w) / 2.0).round(),
                                        (bb.y1 + metrics.height * 0.5).round()],
                                        ColorScheme.normal(), &text);
            }

            self.tabs[current].draw(cx);
        }
    }
}

trait SetDispatch<T, E> {
    fn dispatch(&self, _tab: &T, _ev: &E) -> bool { false }
}

impl<E, T> Dispatch<E> for Set<T> where Set<T>: SetDispatch<T, E>, T: Dispatch<E> {
    fn dispatch(&self, ev: &E) -> bool {
        let current = self.current.get();
        if current < self.tabs.len() {
            let tab = &self.tabs[current];
            tab.dispatch(ev) | SetDispatch::dispatch(self, tab, ev)
        } else {
            false
        }
    }
}

impl<T> SetDispatch<T, MouseDown> for Set<T> where T: Layout {
    fn dispatch(&self, tab: &T, ev: &MouseDown) -> bool {
        let bb = self.bb();
        let pos = [ev.x, ev.y];
        if bb.contains(pos) && !tab.bb().contains(pos) {
            let current = ((ev.x - bb.x1) / TAB_WIDTH) as usize;
            if current < self.tabs.len() {
                self.current.set(current);
                return true;
            }
        }
        false
    }
}

impl<T> SetDispatch<T, MouseUp> for Set<T> {}
impl<T> SetDispatch<T, MouseMove> for Set<T> {}
impl<T> SetDispatch<T, MouseScroll> for Set<T> {}
impl<T> SetDispatch<T, Update> for Set<T> {}
impl<'a, T> SetDispatch<T, TextInput<'a>> for Set<T> {}
impl<T> SetDispatch<T, KeyDown> for Set<T> {}
impl<T> SetDispatch<T, KeyUp> for Set<T> {}
<|endoftext|>


<commit_before>//! A vertex with and without textures attached.
use gl::types::*;
use color::Color4;
use nalgebra::{Pnt2,Pnt3,Vec2,Vec3};

#[deriving(Show, Clone, Copy, PartialEq)]
/// An untextured rendering vertex, with position and color.
pub struct ColoredVertex {
  /// The 3-d position of this vertex in world space.
  pub position: Pnt3<GLfloat>,
  /// The color to apply to this vertex, in lieu of a texture.
  pub color:    Color4<GLfloat>,
}

#[test]
fn check_vertex_size() {
  assert_eq!(mem::size_of::<ColoredVertex>(), 7*4);
  assert_eq!(mem::size_of::<TextureVertex>(), 5*4);
}

impl ColoredVertex {
  /// Generates two colored triangles, representing a square, at z=0.
  /// The bounds of the square is represented by `b`.
  pub fn square(min: Pnt2<GLfloat>, max: Pnt2<GLfloat>, color: Color4<GLfloat>) -> [ColoredVertex, ..6] {
    let vtx = |x, y| {
        ColoredVertex { position: Pnt3::new(x, y, 0.0), color: color }
      };

    [
      vtx(min.x, min.y), vtx(max.x, max.y), vtx(min.x, max.y),
      vtx(min.x, min.y), vtx(max.x, min.y), vtx(max.x, max.y),
    ]
  }
}

#[deriving(Show, Clone, Copy, PartialEq)]
/// A point in the world with corresponding texture data.
///
/// The texture position is [0, 1].
pub struct TextureVertex {
  /// The position of this vertex in the world.
  pub world_position:  Vec3<GLfloat>,

  /// The position of this vertex on a texture. The range of valid values
  /// in each dimension is [0, 1].
  pub texture_position: Vec2<GLfloat>,
}

impl TextureVertex {
  /// Generates two textured triangles, representing a square in 2D space,
  /// at z = 0.
  /// The bounds of the square is represented by `b`.
  ///
  /// The coordinates on the texture will implicitly be the "whole thing".
  /// i.e. [(0, 0), (1, 1)].
  pub fn square(min: Vec2<GLfloat>, max: Vec2<GLfloat>) -> [TextureVertex, ..6] {
    let vtx = |x, y, tx, ty| {
        TextureVertex {
          world_position:  Vec3::new(x, y, 0.0),
          texture_position: Vec2::new(tx, ty),
        }
      };

    [
      vtx(min.x, min.y, 0.0, 0.0),
      vtx(max.x, max.y, 1.0, 1.0),
      vtx(min.x, max.y, 0.0, 1.0),

      vtx(min.x, min.y, 0.0, 0.0),
      vtx(max.x, min.y, 1.0, 0.0),
      vtx(max.x, max.y, 1.0, 1.0),
    ]
  }
}

#[deriving(Show, Clone, Copy, PartialEq)]
/// A point in the world with corresponding texture and normal data.
///
/// The texture position is [0, 1].
pub struct WorldTextureVertex {
  /// The position of this vertex in the world.
  pub world_position:  Vec3<GLfloat>,

  /// The position of this vertex on a texture. The range of valid values
  /// in each dimension is [0, 1].
  pub texture_position: Vec2<GLfloat>,

  /// The normal vector for this vertex. We assume the length is 1.
  pub normal: Vec3<GLfloat>,
}
<commit_msg>fix broken tests<commit_after>//! A vertex with and without textures attached.
use gl::types::*;
use color::Color4;
use nalgebra::{Pnt2,Pnt3,Vec2,Vec3};
#[cfg(test)]
use std::mem;

#[deriving(Show, Clone, Copy, PartialEq)]
/// An untextured rendering vertex, with position and color.
pub struct ColoredVertex {
  /// The 3-d position of this vertex in world space.
  pub position: Pnt3<GLfloat>,
  /// The color to apply to this vertex, in lieu of a texture.
  pub color:    Color4<GLfloat>,
}

#[test]
fn check_vertex_size() {
  assert_eq!(mem::size_of::<ColoredVertex>(), 7*4);
  assert_eq!(mem::size_of::<TextureVertex>(), 5*4);
}

impl ColoredVertex {
  /// Generates two colored triangles, representing a square, at z=0.
  /// The bounds of the square is represented by `b`.
  pub fn square(min: Pnt2<GLfloat>, max: Pnt2<GLfloat>, color: Color4<GLfloat>) -> [ColoredVertex, ..6] {
    let vtx = |x, y| {
        ColoredVertex { position: Pnt3::new(x, y, 0.0), color: color }
      };

    [
      vtx(min.x, min.y), vtx(max.x, max.y), vtx(min.x, max.y),
      vtx(min.x, min.y), vtx(max.x, min.y), vtx(max.x, max.y),
    ]
  }
}

#[deriving(Show, Clone, Copy, PartialEq)]
/// A point in the world with corresponding texture data.
///
/// The texture position is [0, 1].
pub struct TextureVertex {
  /// The position of this vertex in the world.
  pub world_position:  Vec3<GLfloat>,

  /// The position of this vertex on a texture. The range of valid values
  /// in each dimension is [0, 1].
  pub texture_position: Vec2<GLfloat>,
}

impl TextureVertex {
  /// Generates two textured triangles, representing a square in 2D space,
  /// at z = 0.
  /// The bounds of the square is represented by `b`.
  ///
  /// The coordinates on the texture will implicitly be the "whole thing".
  /// i.e. [(0, 0), (1, 1)].
  pub fn square(min: Vec2<GLfloat>, max: Vec2<GLfloat>) -> [TextureVertex, ..6] {
    let vtx = |x, y, tx, ty| {
        TextureVertex {
          world_position:  Vec3::new(x, y, 0.0),
          texture_position: Vec2::new(tx, ty),
        }
      };

    [
      vtx(min.x, min.y, 0.0, 0.0),
      vtx(max.x, max.y, 1.0, 1.0),
      vtx(min.x, max.y, 0.0, 1.0),

      vtx(min.x, min.y, 0.0, 0.0),
      vtx(max.x, min.y, 1.0, 0.0),
      vtx(max.x, max.y, 1.0, 1.0),
    ]
  }
}

#[deriving(Show, Clone, Copy, PartialEq)]
/// A point in the world with corresponding texture and normal data.
///
/// The texture position is [0, 1].
pub struct WorldTextureVertex {
  /// The position of this vertex in the world.
  pub world_position:  Vec3<GLfloat>,

  /// The position of this vertex on a texture. The range of valid values
  /// in each dimension is [0, 1].
  pub texture_position: Vec2<GLfloat>,

  /// The normal vector for this vertex. We assume the length is 1.
  pub normal: Vec3<GLfloat>,
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::io::Write;
use std::iter::once;

use failure::{Error, err_msg};
use rand::random;
use serde_json;

use encode::EncodeAvro;
use schema::Schema;
use types::{ToAvro, Value};

pub struct Writer<'a, 'b> {
    schema: &'a Schema,
    writer: &'b mut Write,
    marker: Vec<u8>,
    has_header: bool,
}

impl<'a, 'b> Writer<'a, 'b> {
    pub fn new<W: Write>(schema: &'a Schema, writer: &'b mut W) -> Writer<'a, 'b> where W: Write {
        let mut marker = Vec::with_capacity(16);
        for _ in 0..16 {
            marker.push(random::<u8>());
        }

        Writer {
            schema: schema,
            writer: writer,
            marker: marker,
            has_header: false,
        }
    }

    pub fn header(&mut self) -> Result<usize, Error> {
        let mut n = self.append_raw(Value::Fixed(4, vec!['O' as u8, 'b' as u8, 'j' as u8, 1u8]))?;

        let schema_value: serde_json::Value = (*self.schema).clone().into();

        let mut metadata = HashMap::new();
        metadata.insert("avro.schema", Value::Bytes(serde_json::to_string(&schema_value)?.into_bytes()));
        metadata.insert("avro.codec", Value::Bytes("null".to_owned().into_bytes()));

        n += self.append_raw(metadata.avro())?;
        n += self.append_marker()?;

        Ok(n)
    }

    pub fn append<V>(&mut self, value: V) -> Result<usize, Error> where V: ToAvro {
        self.extend(once(value))
    }

    fn append_marker(&mut self) -> Result<usize, Error> {
        // using .writer.write directly to avoid mutable borrow of self
        // with ref borrowing of self.marker
        Ok(self.writer.write(&self.marker)?)
    }

    fn append_raw<V>(&mut self, value: V) -> Result<usize, Error> where V: EncodeAvro {
        Ok(self.writer.write(value.encode().as_ref())?)  // TODO: really?
    }

    pub fn extend<I, V>(&mut self, values: I) -> Result<usize, Error>
        where V: ToAvro, I: Iterator<Item=V>
    {
        let mut num_values = 0;
        let stream = values
            .map(|value| value.avro().with_schema(self.schema))
            .collect::<Option<Vec<_>>>()
            .ok_or_else(|| err_msg("value does not match given schema"))?
            .into_iter()
            .fold(Vec::new(), |mut acc, value| {
                num_values += 1;
                acc.extend(value.encode()); acc
            });

        if !self.has_header {
            self.has_header = true;
            self.header()?;
        }

        Ok(self.append_raw(num_values)? +
            self.append_raw(stream.len())? +
            self.writer.write(stream.as_ref())? +
            self.append_marker()?)
    }
}
<commit_msg>Consider header written only on success<commit_after>use std::collections::HashMap;
use std::io::Write;
use std::iter::once;

use failure::{Error, err_msg};
use rand::random;
use serde_json;

use encode::EncodeAvro;
use schema::Schema;
use types::{ToAvro, Value};

pub struct Writer<'a, 'b> {
    schema: &'a Schema,
    writer: &'b mut Write,
    marker: Vec<u8>,
    has_header: bool,
}

impl<'a, 'b> Writer<'a, 'b> {
    pub fn new<W: Write>(schema: &'a Schema, writer: &'b mut W) -> Writer<'a, 'b> where W: Write {
        let mut marker = Vec::with_capacity(16);
        for _ in 0..16 {
            marker.push(random::<u8>());
        }

        Writer {
            schema: schema,
            writer: writer,
            marker: marker,
            has_header: false,
        }
    }

    pub fn header(&mut self) -> Result<usize, Error> {
        let mut n = self.append_raw(Value::Fixed(4, vec!['O' as u8, 'b' as u8, 'j' as u8, 1u8]))?;

        let schema_value: serde_json::Value = (*self.schema).clone().into();

        let mut metadata = HashMap::new();
        metadata.insert("avro.schema", Value::Bytes(serde_json::to_string(&schema_value)?.into_bytes()));
        metadata.insert("avro.codec", Value::Bytes("null".to_owned().into_bytes()));

        n += self.append_raw(metadata.avro())?;
        n += self.append_marker()?;

        Ok(n)
    }

    pub fn append<V>(&mut self, value: V) -> Result<usize, Error> where V: ToAvro {
        self.extend(once(value))
    }

    fn append_marker(&mut self) -> Result<usize, Error> {
        // using .writer.write directly to avoid mutable borrow of self
        // with ref borrowing of self.marker
        Ok(self.writer.write(&self.marker)?)
    }

    fn append_raw<V>(&mut self, value: V) -> Result<usize, Error> where V: EncodeAvro {
        Ok(self.writer.write(value.encode().as_ref())?)  // TODO: really?
    }

    pub fn extend<I, V>(&mut self, values: I) -> Result<usize, Error>
        where V: ToAvro, I: Iterator<Item=V>
    {
        let mut num_values = 0;
        let stream = values
            .map(|value| value.avro().with_schema(self.schema))
            .collect::<Option<Vec<_>>>()
            .ok_or_else(|| err_msg("value does not match given schema"))?
            .into_iter()
            .fold(Vec::new(), |mut acc, value| {
                num_values += 1;
                acc.extend(value.encode()); acc
            });

        if !self.has_header {
            self.header()?;
            self.has_header = true;
        }

        Ok(self.append_raw(num_values)? +
            self.append_raw(stream.len())? +
            self.writer.write(stream.as_ref())? +
            self.append_marker()?)
    }
}
<|endoftext|>


<commit_before>use insta::assert_display_snapshot_matches;

#[cfg(test)]
fn transpile_from_file(test_filename: &str) -> String {
    let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
    let test_case_path: std::path::PathBuf = [&manifest_dir, "tests", "assets", test_filename]
        .iter()
        .collect();
    let test_case_file = std::fs::File::open(test_case_path).unwrap();
    let test_case_buf = std::io::BufReader::new(test_case_file);
    let lines = indentexlib::preprocessing::read_and_trim_lines(test_case_buf).unwrap();

    let default_options = indentexlib::TranspileOptions {
        flatten_output: false,
        prepend_do_not_edit_notice: true,
    };

    indentexlib::transpile(lines, &default_options)
}

#[test]
fn transpile_corner_cases() {
    assert_display_snapshot_matches!(
        "corner_cases",
        transpile_from_file("corner_cases.inden.tex")
    );
}

#[test]
fn transpile_envs() {
    assert_display_snapshot_matches!("envs", transpile_from_file("envs.inden.tex"));
}

#[test]
fn transpile_large_corpus() {
    assert_display_snapshot_matches!(
        "large_corpus",
        transpile_from_file("large_corpus.inden.tex")
    );
}

#[test]
fn transpile_list_like() {
    assert_display_snapshot_matches!("list_like", transpile_from_file("list_like.inden.tex"));
}

#[test]
fn transpile_mixed_tabs() {
    assert_display_snapshot_matches!(
        "mixed_tabs",
        transpile_from_file("mixed_tabs.inden.tex")
    );
}

#[test]
fn transpile_single_line_cmds() {
    assert_display_snapshot_matches!(
        "single_line_cmds",
        transpile_from_file("single_line_cmds.inden.tex")
    );
}
<commit_msg>Apply cargo fmt<commit_after>use insta::assert_display_snapshot_matches;

#[cfg(test)]
fn transpile_from_file(test_filename: &str) -> String {
    let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
    let test_case_path: std::path::PathBuf = [&manifest_dir, "tests", "assets", test_filename]
        .iter()
        .collect();
    let test_case_file = std::fs::File::open(test_case_path).unwrap();
    let test_case_buf = std::io::BufReader::new(test_case_file);
    let lines = indentexlib::preprocessing::read_and_trim_lines(test_case_buf).unwrap();

    let default_options = indentexlib::TranspileOptions {
        flatten_output: false,
        prepend_do_not_edit_notice: true,
    };

    indentexlib::transpile(lines, &default_options)
}

#[test]
fn transpile_corner_cases() {
    assert_display_snapshot_matches!(
        "corner_cases",
        transpile_from_file("corner_cases.inden.tex")
    );
}

#[test]
fn transpile_envs() {
    assert_display_snapshot_matches!("envs", transpile_from_file("envs.inden.tex"));
}

#[test]
fn transpile_large_corpus() {
    assert_display_snapshot_matches!(
        "large_corpus",
        transpile_from_file("large_corpus.inden.tex")
    );
}

#[test]
fn transpile_list_like() {
    assert_display_snapshot_matches!("list_like", transpile_from_file("list_like.inden.tex"));
}

#[test]
fn transpile_mixed_tabs() {
    assert_display_snapshot_matches!("mixed_tabs", transpile_from_file("mixed_tabs.inden.tex"));
}

#[test]
fn transpile_single_line_cmds() {
    assert_display_snapshot_matches!(
        "single_line_cmds",
        transpile_from_file("single_line_cmds.inden.tex")
    );
}
<|endoftext|>


<commit_before><commit_msg>Tweak malformed gid handling<commit_after><|endoftext|>


<commit_before>use std::collections::{HashMap, HashSet};
use intern::{intern, read, InternedString};
use grammar::parse_tree::{Alternative, Condition, ConditionOp, ExprSymbol, Grammar, GrammarItem,
                          MacroSymbol, NonterminalData, RepeatOp, RepeatSymbol,
                          Span, Symbol, TypeRef};
use normalize::{NormResult, NormError};
use normalize::norm_util::{self, Symbols};
use regex::Regex;
use std::mem;

#[cfg(test)]
mod test;

pub fn expand_macros(input: Grammar) -> NormResult<Grammar> {
    let Grammar { span, type_name, items } = input;

    let (macro_defs, mut items): (Vec<_>, Vec<_>) =
        items.into_iter().partition(|mi| mi.is_macro_def());

    let macro_defs: HashMap<InternedString, NonterminalData> =
        macro_defs.into_iter()
                  .map(|md| match md {
                      GrammarItem::Nonterminal(data) => (data.name, data),
                      _ => unreachable!()
                  })
                  .collect();

    let mut expander = MacroExpander::new(macro_defs);
    try!(expander.expand(&mut items));

    Ok(Grammar { span: span, type_name: type_name, items: items })
}

struct MacroExpander {
    macro_defs: HashMap<InternedString, NonterminalData>,
    expansion_set: HashSet<InternedString>,
    expansion_stack: Vec<Symbol>,
}

impl MacroExpander {
    fn new(macro_defs: HashMap<InternedString, NonterminalData>) -> MacroExpander {
        MacroExpander {
            macro_defs: macro_defs,
            expansion_stack: Vec::new(),
            expansion_set: HashSet::new()
        }
    }

    fn expand(&mut self, items: &mut Vec<GrammarItem>) -> NormResult<()> {
        let mut counter = 0;
        loop {
            println!("Next round: counter={} items.len()={}",
                     counter, items.len());

            // Find any macro uses in items added since last round and
            // replace them in place with the expanded version:
            for item in &mut items[counter..] {
                self.replace_item(item);
            }
            counter = items.len();

            // No more expansion to do.
            if self.expansion_stack.is_empty() {
                return Ok(());
            }

            // Drain expansion stack:
            while let Some(sym) = self.expansion_stack.pop() {
                println!("Drained {:?}", sym);
                match sym {
                    Symbol::Macro(msym) =>
                        items.push(try!(self.expand_macro_symbol(msym))),
                    Symbol::Expr(expr) =>
                        items.push(try!(self.expand_expr_symbol(expr))),
                    Symbol::Repeat(repeat) =>
                        items.push(try!(self.expand_repeat_symbol(*repeat))),
                    _ =>
                        assert!(false, "don't know how to expand `{:?}`", sym)
                }
            }
        }
    }

    fn replace_item(&mut self, item: &mut GrammarItem) {
        match *item {
            GrammarItem::TokenType(..) => { }
            GrammarItem::Nonterminal(ref mut data) => {
                // Should not encounter macro definitions here,
                // they've already been siphoned off.
                assert!(!data.is_macro_def());

                for alternative in &mut data.alternatives {
                    self.replace_symbols(&mut alternative.expr.symbols);
                }
            }
        }
    }

    fn replace_symbols(&mut self, symbols: &mut [Symbol]) {
        for symbol in symbols {
            self.replace_symbol(symbol);
        }
    }

    fn replace_symbol(&mut self, symbol: &mut Symbol) {
        match *symbol {
            Symbol::Macro(ref mut m) => {
                for sym in &mut m.args {
                    self.replace_symbol(sym);
                }
            }
            Symbol::Expr(ref mut expr) => {
                self.replace_symbols(&mut expr.symbols);
            }
            Symbol::Repeat(ref mut repeat) => {
                self.replace_symbol(&mut repeat.symbol);
            }
            Symbol::Terminal(_) |
            Symbol::Nonterminal(_) => {
                return;
            }
            Symbol::Choose(ref mut sym) |
            Symbol::Name(_, ref mut sym) => {
                self.replace_symbol(sym);
                return;
            }
        }

        // only symbols we intend to expand fallthrough to here

        let key = intern(&symbol.canonical_form());
        let to_expand = mem::replace(symbol, Symbol::Nonterminal(key));
        if self.expansion_set.insert(key) {
            self.expansion_stack.push(to_expand);
        }
    }

    ///////////////////////////////////////////////////////////////////////////
    // Macro expansion

    fn expand_macro_symbol(&mut self, msym: MacroSymbol) -> NormResult<GrammarItem> {
        let msym_name = intern(&msym.canonical_form());

        let mdef = match self.macro_defs.get(&msym.name) {
            Some(v) => v,
            None => return_err!(msym.span, "no macro definition found for `{}`", msym.name)
        };

        if mdef.args.len() != msym.args.len() {
            return_err!(msym.span, "expected {} arguments to `{}` but found {}",
                        mdef.args.len(), msym.name, msym.args.len());
        }

        let args: HashMap<InternedString, Symbol> =
            mdef.args.iter()
                     .cloned()
                     .zip(msym.args.into_iter())
                     .collect();

        let type_decl = mdef.type_decl.as_ref().map(|tr| self.macro_expand_type_ref(&args, tr));

        // due to the use of `try!`, it's a bit awkward to write this with an iterator
        let mut alternatives: Vec<Alternative> = vec![];

        for alternative in &mdef.alternatives {
            if !try!(self.evaluate_cond(&args, &alternative.condition)) {
                continue;
            }
            alternatives.push(Alternative {
                span: msym.span,
                expr: self.macro_expand_expr_symbol(&args, &alternative.expr),
                condition: None,
                action: alternative.action.clone(),
            });
        }

        Ok(GrammarItem::Nonterminal(NonterminalData {
            span: msym.span,
            name: msym_name,
            args: vec![],
            type_decl: type_decl,
            alternatives: alternatives
        }))
    }

    fn macro_expand_type_refs(&self,
                              args: &HashMap<InternedString, Symbol>,
                              type_refs: &[TypeRef])
                              -> Vec<TypeRef>
    {
        type_refs.iter().map(|tr| self.macro_expand_type_ref(args, tr)).collect()
    }

    fn macro_expand_type_ref(&self,
                             args: &HashMap<InternedString, Symbol>,
                             type_ref: &TypeRef)
                             -> TypeRef
    {
        match *type_ref {
            TypeRef::Tuple(ref trs) =>
                TypeRef::Tuple(self.macro_expand_type_refs(args, trs)),
            TypeRef::Nominal { ref path, ref types } =>
                TypeRef::Nominal { path: path.clone(),
                                   types: self.macro_expand_type_refs(args, types) },
            TypeRef::Lifetime(id) =>
                TypeRef::Lifetime(id),
            TypeRef::OfSymbol(ref sym) =>
                TypeRef::OfSymbol(sym.clone()),
            TypeRef::Id(id) => {
                match args.get(&id) {
                    Some(sym) => TypeRef::OfSymbol(sym.clone()),
                    None => TypeRef::Nominal { path: vec![id], types: vec![] },
                }
            }
        }
    }

    fn evaluate_cond(&self,
                     args: &HashMap<InternedString, Symbol>,
                     opt_cond: &Option<Condition>)
                     -> NormResult<bool>
    {
        if let Some(ref c) = *opt_cond {
            match args[&c.lhs] {
                Symbol::Terminal(lhs) => {
                    match c.op {
                        ConditionOp::Equals => Ok(lhs == c.rhs),
                        ConditionOp::NotEquals => Ok(lhs != c.rhs),
                        ConditionOp::Match => self.re_match(c.span, lhs, c.rhs),
                        ConditionOp::NotMatch => Ok(!try!(self.re_match(c.span, lhs, c.rhs))),
                    }
                }
                ref lhs => {
                    return_err!(
                        c.span,
                        "invalid condition LHS `{}`, expected a terminal, not `{}`", c.lhs, lhs);
                }
            }
        } else {
            Ok(true)
        }
    }

    fn re_match(&self, span: Span, lhs: InternedString, regex: InternedString) -> NormResult<bool> {
        read(|interner| {
            let re = match Regex::new(interner.data(regex)) {
                Ok(re) => re,
                Err(err) => return_err!(span, "invalid regular expression `{}`: {}", regex, err),
            };
            Ok(re.is_match(interner.data(lhs)))
        })
    }

    fn macro_expand_symbols(&self,
                            args: &HashMap<InternedString, Symbol>,
                            expr: &[Symbol])
                            -> Vec<Symbol>
    {
        expr.iter().map(|s| self.macro_expand_symbol(args, s)).collect()
    }

    fn macro_expand_expr_symbol(&self,
                                args: &HashMap<InternedString, Symbol>,
                                expr: &ExprSymbol)
                                -> ExprSymbol
    {
        ExprSymbol { span: expr.span, // FIXME derived span
                     symbols: self.macro_expand_symbols(args, &expr.symbols) }
    }

    fn macro_expand_symbol(&self,
                           args: &HashMap<InternedString, Symbol>,
                           symbol: &Symbol)
                           -> Symbol
    {
        match *symbol {
            Symbol::Expr(ref expr) =>
                Symbol::Expr(self.macro_expand_expr_symbol(args, expr)),
            Symbol::Terminal(id) =>
                Symbol::Terminal(id),
            Symbol::Nonterminal(id) =>
                match args.get(&id) {
                    Some(sym) => sym.clone(),
                    None => Symbol::Nonterminal(id),
                },
            Symbol::Macro(ref msym) =>
                Symbol::Macro(MacroSymbol {
                    name: msym.name,
                    args: self.macro_expand_symbols(args, &msym.args),
                    span: msym.span,
                }),
            Symbol::Repeat(ref r) =>
                Symbol::Repeat(Box::new(RepeatSymbol {
                    span: r.span,
                    op: r.op,
                    symbol: self.macro_expand_symbol(args, &r.symbol)
                })),
            Symbol::Choose(ref sym) =>
                Symbol::Choose(Box::new(self.macro_expand_symbol(args, sym))),
            Symbol::Name(id, ref sym) =>
                Symbol::Name(id, Box::new(self.macro_expand_symbol(args, sym))),
        }
    }

    ///////////////////////////////////////////////////////////////////////////
    // Expr expansion

    fn expand_expr_symbol(&mut self, expr: ExprSymbol) -> NormResult<GrammarItem> {
        let name = intern(&expr.canonical_form());

        let ty_ref = match norm_util::analyze_expr(&expr) {
            Symbols::Named(names) => {
                let (ex_id, ex_sym) = names[0];
                return_err!(
                    expr.span,
                    "named symbols like `~{}:{}` are only allowed at the top-level of a nonterminal",
                    ex_id, ex_sym)
            }
            Symbols::Anon(syms) => {
                maybe_tuple(
                    syms.into_iter()
                        .cloned()
                        .map(TypeRef::OfSymbol)
                        .collect())
            }
        };

        Ok(GrammarItem::Nonterminal(NonterminalData {
            span: expr.span,
            name: name,
            args: vec![],
            type_decl: Some(ty_ref),
            alternatives: vec![Alternative { span: expr.span,
                                             expr: expr,
                                             condition: None,
                                             action: Some(format!("(~~)")) }]
        }))
    }

    ///////////////////////////////////////////////////////////////////////////
    // Expr expansion

    fn expand_repeat_symbol(&mut self, repeat: RepeatSymbol) -> NormResult<GrammarItem> {
        let name = intern(&repeat.canonical_form());
        let v = intern("v");
        let e = intern("e");

        let base_symbol_ty = TypeRef::OfSymbol(repeat.symbol.clone());

        match repeat.op {
            RepeatOp::Star => {
                let path = vec![intern("std"), intern("vec"), intern("Vec")];
                let ty_ref = TypeRef::Nominal { path: path, types: vec![base_symbol_ty] };

                Ok(GrammarItem::Nonterminal(NonterminalData {
                    span: repeat.span,
                    name: name,
                    args: vec![],
                    type_decl: Some(ty_ref),
                    alternatives: vec![
                        // X* =
                        Alternative {
                            span: repeat.span,
                            expr: ExprSymbol {
                                span: repeat.span,
                                symbols: vec![],
                            },
                            condition: None,
                            action: Some(format!("vec![]"))
                        },

                        // X* = ~v:X+ ~e:X
                        Alternative {
                            span: repeat.span,
                            expr: ExprSymbol {
                                span: repeat.span,
                                symbols: vec![Symbol::Name(v, Box::new(Symbol::Nonterminal(name))),
                                              Symbol::Name(e, Box::new(repeat.symbol.clone()))]
                            },
                            condition: None,
                            action: Some(format!("{{ let mut v = v; v.push(e); v }}"))
                        }],
                }))
            }

            RepeatOp::Plus => {
                let path = vec![intern("std"), intern("vec"), intern("Vec")];
                let ty_ref = TypeRef::Nominal { path: path, types: vec![base_symbol_ty] };

                Ok(GrammarItem::Nonterminal(NonterminalData {
                    span: repeat.span,
                    name: name,
                    args: vec![],
                    type_decl: Some(ty_ref),
                    alternatives: vec![
                        // X+ = X
                        Alternative {
                            span: repeat.span,
                            expr: ExprSymbol {
                                span: repeat.span,
                                symbols: vec![repeat.symbol.clone()]
                            },
                            condition: None,
                            action: Some(format!("vec![~~]"))
                        },

                        // X+ = ~v:X+ ~e:X
                        Alternative {
                            span: repeat.span,
                            expr: ExprSymbol {
                                span: repeat.span,
                                symbols: vec![Symbol::Name(v, Box::new(Symbol::Nonterminal(name))),
                                              Symbol::Name(e, Box::new(repeat.symbol.clone()))]
                            },
                            condition: None,
                            action: Some(format!("{{ let mut v = v; v.push(e); v }}"))
                        }],
                }))
            }

            RepeatOp::Question => {
                let path = vec![intern("std"), intern("option"), intern("Option")];
                let ty_ref = TypeRef::Nominal { path: path, types: vec![base_symbol_ty] };

                Ok(GrammarItem::Nonterminal(NonterminalData {
                    span: repeat.span,
                    name: name,
                    args: vec![],
                    type_decl: Some(ty_ref),
                    alternatives: vec![
                        // X? = X => Some(~~)
                        Alternative { span: repeat.span,
                                      expr: ExprSymbol {
                                          span: repeat.span,
                                          symbols: vec![repeat.symbol.clone()]
                                      },
                                      condition: None,
                                      action: Some(format!("Some(~~)")) },

                        // X? = { => None; }
                        Alternative { span: repeat.span,
                                      expr: ExprSymbol {
                                          span: repeat.span,
                                          symbols: vec![]
                                      },
                                      condition: None,
                                      action: Some(format!("None")) }]
                }))
            }
        }
    }

}

fn maybe_tuple(v: Vec<TypeRef>) -> TypeRef {
    if v.len() == 1 {
        v.into_iter().next().unwrap()
    } else {
        TypeRef::Tuple(v)
    }
}
<commit_msg>remove debugging printfs<commit_after>use std::collections::{HashMap, HashSet};
use intern::{intern, read, InternedString};
use grammar::parse_tree::{Alternative, Condition, ConditionOp, ExprSymbol, Grammar, GrammarItem,
                          MacroSymbol, NonterminalData, RepeatOp, RepeatSymbol,
                          Span, Symbol, TypeRef};
use normalize::{NormResult, NormError};
use normalize::norm_util::{self, Symbols};
use regex::Regex;
use std::mem;

#[cfg(test)]
mod test;

pub fn expand_macros(input: Grammar) -> NormResult<Grammar> {
    let Grammar { span, type_name, items } = input;

    let (macro_defs, mut items): (Vec<_>, Vec<_>) =
        items.into_iter().partition(|mi| mi.is_macro_def());

    let macro_defs: HashMap<InternedString, NonterminalData> =
        macro_defs.into_iter()
                  .map(|md| match md {
                      GrammarItem::Nonterminal(data) => (data.name, data),
                      _ => unreachable!()
                  })
                  .collect();

    let mut expander = MacroExpander::new(macro_defs);
    try!(expander.expand(&mut items));

    Ok(Grammar { span: span, type_name: type_name, items: items })
}

struct MacroExpander {
    macro_defs: HashMap<InternedString, NonterminalData>,
    expansion_set: HashSet<InternedString>,
    expansion_stack: Vec<Symbol>,
}

impl MacroExpander {
    fn new(macro_defs: HashMap<InternedString, NonterminalData>) -> MacroExpander {
        MacroExpander {
            macro_defs: macro_defs,
            expansion_stack: Vec::new(),
            expansion_set: HashSet::new()
        }
    }

    fn expand(&mut self, items: &mut Vec<GrammarItem>) -> NormResult<()> {
        let mut counter = 0;
        loop {
            // Find any macro uses in items added since last round and
            // replace them in place with the expanded version:
            for item in &mut items[counter..] {
                self.replace_item(item);
            }
            counter = items.len();

            // No more expansion to do.
            if self.expansion_stack.is_empty() {
                return Ok(());
            }

            // Drain expansion stack:
            while let Some(sym) = self.expansion_stack.pop() {
                match sym {
                    Symbol::Macro(msym) =>
                        items.push(try!(self.expand_macro_symbol(msym))),
                    Symbol::Expr(expr) =>
                        items.push(try!(self.expand_expr_symbol(expr))),
                    Symbol::Repeat(repeat) =>
                        items.push(try!(self.expand_repeat_symbol(*repeat))),
                    _ =>
                        assert!(false, "don't know how to expand `{:?}`", sym)
                }
            }
        }
    }

    fn replace_item(&mut self, item: &mut GrammarItem) {
        match *item {
            GrammarItem::TokenType(..) => { }
            GrammarItem::Nonterminal(ref mut data) => {
                // Should not encounter macro definitions here,
                // they've already been siphoned off.
                assert!(!data.is_macro_def());

                for alternative in &mut data.alternatives {
                    self.replace_symbols(&mut alternative.expr.symbols);
                }
            }
        }
    }

    fn replace_symbols(&mut self, symbols: &mut [Symbol]) {
        for symbol in symbols {
            self.replace_symbol(symbol);
        }
    }

    fn replace_symbol(&mut self, symbol: &mut Symbol) {
        match *symbol {
            Symbol::Macro(ref mut m) => {
                for sym in &mut m.args {
                    self.replace_symbol(sym);
                }
            }
            Symbol::Expr(ref mut expr) => {
                self.replace_symbols(&mut expr.symbols);
            }
            Symbol::Repeat(ref mut repeat) => {
                self.replace_symbol(&mut repeat.symbol);
            }
            Symbol::Terminal(_) |
            Symbol::Nonterminal(_) => {
                return;
            }
            Symbol::Choose(ref mut sym) |
            Symbol::Name(_, ref mut sym) => {
                self.replace_symbol(sym);
                return;
            }
        }

        // only symbols we intend to expand fallthrough to here

        let key = intern(&symbol.canonical_form());
        let to_expand = mem::replace(symbol, Symbol::Nonterminal(key));
        if self.expansion_set.insert(key) {
            self.expansion_stack.push(to_expand);
        }
    }

    ///////////////////////////////////////////////////////////////////////////
    // Macro expansion

    fn expand_macro_symbol(&mut self, msym: MacroSymbol) -> NormResult<GrammarItem> {
        let msym_name = intern(&msym.canonical_form());

        let mdef = match self.macro_defs.get(&msym.name) {
            Some(v) => v,
            None => return_err!(msym.span, "no macro definition found for `{}`", msym.name)
        };

        if mdef.args.len() != msym.args.len() {
            return_err!(msym.span, "expected {} arguments to `{}` but found {}",
                        mdef.args.len(), msym.name, msym.args.len());
        }

        let args: HashMap<InternedString, Symbol> =
            mdef.args.iter()
                     .cloned()
                     .zip(msym.args.into_iter())
                     .collect();

        let type_decl = mdef.type_decl.as_ref().map(|tr| self.macro_expand_type_ref(&args, tr));

        // due to the use of `try!`, it's a bit awkward to write this with an iterator
        let mut alternatives: Vec<Alternative> = vec![];

        for alternative in &mdef.alternatives {
            if !try!(self.evaluate_cond(&args, &alternative.condition)) {
                continue;
            }
            alternatives.push(Alternative {
                span: msym.span,
                expr: self.macro_expand_expr_symbol(&args, &alternative.expr),
                condition: None,
                action: alternative.action.clone(),
            });
        }

        Ok(GrammarItem::Nonterminal(NonterminalData {
            span: msym.span,
            name: msym_name,
            args: vec![],
            type_decl: type_decl,
            alternatives: alternatives
        }))
    }

    fn macro_expand_type_refs(&self,
                              args: &HashMap<InternedString, Symbol>,
                              type_refs: &[TypeRef])
                              -> Vec<TypeRef>
    {
        type_refs.iter().map(|tr| self.macro_expand_type_ref(args, tr)).collect()
    }

    fn macro_expand_type_ref(&self,
                             args: &HashMap<InternedString, Symbol>,
                             type_ref: &TypeRef)
                             -> TypeRef
    {
        match *type_ref {
            TypeRef::Tuple(ref trs) =>
                TypeRef::Tuple(self.macro_expand_type_refs(args, trs)),
            TypeRef::Nominal { ref path, ref types } =>
                TypeRef::Nominal { path: path.clone(),
                                   types: self.macro_expand_type_refs(args, types) },
            TypeRef::Lifetime(id) =>
                TypeRef::Lifetime(id),
            TypeRef::OfSymbol(ref sym) =>
                TypeRef::OfSymbol(sym.clone()),
            TypeRef::Id(id) => {
                match args.get(&id) {
                    Some(sym) => TypeRef::OfSymbol(sym.clone()),
                    None => TypeRef::Nominal { path: vec![id], types: vec![] },
                }
            }
        }
    }

    fn evaluate_cond(&self,
                     args: &HashMap<InternedString, Symbol>,
                     opt_cond: &Option<Condition>)
                     -> NormResult<bool>
    {
        if let Some(ref c) = *opt_cond {
            match args[&c.lhs] {
                Symbol::Terminal(lhs) => {
                    match c.op {
                        ConditionOp::Equals => Ok(lhs == c.rhs),
                        ConditionOp::NotEquals => Ok(lhs != c.rhs),
                        ConditionOp::Match => self.re_match(c.span, lhs, c.rhs),
                        ConditionOp::NotMatch => Ok(!try!(self.re_match(c.span, lhs, c.rhs))),
                    }
                }
                ref lhs => {
                    return_err!(
                        c.span,
                        "invalid condition LHS `{}`, expected a terminal, not `{}`", c.lhs, lhs);
                }
            }
        } else {
            Ok(true)
        }
    }

    fn re_match(&self, span: Span, lhs: InternedString, regex: InternedString) -> NormResult<bool> {
        read(|interner| {
            let re = match Regex::new(interner.data(regex)) {
                Ok(re) => re,
                Err(err) => return_err!(span, "invalid regular expression `{}`: {}", regex, err),
            };
            Ok(re.is_match(interner.data(lhs)))
        })
    }

    fn macro_expand_symbols(&self,
                            args: &HashMap<InternedString, Symbol>,
                            expr: &[Symbol])
                            -> Vec<Symbol>
    {
        expr.iter().map(|s| self.macro_expand_symbol(args, s)).collect()
    }

    fn macro_expand_expr_symbol(&self,
                                args: &HashMap<InternedString, Symbol>,
                                expr: &ExprSymbol)
                                -> ExprSymbol
    {
        ExprSymbol { span: expr.span, // FIXME derived span
                     symbols: self.macro_expand_symbols(args, &expr.symbols) }
    }

    fn macro_expand_symbol(&self,
                           args: &HashMap<InternedString, Symbol>,
                           symbol: &Symbol)
                           -> Symbol
    {
        match *symbol {
            Symbol::Expr(ref expr) =>
                Symbol::Expr(self.macro_expand_expr_symbol(args, expr)),
            Symbol::Terminal(id) =>
                Symbol::Terminal(id),
            Symbol::Nonterminal(id) =>
                match args.get(&id) {
                    Some(sym) => sym.clone(),
                    None => Symbol::Nonterminal(id),
                },
            Symbol::Macro(ref msym) =>
                Symbol::Macro(MacroSymbol {
                    name: msym.name,
                    args: self.macro_expand_symbols(args, &msym.args),
                    span: msym.span,
                }),
            Symbol::Repeat(ref r) =>
                Symbol::Repeat(Box::new(RepeatSymbol {
                    span: r.span,
                    op: r.op,
                    symbol: self.macro_expand_symbol(args, &r.symbol)
                })),
            Symbol::Choose(ref sym) =>
                Symbol::Choose(Box::new(self.macro_expand_symbol(args, sym))),
            Symbol::Name(id, ref sym) =>
                Symbol::Name(id, Box::new(self.macro_expand_symbol(args, sym))),
        }
    }

    ///////////////////////////////////////////////////////////////////////////
    // Expr expansion

    fn expand_expr_symbol(&mut self, expr: ExprSymbol) -> NormResult<GrammarItem> {
        let name = intern(&expr.canonical_form());

        let ty_ref = match norm_util::analyze_expr(&expr) {
            Symbols::Named(names) => {
                let (ex_id, ex_sym) = names[0];
                return_err!(
                    expr.span,
                    "named symbols like `~{}:{}` are only allowed at the top-level of a nonterminal",
                    ex_id, ex_sym)
            }
            Symbols::Anon(syms) => {
                maybe_tuple(
                    syms.into_iter()
                        .cloned()
                        .map(TypeRef::OfSymbol)
                        .collect())
            }
        };

        Ok(GrammarItem::Nonterminal(NonterminalData {
            span: expr.span,
            name: name,
            args: vec![],
            type_decl: Some(ty_ref),
            alternatives: vec![Alternative { span: expr.span,
                                             expr: expr,
                                             condition: None,
                                             action: Some(format!("(~~)")) }]
        }))
    }

    ///////////////////////////////////////////////////////////////////////////
    // Expr expansion

    fn expand_repeat_symbol(&mut self, repeat: RepeatSymbol) -> NormResult<GrammarItem> {
        let name = intern(&repeat.canonical_form());
        let v = intern("v");
        let e = intern("e");

        let base_symbol_ty = TypeRef::OfSymbol(repeat.symbol.clone());

        match repeat.op {
            RepeatOp::Star => {
                let path = vec![intern("std"), intern("vec"), intern("Vec")];
                let ty_ref = TypeRef::Nominal { path: path, types: vec![base_symbol_ty] };

                Ok(GrammarItem::Nonterminal(NonterminalData {
                    span: repeat.span,
                    name: name,
                    args: vec![],
                    type_decl: Some(ty_ref),
                    alternatives: vec![
                        // X* =
                        Alternative {
                            span: repeat.span,
                            expr: ExprSymbol {
                                span: repeat.span,
                                symbols: vec![],
                            },
                            condition: None,
                            action: Some(format!("vec![]"))
                        },

                        // X* = ~v:X+ ~e:X
                        Alternative {
                            span: repeat.span,
                            expr: ExprSymbol {
                                span: repeat.span,
                                symbols: vec![Symbol::Name(v, Box::new(Symbol::Nonterminal(name))),
                                              Symbol::Name(e, Box::new(repeat.symbol.clone()))]
                            },
                            condition: None,
                            action: Some(format!("{{ let mut v = v; v.push(e); v }}"))
                        }],
                }))
            }

            RepeatOp::Plus => {
                let path = vec![intern("std"), intern("vec"), intern("Vec")];
                let ty_ref = TypeRef::Nominal { path: path, types: vec![base_symbol_ty] };

                Ok(GrammarItem::Nonterminal(NonterminalData {
                    span: repeat.span,
                    name: name,
                    args: vec![],
                    type_decl: Some(ty_ref),
                    alternatives: vec![
                        // X+ = X
                        Alternative {
                            span: repeat.span,
                            expr: ExprSymbol {
                                span: repeat.span,
                                symbols: vec![repeat.symbol.clone()]
                            },
                            condition: None,
                            action: Some(format!("vec![~~]"))
                        },

                        // X+ = ~v:X+ ~e:X
                        Alternative {
                            span: repeat.span,
                            expr: ExprSymbol {
                                span: repeat.span,
                                symbols: vec![Symbol::Name(v, Box::new(Symbol::Nonterminal(name))),
                                              Symbol::Name(e, Box::new(repeat.symbol.clone()))]
                            },
                            condition: None,
                            action: Some(format!("{{ let mut v = v; v.push(e); v }}"))
                        }],
                }))
            }

            RepeatOp::Question => {
                let path = vec![intern("std"), intern("option"), intern("Option")];
                let ty_ref = TypeRef::Nominal { path: path, types: vec![base_symbol_ty] };

                Ok(GrammarItem::Nonterminal(NonterminalData {
                    span: repeat.span,
                    name: name,
                    args: vec![],
                    type_decl: Some(ty_ref),
                    alternatives: vec![
                        // X? = X => Some(~~)
                        Alternative { span: repeat.span,
                                      expr: ExprSymbol {
                                          span: repeat.span,
                                          symbols: vec![repeat.symbol.clone()]
                                      },
                                      condition: None,
                                      action: Some(format!("Some(~~)")) },

                        // X? = { => None; }
                        Alternative { span: repeat.span,
                                      expr: ExprSymbol {
                                          span: repeat.span,
                                          symbols: vec![]
                                      },
                                      condition: None,
                                      action: Some(format!("None")) }]
                }))
            }
        }
    }

}

fn maybe_tuple(v: Vec<TypeRef>) -> TypeRef {
    if v.len() == 1 {
        v.into_iter().next().unwrap()
    } else {
        TypeRef::Tuple(v)
    }
}
<|endoftext|>


<commit_before>//! This module holds the machinery behind LabelledGeneric.
//!
//! A LabelledGeneric instance is pretty much exactly the same as a Generic instance, except
//! that the generic representation should contain information about field names.
//!
//! Having a separate trait for LabelledGenerics gives us the freedom to derive both
//! lablled and non-labelled generic type class instances for our types.
//!
//! Asides from the main LabelledGeneric trait, this module holds helper methods that allow
//! users to use LabelledGeneric without using universal function call syntax.
//!
//! In addition, this module holds macro-generated enums that map to letters in field names (identifiers).
//!
/// ```
/// # #[macro_use] extern crate frunk_core;
/// # use frunk_core::labelled::*;
/// # use frunk_core::hlist::*;
/// # fn main() {
/// let labelled = field![(n,a,m,e), "Lloyd"];
/// assert_eq!(labelled.name, "name")
/// # }
/// ```

use std::marker::PhantomData;
use hlist::*;
use std::fmt;

/// A trait that converts from a type to a labelled generic representation
///
/// For the most part, you should be using the derivation that is available through
/// frunk_derive to generate instances of this typeclass for your types.
///
/// I would highly recommend you check out `derivation_tests.rs` to see how to actually use
/// this trait in real life. Since frunk_derive depends on this trait, I can't actually
/// pull it in as a dependency here (otherwise the dependency would be circular) and show
/// how to use it in a proper doc test.
///
/// ```rust,ignore
/// #[derive(LabelledGeneric)]
/// struct NewUser<'a> {
///     first_name: &'a str,
///     last_name: &'a str,
///     age: usize,
/// }
///
/// #[derive(LabelledGeneric)]
/// struct SavedUser<'a> {
///     first_name: &'a str,
///     last_name: &'a str,
///     age: usize,
/// }
///
/// let n_user = NewUser {
///     first_name: "Joe",
///     last_name: "Blow",
///     age: 30,
/// };
///
/// let s_user = <SavedUser as LabelledGeneric>::sculpted_convert_from(n_user); // done
/// ```
pub trait LabelledGeneric {
    /// The labelled generic representation type
    type Repr;

    /// Go from something to Repr
    fn into(self) -> Self::Repr;

    /// Go from labelled Repr to something
    fn from(r: Self::Repr) -> Self;

    /// From one type to another using a type with a compatible labelled generic representation
    fn convert_from<A>(a: A) -> Self
        where A: LabelledGeneric<Repr = Self::Repr>,
              Self: Sized
    {
        let repr = <A as LabelledGeneric>::into(a);
        <Self as LabelledGeneric>::from(repr)
    }

    /// Converts from another type A into Self assuming that A and Self have labelled generic representations
    /// that can be sculpted into each other.
    ///
    /// Note that this method tosses away the "remainder" of the sculpted representation. In other
    /// words, anything that is not needed from A gets tossed out.
    fn sculpted_convert_from<A, Indices>(a: A) -> Self
        where A: LabelledGeneric,
              Self: Sized,
    // The labelled representation of A must be sculpt-able into the labelled representation of Self
              <A as LabelledGeneric>::Repr: Sculptor<<Self as LabelledGeneric>::Repr, Indices> {
        let a_gen = <A as LabelledGeneric>::into(a);
        // We toss away the remainder.
        let (self_gen, _): (<Self as LabelledGeneric>::Repr, _) = a_gen.sculpt();
        <Self as LabelledGeneric>::from(self_gen)
    }
}

/// Given a labelled generic Representation of an A, returns A
pub fn from_labelled_generic<A, Repr>(gen: Repr) -> A
    where A: LabelledGeneric<Repr = Repr>
{
    <A as LabelledGeneric>::from(gen)
}

/// Given an A, returns its labelled generic Representation
pub fn into_labelled_generic<A, Repr>(a: A) -> Repr
    where A: LabelledGeneric<Repr = Repr>
{
    <A as LabelledGeneric>::into(a)
}

/// Converts one type into another assuming they have the same labelled generic Representation
pub fn labelled_convert_from<A, B, Repr>(a: A) -> B
    where A: LabelledGeneric<Repr = Repr>,
          B: LabelledGeneric<Repr = Repr>
{
    <B as LabelledGeneric>::convert_from(a)
}

/// Converts from one type into another assuming that their labelled generic representations
/// can be sculpted into each other.
///
/// The "Indices" type parameter allows the compiler to figure out that the two representations
/// can indeed be morphed into each other.
pub fn sculpted_convert_from<A, B, Indices>(a: A) -> B
    where A: LabelledGeneric,
          B: LabelledGeneric,
// The labelled representation of A must be sculpt-able into the labelled representation of B
          <A as LabelledGeneric>::Repr: Sculptor<<B as LabelledGeneric>::Repr, Indices> {
    <B as LabelledGeneric>::sculpted_convert_from(a)
}

// Create a bunch of enums that can be used to represent characters on the type level
macro_rules! create_enums_for {
    ($($i: ident)*) => {
        $(
            #[allow(non_snake_case, non_camel_case_types)]
            #[derive(PartialEq, Debug, Eq, Clone, Copy, PartialOrd, Ord)]
            pub enum $i {}
        )*
    }
}

// Add more as needed.
create_enums_for! { a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z __ _1 _2 _3 _4 _5 _6 _7 _8 _9 _0 }

/// A Label contains a type-level Name, a runtime value, and
/// a reference to a `&'static str` name.
///
/// To construct one, use the `field!` macro.
///
/// ```
/// # #[macro_use] extern crate frunk_core;
/// # use frunk_core::labelled::*;
/// # use frunk_core::hlist::*;
/// # fn main() {
/// let labelled = field![(n,a,m,e), "joe"];
/// assert_eq!(labelled.name, "name")
/// # }
/// ```
#[derive(PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
pub struct Field<Name, Type> {
    name_type_holder: PhantomData<Name>,
    pub name: &'static str,
    pub value: Type,
}

impl <Name, Type> fmt::Debug for Field<Name, Type>
    where Type: fmt::Debug {

    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let v_debug = format!("{:?}", self.value);
        write!(f, "Field{{ name: {}, value: {} }}", self.name, v_debug)
    }
}

/// Returns a new Field for a given value and custom name.
///
/// If you don't want to provide a custom name and want to rely on the type you provide
/// to build a name, then please use the field! macro.
///
/// ```
/// # use frunk_core::labelled::*;
/// let l = field_with_name::<(n,a,m,e),_>("name", "joe");
/// assert_eq!(l.value, "joe");
/// assert_eq!(l.name, "name");
/// ```
pub fn field_with_name<Label, Value>(name: &'static str, value: Value) -> Field<Label, Value> {
    Field {
        name_type_holder: PhantomData,
        name: name,
        value: value,
    }
}

/// Trait for turning a Field HList into an un-labelled HList
pub trait IntoUnlabelled {
    type Output;

    /// Turns the current HList into an unlabelled on.
    ///
    /// Effectively extracts the values held inside the individual Field
    ///
    /// ```
    /// # #[macro_use] extern crate frunk_core;
    /// # use frunk_core::labelled::*;
    /// # use frunk_core::hlist::*;
    /// # fn main() {
    ///
    /// let labelled_hlist = hlist![
    ///     field!((n, a, m, e), "joe"),
    ///     field!((a, g, e), 3)
    /// ];
    ///
    /// let unlabelled = labelled_hlist.into_unlabelled();
    ///
    /// assert_eq!(unlabelled, hlist!["joe", 3])
    /// # }
    /// ```
    fn into_unlabelled(self) -> Self::Output;
}

/// Implementation for HNil
impl IntoUnlabelled for HNil {
    type Output = HNil;
    fn into_unlabelled(self) -> Self::Output {
        self
    }
}

/// Implementation when we have a non-empty HCons holding a label in its head
impl<Label, Value, Tail> IntoUnlabelled for HCons<Field<Label, Value>, Tail>
    where Tail: IntoUnlabelled
{
    type Output = HCons<Value, <Tail as IntoUnlabelled>::Output>;

    fn into_unlabelled(self) -> Self::Output {
        HCons {
            head: self.head.value,
            tail: self.tail.into_unlabelled(),
        }
    }
}

/// Used for creating a Field
///
/// There are 3 forms of this macro:
///
/// * Create an instance of the `Field` struct with a tuple name type
///   and any given value. The runtime-retrievable static name
///   field will be set to the the concatenation of the types passed in the
///   tuple type used as the first argument.
///
/// ```
/// # #[macro_use] extern crate frunk_core;
/// # use frunk_core::labelled::*;
/// # use frunk_core::hlist::*;
/// # fn main() {
/// let labelled = field![(n,a,m,e), "joe"];
/// assert_eq!(labelled.name, "name")
/// # }
/// ```
///
/// * Create an instance of the `Field` struct with a custom, non-tuple
///   name type and a value. The runtime-retrievable static name field
///   will be set to the stringified version of the type provided.
///
/// ```
/// # #[macro_use] extern crate frunk_core;
/// # use frunk_core::labelled::*;
/// # use frunk_core::hlist::*;
/// # fn main() {
/// enum first_name {}
/// let labelled = field![first_name, "Joe"];
/// assert_eq!(labelled.name, "first_name");
/// # }
/// ```
///
/// * Create an instance of the `Field` struct with any name type and value,
///   _and_ a custom name, passed as the last argument in the macro
///
/// ```
/// # #[macro_use] extern crate frunk_core;
/// # use frunk_core::labelled::*;
/// # use frunk_core::hlist::*;
/// # fn main() {
/// let labelled = field![(a,g,e), 30, "Age"];
/// assert_eq!(labelled.name, "Age");
/// # }
/// ```
#[macro_export]
macro_rules! field {
    // No name provided and type is a tuple
    (($($repeated: ty),*), $value: expr) => {
        field!( ($($repeated),*), $value, concat!( $(stringify!($repeated)),* ) )
    };
    // No name provided and type is a tuple, but with trailing commas
    (($($repeated: ty,)*), $value: expr) => {
        field!( ($($repeated),*), $value )
    };
    // We are provided any type, with no stable name
    ($name_type: ty, $value: expr) => {
        field!( $name_type, $value, stringify!($name_type) )
    };
    // We are provided any type, with a stable name
    ($name_type: ty, $value: expr, $name: expr) => {
        $crate::labelled::field_with_name::<$name_type,_>($name, $value)
    }
}

#[test]
fn test_label_new_building() {
    let l1 = field!((a, b, c), 3);
    assert_eq!(l1.value, 3);
    assert_eq!(l1.name, "abc");
    let l2 = field!((a, b, c,), 3);
    assert_eq!(l2.value, 3);
    assert_eq!(l2.name, "abc");

    // test named
    let l3 = field!((a,b,c), 3, "nope");
    assert_eq!(l3.value, 3);
    assert_eq!(l3.name, "nope");
    let l4 = field!((a,b,c,), 3, "nope");
    assert_eq!(l4.value, 3);
    assert_eq!(l4.name, "nope");
}

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

    #[test]
    fn test_field_construction() {
        let f1 = field!((a, g, e), 3);
        let f2 = field!((a, g, e), 3);
        assert_eq!(f1, f2)
    }

    #[test]
    fn test_anonymous_record_useage() {
        let record = hlist![
            field!((n, a, m, e), "Joe"),
            field!((a, g, e), 30)
        ];
        let (name, _): (Field<(n, a, m, e), _>, _) = record.pluck();
        assert_eq!(name.value, "Joe")
    }

    #[test]
    fn test_unlabelling() {
        let labelled_hlist = hlist![
            field!((n, a, m, e), "joe"),
            field!((a, g, e), 3)];
        let unlabelled = labelled_hlist.into_unlabelled();
        assert_eq!(unlabelled, hlist!["joe", 3])
    }

    #[test]
    fn test_name() {
        let labelled = field!((n, a, m, e), "joe");
        assert_eq!(labelled.name, "name")
    }
}
<commit_msg>Improve field docs by showing .value usage<commit_after>//! This module holds the machinery behind LabelledGeneric.
//!
//! A LabelledGeneric instance is pretty much exactly the same as a Generic instance, except
//! that the generic representation should contain information about field names.
//!
//! Having a separate trait for LabelledGenerics gives us the freedom to derive both
//! lablled and non-labelled generic type class instances for our types.
//!
//! Asides from the main LabelledGeneric trait, this module holds helper methods that allow
//! users to use LabelledGeneric without using universal function call syntax.
//!
//! In addition, this module holds macro-generated enums that map to letters in field names (identifiers).
//!
/// ```
/// # #[macro_use] extern crate frunk_core;
/// # use frunk_core::labelled::*;
/// # use frunk_core::hlist::*;
/// # fn main() {
/// let labelled = field![(n,a,m,e), "Lloyd"];
/// assert_eq!(labelled.name, "name");
/// assert_eq!(labelled.value, "Lloyd")
/// # }
/// ```

use std::marker::PhantomData;
use hlist::*;
use std::fmt;

/// A trait that converts from a type to a labelled generic representation
///
/// For the most part, you should be using the derivation that is available through
/// frunk_derive to generate instances of this typeclass for your types.
///
/// I would highly recommend you check out `derivation_tests.rs` to see how to actually use
/// this trait in real life. Since frunk_derive depends on this trait, I can't actually
/// pull it in as a dependency here (otherwise the dependency would be circular) and show
/// how to use it in a proper doc test.
///
/// ```rust,ignore
/// #[derive(LabelledGeneric)]
/// struct NewUser<'a> {
///     first_name: &'a str,
///     last_name: &'a str,
///     age: usize,
/// }
///
/// #[derive(LabelledGeneric)]
/// struct SavedUser<'a> {
///     first_name: &'a str,
///     last_name: &'a str,
///     age: usize,
/// }
///
/// let n_user = NewUser {
///     first_name: "Joe",
///     last_name: "Blow",
///     age: 30,
/// };
///
/// let s_user = <SavedUser as LabelledGeneric>::sculpted_convert_from(n_user); // done
/// ```
pub trait LabelledGeneric {
    /// The labelled generic representation type
    type Repr;

    /// Go from something to Repr
    fn into(self) -> Self::Repr;

    /// Go from labelled Repr to something
    fn from(r: Self::Repr) -> Self;

    /// From one type to another using a type with a compatible labelled generic representation
    fn convert_from<A>(a: A) -> Self
        where A: LabelledGeneric<Repr = Self::Repr>,
              Self: Sized
    {
        let repr = <A as LabelledGeneric>::into(a);
        <Self as LabelledGeneric>::from(repr)
    }

    /// Converts from another type A into Self assuming that A and Self have labelled generic representations
    /// that can be sculpted into each other.
    ///
    /// Note that this method tosses away the "remainder" of the sculpted representation. In other
    /// words, anything that is not needed from A gets tossed out.
    fn sculpted_convert_from<A, Indices>(a: A) -> Self
        where A: LabelledGeneric,
              Self: Sized,
    // The labelled representation of A must be sculpt-able into the labelled representation of Self
              <A as LabelledGeneric>::Repr: Sculptor<<Self as LabelledGeneric>::Repr, Indices> {
        let a_gen = <A as LabelledGeneric>::into(a);
        // We toss away the remainder.
        let (self_gen, _): (<Self as LabelledGeneric>::Repr, _) = a_gen.sculpt();
        <Self as LabelledGeneric>::from(self_gen)
    }
}

/// Given a labelled generic Representation of an A, returns A
pub fn from_labelled_generic<A, Repr>(gen: Repr) -> A
    where A: LabelledGeneric<Repr = Repr>
{
    <A as LabelledGeneric>::from(gen)
}

/// Given an A, returns its labelled generic Representation
pub fn into_labelled_generic<A, Repr>(a: A) -> Repr
    where A: LabelledGeneric<Repr = Repr>
{
    <A as LabelledGeneric>::into(a)
}

/// Converts one type into another assuming they have the same labelled generic Representation
pub fn labelled_convert_from<A, B, Repr>(a: A) -> B
    where A: LabelledGeneric<Repr = Repr>,
          B: LabelledGeneric<Repr = Repr>
{
    <B as LabelledGeneric>::convert_from(a)
}

/// Converts from one type into another assuming that their labelled generic representations
/// can be sculpted into each other.
///
/// The "Indices" type parameter allows the compiler to figure out that the two representations
/// can indeed be morphed into each other.
pub fn sculpted_convert_from<A, B, Indices>(a: A) -> B
    where A: LabelledGeneric,
          B: LabelledGeneric,
// The labelled representation of A must be sculpt-able into the labelled representation of B
          <A as LabelledGeneric>::Repr: Sculptor<<B as LabelledGeneric>::Repr, Indices> {
    <B as LabelledGeneric>::sculpted_convert_from(a)
}

// Create a bunch of enums that can be used to represent characters on the type level
macro_rules! create_enums_for {
    ($($i: ident)*) => {
        $(
            #[allow(non_snake_case, non_camel_case_types)]
            #[derive(PartialEq, Debug, Eq, Clone, Copy, PartialOrd, Ord)]
            pub enum $i {}
        )*
    }
}

// Add more as needed.
create_enums_for! { a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z __ _1 _2 _3 _4 _5 _6 _7 _8 _9 _0 }

/// A Label contains a type-level Name, a runtime value, and
/// a reference to a `&'static str` name.
///
/// To construct one, use the `field!` macro.
///
/// ```
/// # #[macro_use] extern crate frunk_core;
/// # use frunk_core::labelled::*;
/// # use frunk_core::hlist::*;
/// # fn main() {
/// let labelled = field![(n,a,m,e), "joe"];
/// assert_eq!(labelled.name, "name");
/// assert_eq!(labelled.value, "joe")
/// # }
/// ```
#[derive(PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
pub struct Field<Name, Type> {
    name_type_holder: PhantomData<Name>,
    pub name: &'static str,
    pub value: Type,
}

impl <Name, Type> fmt::Debug for Field<Name, Type>
    where Type: fmt::Debug {

    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let v_debug = format!("{:?}", self.value);
        write!(f, "Field{{ name: {}, value: {} }}", self.name, v_debug)
    }
}

/// Returns a new Field for a given value and custom name.
///
/// If you don't want to provide a custom name and want to rely on the type you provide
/// to build a name, then please use the field! macro.
///
/// ```
/// # use frunk_core::labelled::*;
/// let l = field_with_name::<(n,a,m,e),_>("name", "joe");
/// assert_eq!(l.value, "joe");
/// assert_eq!(l.name, "name");
/// ```
pub fn field_with_name<Label, Value>(name: &'static str, value: Value) -> Field<Label, Value> {
    Field {
        name_type_holder: PhantomData,
        name: name,
        value: value,
    }
}

/// Trait for turning a Field HList into an un-labelled HList
pub trait IntoUnlabelled {
    type Output;

    /// Turns the current HList into an unlabelled on.
    ///
    /// Effectively extracts the values held inside the individual Field
    ///
    /// ```
    /// # #[macro_use] extern crate frunk_core;
    /// # use frunk_core::labelled::*;
    /// # use frunk_core::hlist::*;
    /// # fn main() {
    ///
    /// let labelled_hlist = hlist![
    ///     field!((n, a, m, e), "joe"),
    ///     field!((a, g, e), 3)
    /// ];
    ///
    /// let unlabelled = labelled_hlist.into_unlabelled();
    ///
    /// assert_eq!(unlabelled, hlist!["joe", 3])
    /// # }
    /// ```
    fn into_unlabelled(self) -> Self::Output;
}

/// Implementation for HNil
impl IntoUnlabelled for HNil {
    type Output = HNil;
    fn into_unlabelled(self) -> Self::Output {
        self
    }
}

/// Implementation when we have a non-empty HCons holding a label in its head
impl<Label, Value, Tail> IntoUnlabelled for HCons<Field<Label, Value>, Tail>
    where Tail: IntoUnlabelled
{
    type Output = HCons<Value, <Tail as IntoUnlabelled>::Output>;

    fn into_unlabelled(self) -> Self::Output {
        HCons {
            head: self.head.value,
            tail: self.tail.into_unlabelled(),
        }
    }
}

/// Used for creating a Field
///
/// There are 3 forms of this macro:
///
/// * Create an instance of the `Field` struct with a tuple name type
///   and any given value. The runtime-retrievable static name
///   field will be set to the the concatenation of the types passed in the
///   tuple type used as the first argument.
///
/// ```
/// # #[macro_use] extern crate frunk_core;
/// # use frunk_core::labelled::*;
/// # use frunk_core::hlist::*;
/// # fn main() {
/// let labelled = field![(n,a,m,e), "joe"];
/// assert_eq!(labelled.name, "name");
/// assert_eq!(labelled.value, "joe")
/// # }
/// ```
///
/// * Create an instance of the `Field` struct with a custom, non-tuple
///   name type and a value. The runtime-retrievable static name field
///   will be set to the stringified version of the type provided.
///
/// ```
/// # #[macro_use] extern crate frunk_core;
/// # use frunk_core::labelled::*;
/// # use frunk_core::hlist::*;
/// # fn main() {
/// enum first_name {}
/// let labelled = field![first_name, "Joe"];
/// assert_eq!(labelled.name, "first_name");
/// assert_eq!(labelled.value, "Joe");
/// # }
/// ```
///
/// * Create an instance of the `Field` struct with any name type and value,
///   _and_ a custom name, passed as the last argument in the macro
///
/// ```
/// # #[macro_use] extern crate frunk_core;
/// # use frunk_core::labelled::*;
/// # use frunk_core::hlist::*;
/// # fn main() {
/// let labelled = field![(a,g,e), 30, "Age"];
/// assert_eq!(labelled.name, "Age");
/// assert_eq!(labelled.value, 30);
/// # }
/// ```
#[macro_export]
macro_rules! field {
    // No name provided and type is a tuple
    (($($repeated: ty),*), $value: expr) => {
        field!( ($($repeated),*), $value, concat!( $(stringify!($repeated)),* ) )
    };
    // No name provided and type is a tuple, but with trailing commas
    (($($repeated: ty,)*), $value: expr) => {
        field!( ($($repeated),*), $value )
    };
    // We are provided any type, with no stable name
    ($name_type: ty, $value: expr) => {
        field!( $name_type, $value, stringify!($name_type) )
    };
    // We are provided any type, with a stable name
    ($name_type: ty, $value: expr, $name: expr) => {
        $crate::labelled::field_with_name::<$name_type,_>($name, $value)
    }
}

#[test]
fn test_label_new_building() {
    let l1 = field!((a, b, c), 3);
    assert_eq!(l1.value, 3);
    assert_eq!(l1.name, "abc");
    let l2 = field!((a, b, c,), 3);
    assert_eq!(l2.value, 3);
    assert_eq!(l2.name, "abc");

    // test named
    let l3 = field!((a,b,c), 3, "nope");
    assert_eq!(l3.value, 3);
    assert_eq!(l3.name, "nope");
    let l4 = field!((a,b,c,), 3, "nope");
    assert_eq!(l4.value, 3);
    assert_eq!(l4.name, "nope");
}

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

    #[test]
    fn test_field_construction() {
        let f1 = field!((a, g, e), 3);
        let f2 = field!((a, g, e), 3);
        assert_eq!(f1, f2)
    }

    #[test]
    fn test_anonymous_record_useage() {
        let record = hlist![
            field!((n, a, m, e), "Joe"),
            field!((a, g, e), 30)
        ];
        let (name, _): (Field<(n, a, m, e), _>, _) = record.pluck();
        assert_eq!(name.value, "Joe")
    }

    #[test]
    fn test_unlabelling() {
        let labelled_hlist = hlist![
            field!((n, a, m, e), "joe"),
            field!((a, g, e), 3)];
        let unlabelled = labelled_hlist.into_unlabelled();
        assert_eq!(unlabelled, hlist!["joe", 3])
    }

    #[test]
    fn test_name() {
        let labelled = field!((n, a, m, e), "joe");
        assert_eq!(labelled.name, "name")
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(std_misc)]
extern crate rustfmt;

use std::collections::HashMap;
use std::fs;
use std::io::Read;
use rustfmt::*;

// For now, the only supported regression tests are idempotent tests - the input and
// output must match exactly.
// FIXME(#28) would be good to check for error messages and fail on them, or at least report.
#[test]
fn idempotent_tests() {
    println!("Idempotent tests:");

    // Get all files in the tests/idem directory
    let files = fs::read_dir("tests/idem").unwrap();
    let files2 = fs::read_dir("tests").unwrap();
    let files3 = fs::read_dir("src/bin").unwrap();
    // For each file, run rustfmt and collect the output

    let mut count = 0;
    let mut fails = 0;
    for entry in files.chain(files2).chain(files3) {
        let path = entry.unwrap().path();
        let file_name = path.to_str().unwrap();
        if !file_name.ends_with(".rs") {
            continue;
        }
        println!("Testing '{}'...", file_name);
        match idempotent_check(file_name.to_owned()) {
            Ok(()) => {},
            Err(m) => {
                print_mismatches(m);
                fails += 1;
            },
        }
        count += 1;
    }
    // And also dogfood rustfmt!
    println!("Testing 'src/lib.rs'...");
    match idempotent_check("src/lib.rs".to_owned()) {
        Ok(()) => {},
        Err(m) => {
            print_mismatches(m);
            fails += 1;
        },
    }
    count += 1;

    // Display results
    println!("Ran {} idempotent tests; {} failures.", count, fails);
    assert!(fails == 0, "{} idempotent tests failed", fails);
}

// Compare output to input.
fn print_mismatches(result: HashMap<String, String>) {
    for (file_name, fmt_text) in result {
        println!("Mismatch in {}.", file_name);
        println!("{}", fmt_text);
    }
}

// Ick, just needed to get a &'static to handle_result.
static HANDLE_RESULT: &'static Fn(HashMap<String, String>) = &handle_result;

pub fn idempotent_check(filename: String) -> Result<(), HashMap<String, String>> {
    use std::thread;
    use std::fs;
    use std::io::Read;
    let args = vec!["rustfmt".to_owned(), filename];
    thread::spawn(move || {
        run(args, WriteMode::Return(HANDLE_RESULT));
    }).join().map_err(|mut any|
        any.downcast_mut::<HashMap<String, String>>()
           .unwrap() // i know it is a hashmap
           .drain() // i only get a reference :(
           .collect() // so i need to turn it into an iter and then back
    )
}

// Compare output to input.
fn handle_result(result: HashMap<String, String>) {
    let mut failures = HashMap::new();

    for (file_name, fmt_text) in result {
        let mut f = fs::File::open(&file_name).unwrap();
        let mut text = String::new();
        // TODO: speedup by running through bytes iterator
        f.read_to_string(&mut text).unwrap();
        if fmt_text != text {
            failures.insert(file_name, fmt_text);
        }
    }
    if !failures.is_empty() {
        panic!(failures);
    }
}
<commit_msg>addressed comments<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern crate rustfmt;

use std::collections::HashMap;
use std::fs;
use std::io::Read;
use std::thread;
use rustfmt::*;

// For now, the only supported regression tests are idempotent tests - the input and
// output must match exactly.
// FIXME(#28) would be good to check for error messages and fail on them, or at least report.
#[test]
fn idempotent_tests() {
    println!("Idempotent tests:");

    // Get all files in the tests/idem directory
    let files = fs::read_dir("tests/idem").unwrap();
    let files = files.chain(fs::read_dir("tests").unwrap());
    let files = files.chain(fs::read_dir("src/bin").unwrap());
    // turn a DirEntry into a String that represents the relative path to the file
    let files = files.map(|e| e.unwrap().path().to_str().unwrap().to_owned());
    // hack because there's no `IntoIterator` impl for `[T; N]`
    let files = files.chain(Some("src/lib.rs".to_owned()).into_iter());

    // For each file, run rustfmt and collect the output
    let mut count = 0;
    let mut fails = 0;
    for file_name in files.filter(|f| f.ends_with(".rs")) {
        println!("Testing '{}'...", file_name);
        match idempotent_check(file_name) {
            Ok(()) => {},
            Err(m) => {
                print_mismatches(m);
                fails += 1;
            },
        }
        count += 1;
    }

    // Display results
    println!("Ran {} idempotent tests; {} failures.", count, fails);
    assert!(fails == 0, "{} idempotent tests failed", fails);
}

// Compare output to input.
fn print_mismatches(result: HashMap<String, String>) {
    for (file_name, fmt_text) in result {
        println!("Mismatch in {}.", file_name);
        println!("{}", fmt_text);
    }
}

// Ick, just needed to get a &'static to handle_result.
static HANDLE_RESULT: &'static Fn(HashMap<String, String>) = &handle_result;

pub fn idempotent_check(filename: String) -> Result<(), HashMap<String, String>> {
    let args = vec!["rustfmt".to_owned(), filename];
    // this thread is not used for concurrency, but rather to workaround the issue that the passed
    // function handle needs to have static lifetime. Instead of using a global RefCell, we use
    // panic to return a result in case of failure. This has the advantage of smoothing the road to
    // multithreaded rustfmt
    thread::spawn(move || {
        run(args, WriteMode::Return(HANDLE_RESULT));
    }).join().map_err(|any|
        // i know it is a hashmap
        *any.downcast().unwrap()
    )
}

// Compare output to input.
fn handle_result(result: HashMap<String, String>) {
    let mut failures = HashMap::new();

    for (file_name, fmt_text) in result {
        let mut f = fs::File::open(&file_name).unwrap();
        let mut text = String::new();
        // TODO: speedup by running through bytes iterator
        f.read_to_string(&mut text).unwrap();
        if fmt_text != text {
            failures.insert(file_name, fmt_text);
        }
    }
    if !failures.is_empty() {
        panic!(failures);
    }
}
<|endoftext|>


<commit_before>
extern crate ndarray;
extern crate ndarray_linalg as linalg;

use ndarray::prelude::*;
use linalg::Matrix;

fn assert_almost_eq(a: f64, b: f64) {
    let rel_dev = (a - b).abs() / (a.abs() + b.abs());
    if rel_dev > 1.0e-7 {
        panic!("a={:?}, b={:?} are not almost equal", a, b);
    }
}

#[test]
fn test_matrix_norm_3x4() {
    let a = arr2(&[[3.0, 1.0, 1.0, 1.0], [1.0, 3.0, 1.0, 1.0], [1.0, 1.0, 3.0, 1.0]]);
    assert_almost_eq(a.norm_1(), 5.0);
    assert_almost_eq(a.norm_i(), 6.0);
}

#[test]
fn test_matrix_norm_3x4_t() {
    let a = arr2(&[[3.0, 1.0, 1.0, 1.0], [1.0, 3.0, 1.0, 1.0], [1.0, 1.0, 3.0, 1.0]])
        .reversed_axes();
    assert_almost_eq(a.norm_1(), 6.0);
    assert_almost_eq(a.norm_i(), 5.0);
}

#[test]
fn test_matrix_norm_4x3() {
    let a = arr2(&[[3.0, 1.0, 1.0], [1.0, 3.0, 1.0], [1.0, 1.0, 3.0], [1.0, 1.0, 1.0]]);
    assert_almost_eq(a.norm_1(), 6.0);
    assert_almost_eq(a.norm_i(), 5.0);
}

#[test]
fn test_matrix_norm_4x3_t() {
    let a = arr2(&[[3.0, 1.0, 1.0], [1.0, 3.0, 1.0], [1.0, 1.0, 3.0], [1.0, 1.0, 1.0]])
        .reversed_axes();
    assert_almost_eq(a.norm_1(), 5.0);
    assert_almost_eq(a.norm_i(), 6.0);
}
<commit_msg>Add test for square matrix<commit_after>
extern crate ndarray;
extern crate ndarray_linalg as linalg;

use ndarray::prelude::*;
use linalg::Matrix;

fn assert_almost_eq(a: f64, b: f64) {
    let rel_dev = (a - b).abs() / (a.abs() + b.abs());
    if rel_dev > 1.0e-7 {
        panic!("a={:?}, b={:?} are not almost equal", a, b);
    }
}

#[test]
fn test_matrix_norm_square() {
    let a = Array::range(1., 10., 1.).into_shape((3, 3)).unwrap();
    assert_almost_eq(a.norm_1(), 18.0);
    assert_almost_eq(a.norm_i(), 24.0);
}

#[test]
fn test_matrix_norm_square_t() {
    let a = Array::range(1., 10., 1.).into_shape((3, 3)).unwrap().reversed_axes();
    assert_almost_eq(a.norm_1(), 24.0);
    assert_almost_eq(a.norm_i(), 18.0);
}

#[test]
fn test_matrix_norm_3x4() {
    let a = arr2(&[[3.0, 1.0, 1.0, 1.0], [1.0, 3.0, 1.0, 1.0], [1.0, 1.0, 3.0, 1.0]]);
    assert_almost_eq(a.norm_1(), 5.0);
    assert_almost_eq(a.norm_i(), 6.0);
}

#[test]
fn test_matrix_norm_3x4_t() {
    let a = arr2(&[[3.0, 1.0, 1.0, 1.0], [1.0, 3.0, 1.0, 1.0], [1.0, 1.0, 3.0, 1.0]])
        .reversed_axes();
    assert_almost_eq(a.norm_1(), 6.0);
    assert_almost_eq(a.norm_i(), 5.0);
}

#[test]
fn test_matrix_norm_4x3() {
    let a = arr2(&[[3.0, 1.0, 1.0], [1.0, 3.0, 1.0], [1.0, 1.0, 3.0], [1.0, 1.0, 1.0]]);
    assert_almost_eq(a.norm_1(), 6.0);
    assert_almost_eq(a.norm_i(), 5.0);
}

#[test]
fn test_matrix_norm_4x3_t() {
    let a = arr2(&[[3.0, 1.0, 1.0], [1.0, 3.0, 1.0], [1.0, 1.0, 3.0], [1.0, 1.0, 1.0]])
        .reversed_axes();
    assert_almost_eq(a.norm_1(), 5.0);
    assert_almost_eq(a.norm_i(), 6.0);
}
<|endoftext|>