<commit_before>use parking_lot::RwLock;
use std::rc::Rc;

use crate::core::view::ContentFilter;
use crate::core::view::FilterData;
use crate::core::view::FilterIo;
use crate::core::view::LayoutEnv;
use crate::core::Editor;

use crate::core::view::View;

use super::TextModeContext;
use crate::core::codec::text::u32_to_char;

use crate::core::codepointinfo::TextStyle;

pub struct WordWrapFilter {
    max_column: u64,
    column_count: u64,
    accum: Vec<FilterIo>,
    display_wrap: bool,

    blank_column_idx: u64,
    blank_accum_idx: u64,
    blank_offset: Option<u64>,
}

impl WordWrapFilter {
    pub fn new() -> Self {
        WordWrapFilter {
            max_column: 0,
            column_count: 0,
            accum: vec![],
            display_wrap: false,
            blank_column_idx: 0,
            blank_accum_idx: 0,
            blank_offset: None,
        }
    }
}

fn set_io_color(io: &FilterIo, color: (u8, u8, u8)) -> FilterIo {
    // flush new line
    let mut new_io = FilterIo {
        // general info
        metadata: false,
        style: TextStyle::new(),
        offset: io.offset,
        size: io.size,
        data: io.data.clone(),
    };

    {
        new_io.style.is_blinking = true;
        new_io.style.is_selected = true;
        new_io.style.bg_color = color;
    }

    new_io
}

fn set_first_column_color(io: &FilterIo) -> FilterIo {
    set_io_color(io, (0, 0, 255))
}

fn build_wrap_point_io(blank_offset: Option<u64>) -> FilterIo {
    FilterIo {
        // general info
        metadata: true,
        style: TextStyle::new(), // TODO(ceg): customize
        offset: blank_offset,
        size: 0,
        data: FilterData::TextInfo {
            real_cp: '\n' as u32,
            displayed_cp: '\\' as u32,
        },
    }
}

impl ContentFilter<'_> for WordWrapFilter {
    fn name(&self) -> &'static str {
        &"WordWrapFilter"
    }

    fn setup(
        &mut self,
        _editor: &Editor,
        env: &mut LayoutEnv,
        view: &Rc<RwLock<View>>,
        _parent_view: Option<&View<'static>>,
    ) {
        self.max_column = env.screen.width() as u64;
        self.column_count = 0;
        self.accum = Vec::new();

        // the screen is the final output
        // TODO(ceg): ask env.screen.cp_width(cp) -> nb_cells
        // or embed cp_width in FilterIo meta ?

        let v = view.read();
        if v.check_mode_ctx::<TextModeContext>("text-mode") {
            let tm = v.mode_ctx::<TextModeContext>("text-mode");
            self.display_wrap = tm.display_word_wrap;
        }
    }

    /*
            TODO(ceg): filters dependencies: check in view's filter_array that
            dep.index < cur_filter.index or (and WARN)
            we can push multiple times new instance of a filter :-)

            prerequisite:
            - tab expansion before: ('\t' -> ' ' should be done before)

        display offset in status

            NB: accum.len() != column count

    BEFORE:
            |  0    1    2    3    4    5    6    7    8 |  9  |        max_col = 10
            | xx |    |   ww    | xx | xx | xx | xx | xx | xx  |
                   ^      ^                                       ^
                   |      |_ wide char (2 cols)                   |
                   |                                              |
             blank_col_idx                                    colum_count = 10

            |  0    1    2    3    4    5    6    7    8 |  9  | max_col = 10
            | xx |    |   ww    | xx | xx | xx | xx | xx | xx  | <ADD>
                   ^      ^
                   |      |_ wide char (2 cols)                   ^
                   |                                              |
             blank_col_idx                                    colum_count

            column_count + width(<ADD>) >= max_col  : compute wrap

        */

    fn run(
        &mut self,
        _view: &View,
        env: &mut LayoutEnv,
        filter_in: &Vec<FilterIo>,
        filter_out: &mut Vec<FilterIo>,
    ) {
        if self.max_column <= 2
        /* TODO(ceg) screen.max_char_width() */
        {
            *filter_out = filter_in.clone();
            return;
        }

        let debug = false;

        for io in filter_in.iter() {
            if let FilterIo {
                data: FilterData::TextInfo { real_cp, .. },
                ..
            } = &*io
            {
                let c = u32_to_char(*real_cp);
                let width = env.screen.char_width(c) as u64;

                if debug {
                    dbg_println!("WRAP: -------------");
                    dbg_println!("WRAP: BEFORE : char '{}', width {}, column_count {} max_column {} blank_accum_idx {} blank_offset {:?} blank_column_idx {}",
                    c, width, self.column_count , self.max_column, self.blank_accum_idx , self.blank_offset , self.blank_column_idx);
                    dbg_println!(
                        "WRAP: self.column_count + width {}",
                        self.column_count + width
                    );
                    dbg_println!("WRAP: self.accum.len() {}", self.accum.len());

                    dbg_println!("WRAP:    >>>>>");
                }

                // width overflow  ?
                if self.column_count + width > self.max_column {
                    // not blank and accum > max column
                    if debug {
                        dbg_println!("WRAP: OVERFLOW self.column_count + width >= self.max_column");
                    }

                    // have previous blank ? => split accum after blank, insert '\' wrap point
                    if self.blank_offset.is_some()
                        && c != ' '
                        && c != '\n' // user option ?
                        && self.blank_column_idx > 0
                        && self.blank_column_idx + 1 != self.max_column
                    {
                        if debug {
                            dbg_println!("WRAP: line contains blank");

                            dbg_println!("WRAP: add WRAP POINT");
                        }
                        let mut fnl = build_wrap_point_io(self.blank_offset);

                        // TODO(ceg): add use option
                        if true || self.display_wrap {
                            fnl.style.color = (255, 255, 0); // yellow '\'
                        }

                        let mut new = self.accum.split_off(self.blank_accum_idx as usize + 1);
                        if debug {
                            dbg_println!(
                                "WRAP: add WRAP POINT  SPLIT LEFT  , accum.len() {}",
                                self.accum.len()
                            );
                            dbg_println!(
                                "WRAP: add WRAP POINT  SPLIT RIGHT , new.len()   {}",
                                new.len()
                            );
                        }

                        if self.display_wrap {
                            // replace front/back
                            if !new.is_empty() {
                                let nio = new.remove(0);
                                new.insert(0, set_io_color(&nio, (255, 0, 0)));
                            }
                        }

                        filter_out.append(&mut self.accum);
                        self.accum = new;

                        filter_out.push(fnl);
                        if debug {
                            dbg_println!("WRAP: *** FLUSH *** accum.len() {}", self.accum.len());
                        }

                        // "current word" size
                        self.column_count = self.max_column - self.blank_column_idx - 1;

                        self.blank_accum_idx = 0;
                        self.blank_offset = None;
                        self.blank_column_idx = 0;
                    } else {
                        if debug {
                            dbg_println!("WRAP: line contains NO leading blank");
                        }

                        if self.display_wrap {
                            // replace  last char
                            if !self.accum.is_empty() {
                                let nio = self.accum.pop().unwrap();
                                self.accum.push(set_io_color(&nio, (0, 255, 0)));
                            }
                        }

                        // FLUSH
                        if debug {
                            dbg_println!("WRAP: FLUSH accum");
                        }
                        filter_out.append(&mut self.accum);
                        self.column_count = 0;
                    }
                    /* FALLTHROUGH  */
                }

                // APPEND
                if self.display_wrap && self.column_count == 0 {
                    self.accum.push(set_first_column_color(&io));
                } else {
                    self.accum.push(io.clone());
                }

                // self.column_count + width < self.max_column
                self.column_count += width; // char fits

                // check
                // new line before max column
                if c == '\n' {
                    if debug {
                        dbg_println!("WRAP: *** LF found: restart @ offset {:?} ***", io.offset);
                    }

                    if self.display_wrap {
                        self.accum.pop();
                        self.accum.push(set_io_color(&io, (128, 255, 255)));
                    }
                    // restart
                    filter_out.append(&mut self.accum);
                    self.column_count = 0;
                    self.blank_accum_idx = 0;
                    self.blank_offset = None;
                    self.blank_column_idx = 0;
                }

                if c == ' ' {
                    // remember blank idx/offset to build wrap point
                    self.blank_column_idx = self.column_count - width;
                    self.blank_accum_idx = self.accum.len() as u64 - 1;
                    self.blank_offset = io.offset;

                    if debug {
                        dbg_println!(
                            "WRAP: ***found BLANK: @ offset {:?} col_idx {} ***",
                            io.offset,
                            self.blank_column_idx
                        );
                    }

                    if self.display_wrap && self.blank_column_idx > 0 {
                        self.accum.pop();
                        self.accum.push(set_io_color(&io, (255, 255, 0)));
                    }
                }
            } else {
                /*  unhandled input type */
                self.accum.push(io.clone());
                filter_out.append(&mut self.accum);
            }
        }
    }

    fn finish(&mut self, _view: &View, _env: &mut LayoutEnv) {
        dbg_println!("WRAP: FINISH");
        // TODO finish count ...
        // self.finish_count += 1;
    }
}
<commit_msg>word wrap: remove debug code<commit_after>use parking_lot::RwLock;
use std::rc::Rc;

use crate::core::view::ContentFilter;
use crate::core::view::FilterData;
use crate::core::view::FilterIo;
use crate::core::view::LayoutEnv;
use crate::core::Editor;

use crate::core::view::View;

use super::TextModeContext;
use crate::core::codec::text::u32_to_char;

use crate::core::codepointinfo::TextStyle;

pub struct WordWrapFilter {
    max_column: u64,
    column_count: u64,
    accum: Vec<FilterIo>,
    display_wrap: bool,

    blank_column_idx: u64,
    blank_accum_idx: u64,
    blank_offset: Option<u64>,
}

impl WordWrapFilter {
    pub fn new() -> Self {
        WordWrapFilter {
            max_column: 0,
            column_count: 0,
            accum: vec![],
            display_wrap: false,
            blank_column_idx: 0,
            blank_accum_idx: 0,
            blank_offset: None,
        }
    }
}

fn set_io_color(io: &FilterIo, color: (u8, u8, u8)) -> FilterIo {
    // flush new line
    let mut new_io = FilterIo {
        // general info
        metadata: false,
        style: TextStyle::new(),
        offset: io.offset,
        size: io.size,
        data: io.data.clone(),
    };

    {
        new_io.style.is_blinking = true;
        new_io.style.is_selected = true;
        new_io.style.bg_color = color;
    }

    new_io
}

fn set_first_column_color(io: &FilterIo) -> FilterIo {
    set_io_color(io, (0, 0, 255))
}

fn build_wrap_point_io(blank_offset: Option<u64>) -> FilterIo {
    FilterIo {
        // general info
        metadata: true,
        style: TextStyle::new(), // TODO(ceg): customize
        offset: blank_offset,
        size: 0,
        data: FilterData::TextInfo {
            real_cp: '\n' as u32,
            displayed_cp: '\\' as u32,
        },
    }
}

impl ContentFilter<'_> for WordWrapFilter {
    fn name(&self) -> &'static str {
        &"WordWrapFilter"
    }

    fn setup(
        &mut self,
        _editor: &Editor,
        env: &mut LayoutEnv,
        view: &Rc<RwLock<View>>,
        _parent_view: Option<&View<'static>>,
    ) {
        self.max_column = env.screen.width() as u64;
        self.column_count = 0;
        self.accum = Vec::new();

        // the screen is the final output
        // TODO(ceg): ask env.screen.cp_width(cp) -> nb_cells
        // or embed cp_width in FilterIo meta ?

        let v = view.read();
        if v.check_mode_ctx::<TextModeContext>("text-mode") {
            let tm = v.mode_ctx::<TextModeContext>("text-mode");
            self.display_wrap = tm.display_word_wrap;
        }
    }

    /*
            TODO(ceg): filters dependencies: check in view's filter_array that
            dep.index < cur_filter.index or (and WARN)
            we can push multiple times new instance of a filter :-)

            prerequisite:
            - tab expansion before: ('\t' -> ' ' should be done before)

        display offset in status

            NB: accum.len() != column count

    BEFORE:
            |  0    1    2    3    4    5    6    7    8 |  9  |        max_col = 10
            | xx |    |   ww    | xx | xx | xx | xx | xx | xx  |
                   ^      ^                                       ^
                   |      |_ wide char (2 cols)                   |
                   |                                              |
             blank_col_idx                                    colum_count = 10

            |  0    1    2    3    4    5    6    7    8 |  9  | max_col = 10
            | xx |    |   ww    | xx | xx | xx | xx | xx | xx  | <ADD>
                   ^      ^
                   |      |_ wide char (2 cols)                   ^
                   |                                              |
             blank_col_idx                                    colum_count

            column_count + width(<ADD>) >= max_col  : compute wrap

        */

    fn run(
        &mut self,
        _view: &View,
        env: &mut LayoutEnv,
        filter_in: &Vec<FilterIo>,
        filter_out: &mut Vec<FilterIo>,
    ) {
        if self.max_column <= 2
        /* TODO(ceg) screen.max_char_width() */
        {
            *filter_out = filter_in.clone();
            return;
        }

        for io in filter_in.iter() {
            if let FilterIo {
                data: FilterData::TextInfo { real_cp, .. },
                ..
            } = &*io
            {
                let c = u32_to_char(*real_cp);
                let width = env.screen.char_width(c) as u64;

                // width overflow  ?
                if self.column_count + width > self.max_column {
                    // not blank and accum > max column

                    // have previous blank ? => split accum after blank, insert '\' wrap point
                    if self.blank_offset.is_some()
                        && c != ' '
                        && c != '\n' // user option ?
                        && self.blank_column_idx > 0
                        && self.blank_column_idx + 1 != self.max_column
                    {
                        let mut fnl = build_wrap_point_io(self.blank_offset);

                        // TODO(ceg): add use option
                        if true || self.display_wrap {
                            fnl.style.color = (255, 255, 0); // yellow '\'
                        }

                        let mut new = self.accum.split_off(self.blank_accum_idx as usize + 1);

                        if self.display_wrap {
                            // replace front/back
                            if !new.is_empty() {
                                let nio = new.remove(0);
                                new.insert(0, set_io_color(&nio, (255, 0, 0)));
                            }
                        }

                        filter_out.append(&mut self.accum);
                        self.accum = new;

                        filter_out.push(fnl);

                        // "current word" size
                        self.column_count = self.max_column - self.blank_column_idx - 1;

                        self.blank_accum_idx = 0;
                        self.blank_offset = None;
                        self.blank_column_idx = 0;
                    } else {
                        if self.display_wrap {
                            // replace  last char
                            if !self.accum.is_empty() {
                                let nio = self.accum.pop().unwrap();
                                self.accum.push(set_io_color(&nio, (0, 255, 0)));
                            }
                        }

                        // FLUSH
                        filter_out.append(&mut self.accum);
                        self.column_count = 0;
                    }
                    /* FALLTHROUGH  */
                }

                // APPEND
                if self.display_wrap && self.column_count == 0 {
                    self.accum.push(set_first_column_color(&io));
                } else {
                    self.accum.push(io.clone());
                }

                // self.column_count + width < self.max_column
                self.column_count += width; // char fits

                // check
                // new line before max column
                if c == '\n' {
                    if self.display_wrap {
                        self.accum.pop();
                        self.accum.push(set_io_color(&io, (128, 255, 255)));
                    }
                    // restart
                    filter_out.append(&mut self.accum);
                    self.column_count = 0;
                    self.blank_accum_idx = 0;
                    self.blank_offset = None;
                    self.blank_column_idx = 0;
                }

                if c == ' ' {
                    // remember blank idx/offset to build wrap point
                    self.blank_column_idx = self.column_count - width;
                    self.blank_accum_idx = self.accum.len() as u64 - 1;
                    self.blank_offset = io.offset;

                    if self.display_wrap && self.blank_column_idx > 0 {
                        self.accum.pop();
                        self.accum.push(set_io_color(&io, (255, 255, 0)));
                    }
                }
            } else {
                /*  unhandled input type */
                self.accum.push(io.clone());
                filter_out.append(&mut self.accum);
            }
        }
    }

    fn finish(&mut self, _view: &View, _env: &mut LayoutEnv) {
        dbg_println!("WRAP: FINISH");
        // TODO finish count ...
        // self.finish_count += 1;
    }
}
<|endoftext|>


<commit_before>use std::convert::AsRef;

#[derive(PartialEq, Eq, Debug)]
pub struct RibonucleicAcid {
    nucleotides: String
}

impl RibonucleicAcid {
    pub fn new(nucleotides: &str) -> RibonucleicAcid {
        RibonucleicAcid { nucleotides: nucleotides.to_string() }
    }
}

#[derive(PartialEq, Eq, Debug)]
pub struct DeoxyribonucleicAcid {
    nucleotides: String
}

fn transcribe_dna_rna(c: char) -> char {
    match c {
        'C' => 'G',
        'G' => 'C',
        'A' => 'U',
        'T' => 'A',
        _   => c
    }
}

impl DeoxyribonucleicAcid {
    pub fn new(nucleotides: &str) -> DeoxyribonucleicAcid {
        DeoxyribonucleicAcid { nucleotides: nucleotides.to_string() }
    }

    pub fn to_rna(&self) -> RibonucleicAcid {
        let rna_nucleotides = self.nucleotides.chars().map(transcribe_dna_rna).collect();
        RibonucleicAcid { nucleotides: rna_nucleotides }
    }
}
<commit_msg>rna-transcript: remove unused AsRef use<commit_after>#[derive(PartialEq, Eq, Debug)]
pub struct RibonucleicAcid {
    nucleotides: String
}

impl RibonucleicAcid {
    pub fn new(nucleotides: &str) -> RibonucleicAcid {
        RibonucleicAcid { nucleotides: nucleotides.to_string() }
    }
}

#[derive(PartialEq, Eq, Debug)]
pub struct DeoxyribonucleicAcid {
    nucleotides: String
}

fn transcribe_dna_rna(c: char) -> char {
    match c {
        'C' => 'G',
        'G' => 'C',
        'A' => 'U',
        'T' => 'A',
        _   => c
    }
}

impl DeoxyribonucleicAcid {
    pub fn new(nucleotides: &str) -> DeoxyribonucleicAcid {
        DeoxyribonucleicAcid { nucleotides: nucleotides.to_string() }
    }

    pub fn to_rna(&self) -> RibonucleicAcid {
        let rna_nucleotides = self.nucleotides.chars().map(transcribe_dna_rna).collect();
        RibonucleicAcid { nucleotides: rna_nucleotides }
    }
}
<|endoftext|>


<commit_before>use autograd::{Function, FuncIntf, FuncDelegate, Variable, FIWrap};
use tensor::{TensorKindList, OptTensorKindList};
use ::*;

#[builder(pattern="owned")]
#[derive(Builder, Clone)]
pub struct ConvNdArgs {
    stride: Vec<u32>,
    padding: Vec<u32>,
    dilation: Vec<u32>,
    kernel_size: Vec<usize>,
    #[builder(default="false")]
    transposed: bool,
    output_padding: Vec<u32>,
    groups: u32,
    #[builder(default="false")]
    benchmark: bool,
    #[builder(default="false")]
    cudnn_enabled: bool,
}

#[builder(pattern="owned")]
#[derive(Builder)]
pub struct Conv1dFArgs {
    #[builder(default="vec![1]")]
    pub kernel_size: Vec<usize>,
    #[builder(default="vec![1]")]
    stride: Vec<u32>,
    #[builder(default="vec![0]")]
    padding: Vec<u32>,
    #[builder(default="vec![1]")]
    dilation: Vec<u32>,
    #[builder(default="1")]
    groups: u32,
}

impl Default for Conv1dFArgs {
    fn default() -> Self {
        Conv1dFArgsBuilder::default().build().unwrap()
    }
}

#[builder(pattern="owned")]
#[derive(Builder)]
pub struct Conv2dFArgs {
    #[builder(default="1")]
    pub in_features: usize,
    #[builder(default="1")]
    pub out_features: usize,
    #[builder(default="vec![1, 1]")]
    pub kernel_size: Vec<usize>,
    #[builder(default="vec![1, 1]")]
    pub stride: Vec<u32>,
    #[builder(default="vec![0, 0]")]
    pub padding: Vec<u32>,
    #[builder(default="vec![1, 1]")]
    pub dilation: Vec<u32>,
    #[builder(default="1")]
    pub groups: u32,
}

impl Default for Conv2dFArgs {
    fn default() -> Self {
        Conv2dFArgsBuilder::default().build().unwrap()
    }
}

#[builder(pattern="owned")]
#[derive(Builder)]
pub struct Conv3dFArgs {
    #[builder(default="vec![1, 1, 1]")]
    pub kernel_size: Vec<usize>,
    #[builder(default="vec![1, 1, 1]")]
    stride: Vec<u32>,
    #[builder(default="vec![0, 0, 0]")]
    padding: Vec<u32>,
    #[builder(default="vec![1, 1, 1]")]
    dilation: Vec<u32>,
    #[builder(default="1")]
    groups: u32,
}

impl Default for Conv3dFArgs {
    fn default() -> Self {
        Conv3dFArgsBuilder::default().build().unwrap()
    }
}

impl<'a> From<&'a mut Conv2dFArgs> for ConvNdArgs {
    fn from(input: &'a mut Conv2dFArgs) -> Self {
        ConvNdArgsBuilder::default()
            .stride(input.stride.clone())
            .padding(input.padding.clone())
            .dilation(input.dilation.clone())
            .groups(input.groups)
            .output_padding(vec![0, 0])
            .build()
            .unwrap()
    }
}

impl_func_args!(ConvNd, ConvNdArgs);

impl FuncIntf for ConvNd {
    fn forward(&mut self, mut input: &mut TensorKindList) -> TensorKindList {
        // run native code here
        unimplemented!()
    }
    fn backward(&mut self, mut input: &mut OptTensorKindList) -> OptTensorKindList {
        // run native code here
        unimplemented!()
    }
}
<commit_msg>copy kernel_size<commit_after>use autograd::{Function, FuncIntf, FuncDelegate, Variable, FIWrap};
use tensor::{TensorKindList, OptTensorKindList};
use ::*;

#[builder(pattern="owned")]
#[derive(Builder, Clone)]
pub struct ConvNdArgs {
    stride: Vec<u32>,
    padding: Vec<u32>,
    dilation: Vec<u32>,
    kernel_size: Vec<usize>,
    #[builder(default="false")]
    transposed: bool,
    output_padding: Vec<u32>,
    groups: u32,
    #[builder(default="false")]
    benchmark: bool,
    #[builder(default="false")]
    cudnn_enabled: bool,
}

#[builder(pattern="owned")]
#[derive(Builder)]
pub struct Conv1dFArgs {
    #[builder(default="vec![1]")]
    pub kernel_size: Vec<usize>,
    #[builder(default="vec![1]")]
    stride: Vec<u32>,
    #[builder(default="vec![0]")]
    padding: Vec<u32>,
    #[builder(default="vec![1]")]
    dilation: Vec<u32>,
    #[builder(default="1")]
    groups: u32,
}

impl Default for Conv1dFArgs {
    fn default() -> Self {
        Conv1dFArgsBuilder::default().build().unwrap()
    }
}

#[builder(pattern="owned")]
#[derive(Builder)]
pub struct Conv2dFArgs {
    #[builder(default="1")]
    pub in_features: usize,
    #[builder(default="1")]
    pub out_features: usize,
    #[builder(default="vec![1, 1]")]
    pub kernel_size: Vec<usize>,
    #[builder(default="vec![1, 1]")]
    pub stride: Vec<u32>,
    #[builder(default="vec![0, 0]")]
    pub padding: Vec<u32>,
    #[builder(default="vec![1, 1]")]
    pub dilation: Vec<u32>,
    #[builder(default="1")]
    pub groups: u32,
}

impl Default for Conv2dFArgs {
    fn default() -> Self {
        Conv2dFArgsBuilder::default().build().unwrap()
    }
}

#[builder(pattern="owned")]
#[derive(Builder)]
pub struct Conv3dFArgs {
    #[builder(default="vec![1, 1, 1]")]
    pub kernel_size: Vec<usize>,
    #[builder(default="vec![1, 1, 1]")]
    stride: Vec<u32>,
    #[builder(default="vec![0, 0, 0]")]
    padding: Vec<u32>,
    #[builder(default="vec![1, 1, 1]")]
    dilation: Vec<u32>,
    #[builder(default="1")]
    groups: u32,
}

impl Default for Conv3dFArgs {
    fn default() -> Self {
        Conv3dFArgsBuilder::default().build().unwrap()
    }
}

impl<'a> From<&'a mut Conv2dFArgs> for ConvNdArgs {
    fn from(input: &'a mut Conv2dFArgs) -> Self {
        ConvNdArgsBuilder::default()
            .kernel_size(input.kernel_size.clone())
            .stride(input.stride.clone())
            .padding(input.padding.clone())
            .dilation(input.dilation.clone())
            .groups(input.groups)
            .output_padding(vec![0, 0])
            .build()
            .unwrap()
    }
}

impl_func_args!(ConvNd, ConvNdArgs);

impl FuncIntf for ConvNd {
    fn forward(&mut self, mut input: &mut TensorKindList) -> TensorKindList {
        // run native code here
        unimplemented!()
    }
    fn backward(&mut self, mut input: &mut OptTensorKindList) -> OptTensorKindList {
        // run native code here
        unimplemented!()
    }
}
<|endoftext|>


<commit_before>//! The `parser` module contains a lot of useful functionality
//! to parse tokens from the lexer (and creating the parsetree
//! and the ast)
//! its an predictiv parser for a LL(1) grammar
//! for more info about the parser: look in the Compiler Dragonbook,
//! Chapter 4.4.4, "Nonrecursive Predictive Parsing"

use frontend::lexer::Token;
use frontend::ast;
use frontend::parsetree::{PNode};
use self::NonTerminalType::*;
use frontend::lexer::Token::*;

pub fn parse_tokens(tokens: Vec<Token>) -> ast::AST {
    let mut parser: Parser = Parser::new(tokens);
    parser.parsing();
    parser.ast
}

//==============================
// grammar

#[derive(Debug, Copy, Clone)]
pub enum NonTerminalType {
    S,
    Sf,
    Passage,
    Passagef,
    PassageContent,
    Formating,
    BoldFormatting,
    ItalicFormatting,
    MonoFormatting,
    MonoContent,
    Link,
    Makro,
    Makrof,
    ExpressionList,
    ExpressionListf,
    Expression,
    E,
    E2,
    T,
    T2,
    B,
    B2,
    F,
    F2,
    G,
    G2,
    H,
    DataType,
    AssignVariable,
}

//==============================
// parser

struct Parser {
    ast: ast::AST,
    stack: Vec<PNode>,
    tokens: Vec<Token>,
    lookahead: usize
}

impl Parser {
    pub fn new(tokens: Vec<Token>) -> Parser {
        Parser {
            ast: ast::AST::new(),
            stack: Vec::new(),
            tokens: tokens,
            lookahead: 0
        }
    }

    /// the predictive stack ll(1) parsing routine
    pub fn parsing(&mut self) {
        // push Start-Non-Terminal to the stack
        self.stack.push(PNode::new_non_terminal(S));

        while let Some(top) = self.stack.pop() {
            match top {
                PNode::NonTerminal(ref node) => {
                    self.apply_grammar(node.clone());
                }
                PNode::Terminal(_) => {
                    self.next_token();
                }
            }
        }
    }

    /// apply the ll(1) grammar
    /// the match-statement simulates the parsing-table behavior
    ///
    fn apply_grammar(&mut self, top: NonTerminalType) {
        if let Some(token) = self.tokens.get_mut(self.lookahead) {

            // the frst item in the tuple is the current state and
            // the snd is the current lookup-token
            let state_first: (NonTerminalType, &Token) = (top, token);

            let mut new_nodes = Vec::new();

            debug!("match {:?}", state_first);
            match state_first {
                (S, &TokPassageName(_)) => {
                    new_nodes.push(PNode::new_non_terminal(Passage));
                    new_nodes.push(PNode::new_non_terminal(Sf));
                },
                (Sf, &TokPassageName(_)) => {
                    new_nodes.push(PNode::new_non_terminal(S));
                },
                (Passage, &TokPassageName(ref name)) => {
                    new_nodes.push(PNode::new_terminal(TokPassageName(name.clone())));
                    new_nodes.push(PNode::new_non_terminal(PassageContent));

                    // ast
                    self.ast.add_passage(TokPassageName(name.clone()));
                },

                // PassageContent
                (PassageContent, &TokText(ref text)) => {
                    new_nodes.push(PNode::new_terminal(TokText(text.clone())));
                    new_nodes.push(PNode::new_non_terminal(PassageContent));

                    // ast
                    self.ast.add_child(TokText(text.clone()));
                },
                (PassageContent, &TokFormatBoldStart) |
                (PassageContent, &TokFormatItalicStart) |
                (PassageContent, &TokFormatMonoStart) => {
                    new_nodes.push(PNode::new_non_terminal(Formating));
                    new_nodes.push(PNode::new_non_terminal(PassageContent));
                },
                (PassageContent, &TokPassageLink(_, _)) => {
                    new_nodes.push(PNode::new_non_terminal(Link));
                    new_nodes.push(PNode::new_non_terminal(PassageContent));
                },
                (PassageContent, &TokNewLine) => {
                    new_nodes.push(PNode::new_terminal(TokNewLine));
                    new_nodes.push(PNode::new_non_terminal(PassageContent));

                    // ast
                    self.ast.add_child(TokNewLine);
                },
                (PassageContent, &TokSet) |
                (PassageContent, &TokIf) |
                (PassageContent, &TokVariable(_)) |
                (PassageContent, &TokMakroVar(_)) |
                (PassageContent, &TokMakroPassageName(_)) => {
                    new_nodes.push(PNode::new_non_terminal(Makro));
                    new_nodes.push(PNode::new_non_terminal(PassageContent));
                },
                (PassageContent, &TokEndIf) => {
                    // jump one ast-level higher
                    debug!("pop TokEndIf Passage;");

                    self.ast.up_child(TokEndIf, true);
                },
                (PassageContent, &TokFormatBoldEnd) => {
                    // jump one ast-level higher
                    self.ast.up();
                },
                (PassageContent, &TokFormatItalicEnd) => {
                    // jump one ast-level higher
                    self.ast.up();
                },
                (PassageContent, _) => {
                    // PassageContent -> ε
                },

                // Formating
                (Formating, &TokFormatBoldStart) => {
                    new_nodes.push(PNode::new_non_terminal(BoldFormatting));
                },
                (Formating, &TokFormatItalicStart) => {
                    new_nodes.push(PNode::new_non_terminal(ItalicFormatting));
                },
                (Formating, &TokFormatMonoStart) => {
                    new_nodes.push(PNode::new_non_terminal(MonoFormatting));
                },

                // BoldFormatting
                (BoldFormatting, &TokFormatBoldStart) => {
                    new_nodes.push(PNode::new_terminal(TokFormatBoldStart));
                    new_nodes.push(PNode::new_non_terminal(PassageContent));
                    new_nodes.push(PNode::new_terminal(TokFormatBoldEnd));

                    //ast
                    self.ast.child_down(TokFormatBoldStart);
                },

                // ItalicFormatting
                (ItalicFormatting, &TokFormatItalicStart) => {
                    new_nodes.push(PNode::new_terminal(TokFormatItalicStart));
                    new_nodes.push(PNode::new_non_terminal(PassageContent));
                    new_nodes.push(PNode::new_terminal(TokFormatItalicEnd));

                    //ast
                    self.ast.child_down(TokFormatItalicStart);
                },

                // MonoFormatting
                (MonoFormatting, &TokFormatMonoStart) => {
                    new_nodes.push(PNode::new_terminal(TokFormatMonoStart));
                    new_nodes.push(PNode::new_non_terminal(MonoContent));
                    new_nodes.push(PNode::new_terminal(TokFormatMonoEnd));

                    //ast
                    self.ast.child_down(TokFormatMonoStart);
                },

                // MonoContent
                (MonoContent, &TokText(ref text)) => {
                    new_nodes.push(PNode::new_terminal(TokText(text.clone())));
                    new_nodes.push(PNode::new_non_terminal(MonoContent));

                    // ast
                    self.ast.add_child(TokText(text.clone()));
                },
                (MonoContent, &TokNewLine) => {
                    new_nodes.push(PNode::new_terminal(TokNewLine));
                    new_nodes.push(PNode::new_non_terminal(MonoContent));
                },

                (MonoContent, &TokFormatMonoEnd) => {
                    // jump one ast-level higher
                    self.ast.up();
                },

                // Link
                (Link, &TokPassageLink(ref text, ref name)) => {
                    new_nodes.push(PNode::new_terminal(TokPassageLink(text.clone(), name.clone())));

                    // ast
                    self.ast.add_child(TokPassageLink(text.clone(), name.clone()));
                },

                // Makro
                (Makro, &TokSet) => {
                    new_nodes.push(PNode::new_terminal(TokSet));
                    new_nodes.push(PNode::new_non_terminal(ExpressionList));
                    new_nodes.push(PNode::new_terminal(TokMakroEnd));
                },
                (Makro, &TokIf) => {
                    new_nodes.push(PNode::new_terminal(TokIf));
                    new_nodes.push(PNode::new_non_terminal(ExpressionList));
                    new_nodes.push(PNode::new_terminal(TokMakroEnd));
                    new_nodes.push(PNode::new_non_terminal(PassageContent));
                    new_nodes.push(PNode::new_non_terminal(Makrof));

                    // ast
                    self.ast.two_childs_down(TokIf, TokPseudo);
                },
                // means <<$var>>
                (Makro, &TokMakroVar(ref name)) => {
                    new_nodes.push(PNode::new_terminal(TokMakroVar(name.clone())));
                    new_nodes.push(PNode::new_terminal(TokMakroEnd));

                    // ast
                    self.ast.add_child(TokMakroVar(name.clone()));
                },
                // means <<passagename>>
                (Makro, &TokMakroPassageName(ref name)) => {
                    new_nodes.push(PNode::new_terminal(TokMakroPassageName(name.clone())));
                    new_nodes.push(PNode::new_terminal(TokMakroEnd));

                    // ast
                    self.ast.add_child(&self.ast_path, TokMakroPassageName(name.clone()));
                },
                // Makrof
                (Makrof, &TokElse) => {
                    new_nodes.push(PNode::new_terminal(TokElse));
                    new_nodes.push(PNode::new_terminal(TokMakroEnd));
                    new_nodes.push(PNode::new_non_terminal(PassageContent));
                    new_nodes.push(PNode::new_terminal(TokEndIf));
                    new_nodes.push(PNode::new_terminal(TokMakroEnd));

                    // ast
                    self.ast.up_child_down(TokElse);
                },
                (Makrof, &TokEndIf) => {
                    new_nodes.push(PNode::new_terminal(TokEndIf));
                    new_nodes.push(PNode::new_terminal(TokMakroEnd));

                    // ast
                    self.ast.up_child(TokEndIf, false);
                }

                // ExpressionList
                (ExpressionList, &TokVariable(_)) |
                (ExpressionList, &TokInt(_)) |
                (ExpressionList, &TokString(_)) |
                (ExpressionList, &TokBoolean(_)) |
                (ExpressionList, &TokAssign(_, _)) => {
                    new_nodes.push(PNode::new_non_terminal(Expression));
                    new_nodes.push(PNode::new_non_terminal(ExpressionListf));
                },

                // ExpressionListf
                (ExpressionListf, &TokMakroEnd) => {
                    debug!("pop TokMakroEnd");
                    self.ast.up();

                },
                (ExpressionListf, _) => {
                    // ExpressionListf -> ε
                },

                // Expression
                (Expression, &TokVariable(_)) |
                (Expression, &TokInt(_)) |
                (Expression, &TokString(_)) |
                (Expression, &TokBoolean(_)) => {
                    new_nodes.push(PNode::new_non_terminal(E));
                },
                (Expression, &TokAssign(_, _)) => {
                    new_nodes.push(PNode::new_non_terminal(AssignVariable));
                },

                // E
                (E, &TokVariable(_)) |
                (E, &TokInt(_)) |
                (E, &TokString(_)) |
                (E, &TokBoolean(_)) => {
                    new_nodes.push(PNode::new_non_terminal(T));
                    new_nodes.push(PNode::new_non_terminal(E2));
                },

                // E2
                (E2, _) => {
                    // E2 -> ε
                },

                // T
                (T, &TokVariable(_)) |
                (T, &TokInt(_)) |
                (T, &TokString(_)) |
                (T, &TokBoolean(_)) => {
                    new_nodes.push(PNode::new_non_terminal(B));
                    new_nodes.push(PNode::new_non_terminal(T2));
                },

                // T2
                (T2, _) => {
                    // T2 -> ε
                },

                // B
                (B, &TokVariable(_)) |
                (B, &TokInt(_)) |
                (B, &TokString(_)) |
                (B, &TokBoolean(_)) => {
                    new_nodes.push(PNode::new_non_terminal(F));
                    new_nodes.push(PNode::new_non_terminal(B2));
                },

                // B2
                (B2, &TokCompOp(ref op)) => {
                    new_nodes.push(PNode::new_terminal(TokCompOp(op.clone())));
                    new_nodes.push(PNode::new_non_terminal(F));
                    new_nodes.push(PNode::new_non_terminal(B2));

                    // ast
                    self.ast.add_child(TokCompOp(op.clone()));
                },
                (B2, _) => {
                    // B2 -> ε
                },

                // F
                (F, &TokVariable(_)) |
                (F, &TokInt(_)) |
                (F, &TokString(_)) |
                (F, &TokBoolean(_)) => {
                    new_nodes.push(PNode::new_non_terminal(G));
                    new_nodes.push(PNode::new_non_terminal(F2));
                },

                // F2
                (F2, _) => {
                    // F2 -> ε
                },

                // G
                (G, &TokVariable(_)) |
                (G, &TokInt(_)) |
                (G, &TokString(_)) |
                (G, &TokBoolean(_)) => {
                    new_nodes.push(PNode::new_non_terminal(H));
                    new_nodes.push(PNode::new_non_terminal(G2));
                },

                // G2
                (G2, _) => {
                    // G2 -> ε
                },

                // H
                (H, &TokInt(_)) |
                (H, &TokString(_)) |
                (H, &TokBoolean(_)) => {
                    new_nodes.push(PNode::new_non_terminal(DataType));
                },
                (H, &TokVariable(ref name)) => {
                    new_nodes.push(PNode::new_terminal(TokVariable(name.clone())));

                    // ast
                    self.ast.add_child(TokVariable(name.clone()));
                },

                // AssignVariable
                (AssignVariable, &TokAssign(ref name, ref assign)) => {
                    new_nodes.push(PNode::new_terminal(TokAssign(name.clone(), assign.clone())));
                    new_nodes.push(PNode::new_non_terminal(E));

                    //ast
                    self.ast.child_down(TokAssign(name.clone(), assign.clone()));
                },

                // DataType
                (DataType, &TokInt(ref value)) => {
                    new_nodes.push(PNode::new_terminal(TokInt(value.clone())));

                    // ast
                    self.ast.add_child(TokInt(value.clone()));
                },
                (DataType, &TokString(ref value)) => {
                    new_nodes.push(PNode::new_terminal(TokString(value.clone())));

                    // ast
                    self.ast.add_child(TokString(value.clone()));
                },
                (DataType, &TokBoolean(ref value)) => {
                    new_nodes.push(PNode::new_terminal(TokBoolean(value.clone())));

                    // ast
                    self.ast.add_child(TokBoolean(value.clone()));
                }

                _ => {
                    panic!("not supported grammar: {:?}", state_first);
                }
            }

            // adds the new nodes to the stack (in reversed order)
            while let Some(child) = new_nodes.pop() {
                self.stack.push(child);
            }

        } else {
            // no token left

            // Sf, PassageContent, Linkf,

            match top {
                Sf | PassageContent => {
                    // ... -> ε
                },
                _ => {
                    panic!("Nonterminal '{:?}' is not an allowed end.", top);
                }
            }
        }
    }

    /// sets the lookahead to the next token
    fn next_token(&mut self) {
        self.lookahead = self.lookahead + 1;
    }
}
<commit_msg>update from the master<commit_after>//! The `parser` module contains a lot of useful functionality
//! to parse tokens from the lexer (and creating the parsetree
//! and the ast)
//! its an predictiv parser for a LL(1) grammar
//! for more info about the parser: look in the Compiler Dragonbook,
//! Chapter 4.4.4, "Nonrecursive Predictive Parsing"

use frontend::lexer::Token;
use frontend::ast;
use frontend::parsetree::{PNode};
use self::NonTerminalType::*;
use frontend::lexer::Token::*;

pub fn parse_tokens(tokens: Vec<Token>) -> ast::AST {
    let mut parser: Parser = Parser::new(tokens);
    parser.parsing();
    parser.ast
}

//==============================
// grammar

#[derive(Debug, Copy, Clone)]
pub enum NonTerminalType {
    S,
    Sf,
    Passage,
    Passagef,
    PassageContent,
    Formating,
    BoldFormatting,
    ItalicFormatting,
    MonoFormatting,
    MonoContent,
    Link,
    Makro,
    Makrof,
    ExpressionList,
    ExpressionListf,
    Expression,
    E,
    E2,
    T,
    T2,
    B,
    B2,
    F,
    F2,
    G,
    G2,
    H,
    DataType,
    AssignVariable,
}

//==============================
// parser

struct Parser {
    ast: ast::AST,
    stack: Vec<PNode>,
    tokens: Vec<Token>,
    lookahead: usize
}

impl Parser {
    pub fn new(tokens: Vec<Token>) -> Parser {
        Parser {
            ast: ast::AST::new(),
            stack: Vec::new(),
            tokens: tokens,
            lookahead: 0
        }
    }

    /// the predictive stack ll(1) parsing routine
    pub fn parsing(&mut self) {
        // push Start-Non-Terminal to the stack
        self.stack.push(PNode::new_non_terminal(S));

        while let Some(top) = self.stack.pop() {
            match top {
                PNode::NonTerminal(ref node) => {
                    self.apply_grammar(node.clone());
                }
                PNode::Terminal(_) => {
                    self.next_token();
                }
            }
        }
    }

    /// apply the ll(1) grammar
    /// the match-statement simulates the parsing-table behavior
    ///
    fn apply_grammar(&mut self, top: NonTerminalType) {
        if let Some(token) = self.tokens.get_mut(self.lookahead) {

            // the frst item in the tuple is the current state and
            // the snd is the current lookup-token
            let state_first: (NonTerminalType, &Token) = (top, token);

            let mut new_nodes = Vec::new();

            debug!("match {:?}", state_first);
            match state_first {
                (S, &TokPassageName(_)) => {
                    new_nodes.push(PNode::new_non_terminal(Passage));
                    new_nodes.push(PNode::new_non_terminal(Sf));
                },
                (Sf, &TokPassageName(_)) => {
                    new_nodes.push(PNode::new_non_terminal(S));
                },
                (Passage, &TokPassageName(ref name)) => {
                    new_nodes.push(PNode::new_terminal(TokPassageName(name.clone())));
                    new_nodes.push(PNode::new_non_terminal(PassageContent));

                    // ast
                    self.ast.add_passage(TokPassageName(name.clone()));
                },

                // PassageContent
                (PassageContent, &TokText(ref text)) => {
                    new_nodes.push(PNode::new_terminal(TokText(text.clone())));
                    new_nodes.push(PNode::new_non_terminal(PassageContent));

                    // ast
                    self.ast.add_child(TokText(text.clone()));
                },
                (PassageContent, &TokFormatBoldStart) |
                (PassageContent, &TokFormatItalicStart) |
                (PassageContent, &TokFormatMonoStart) => {
                    new_nodes.push(PNode::new_non_terminal(Formating));
                    new_nodes.push(PNode::new_non_terminal(PassageContent));
                },
                (PassageContent, &TokPassageLink(_, _)) => {
                    new_nodes.push(PNode::new_non_terminal(Link));
                    new_nodes.push(PNode::new_non_terminal(PassageContent));
                },
                (PassageContent, &TokNewLine) => {
                    new_nodes.push(PNode::new_terminal(TokNewLine));
                    new_nodes.push(PNode::new_non_terminal(PassageContent));

                    // ast
                    self.ast.add_child(TokNewLine);
                },
                (PassageContent, &TokSet) |
                (PassageContent, &TokIf) |
                (PassageContent, &TokVariable(_)) |
                (PassageContent, &TokMakroVar(_)) |
                (PassageContent, &TokMakroPassageName(_)) => {
                    new_nodes.push(PNode::new_non_terminal(Makro));
                    new_nodes.push(PNode::new_non_terminal(PassageContent));
                },
                (PassageContent, &TokEndIf) => {
                    // jump one ast-level higher
                    debug!("pop TokEndIf Passage;");

                    self.ast.up_child(TokEndIf, true);
                },
                (PassageContent, &TokFormatBoldEnd) => {
                    // jump one ast-level higher
                    self.ast.up();
                },
                (PassageContent, &TokFormatItalicEnd) => {
                    // jump one ast-level higher
                    self.ast.up();
                },
                (PassageContent, _) => {
                    // PassageContent -> ε
                },

                // Formating
                (Formating, &TokFormatBoldStart) => {
                    new_nodes.push(PNode::new_non_terminal(BoldFormatting));
                },
                (Formating, &TokFormatItalicStart) => {
                    new_nodes.push(PNode::new_non_terminal(ItalicFormatting));
                },
                (Formating, &TokFormatMonoStart) => {
                    new_nodes.push(PNode::new_non_terminal(MonoFormatting));
                },

                // BoldFormatting
                (BoldFormatting, &TokFormatBoldStart) => {
                    new_nodes.push(PNode::new_terminal(TokFormatBoldStart));
                    new_nodes.push(PNode::new_non_terminal(PassageContent));
                    new_nodes.push(PNode::new_terminal(TokFormatBoldEnd));

                    //ast
                    self.ast.child_down(TokFormatBoldStart);
                },

                // ItalicFormatting
                (ItalicFormatting, &TokFormatItalicStart) => {
                    new_nodes.push(PNode::new_terminal(TokFormatItalicStart));
                    new_nodes.push(PNode::new_non_terminal(PassageContent));
                    new_nodes.push(PNode::new_terminal(TokFormatItalicEnd));

                    //ast
                    self.ast.child_down(TokFormatItalicStart);
                },

                // MonoFormatting
                (MonoFormatting, &TokFormatMonoStart) => {
                    new_nodes.push(PNode::new_terminal(TokFormatMonoStart));
                    new_nodes.push(PNode::new_non_terminal(MonoContent));
                    new_nodes.push(PNode::new_terminal(TokFormatMonoEnd));

                    //ast
                    self.ast.child_down(TokFormatMonoStart);
                },

                // MonoContent
                (MonoContent, &TokText(ref text)) => {
                    new_nodes.push(PNode::new_terminal(TokText(text.clone())));
                    new_nodes.push(PNode::new_non_terminal(MonoContent));

                    // ast
                    self.ast.add_child(TokText(text.clone()));
                },
                (MonoContent, &TokNewLine) => {
                    new_nodes.push(PNode::new_terminal(TokNewLine));
                    new_nodes.push(PNode::new_non_terminal(MonoContent));
                },

                (MonoContent, &TokFormatMonoEnd) => {
                    // jump one ast-level higher
                    self.ast.up();
                },

                // Link
                (Link, &TokPassageLink(ref text, ref name)) => {
                    new_nodes.push(PNode::new_terminal(TokPassageLink(text.clone(), name.clone())));

                    // ast
                    self.ast.add_child(TokPassageLink(text.clone(), name.clone()));
                },

                // Makro
                (Makro, &TokSet) => {
                    new_nodes.push(PNode::new_terminal(TokSet));
                    new_nodes.push(PNode::new_non_terminal(ExpressionList));
                    new_nodes.push(PNode::new_terminal(TokMakroEnd));
                },
                (Makro, &TokIf) => {
                    new_nodes.push(PNode::new_terminal(TokIf));
                    new_nodes.push(PNode::new_non_terminal(ExpressionList));
                    new_nodes.push(PNode::new_terminal(TokMakroEnd));
                    new_nodes.push(PNode::new_non_terminal(PassageContent));
                    new_nodes.push(PNode::new_non_terminal(Makrof));

                    // ast
                    self.ast.two_childs_down(TokIf, TokPseudo);
                },
                // means <<$var>>
                (Makro, &TokMakroVar(ref name)) => {
                    new_nodes.push(PNode::new_terminal(TokMakroVar(name.clone())));
                    new_nodes.push(PNode::new_terminal(TokMakroEnd));

                    // ast
                    self.ast.add_child(TokMakroVar(name.clone()));
                },
                // means <<passagename>>
                (Makro, &TokMakroPassageName(ref name)) => {
                    new_nodes.push(PNode::new_terminal(TokMakroPassageName(name.clone())));
                    new_nodes.push(PNode::new_terminal(TokMakroEnd));

                    // ast
                    self.ast.add_child(TokMakroPassageName(name.clone()));
                },
                // Makrof
                (Makrof, &TokElse) => {
                    new_nodes.push(PNode::new_terminal(TokElse));
                    new_nodes.push(PNode::new_terminal(TokMakroEnd));
                    new_nodes.push(PNode::new_non_terminal(PassageContent));
                    new_nodes.push(PNode::new_terminal(TokEndIf));
                    new_nodes.push(PNode::new_terminal(TokMakroEnd));

                    // ast
                    self.ast.up_child_down(TokElse);
                },
                (Makrof, &TokEndIf) => {
                    new_nodes.push(PNode::new_terminal(TokEndIf));
                    new_nodes.push(PNode::new_terminal(TokMakroEnd));

                    // ast
                    self.ast.up_child(TokEndIf, false);
                }

                // ExpressionList
                (ExpressionList, &TokVariable(_)) |
                (ExpressionList, &TokInt(_)) |
                (ExpressionList, &TokString(_)) |
                (ExpressionList, &TokBoolean(_)) |
                (ExpressionList, &TokAssign(_, _)) => {
                    new_nodes.push(PNode::new_non_terminal(Expression));
                    new_nodes.push(PNode::new_non_terminal(ExpressionListf));
                },

                // ExpressionListf
                (ExpressionListf, &TokMakroEnd) => {
                    debug!("pop TokMakroEnd");
                    self.ast.up();

                },
                (ExpressionListf, _) => {
                    // ExpressionListf -> ε
                },

                // Expression
                (Expression, &TokVariable(_)) |
                (Expression, &TokInt(_)) |
                (Expression, &TokString(_)) |
                (Expression, &TokBoolean(_)) => {
                    new_nodes.push(PNode::new_non_terminal(E));
                },
                (Expression, &TokAssign(_, _)) => {
                    new_nodes.push(PNode::new_non_terminal(AssignVariable));
                },

                // E
                (E, &TokVariable(_)) |
                (E, &TokInt(_)) |
                (E, &TokString(_)) |
                (E, &TokBoolean(_)) => {
                    new_nodes.push(PNode::new_non_terminal(T));
                    new_nodes.push(PNode::new_non_terminal(E2));
                },

                // E2
                (E2, _) => {
                    // E2 -> ε
                },

                // T
                (T, &TokVariable(_)) |
                (T, &TokInt(_)) |
                (T, &TokString(_)) |
                (T, &TokBoolean(_)) => {
                    new_nodes.push(PNode::new_non_terminal(B));
                    new_nodes.push(PNode::new_non_terminal(T2));
                },

                // T2
                (T2, _) => {
                    // T2 -> ε
                },

                // B
                (B, &TokVariable(_)) |
                (B, &TokInt(_)) |
                (B, &TokString(_)) |
                (B, &TokBoolean(_)) => {
                    new_nodes.push(PNode::new_non_terminal(F));
                    new_nodes.push(PNode::new_non_terminal(B2));
                },

                // B2
                (B2, &TokCompOp(ref op)) => {
                    new_nodes.push(PNode::new_terminal(TokCompOp(op.clone())));
                    new_nodes.push(PNode::new_non_terminal(F));
                    new_nodes.push(PNode::new_non_terminal(B2));

                    // ast
                    self.ast.add_child(TokCompOp(op.clone()));
                },
                (B2, _) => {
                    // B2 -> ε
                },

                // F
                (F, &TokVariable(_)) |
                (F, &TokInt(_)) |
                (F, &TokString(_)) |
                (F, &TokBoolean(_)) => {
                    new_nodes.push(PNode::new_non_terminal(G));
                    new_nodes.push(PNode::new_non_terminal(F2));
                },

                // F2
                (F2, _) => {
                    // F2 -> ε
                },

                // G
                (G, &TokVariable(_)) |
                (G, &TokInt(_)) |
                (G, &TokString(_)) |
                (G, &TokBoolean(_)) => {
                    new_nodes.push(PNode::new_non_terminal(H));
                    new_nodes.push(PNode::new_non_terminal(G2));
                },

                // G2
                (G2, _) => {
                    // G2 -> ε
                },

                // H
                (H, &TokInt(_)) |
                (H, &TokString(_)) |
                (H, &TokBoolean(_)) => {
                    new_nodes.push(PNode::new_non_terminal(DataType));
                },
                (H, &TokVariable(ref name)) => {
                    new_nodes.push(PNode::new_terminal(TokVariable(name.clone())));

                    // ast
                    self.ast.add_child(TokVariable(name.clone()));
                },

                // AssignVariable
                (AssignVariable, &TokAssign(ref name, ref assign)) => {
                    new_nodes.push(PNode::new_terminal(TokAssign(name.clone(), assign.clone())));
                    new_nodes.push(PNode::new_non_terminal(E));

                    //ast
                    self.ast.child_down(TokAssign(name.clone(), assign.clone()));
                },

                // DataType
                (DataType, &TokInt(ref value)) => {
                    new_nodes.push(PNode::new_terminal(TokInt(value.clone())));

                    // ast
                    self.ast.add_child(TokInt(value.clone()));
                },
                (DataType, &TokString(ref value)) => {
                    new_nodes.push(PNode::new_terminal(TokString(value.clone())));

                    // ast
                    self.ast.add_child(TokString(value.clone()));
                },
                (DataType, &TokBoolean(ref value)) => {
                    new_nodes.push(PNode::new_terminal(TokBoolean(value.clone())));

                    // ast
                    self.ast.add_child(TokBoolean(value.clone()));
                }

                _ => {
                    panic!("not supported grammar: {:?}", state_first);
                }
            }

            // adds the new nodes to the stack (in reversed order)
            while let Some(child) = new_nodes.pop() {
                self.stack.push(child);
            }

        } else {
            // no token left

            // Sf, PassageContent, Linkf,

            match top {
                Sf | PassageContent => {
                    // ... -> ε
                },
                _ => {
                    panic!("Nonterminal '{:?}' is not an allowed end.", top);
                }
            }
        }
    }

    /// sets the lookahead to the next token
    fn next_token(&mut self) {
        self.lookahead = self.lookahead + 1;
    }
}
<|endoftext|>


<commit_before>extern crate proc_macro;
extern crate syn;

#[macro_use]
extern crate quote;

use proc_macro::TokenStream;

#[proc_macro_derive(JSManageable)]
pub fn derive_js_manageable(input: TokenStream) -> TokenStream {
    let s = input.to_string();
    let ast = syn::parse_derive_input(&s).unwrap();
    let gen = impl_js_manageable(&ast);
    gen.parse().unwrap()
}

fn impl_js_manageable(ast: &syn::DeriveInput) -> quote::Tokens {
    let name = &ast.ident;
    let (_, ty_generics, _) = ast.generics.split_for_impl();

    // we assume there's only one lifetime param, not named 'b
    assert!(ast.generics.lifetimes.len() == 1, "deriving JSManageable requires a single lifetime");

    let impl_lifetime = &ast.generics.lifetimes[0].lifetime.ident;
    assert!(impl_lifetime != "'b", "deriving JSManageable requires the lifetime to not be named 'b");

    // we assume there's at least 1 generic type param: the JSCompartment
    assert!(ast.generics.ty_params.len() >= 1);
    let compartment = &ast.generics.ty_params[0].ident;
    let impl_generics = ast.generics.ty_params.iter().map(|ty| quote! { #ty });
    let impl_generics = quote! { #(#impl_generics),* };

    // append the lifetime constraints to the generic type parameters
    let lifetime_constraints = ast.generics.ty_params.iter().map(|ty| {
        let ident = &ty.ident;
        quote! { #ident: 'b }
    });
    let where_clause_predicates = ast.generics.where_clause.predicates.iter().map(|pred| quote! { #pred });
    let where_clause_items = lifetime_constraints.chain(where_clause_predicates).collect::<Vec<_>>();
    let where_clause = if where_clause_items.is_empty() {
        quote! { }
    } else {
        quote! { where #(#where_clause_items),* }
    };

    // the `Aged` associated type params are the ty_params without their bounds
    let aged_ty_params = ast.generics.ty_params.iter().map(|ty| {
        let ident = &ty.ident;
        quote! { #ident }
    });
    let aged_ty_params = quote! { #(#aged_ty_params),* };

    quote! {
        #[allow(unsafe_code)]
        unsafe impl<#impl_lifetime, #impl_generics> JSTraceable for #name #ty_generics {}
        #[allow(unsafe_code)]
        unsafe impl<#impl_lifetime, 'b, #impl_generics> JSManageable<'b, #compartment> for #name #ty_generics #where_clause {
            type Aged = #name<'b, #aged_ty_params>;
        }
    }
}
<commit_msg>Allow constant types to derive JSManageable.<commit_after>extern crate proc_macro;
extern crate syn;

#[macro_use]
extern crate quote;

use proc_macro::TokenStream;

#[proc_macro_derive(JSManageable)]
pub fn derive_js_manageable(input: TokenStream) -> TokenStream {
    let s = input.to_string();
    let ast = syn::parse_derive_input(&s).unwrap();
    let gen = impl_js_manageable(&ast);
    gen.parse().unwrap()
}

fn impl_js_manageable(ast: &syn::DeriveInput) -> quote::Tokens {
    let name = &ast.ident;
    let (_, ty_generics, _) = ast.generics.split_for_impl();

    let impl_generics = ast.generics.ty_params.iter().map(|ty| quote! { #ty });
    let impl_generics = quote! { #(#impl_generics),* };

    // append the lifetime constraints to the generic type parameters
    let lifetime_constraints = ast.generics.ty_params.iter().map(|ty| {
        let ident = &ty.ident;
        quote! { #ident: 'b }
    });

    let where_clause_predicates = ast.generics.where_clause.predicates.iter().map(|pred| quote! { #pred });
    let where_clause_items = lifetime_constraints.chain(where_clause_predicates).collect::<Vec<_>>();
    let where_clause = if where_clause_items.is_empty() {
        quote! { }
    } else {
        quote! { where #(#where_clause_items),* }
    };

    // For types without any liftime parameters, we provide a trivial
    // implementation of `JSManageable`.
    if ast.generics.lifetimes.is_empty() {
        return quote! {
            #[allow(unsafe_code)]
            unsafe impl<#impl_generics> JSTraceable for #name #ty_generics {}
            #[allow(unsafe_code)]
            unsafe impl<'a, C, #impl_generics> JSManageable<'a, C> for #name #ty_generics #where_clause {
                type Aged = #name #ty_generics;
            }
        }
    }

    // we assume there's only one lifetime param, not named 'b
    assert!(ast.generics.lifetimes.len() == 1, "deriving JSManageable requires a single lifetime");

    let impl_lifetime = &ast.generics.lifetimes[0].lifetime.ident;
    assert!(impl_lifetime != "'b", "deriving JSManageable requires the lifetime to not be named 'b");

    // we assume there's at least 1 generic type param: the JSCompartment
    assert!(ast.generics.ty_params.len() >= 1);
    let compartment = &ast.generics.ty_params[0].ident;

    // the `Aged` associated type params are the ty_params without their bounds
    let aged_ty_params = ast.generics.ty_params.iter().map(|ty| {
        let ident = &ty.ident;
        quote! { #ident }
    });
    let aged_ty_params = quote! { #(#aged_ty_params),* };

    quote! {
        #[allow(unsafe_code)]
        unsafe impl<#impl_lifetime, #impl_generics> JSTraceable for #name #ty_generics {}
        #[allow(unsafe_code)]
        unsafe impl<#impl_lifetime, 'b, #impl_generics> JSManageable<'b, #compartment> for #name #ty_generics #where_clause {
            type Aged = #name<'b, #aged_ty_params>;
        }
    }
}
<|endoftext|>


<commit_before>//! `Connections` node and its children.

use parser::binary::{Parser, ParserSource, Attributes};
use loader::binary::simple::{Result, Error};

/// `Connections` node.
#[derive(Debug, Clone, PartialEq)]
pub struct Connections(pub Vec<Connection>);

impl Connections {
    /// Loads node contents from the parser.
    pub fn load<R, P>(mut parser: P) -> Result<Self>
    where
        R: ParserSource,
        P: Parser<R>,
    {
        let mut connections = Vec::new();

        loop {
            let attrs = try_get_node_attrs!(parser, ConnectionAttrs::load);
            connections.push(Connection::load(parser.subtree_parser(), attrs)?);
        }
        Ok(Connections(connections))
    }
}

/// Attributes read from a `C` node.
struct ConnectionAttrs {
    pub source_id: i64,
    pub destination_id: i64,
    pub property: Option<String>,
    pub source_is_prop: bool,
    pub destination_is_prop: bool,
}

impl ConnectionAttrs {
    /// Loads attributes.
    pub fn load<R>(name: &str, mut attrs: Attributes<R>) -> Result<Self>
    where
        R: ParserSource,
    {
        use parser::binary::utils::AttributeValues;

        if name == "C" {
            let (ty, source_id, destination_id) =
                <(String, i64, i64)>::from_attributes(&mut attrs)?
                    .ok_or_else(|| Error::InvalidAttribute("C".to_owned()))?;
            let (source_is_prop, destination_is_prop) = match ty.as_str() {
                "OO" => (false, false),
                "OP" => (false, true),
                "PO" => (true, false),
                "PP" => (true, true),
                _ => return Err(Error::InvalidAttribute("C".to_owned())),
            };
            let property = if attrs.rest_attributes() > 0 {
                Some(String::from_attributes(&mut attrs)?.ok_or_else(|| {
                    Error::InvalidAttribute("C".to_owned())
                })?)
            } else {
                None
            };
            Ok(ConnectionAttrs {
                source_id: source_id,
                destination_id: destination_id,
                property: property,
                source_is_prop: source_is_prop,
                destination_is_prop: destination_is_prop,
            })
        } else {
            Err(Error::UnexpectedNode(name.to_owned()))
        }
    }
}

/// `C` node.
///
/// Note that "the child node will be a **source object** of the parent node", and "the parent node
/// will be the **destination object** of the child node".
/// See [FBX 2017 Developer Help:
/// Connections](http://help.autodesk.com/view/FBX/2017/ENU/?guid=__files_GUID_BB63A93A_7663_4256_B060_8EA35CB0FF3A_htm)
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Connection {
    /// Object ID of the source object.
    pub source: i64,
    /// Object ID of the destination object.
    pub destination: i64,
    /// Property of the connection.
    pub property: Option<String>,
    /// `true` if the source is property.
    pub source_is_prop: bool,
    /// `true` if the destination is property.
    pub destination_is_prop: bool,
}

impl Connection {
    /// Loads node contents from the parser.
    fn load<R, P>(mut parser: P, attrs: ConnectionAttrs) -> Result<Self>
    where
        R: ParserSource,
        P: Parser<R>,
    {
        parser.skip_current_node()?;
        Ok(Connection {
            source: attrs.source_id,
            destination: attrs.destination_id,
            property: attrs.property,
            source_is_prop: attrs.source_is_prop,
            destination_is_prop: attrs.destination_is_prop,
        })
    }
}
<commit_msg>Update link to FBX SDK in doc comments<commit_after>//! `Connections` node and its children.

use parser::binary::{Parser, ParserSource, Attributes};
use loader::binary::simple::{Result, Error};

/// `Connections` node.
#[derive(Debug, Clone, PartialEq)]
pub struct Connections(pub Vec<Connection>);

impl Connections {
    /// Loads node contents from the parser.
    pub fn load<R, P>(mut parser: P) -> Result<Self>
    where
        R: ParserSource,
        P: Parser<R>,
    {
        let mut connections = Vec::new();

        loop {
            let attrs = try_get_node_attrs!(parser, ConnectionAttrs::load);
            connections.push(Connection::load(parser.subtree_parser(), attrs)?);
        }
        Ok(Connections(connections))
    }
}

/// Attributes read from a `C` node.
struct ConnectionAttrs {
    pub source_id: i64,
    pub destination_id: i64,
    pub property: Option<String>,
    pub source_is_prop: bool,
    pub destination_is_prop: bool,
}

impl ConnectionAttrs {
    /// Loads attributes.
    pub fn load<R>(name: &str, mut attrs: Attributes<R>) -> Result<Self>
    where
        R: ParserSource,
    {
        use parser::binary::utils::AttributeValues;

        if name == "C" {
            let (ty, source_id, destination_id) =
                <(String, i64, i64)>::from_attributes(&mut attrs)?
                    .ok_or_else(|| Error::InvalidAttribute("C".to_owned()))?;
            let (source_is_prop, destination_is_prop) = match ty.as_str() {
                "OO" => (false, false),
                "OP" => (false, true),
                "PO" => (true, false),
                "PP" => (true, true),
                _ => return Err(Error::InvalidAttribute("C".to_owned())),
            };
            let property = if attrs.rest_attributes() > 0 {
                Some(String::from_attributes(&mut attrs)?.ok_or_else(|| {
                    Error::InvalidAttribute("C".to_owned())
                })?)
            } else {
                None
            };
            Ok(ConnectionAttrs {
                source_id: source_id,
                destination_id: destination_id,
                property: property,
                source_is_prop: source_is_prop,
                destination_is_prop: destination_is_prop,
            })
        } else {
            Err(Error::UnexpectedNode(name.to_owned()))
        }
    }
}

/// `C` node.
///
/// Note that "the child node will be a **source object** of the parent node", and "the parent node
/// will be the **destination object** of the child node".
/// See [FBX 2018 Developer Help:
/// Connections](https://help.autodesk.com/view/FBX/2018/ENU/?guid=__files_GUID_BB63A93A_7663_4256_B060_8EA35CB0FF3A_htm)
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Connection {
    /// Object ID of the source object.
    pub source: i64,
    /// Object ID of the destination object.
    pub destination: i64,
    /// Property of the connection.
    pub property: Option<String>,
    /// `true` if the source is property.
    pub source_is_prop: bool,
    /// `true` if the destination is property.
    pub destination_is_prop: bool,
}

impl Connection {
    /// Loads node contents from the parser.
    fn load<R, P>(mut parser: P, attrs: ConnectionAttrs) -> Result<Self>
    where
        R: ParserSource,
        P: Parser<R>,
    {
        parser.skip_current_node()?;
        Ok(Connection {
            source: attrs.source_id,
            destination: attrs.destination_id,
            property: attrs.property,
            source_is_prop: attrs.source_is_prop,
            destination_is_prop: attrs.destination_is_prop,
        })
    }
}
<|endoftext|>


<commit_before>// Copyright 2012 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.

// Test for concurrent tasks

// xfail-test OOM on linux-32 without opts

use std::os;
use std::task;
use std::uint;
use std::vec;

fn calc(children: uint, parent_wait_chan: &Chan<Chan<Chan<int>>>) {

    let wait_ports: ~[Port<Chan<Chan<int>>>] = vec::from_fn(children, |_| {
        let (wait_port, wait_chan) = stream::<Chan<Chan<int>>>();
        do task::spawn {
            calc(children / 2, &wait_chan);
        }
        wait_port
    });

    let child_start_chans: ~[Chan<Chan<int>>] =
        wait_ports.move_iter().map(|port| port.recv()).collect();

    let (start_port, start_chan) = stream::<Chan<int>>();
    parent_wait_chan.send(start_chan);
    let parent_result_chan: Chan<int> = start_port.recv();

    let child_sum_ports: ~[Port<int>] =
        child_start_chans.move_iter().map(|child_start_chan| {
            let (child_sum_port, child_sum_chan) = stream::<int>();
            child_start_chan.send(child_sum_chan);
            child_sum_port
    }).collect();

    let sum = child_sum_ports.move_iter().fold(0, |sum, sum_port| sum + sum_port.recv() );

    parent_result_chan.send(sum + 1);
}

fn main() {
    let args = os::args();
    let args = if os::getenv("RUST_BENCH").is_some() {
        ~[~"", ~"30"]
    } else if args.len() <= 1u {
        ~[~"", ~"10"]
    } else {
        args
    };

    let children = from_str::<uint>(args[1]).get();
    let (wait_port, wait_chan) = stream();
    do task::spawn {
        calc(children, &wait_chan);
    };

    let start_chan = wait_port.recv();
    let (sum_port, sum_chan) = stream::<int>();
    start_chan.send(sum_chan);
    let sum = sum_port.recv();
    error!("How many tasks? %d tasks.", sum);
}
<commit_msg>Update task-perf-one-million<commit_after>// Copyright 2012 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.

// Test for concurrent tasks

// xfail-test OOM on linux-32 without opts

use std::os;
use std::task;
use std::uint;
use std::vec;

fn calc(children: uint, parent_wait_chan: &Chan<Chan<Chan<int>>>) {

    let wait_ports: ~[Port<Chan<Chan<int>>>] = vec::from_fn(children, |_| {
        let (wait_port, wait_chan) = stream::<Chan<Chan<int>>>();
        do task::spawn {
            calc(children / 2, &wait_chan);
        }
        wait_port
    });

    let child_start_chans: ~[Chan<Chan<int>>] =
        wait_ports.move_iter().map(|port| port.recv()).collect();

    let (start_port, start_chan) = stream::<Chan<int>>();
    parent_wait_chan.send(start_chan);
    let parent_result_chan: Chan<int> = start_port.recv();

    let child_sum_ports: ~[Port<int>] =
        child_start_chans.move_iter().map(|child_start_chan| {
            let (child_sum_port, child_sum_chan) = stream::<int>();
            child_start_chan.send(child_sum_chan);
            child_sum_port
    }).collect();

    let sum = child_sum_ports.move_iter().fold(0, |sum, sum_port| sum + sum_port.recv() );

    parent_result_chan.send(sum + 1);
}

fn main() {
    let args = os::args();
    let args = if os::getenv("RUST_BENCH").is_some() {
        ~[~"", ~"30"]
    } else if args.len() <= 1u {
        ~[~"", ~"10"]
    } else {
        args
    };

    let children = from_str::<uint>(args[1]).unwrap();
    let (wait_port, wait_chan) = stream();
    do task::spawn {
        calc(children, &wait_chan);
    };

    let start_chan = wait_port.recv();
    let (sum_port, sum_chan) = stream::<int>();
    start_chan.send(sum_chan);
    let sum = sum_port.recv();
    error!("How many tasks? {} tasks.", sum);
}
<|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/. */

extern crate webrender_build;

use std::borrow::Cow;
use std::env;
use std::fs::{canonicalize, read_dir, File};
use std::io::prelude::*;
use std::path::{Path, PathBuf};
use std::collections::hash_map::DefaultHasher;
use std::hash::Hasher;
use webrender_build::shader::*;
use webrender_build::shader_features::{ShaderFeatureFlags, get_shader_features};

/// Compute the shader path for insertion into the include_str!() macro.
/// This makes for more compact generated code than inserting the literal
/// shader source into the generated file.
///
/// If someone is building on a network share, I'm sorry.
fn escape_include_path(path: &Path) -> String {
    let full_path = canonicalize(path).unwrap();
    let full_name = full_path.as_os_str().to_str().unwrap();
    let full_name = full_name.replace("\\\\?\\", "");
    let full_name = full_name.replace("\\", "/");

    full_name
}

fn write_unoptimized_shaders(mut glsl_files: Vec<PathBuf>, shader_file: &mut File) -> Result<(), std::io::Error> {
    writeln!(
        shader_file,
        "  pub static ref UNOPTIMIZED_SHADERS: HashMap<&'static str, SourceWithDigest> = {{"
    )?;
    writeln!(shader_file, "    let mut shaders = HashMap::new();")?;

    // Sort the file list so that the shaders.rs file is filled
    // deterministically.
    glsl_files.sort_by(|a, b| a.file_name().cmp(&b.file_name()));

    for glsl in glsl_files {
        // Compute the shader name.
        assert!(glsl.is_file());
        let shader_name = glsl.file_name().unwrap().to_str().unwrap();
        let shader_name = shader_name.replace(".glsl", "");

        // Compute a digest of the #include-expanded shader source. We store
        // this as a literal alongside the source string so that we don't need
        // to hash large strings at runtime.
        let mut hasher = DefaultHasher::new();
        let base = glsl.parent().unwrap();
        assert!(base.is_dir());
        ShaderSourceParser::new().parse(
            Cow::Owned(shader_source_from_file(&glsl)),
            &|f| Cow::Owned(shader_source_from_file(&base.join(&format!("{}.glsl", f)))),
            &mut |s| hasher.write(s.as_bytes()),
        );
        let digest: ProgramSourceDigest = hasher.into();

        writeln!(
            shader_file,
            "    shaders.insert(\"{}\", SourceWithDigest {{ source: include_str!(\"{}\"), digest: \"{}\"}});",
            shader_name,
            escape_include_path(&glsl),
            digest,
        )?;
    }
    writeln!(shader_file, "    shaders")?;
    writeln!(shader_file, "  }};")?;

    Ok(())
}

#[derive(Clone, Debug)]
struct ShaderOptimizationInput {
    shader_name: &'static str,
    config: String,
    gl_version: ShaderVersion,
}

#[derive(Debug)]
struct ShaderOptimizationOutput {
    full_shader_name: String,
    gl_version: ShaderVersion,
    vert_file_path: PathBuf,
    frag_file_path: PathBuf,
    digest: ProgramSourceDigest,
}

#[derive(Debug)]
struct ShaderOptimizationError {
    shader: ShaderOptimizationInput,
    message: String,
}

fn write_optimized_shaders(shader_dir: &Path, shader_file: &mut File, out_dir: &str) -> Result<(), std::io::Error> {
    writeln!(
        shader_file,
        "  pub static ref OPTIMIZED_SHADERS: HashMap<(ShaderVersion, &'static str), OptimizedSourceWithDigest> = {{"
    )?;
    writeln!(shader_file, "    let mut shaders = HashMap::new();")?;

    // The full set of optimized shaders can be quite large, so only optimize
    // for the GL version we expect to be used on the target platform. If a different GL
    // version is used we will simply fall back to the unoptimized shaders.
    let shader_versions = if cfg!(target_os = "android") || cfg!(target_os = "windows") {
        [ShaderVersion::Gles]
    } else {
        [ShaderVersion::Gl]
    };

    let mut shaders = Vec::default();
    for &gl_version in &shader_versions {
        let mut flags = ShaderFeatureFlags::all();
        if gl_version != ShaderVersion::Gl {
            flags.remove(ShaderFeatureFlags::GL);
        }
        if gl_version != ShaderVersion::Gles {
            flags.remove(ShaderFeatureFlags::GLES);
            flags.remove(ShaderFeatureFlags::TEXTURE_EXTERNAL);
        }
        flags.remove(ShaderFeatureFlags::DITHERING);
        flags.remove(ShaderFeatureFlags::PIXEL_LOCAL_STORAGE);

        for (shader_name, configs) in get_shader_features(flags) {
            for config in configs {
                shaders.push(ShaderOptimizationInput {
                    shader_name,
                    config,
                    gl_version,
                });
            }
        }
    }

    let outputs = build_parallel::compile_objects(&|shader: &ShaderOptimizationInput| {
        println!("Optimizing shader {:?}", shader);
        let target = match shader.gl_version {
            ShaderVersion::Gl => glslopt::Target::OpenGl,
            ShaderVersion::Gles => glslopt::Target::OpenGles30,
        };
        let glslopt_ctx = glslopt::Context::new(target);

        let features = shader.config.split(",").filter(|f| !f.is_empty()).collect::<Vec<_>>();

        let (vert_src, frag_src) = build_shader_strings(
            shader.gl_version,
            &features,
            shader.shader_name,
            &|f| Cow::Owned(shader_source_from_file(&shader_dir.join(&format!("{}.glsl", f)))),
        );

        let full_shader_name = if shader.config.is_empty() {
            shader.shader_name.to_string()
        } else {
            format!("{}_{}", shader.shader_name, shader.config.replace(",", "_"))
        };

        let vert = glslopt_ctx.optimize(glslopt::ShaderType::Vertex, vert_src);
        if !vert.get_status() {
            return Err(ShaderOptimizationError {
                shader: shader.clone(),
                message: vert.get_log().to_string(),
            });
        }
        let frag = glslopt_ctx.optimize(glslopt::ShaderType::Fragment, frag_src);
        if !frag.get_status() {
            return Err(ShaderOptimizationError {
                shader: shader.clone(),
                message: frag.get_log().to_string(),
            });
        }

        let vert_source = vert.get_output().unwrap();
        let frag_source = frag.get_output().unwrap();

        // Compute a digest of the optimized shader sources. We store this
        // as a literal alongside the source string so that we don't need
        // to hash large strings at runtime.
        let mut hasher = DefaultHasher::new();
        hasher.write(vert_source.as_bytes());
        hasher.write(frag_source.as_bytes());
        let digest: ProgramSourceDigest = hasher.into();

        let vert_file_path = Path::new(out_dir)
            .join(format!("{}_{:?}.vert", full_shader_name, shader.gl_version));
        let mut vert_file = File::create(&vert_file_path).unwrap();
        vert_file.write_all(vert_source.as_bytes()).unwrap();
        let frag_file_path = vert_file_path.with_extension("frag");
        let mut frag_file = File::create(&frag_file_path).unwrap();
        frag_file.write_all(frag_source.as_bytes()).unwrap();

        println!("Finished optimizing shader {:?}", shader);

        Ok(ShaderOptimizationOutput {
            full_shader_name,
            gl_version: shader.gl_version,
            vert_file_path,
            frag_file_path,
            digest,
        })
    }, &shaders);

    match outputs {
        Ok(mut outputs) => {
            // Sort the shader list so that the shaders.rs file is filled
            // deterministically.
            outputs.sort_by(|a, b| {
                (a.gl_version, a.full_shader_name.clone()).cmp(&(b.gl_version, b.full_shader_name.clone()))
            });

            for shader in outputs {
                writeln!(
                    shader_file,
                    "    shaders.insert(({}, \"{}\"), OptimizedSourceWithDigest {{",
                    shader.gl_version.variant_name(),
                    shader.full_shader_name,
                )?;
                writeln!(
                    shader_file,
                    "        vert_source: include_str!(\"{}\"),",
                    escape_include_path(&shader.vert_file_path),
                )?;
                writeln!(
                    shader_file,
                    "        frag_source: include_str!(\"{}\"),",
                    escape_include_path(&shader.frag_file_path),
                )?;
                writeln!(shader_file, "        digest: \"{}\",", shader.digest)?;
                writeln!(shader_file, "    }});")?;
            }
        }
        Err(err) => match err {
            build_parallel::Error::BuildError(err) => {
                panic!("Error optimizing shader {:?}: {}", err.shader, err.message)
            }
            _ => panic!("Error optimizing shaders."),
        }
    }

    writeln!(shader_file, "    shaders")?;
    writeln!(shader_file, "  }};")?;

    Ok(())
}

fn main() -> Result<(), std::io::Error> {
    let out_dir = env::var("OUT_DIR").unwrap_or("out".to_owned());

    let shaders_file_path = Path::new(&out_dir).join("shaders.rs");
    let mut glsl_files = vec![];

    println!("cargo:rerun-if-changed=res");
    let res_dir = Path::new("res");
    for entry in read_dir(res_dir)? {
        let entry = entry?;
        let path = entry.path();

        if entry.file_name().to_str().unwrap().ends_with(".glsl") {
            println!("cargo:rerun-if-changed={}", path.display());
            glsl_files.push(path.to_owned());
        }
    }

    let mut shader_file = File::create(shaders_file_path)?;

    writeln!(shader_file, "/// AUTO GENERATED BY build.rs\n")?;
    writeln!(shader_file, "use std::collections::HashMap;\n")?;
    writeln!(shader_file, "use webrender_build::shader::ShaderVersion;\n")?;
    writeln!(shader_file, "pub struct SourceWithDigest {{")?;
    writeln!(shader_file, "    pub source: &'static str,")?;
    writeln!(shader_file, "    pub digest: &'static str,")?;
    writeln!(shader_file, "}}\n")?;
    writeln!(shader_file, "pub struct OptimizedSourceWithDigest {{")?;
    writeln!(shader_file, "    pub vert_source: &'static str,")?;
    writeln!(shader_file, "    pub frag_source: &'static str,")?;
    writeln!(shader_file, "    pub digest: &'static str,")?;
    writeln!(shader_file, "}}\n")?;
    writeln!(shader_file, "lazy_static! {{")?;

    write_unoptimized_shaders(glsl_files, &mut shader_file)?;
    writeln!(shader_file, "")?;
    write_optimized_shaders(&res_dir, &mut shader_file, &out_dir)?;
    writeln!(shader_file, "}}")?;

    Ok(())
}
<commit_msg>Bug 1632444 - Correctly detect target OS when deciding what shaders to optimize. r=jrmuizel<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/. */

extern crate webrender_build;

use std::borrow::Cow;
use std::env;
use std::fs::{canonicalize, read_dir, File};
use std::io::prelude::*;
use std::path::{Path, PathBuf};
use std::collections::hash_map::DefaultHasher;
use std::hash::Hasher;
use webrender_build::shader::*;
use webrender_build::shader_features::{ShaderFeatureFlags, get_shader_features};

/// Compute the shader path for insertion into the include_str!() macro.
/// This makes for more compact generated code than inserting the literal
/// shader source into the generated file.
///
/// If someone is building on a network share, I'm sorry.
fn escape_include_path(path: &Path) -> String {
    let full_path = canonicalize(path).unwrap();
    let full_name = full_path.as_os_str().to_str().unwrap();
    let full_name = full_name.replace("\\\\?\\", "");
    let full_name = full_name.replace("\\", "/");

    full_name
}

fn write_unoptimized_shaders(mut glsl_files: Vec<PathBuf>, shader_file: &mut File) -> Result<(), std::io::Error> {
    writeln!(
        shader_file,
        "  pub static ref UNOPTIMIZED_SHADERS: HashMap<&'static str, SourceWithDigest> = {{"
    )?;
    writeln!(shader_file, "    let mut shaders = HashMap::new();")?;

    // Sort the file list so that the shaders.rs file is filled
    // deterministically.
    glsl_files.sort_by(|a, b| a.file_name().cmp(&b.file_name()));

    for glsl in glsl_files {
        // Compute the shader name.
        assert!(glsl.is_file());
        let shader_name = glsl.file_name().unwrap().to_str().unwrap();
        let shader_name = shader_name.replace(".glsl", "");

        // Compute a digest of the #include-expanded shader source. We store
        // this as a literal alongside the source string so that we don't need
        // to hash large strings at runtime.
        let mut hasher = DefaultHasher::new();
        let base = glsl.parent().unwrap();
        assert!(base.is_dir());
        ShaderSourceParser::new().parse(
            Cow::Owned(shader_source_from_file(&glsl)),
            &|f| Cow::Owned(shader_source_from_file(&base.join(&format!("{}.glsl", f)))),
            &mut |s| hasher.write(s.as_bytes()),
        );
        let digest: ProgramSourceDigest = hasher.into();

        writeln!(
            shader_file,
            "    shaders.insert(\"{}\", SourceWithDigest {{ source: include_str!(\"{}\"), digest: \"{}\"}});",
            shader_name,
            escape_include_path(&glsl),
            digest,
        )?;
    }
    writeln!(shader_file, "    shaders")?;
    writeln!(shader_file, "  }};")?;

    Ok(())
}

#[derive(Clone, Debug)]
struct ShaderOptimizationInput {
    shader_name: &'static str,
    config: String,
    gl_version: ShaderVersion,
}

#[derive(Debug)]
struct ShaderOptimizationOutput {
    full_shader_name: String,
    gl_version: ShaderVersion,
    vert_file_path: PathBuf,
    frag_file_path: PathBuf,
    digest: ProgramSourceDigest,
}

#[derive(Debug)]
struct ShaderOptimizationError {
    shader: ShaderOptimizationInput,
    message: String,
}

fn write_optimized_shaders(shader_dir: &Path, shader_file: &mut File, out_dir: &str) -> Result<(), std::io::Error> {
    writeln!(
        shader_file,
        "  pub static ref OPTIMIZED_SHADERS: HashMap<(ShaderVersion, &'static str), OptimizedSourceWithDigest> = {{"
    )?;
    writeln!(shader_file, "    let mut shaders = HashMap::new();")?;

    // The full set of optimized shaders can be quite large, so only optimize
    // for the GL version we expect to be used on the target platform. If a different GL
    // version is used we will simply fall back to the unoptimized shaders.
    let shader_versions = match env::var("CARGO_CFG_TARGET_OS").as_ref().map(|s| &**s) {
        Ok("android") | Ok("windows") => [ShaderVersion::Gles],
        _ => [ShaderVersion::Gl],
    };

    let mut shaders = Vec::default();
    for &gl_version in &shader_versions {
        let mut flags = ShaderFeatureFlags::all();
        if gl_version != ShaderVersion::Gl {
            flags.remove(ShaderFeatureFlags::GL);
        }
        if gl_version != ShaderVersion::Gles {
            flags.remove(ShaderFeatureFlags::GLES);
            flags.remove(ShaderFeatureFlags::TEXTURE_EXTERNAL);
        }
        flags.remove(ShaderFeatureFlags::DITHERING);
        flags.remove(ShaderFeatureFlags::PIXEL_LOCAL_STORAGE);

        for (shader_name, configs) in get_shader_features(flags) {
            for config in configs {
                shaders.push(ShaderOptimizationInput {
                    shader_name,
                    config,
                    gl_version,
                });
            }
        }
    }

    let outputs = build_parallel::compile_objects(&|shader: &ShaderOptimizationInput| {
        println!("Optimizing shader {:?}", shader);
        let target = match shader.gl_version {
            ShaderVersion::Gl => glslopt::Target::OpenGl,
            ShaderVersion::Gles => glslopt::Target::OpenGles30,
        };
        let glslopt_ctx = glslopt::Context::new(target);

        let features = shader.config.split(",").filter(|f| !f.is_empty()).collect::<Vec<_>>();

        let (vert_src, frag_src) = build_shader_strings(
            shader.gl_version,
            &features,
            shader.shader_name,
            &|f| Cow::Owned(shader_source_from_file(&shader_dir.join(&format!("{}.glsl", f)))),
        );

        let full_shader_name = if shader.config.is_empty() {
            shader.shader_name.to_string()
        } else {
            format!("{}_{}", shader.shader_name, shader.config.replace(",", "_"))
        };

        let vert = glslopt_ctx.optimize(glslopt::ShaderType::Vertex, vert_src);
        if !vert.get_status() {
            return Err(ShaderOptimizationError {
                shader: shader.clone(),
                message: vert.get_log().to_string(),
            });
        }
        let frag = glslopt_ctx.optimize(glslopt::ShaderType::Fragment, frag_src);
        if !frag.get_status() {
            return Err(ShaderOptimizationError {
                shader: shader.clone(),
                message: frag.get_log().to_string(),
            });
        }

        let vert_source = vert.get_output().unwrap();
        let frag_source = frag.get_output().unwrap();

        // Compute a digest of the optimized shader sources. We store this
        // as a literal alongside the source string so that we don't need
        // to hash large strings at runtime.
        let mut hasher = DefaultHasher::new();
        hasher.write(vert_source.as_bytes());
        hasher.write(frag_source.as_bytes());
        let digest: ProgramSourceDigest = hasher.into();

        let vert_file_path = Path::new(out_dir)
            .join(format!("{}_{:?}.vert", full_shader_name, shader.gl_version));
        let mut vert_file = File::create(&vert_file_path).unwrap();
        vert_file.write_all(vert_source.as_bytes()).unwrap();
        let frag_file_path = vert_file_path.with_extension("frag");
        let mut frag_file = File::create(&frag_file_path).unwrap();
        frag_file.write_all(frag_source.as_bytes()).unwrap();

        println!("Finished optimizing shader {:?}", shader);

        Ok(ShaderOptimizationOutput {
            full_shader_name,
            gl_version: shader.gl_version,
            vert_file_path,
            frag_file_path,
            digest,
        })
    }, &shaders);

    match outputs {
        Ok(mut outputs) => {
            // Sort the shader list so that the shaders.rs file is filled
            // deterministically.
            outputs.sort_by(|a, b| {
                (a.gl_version, a.full_shader_name.clone()).cmp(&(b.gl_version, b.full_shader_name.clone()))
            });

            for shader in outputs {
                writeln!(
                    shader_file,
                    "    shaders.insert(({}, \"{}\"), OptimizedSourceWithDigest {{",
                    shader.gl_version.variant_name(),
                    shader.full_shader_name,
                )?;
                writeln!(
                    shader_file,
                    "        vert_source: include_str!(\"{}\"),",
                    escape_include_path(&shader.vert_file_path),
                )?;
                writeln!(
                    shader_file,
                    "        frag_source: include_str!(\"{}\"),",
                    escape_include_path(&shader.frag_file_path),
                )?;
                writeln!(shader_file, "        digest: \"{}\",", shader.digest)?;
                writeln!(shader_file, "    }});")?;
            }
        }
        Err(err) => match err {
            build_parallel::Error::BuildError(err) => {
                panic!("Error optimizing shader {:?}: {}", err.shader, err.message)
            }
            _ => panic!("Error optimizing shaders."),
        }
    }

    writeln!(shader_file, "    shaders")?;
    writeln!(shader_file, "  }};")?;

    Ok(())
}

fn main() -> Result<(), std::io::Error> {
    let out_dir = env::var("OUT_DIR").unwrap_or("out".to_owned());

    let shaders_file_path = Path::new(&out_dir).join("shaders.rs");
    let mut glsl_files = vec![];

    println!("cargo:rerun-if-changed=res");
    let res_dir = Path::new("res");
    for entry in read_dir(res_dir)? {
        let entry = entry?;
        let path = entry.path();

        if entry.file_name().to_str().unwrap().ends_with(".glsl") {
            println!("cargo:rerun-if-changed={}", path.display());
            glsl_files.push(path.to_owned());
        }
    }

    let mut shader_file = File::create(shaders_file_path)?;

    writeln!(shader_file, "/// AUTO GENERATED BY build.rs\n")?;
    writeln!(shader_file, "use std::collections::HashMap;\n")?;
    writeln!(shader_file, "use webrender_build::shader::ShaderVersion;\n")?;
    writeln!(shader_file, "pub struct SourceWithDigest {{")?;
    writeln!(shader_file, "    pub source: &'static str,")?;
    writeln!(shader_file, "    pub digest: &'static str,")?;
    writeln!(shader_file, "}}\n")?;
    writeln!(shader_file, "pub struct OptimizedSourceWithDigest {{")?;
    writeln!(shader_file, "    pub vert_source: &'static str,")?;
    writeln!(shader_file, "    pub frag_source: &'static str,")?;
    writeln!(shader_file, "    pub digest: &'static str,")?;
    writeln!(shader_file, "}}\n")?;
    writeln!(shader_file, "lazy_static! {{")?;

    write_unoptimized_shaders(glsl_files, &mut shader_file)?;
    writeln!(shader_file, "")?;
    write_optimized_shaders(&res_dir, &mut shader_file, &out_dir)?;
    writeln!(shader_file, "}}")?;

    Ok(())
}
<|endoftext|>