<commit_before>use std::fs::File;
use std::io::prelude::*;
use std::str::FromStr;
use std::error::Error;
use std::iter::FromIterator;
use std::fmt;
use std::collections::{BTreeMap, HashSet};
use std::num::Wrapping;
use euclid::{Point2D, Rect};
use rustc_serialize::Decodable;
use toml;
use scancode::Scancode;
use calx_resource::ResourceStore;
use calx_grid::{Dir6, LegendBuilder, Prefab};
use world::{self, Location, Portal, Query, Terraform, TerrainQuery, World};
use world::terrain;
use display;
use vitral;

enum PaintMode {
    Terrain(u8, u8),
    Portal,
}

/// Top-level application state for gameplay.
pub struct View {
    pub world: World,
    mode: PaintMode,
    /// Camera and second camera (for portaling)
    camera: (Location, Location),
    /// Do the two cameras move together?
    camera_lock: bool,

    console: display::Console,
    console_is_large: bool,
}

impl View {
    pub fn new(world: World) -> View {
        View {
            world: world,
            mode: PaintMode::Terrain(7, 2),
            camera: (Location::new(0, 0, 0), Location::new(0, 8, 0)),
            camera_lock: false,
            console: display::Console::new(),
            console_is_large: false,
        }
    }

    pub fn draw(&mut self, context: &mut display::Context, screen_area: &Rect<f32>) {
        context.ui.set_clip_rect(Some(*screen_area));

        let camera_loc = self.camera.0;

        let mut view = display::WorldView::new(camera_loc, *screen_area);
        view.show_cursor = true;
        view.highlight_offscreen_tiles = true;
        view.draw(&self.world, context);

        if let Some(loc) = view.cursor_loc {
            match self.mode {
                PaintMode::Terrain(draw, erase) => {
                    if context.ui.is_mouse_pressed(vitral::MouseButton::Left) {
                        self.world.set_terrain(loc, draw);
                    }

                    if context.ui.is_mouse_pressed(vitral::MouseButton::Right) {
                        self.world.set_terrain(loc, erase);
                    }
                }

                PaintMode::Portal => {
                    let (a, b) = self.camera;
                    if a != b && context.ui.is_mouse_pressed(vitral::MouseButton::Left) {
                        self.world.set_portal(loc, Portal::new(a, b));
                    }
                    if context.ui.is_mouse_pressed(vitral::MouseButton::Right) {
                        self.world.remove_portal(loc);
                    }
                }
            }
        }

        match self.mode {
            PaintMode::Terrain(mut fore, mut back) => {
                let fore_id = terrain::Id::from_u8(fore).unwrap();
                let back_id = terrain::Id::from_u8(back).unwrap();

                if context.ui.button(&format!("left: {:?}", fore_id)) {
                    fore += 1;
                    fore %= terrain::Id::_MaxTerrain as u8;
                }

                if context.ui.button(&format!("right: {:?}", back_id)) {
                    back += 1;
                    back %= terrain::Id::_MaxTerrain as u8;
                }

                self.mode = PaintMode::Terrain(fore, back);
            }
            _ => {
                unimplemented!();
            }
        }

        for (y, loc) in view.cursor_loc.iter().enumerate() {
            let font = context.ui.default_font();
            context.ui.draw_text(&*font,
                                 Point2D::new(400.0, y as f32 * 20.0 + 20.0),
                                 [1.0, 1.0, 1.0, 1.0],
                                 &format!("{:?}", loc));
        }

        context.ui.set_clip_rect(None);

        if self.console_is_large {
            let mut console_area = *screen_area;
            console_area.size.height /= 2.0;
            self.console.draw_large(context, &console_area);
        } else {
            self.console.draw_small(context, screen_area);
        }

        if let Some(scancode) = context.backend.poll_key().and_then(|k| Scancode::new(k.scancode)) {
            if self.console_is_large {
                self.console_input(context, scancode);
            } else {
                self.editor_input(context, scancode);
            }
        }
    }

    command_parser!{
        fn load(&mut self, path: String);
        fn save(&mut self, path: String);
    }

    fn load(&mut self, path: String) {
        fn loader(path: String) -> Result<Prefab<terrain::Id>, Box<Error>> {
            let mut file = File::open(path)?;
            let mut s = String::new();
            file.read_to_string(&mut s)?;
            let mut parser = toml::Parser::new(&s);

            let mut decoder = toml::Decoder::new(
                toml::Value::Table(parser.parse().ok_or_else(|| format!("Parse errors: {:?}", parser.errors))?));
            let save = MapSave::decode(&mut decoder)?;

            // Turn map into prefab.
            let prefab: Prefab<char> = Prefab::from_text_hexmap(&save.map);
            let prefab: Prefab<terrain::Id> = prefab.map(|item| {
                // TODO: REALLY need error handling instead of panicing here, since we're possibly
                // dealing with input from the outside, but can't do error pattern in the map
                // closure. Fix by adding a pre-verify stage that checks that all map glyphs are
                // found in legend and that all legend items can be instantiated.
                let item = save.legend
                               .get(&item)
                               .expect(&format!("Glyph '{}' not found in legend!", item));
                terrain::Id::from_str(&item.t)
                    .expect(&format!("Unknown terrain type '{}'!", item.t))
            });

            Ok(prefab)
        }

        let prefab = match loader(path) {
            Ok(prefab) => prefab,
            Err(e) => {
                let _ = writeln!(&mut self.console, "Load error: {}", e);
                return;
            }
        };

        // Apply map.
        self.world = World::new(1);

        // Prefabs do not contain positioning data. The standard fullscreen prefab which includes a
        // one-cell wide offscreen boundary goes in a bounding box with origin at (-21, -22).
        let offset = Point2D::new(-21, -22);

        for y in 0..prefab.dim().height {
            for x in 0..prefab.dim().width {
                let p = Point2D::new(x as i32, y as i32);
                if let Some(&t) = prefab.get(p) {
                    let loc = Location::origin() + p + offset;
                    self.world.set_terrain(loc, t as u8);
                }
            }
        }
    }

    fn save(&mut self, path: String) {
        let mut locs = HashSet::new();
        for x in world::onscreen_locations().iter() {
            locs.insert(Location::origin() + *x);
            // Get the immediate border around the actual screen cells, these will also affect the
            // visual look of the map.
            for d in Dir6::iter() {
                locs.insert(Location::origin() + *x + d.to_v2());
            }
        }

        let prefab = self.world.extract_prefab(locs.iter().map(|&x| x));
        const ALPHABET: &'static str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ\
             abcdefghijklmnopqrstuvwxyz\
             0123456789";

        let mut legend_builder = LegendBuilder::new(ALPHABET.to_string(),
                                                    move |x: &(terrain::Id, Vec<String>)| {
                                                        let &(ref t, ref e) = x;
                                                        if e.is_empty() {
                                                            terrain::Tile::get_resource(&(*t as u8))
                                                                .unwrap()
                                                                .preferred_map_chars()
                                                        } else {
                                                            ""
                                                        }
                                                    });

        // TODO: Turn legend failure into a recoverable error.
        // (Can't do that from inside a closure, so will need a separate checkup stage)
        let prefab = prefab.map(|e| {
            legend_builder.add(&e).expect("Unable to build legend, too many unique spawns?")
        });

        let legend: BTreeMap<char, LegendItem> = legend_builder.legend
                                                               .into_iter()
                                                               .map(|(c, t)| {
                                                                   (c,
                                                                    LegendItem {
                                                                       t: format!("{:?}", t.0),
                                                                       e: t.1.clone(),
                                                                   })
                                                               })
                                                               .collect();

        let save = MapSave {
            map: format!("{}", prefab.hexmap_display()),
            legend: legend,
        };

        match File::create(&path) {
            Ok(mut file) => {
                write!(file, "{}", save).expect("Write to file failed");
                let _ = writeln!(&mut self.console, "Saved '{}'", path);
            }
            Err(e) => {
                let _ = writeln!(&mut self.console, "Couldn't open file {}: {:?}", path, e);
            }
        }
    }

    fn console_input(&mut self, context: &mut display::Context, scancode: Scancode) {
        use scancode::Scancode::*;
        match scancode {
            Tab => {
                self.console_is_large = !self.console_is_large;
            }
            Enter | PadEnter => {
                let input = self.console.get_input();
                let _ = writeln!(&mut self.console, "{}", input);
                if let Err(e) = self.parse(&input) {
                    let _ = writeln!(&mut self.console, "{}", e);
                }
            }
            F12 => context.backend.save_screenshot("mapedit"),
            _ => {}
        }
    }

    fn editor_input(&mut self, context: &mut display::Context, scancode: Scancode) {
        use scancode::Scancode::*;
        match scancode {
            Q => self.move_camera(Point2D::new(-1, 0), 0),
            W => self.move_camera(Point2D::new(-1, -1), 0),
            E => self.move_camera(Point2D::new(0, -1), 0),
            A => self.move_camera(Point2D::new(0, 1), 0),
            S => self.move_camera(Point2D::new(1, 1), 0),
            D => self.move_camera(Point2D::new(1, 0), 0),
            F1 => self.switch_camera(),
            F12 => context.backend.save_screenshot("mapedit"),
            Tab => self.console_is_large = !self.console_is_large,
            RightBracket => self.move_camera(Point2D::new(0, 0), 1),
            LeftBracket => self.move_camera(Point2D::new(0, 0), -1),
            _ => {}
        }
    }

    fn move_camera(&mut self, delta: Point2D<i32>, dz: i8) {
        let second_delta = if self.camera_lock { delta } else { Point2D::new(0, 0) };

        let (a, b) = self.camera;
        self.camera = (a + delta, b + second_delta);

        let z0 = Wrapping(self.camera.0.z) + Wrapping(dz);
        let z1 = Wrapping(self.camera.1.z) + Wrapping(if self.camera_lock { dz } else { 0 });

        self.camera.0.z = z0.0;
        self.camera.1.z = z1.0;
    }

    fn switch_camera(&mut self) {
        let (a, b) = self.camera;
        self.camera = (b, a);
    }
}

/// Type for maps saved into disk.
#[derive(Debug, RustcEncodable, RustcDecodable)]
struct MapSave {
    pub map: String,
    pub legend: BTreeMap<char, LegendItem>,
}

impl fmt::Display for MapSave {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // TOML formatted output.
        writeln!(f, "map = '''")?;
        for line in self.map.lines() {
            writeln!(f, "{}", line.trim_right())?;
        }
        writeln!(f, "'''\n")?;
        writeln!(f, "[legend]")?;
        for (k, v) in self.legend.iter() {
            writeln!(f, "\"{}\" = {}", k, v)?;
        }
        Ok(())
    }
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
struct LegendItem {
    /// Terrain
    pub t: String,
    /// Entities
    pub e: Vec<String>,
}

impl fmt::Display for LegendItem {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // TOML formatted output.
        write!(f, "{{ t = \"{}\", e = [", self.t)?;
        self.e.iter().next().map_or(Ok(()), |e| write!(f, "\"{}\"", e))?;
        for e in self.e.iter().skip(1) {
            write!(f, ", \"{}\"", e)?;
        }
        write!(f, "] }}")
    }
}
<commit_msg>Mapedit UI in designated extra area<commit_after>use std::fs::File;
use std::io::prelude::*;
use std::str::FromStr;
use std::error::Error;
use std::iter::FromIterator;
use std::fmt;
use std::collections::{BTreeMap, HashSet};
use std::num::Wrapping;
use euclid::{Point2D, Rect, Size2D};
use rustc_serialize::Decodable;
use toml;
use scancode::Scancode;
use calx_resource::ResourceStore;
use calx_grid::{Dir6, LegendBuilder, Prefab};
use world::{self, Location, Portal, Query, Terraform, TerrainQuery, World};
use world::terrain;
use display;
use vitral;

enum PaintMode {
    Terrain(u8, u8),
    Portal,
}

/// Top-level application state for gameplay.
pub struct View {
    pub world: World,
    mode: PaintMode,
    /// Camera and second camera (for portaling)
    camera: (Location, Location),
    /// Do the two cameras move together?
    camera_lock: bool,

    console: display::Console,
    console_is_large: bool,
}

impl View {
    pub fn new(world: World) -> View {
        View {
            world: world,
            mode: PaintMode::Terrain(7, 2),
            camera: (Location::new(0, 0, 0), Location::new(0, 8, 0)),
            camera_lock: false,
            console: display::Console::new(),
            console_is_large: false,
        }
    }

    pub fn draw(&mut self, context: &mut display::Context, screen_area: &Rect<f32>) {
        context.ui.set_clip_rect(Some(*screen_area));

        let camera_loc = self.camera.0;

        let mut view = display::WorldView::new(camera_loc, *screen_area);
        view.show_cursor = true;
        view.highlight_offscreen_tiles = true;
        view.draw(&self.world, context);

        if let Some(loc) = view.cursor_loc {
            match self.mode {
                PaintMode::Terrain(draw, erase) => {
                    if context.ui.is_mouse_pressed(vitral::MouseButton::Left) {
                        self.world.set_terrain(loc, draw);
                    }

                    if context.ui.is_mouse_pressed(vitral::MouseButton::Right) {
                        self.world.set_terrain(loc, erase);
                    }
                }

                PaintMode::Portal => {
                    let (a, b) = self.camera;
                    if a != b && context.ui.is_mouse_pressed(vitral::MouseButton::Left) {
                        self.world.set_portal(loc, Portal::new(a, b));
                    }
                    if context.ui.is_mouse_pressed(vitral::MouseButton::Right) {
                        self.world.remove_portal(loc);
                    }
                }
            }
        }

        let ui_top_y = screen_area.size.height;
        context.ui.set_clip_rect(Some(Rect::new(
                Point2D::new(0.0, ui_top_y),
                Size2D::new(screen_area.size.width, 480.0 - ui_top_y))));
        context.ui.layout_pos.y = ui_top_y + 10.0;

        match self.mode {
            PaintMode::Terrain(mut fore, mut back) => {
                let fore_id = terrain::Id::from_u8(fore).unwrap();
                let back_id = terrain::Id::from_u8(back).unwrap();

                if context.ui.button(&format!("left: {:?}", fore_id)) {
                    fore += 1;
                    fore %= terrain::Id::_MaxTerrain as u8;
                }

                if context.ui.button(&format!("right: {:?}", back_id)) {
                    back += 1;
                    back %= terrain::Id::_MaxTerrain as u8;
                }

                self.mode = PaintMode::Terrain(fore, back);
            }
            _ => {
                unimplemented!();
            }
        }

        for (y, loc) in view.cursor_loc.iter().enumerate() {
            let font = context.ui.default_font();
            context.ui.draw_text(&*font,
                                 Point2D::new(400.0, y as f32 * 20.0 + 20.0 + ui_top_y),
                                 [1.0, 1.0, 1.0, 1.0],
                                 &format!("{:?}", loc));
        }

        context.ui.set_clip_rect(None);

        if self.console_is_large {
            let mut console_area = *screen_area;
            console_area.size.height /= 2.0;
            self.console.draw_large(context, &console_area);
        } else {
            self.console.draw_small(context, screen_area);
        }

        if let Some(scancode) = context.backend.poll_key().and_then(|k| Scancode::new(k.scancode)) {
            if self.console_is_large {
                self.console_input(context, scancode);
            } else {
                self.editor_input(context, scancode);
            }
        }
    }

    command_parser!{
        fn load(&mut self, path: String);
        fn save(&mut self, path: String);
    }

    fn load(&mut self, path: String) {
        fn loader(path: String) -> Result<Prefab<terrain::Id>, Box<Error>> {
            let mut file = File::open(path)?;
            let mut s = String::new();
            file.read_to_string(&mut s)?;
            let mut parser = toml::Parser::new(&s);

            let mut decoder = toml::Decoder::new(
                toml::Value::Table(parser.parse().ok_or_else(|| format!("Parse errors: {:?}", parser.errors))?));
            let save = MapSave::decode(&mut decoder)?;

            // Turn map into prefab.
            let prefab: Prefab<char> = Prefab::from_text_hexmap(&save.map);
            let prefab: Prefab<terrain::Id> = prefab.map(|item| {
                // TODO: REALLY need error handling instead of panicing here, since we're possibly
                // dealing with input from the outside, but can't do error pattern in the map
                // closure. Fix by adding a pre-verify stage that checks that all map glyphs are
                // found in legend and that all legend items can be instantiated.
                let item = save.legend
                               .get(&item)
                               .expect(&format!("Glyph '{}' not found in legend!", item));
                terrain::Id::from_str(&item.t)
                    .expect(&format!("Unknown terrain type '{}'!", item.t))
            });

            Ok(prefab)
        }

        let prefab = match loader(path) {
            Ok(prefab) => prefab,
            Err(e) => {
                let _ = writeln!(&mut self.console, "Load error: {}", e);
                return;
            }
        };

        // Apply map.
        self.world = World::new(1);

        // Prefabs do not contain positioning data. The standard fullscreen prefab which includes a
        // one-cell wide offscreen boundary goes in a bounding box with origin at (-21, -22).
        let offset = Point2D::new(-21, -22);

        for y in 0..prefab.dim().height {
            for x in 0..prefab.dim().width {
                let p = Point2D::new(x as i32, y as i32);
                if let Some(&t) = prefab.get(p) {
                    let loc = Location::origin() + p + offset;
                    self.world.set_terrain(loc, t as u8);
                }
            }
        }
    }

    fn save(&mut self, path: String) {
        let mut locs = HashSet::new();
        for x in world::onscreen_locations().iter() {
            locs.insert(Location::origin() + *x);
            // Get the immediate border around the actual screen cells, these will also affect the
            // visual look of the map.
            for d in Dir6::iter() {
                locs.insert(Location::origin() + *x + d.to_v2());
            }
        }

        let prefab = self.world.extract_prefab(locs.iter().map(|&x| x));
        const ALPHABET: &'static str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ\
             abcdefghijklmnopqrstuvwxyz\
             0123456789";

        let mut legend_builder = LegendBuilder::new(ALPHABET.to_string(),
                                                    move |x: &(terrain::Id, Vec<String>)| {
                                                        let &(ref t, ref e) = x;
                                                        if e.is_empty() {
                                                            terrain::Tile::get_resource(&(*t as u8))
                                                                .unwrap()
                                                                .preferred_map_chars()
                                                        } else {
                                                            ""
                                                        }
                                                    });

        // TODO: Turn legend failure into a recoverable error.
        // (Can't do that from inside a closure, so will need a separate checkup stage)
        let prefab = prefab.map(|e| {
            legend_builder.add(&e).expect("Unable to build legend, too many unique spawns?")
        });

        let legend: BTreeMap<char, LegendItem> = legend_builder.legend
                                                               .into_iter()
                                                               .map(|(c, t)| {
                                                                   (c,
                                                                    LegendItem {
                                                                       t: format!("{:?}", t.0),
                                                                       e: t.1.clone(),
                                                                   })
                                                               })
                                                               .collect();

        let save = MapSave {
            map: format!("{}", prefab.hexmap_display()),
            legend: legend,
        };

        match File::create(&path) {
            Ok(mut file) => {
                write!(file, "{}", save).expect("Write to file failed");
                let _ = writeln!(&mut self.console, "Saved '{}'", path);
            }
            Err(e) => {
                let _ = writeln!(&mut self.console, "Couldn't open file {}: {:?}", path, e);
            }
        }
    }

    fn console_input(&mut self, context: &mut display::Context, scancode: Scancode) {
        use scancode::Scancode::*;
        match scancode {
            Tab => {
                self.console_is_large = !self.console_is_large;
            }
            Enter | PadEnter => {
                let input = self.console.get_input();
                let _ = writeln!(&mut self.console, "{}", input);
                if let Err(e) = self.parse(&input) {
                    let _ = writeln!(&mut self.console, "{}", e);
                }
            }
            F12 => context.backend.save_screenshot("mapedit"),
            _ => {}
        }
    }

    fn editor_input(&mut self, context: &mut display::Context, scancode: Scancode) {
        use scancode::Scancode::*;
        match scancode {
            Q => self.move_camera(Point2D::new(-1, 0), 0),
            W => self.move_camera(Point2D::new(-1, -1), 0),
            E => self.move_camera(Point2D::new(0, -1), 0),
            A => self.move_camera(Point2D::new(0, 1), 0),
            S => self.move_camera(Point2D::new(1, 1), 0),
            D => self.move_camera(Point2D::new(1, 0), 0),
            F1 => self.switch_camera(),
            F12 => context.backend.save_screenshot("mapedit"),
            Tab => self.console_is_large = !self.console_is_large,
            RightBracket => self.move_camera(Point2D::new(0, 0), 1),
            LeftBracket => self.move_camera(Point2D::new(0, 0), -1),
            _ => {}
        }
    }

    fn move_camera(&mut self, delta: Point2D<i32>, dz: i8) {
        let second_delta = if self.camera_lock { delta } else { Point2D::new(0, 0) };

        let (a, b) = self.camera;
        self.camera = (a + delta, b + second_delta);

        let z0 = Wrapping(self.camera.0.z) + Wrapping(dz);
        let z1 = Wrapping(self.camera.1.z) + Wrapping(if self.camera_lock { dz } else { 0 });

        self.camera.0.z = z0.0;
        self.camera.1.z = z1.0;
    }

    fn switch_camera(&mut self) {
        let (a, b) = self.camera;
        self.camera = (b, a);
    }
}

/// Type for maps saved into disk.
#[derive(Debug, RustcEncodable, RustcDecodable)]
struct MapSave {
    pub map: String,
    pub legend: BTreeMap<char, LegendItem>,
}

impl fmt::Display for MapSave {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // TOML formatted output.
        writeln!(f, "map = '''")?;
        for line in self.map.lines() {
            writeln!(f, "{}", line.trim_right())?;
        }
        writeln!(f, "'''\n")?;
        writeln!(f, "[legend]")?;
        for (k, v) in self.legend.iter() {
            writeln!(f, "\"{}\" = {}", k, v)?;
        }
        Ok(())
    }
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
struct LegendItem {
    /// Terrain
    pub t: String,
    /// Entities
    pub e: Vec<String>,
}

impl fmt::Display for LegendItem {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // TOML formatted output.
        write!(f, "{{ t = \"{}\", e = [", self.t)?;
        self.e.iter().next().map_or(Ok(()), |e| write!(f, "\"{}\"", e))?;
        for e in self.e.iter().skip(1) {
            write!(f, ", \"{}\"", e)?;
        }
        write!(f, "] }}")
    }
}
<|endoftext|>


<commit_before>// Copyright 2013 The gl-rs developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate gl;
extern crate glfw;

use gl::types::*;
use glfw::{Context, OpenGlProfileHint, WindowHint, WindowMode};
use std::mem;
use std::ptr;
use std::str;
use std::ffi::CString;

// Vertex data
static VERTEX_DATA: [GLfloat; 6] = [
     0.0,  0.5,
     0.5, -0.5,
    -0.5, -0.5
];

// Shader sources
static VS_SRC: &'static str =
   "#version 150\n\
    in vec2 position;\n\
    void main() {\n\
       gl_Position = vec4(position, 0.0, 1.0);\n\
    }";

static FS_SRC: &'static str =
   "#version 150\n\
    out vec4 out_color;\n\
    void main() {\n\
       out_color = vec4(1.0, 1.0, 1.0, 1.0);\n\
    }";

fn compile_shader(src: &str, ty: GLenum) -> GLuint {
    let shader;
    unsafe {
        shader = gl::CreateShader(ty);
        // Attempt to compile the shader
        let c_str = CString::from_slice(src.as_bytes());
        gl::ShaderSource(shader, 1, &c_str.as_ptr(), ptr::null());
        gl::CompileShader(shader);

        // Get the compile status
        let mut status = gl::FALSE as GLint;
        gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);

        // Fail on error
        if status != (gl::TRUE as GLint) {
            let mut len = 0;
            gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len);
            let mut buf = Vec::new();
            buf.set_len((len as usize) - 1); // subtract 1 to skip the trailing null character
            gl::GetShaderInfoLog(shader, len, ptr::null_mut(), buf.as_mut_ptr() as *mut GLchar);
            panic!("{}", str::from_utf8(buf.as_slice()).ok().expect("ShaderInfoLog not valid utf8"));
        }
    }
    shader
}

fn link_program(vs: GLuint, fs: GLuint) -> GLuint { unsafe {
    let program = gl::CreateProgram();
    gl::AttachShader(program, vs);
    gl::AttachShader(program, fs);
    gl::LinkProgram(program);
    // Get the link status
    let mut status = gl::FALSE as GLint;
    gl::GetProgramiv(program, gl::LINK_STATUS, &mut status);

    // Fail on error
    if status != (gl::TRUE as GLint) {
        let mut len: GLint = 0;
        gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut len);
        let mut buf = Vec::new();
        buf.set_len((len as usize) - 1); // subtract 1 to skip the trailing null character
        gl::GetProgramInfoLog(program, len, ptr::null_mut(), buf.as_mut_ptr() as *mut GLchar);
        panic!("{}", str::from_utf8(buf.as_slice()).ok().expect("ProgramInfoLog not valid utf8"));
    }
    program
} }

fn main() {
    let mut glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap();

    // Choose a GL profile that is compatible with OS X 10.7+
    glfw.window_hint(WindowHint::ContextVersion(3, 2));
    glfw.window_hint(WindowHint::OpenglForwardCompat(true));
    glfw.window_hint(WindowHint::OpenglProfile(OpenGlProfileHint::Core));

    let (mut window, _) = glfw.create_window(800, 600, "OpenGL", WindowMode::Windowed)
        .expect("Failed to create GLFW window.");

    // It is essential to make the context current before calling `gl::load_with`.
    window.make_current();

    // Load the OpenGL function pointers
    gl::load_with(|s| window.get_proc_address(s));

    // Create GLSL shaders
    let vs = compile_shader(VS_SRC, gl::VERTEX_SHADER);
    let fs = compile_shader(FS_SRC, gl::FRAGMENT_SHADER);
    let program = link_program(vs, fs);

    let mut vao = 0;
    let mut vbo = 0;

    unsafe {
        // Create Vertex Array Object
        gl::GenVertexArrays(1, &mut vao);
        gl::BindVertexArray(vao);

        // Create a Vertex Buffer Object and copy the vertex data to it
        gl::GenBuffers(1, &mut vbo);
        gl::BindBuffer(gl::ARRAY_BUFFER, vbo);
        gl::BufferData(gl::ARRAY_BUFFER,
                       (VERTEX_DATA.len() * mem::size_of::<GLfloat>()) as GLsizeiptr,
                       mem::transmute(&VERTEX_DATA[0]),
                       gl::STATIC_DRAW);

        // Use shader program
        gl::UseProgram(program);
        gl::BindFragDataLocation(program, 0,
                                 CString::from_slice(b"out_color").as_ptr());

        // Specify the layout of the vertex data
        let pos_attr = gl::GetAttribLocation(program,
                                             CString::from_slice(b"position").as_ptr());
        gl::EnableVertexAttribArray(pos_attr as GLuint);
        gl::VertexAttribPointer(pos_attr as GLuint, 2, gl::FLOAT,
                                gl::FALSE as GLboolean, 0, ptr::null());
    }

    while !window.should_close() {
        // Poll events
        glfw.poll_events();

        unsafe {
            // Clear the screen to black
            gl::ClearColor(0.3, 0.3, 0.3, 1.0);
            gl::Clear(gl::COLOR_BUFFER_BIT);

            // Draw a triangle from the 3 vertices
            gl::DrawArrays(gl::TRIANGLES, 0, 3);
        }

        // Swap buffers
        window.swap_buffers();
    }

    unsafe {
    // Cleanup
        gl::DeleteProgram(program);
        gl::DeleteShader(fs);
        gl::DeleteShader(vs);
        gl::DeleteBuffers(1, &vbo);
        gl::DeleteVertexArrays(1, &vao);
    }
}
<commit_msg>Fix static_triangle example<commit_after>// Copyright 2013 The gl-rs developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate gl;
extern crate glfw;

use gl::types::*;
use glfw::{Context, OpenGlProfileHint, WindowHint, WindowMode};
use std::mem;
use std::ptr;
use std::str;
use std::ffi::CString;

// Vertex data
static VERTEX_DATA: [GLfloat; 6] = [
     0.0,  0.5,
     0.5, -0.5,
    -0.5, -0.5
];

// Shader sources
static VS_SRC: &'static str =
   "#version 150\n\
    in vec2 position;\n\
    void main() {\n\
       gl_Position = vec4(position, 0.0, 1.0);\n\
    }";

static FS_SRC: &'static str =
   "#version 150\n\
    out vec4 out_color;\n\
    void main() {\n\
       out_color = vec4(1.0, 1.0, 1.0, 1.0);\n\
    }";

fn compile_shader(src: &str, ty: GLenum) -> GLuint {
    let shader;
    unsafe {
        shader = gl::CreateShader(ty);
        // Attempt to compile the shader
        let c_str = CString::from_slice(src.as_bytes());
        gl::ShaderSource(shader, 1, &c_str.as_ptr(), ptr::null());
        gl::CompileShader(shader);

        // Get the compile status
        let mut status = gl::FALSE as GLint;
        gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);

        // Fail on error
        if status != (gl::TRUE as GLint) {
            let mut len = 0;
            gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len);
            let mut buf = Vec::new();
            buf.set_len((len as usize) - 1); // subtract 1 to skip the trailing null character
            gl::GetShaderInfoLog(shader, len, ptr::null_mut(), buf.as_mut_ptr() as *mut GLchar);
            panic!("{}", str::from_utf8(buf.as_slice()).ok().expect("ShaderInfoLog not valid utf8"));
        }
    }
    shader
}

fn link_program(vs: GLuint, fs: GLuint) -> GLuint { unsafe {
    let program = gl::CreateProgram();
    gl::AttachShader(program, vs);
    gl::AttachShader(program, fs);
    gl::LinkProgram(program);
    // Get the link status
    let mut status = gl::FALSE as GLint;
    gl::GetProgramiv(program, gl::LINK_STATUS, &mut status);

    // Fail on error
    if status != (gl::TRUE as GLint) {
        let mut len: GLint = 0;
        gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut len);
        let mut buf = Vec::with_capacity(len as usize);
        buf.set_len((len as usize) - 1); // subtract 1 to skip the trailing null character
        gl::GetProgramInfoLog(program, len, ptr::null_mut(), buf.as_mut_ptr() as *mut GLchar);
        panic!("{}", str::from_utf8(buf.as_slice()).ok().expect("ProgramInfoLog not valid utf8"));
    }
    program
} }

fn main() {
    let mut glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap();

    // Choose a GL profile that is compatible with OS X 10.7+
    glfw.window_hint(WindowHint::ContextVersion(3, 2));
    glfw.window_hint(WindowHint::OpenglForwardCompat(true));
    glfw.window_hint(WindowHint::OpenglProfile(OpenGlProfileHint::Core));

    let (mut window, _) = glfw.create_window(800, 600, "OpenGL", WindowMode::Windowed)
        .expect("Failed to create GLFW window.");

    // It is essential to make the context current before calling `gl::load_with`.
    window.make_current();

    // Load the OpenGL function pointers
    gl::load_with(|s| window.get_proc_address(s));

    // Create GLSL shaders
    let vs = compile_shader(VS_SRC, gl::VERTEX_SHADER);
    let fs = compile_shader(FS_SRC, gl::FRAGMENT_SHADER);
    let program = link_program(vs, fs);

    let mut vao = 0;
    let mut vbo = 0;

    unsafe {
        // Create Vertex Array Object
        gl::GenVertexArrays(1, &mut vao);
        gl::BindVertexArray(vao);

        // Create a Vertex Buffer Object and copy the vertex data to it
        gl::GenBuffers(1, &mut vbo);
        gl::BindBuffer(gl::ARRAY_BUFFER, vbo);
        gl::BufferData(gl::ARRAY_BUFFER,
                       (VERTEX_DATA.len() * mem::size_of::<GLfloat>()) as GLsizeiptr,
                       mem::transmute(&VERTEX_DATA[0]),
                       gl::STATIC_DRAW);

        // Use shader program
        gl::UseProgram(program);
        gl::BindFragDataLocation(program, 0,
                                 CString::from_slice(b"out_color").as_ptr());

        // Specify the layout of the vertex data
        let pos_attr = gl::GetAttribLocation(program,
                                             CString::from_slice(b"position").as_ptr());
        gl::EnableVertexAttribArray(pos_attr as GLuint);
        gl::VertexAttribPointer(pos_attr as GLuint, 2, gl::FLOAT,
                                gl::FALSE as GLboolean, 0, ptr::null());
    }

    while !window.should_close() {
        // Poll events
        glfw.poll_events();

        unsafe {
            // Clear the screen to black
            gl::ClearColor(0.3, 0.3, 0.3, 1.0);
            gl::Clear(gl::COLOR_BUFFER_BIT);

            // Draw a triangle from the 3 vertices
            gl::DrawArrays(gl::TRIANGLES, 0, 3);
        }

        // Swap buffers
        window.swap_buffers();
    }

    unsafe {
    // Cleanup
        gl::DeleteProgram(program);
        gl::DeleteShader(fs);
        gl::DeleteShader(vs);
        gl::DeleteBuffers(1, &vbo);
        gl::DeleteVertexArrays(1, &vao);
    }
}
<|endoftext|>


<commit_before>use std::{ffi, ptr, slice};

use spirv_cross::{hlsl, spirv, ErrorCode as SpirvErrorCode};

use winapi::{
    shared::winerror,
    um::{d3d11, d3dcommon, d3dcompiler},
};
use wio::com::ComPtr;

use auxil::{spirv_cross_specialize_ast, ShaderStage};
use hal::{device, pso};

use crate::{conv, Backend, PipelineLayout};

/// Emit error during shader module creation. Used if we don't expect an error
/// but might panic due to an exception in SPIRV-Cross.
fn gen_unexpected_error(err: SpirvErrorCode) -> device::ShaderError {
    let msg = match err {
        SpirvErrorCode::CompilationError(msg) => msg,
        SpirvErrorCode::Unhandled => "Unexpected error".into(),
    };
    device::ShaderError::CompilationFailed(msg)
}

/// Emit error during shader module creation. Used if we execute an query command.
fn gen_query_error(err: SpirvErrorCode) -> device::ShaderError {
    let msg = match err {
        SpirvErrorCode::CompilationError(msg) => msg,
        SpirvErrorCode::Unhandled => "Unknown query error".into(),
    };
    device::ShaderError::CompilationFailed(msg)
}

/// Introspects the input attributes of given SPIR-V shader and returns an optional vertex semantic remapping.
///
/// The returned hashmap has attribute location as a key and an Optional remapping to a two part semantic.
///
/// eg.
/// `2 -> None` means use default semantic `TEXCOORD2`
/// `2 -> Some((0, 2))` means use two part semantic `TEXCOORD0_2`. This is how matrices are represented by spirv-cross.
///
/// This is a temporary workaround for https://github.com/KhronosGroup/SPIRV-Cross/issues/1512.
///
/// This workaround also exists under the same name in the DX12 backend.
pub(crate) fn introspect_spirv_vertex_semantic_remapping(raw_data: &[u32]) -> Result<auxil::FastHashMap<u32, Option<(u32, u32)>>, device::ShaderError> {
    // This is inefficient as we already parse it once before. This is a temporary workaround only called
    // on vertex shaders. If this becomes permanent or shows up in profiles, deduplicate these as first course of action.
    let ast = parse_spirv(raw_data)?;

    let mut map = auxil::FastHashMap::default();

    let inputs = ast.get_shader_resources().map_err(gen_query_error)?.stage_inputs;
    for input in inputs {
        let idx = ast.get_decoration(input.id, spirv::Decoration::Location).map_err(gen_query_error)?;

        let ty = ast.get_type(input.type_id).map_err(gen_query_error)?;

        match ty {
            spirv::Type::Boolean { columns, .. }
                | spirv::Type::Int { columns, .. }
                | spirv::Type::UInt { columns, .. }
                | spirv::Type::Half { columns, .. }
                | spirv::Type::Float { columns, .. }
                | spirv::Type::Double { columns, .. } if columns > 1 => {
                for col in 0..columns {
                    if let Some(_) = map.insert(idx + col, Some((idx, col))) {
                        return Err(device::ShaderError::CompilationFailed(format!("Shader has overlapping input attachments at location {}", idx)))
                    }
                }
            }
            _ => {
                if let Some(_) = map.insert(idx, None) {
                    return Err(device::ShaderError::CompilationFailed(format!("Shader has overlapping input attachments at location {}", idx)))
                }
            }
        }
    }

    Ok(map)
}

pub(crate) fn compile_spirv_entrypoint(
    raw_data: &[u32],
    stage: ShaderStage,
    source: &pso::EntryPoint<Backend>,
    layout: &PipelineLayout,
    features: &hal::Features,
) -> Result<Option<ComPtr<d3dcommon::ID3DBlob>>, device::ShaderError> {
    let mut ast = parse_spirv(raw_data)?;
    spirv_cross_specialize_ast(&mut ast, &source.specialization)?;

    patch_spirv_resources(&mut ast, stage, layout)?;
    let shader_model = hlsl::ShaderModel::V5_0;
    let shader_code = translate_spirv(&mut ast, shader_model, layout, stage, features, source.entry)?;
    log::debug!(
        "Generated {:?} shader:\n{}",
        stage,
        shader_code,
    );

    let real_name = ast
        .get_cleansed_entry_point_name(source.entry, conv::map_stage(stage))
        .map_err(gen_query_error)?;

    // TODO: opt: don't query *all* entry points.
    let entry_points = ast.get_entry_points().map_err(gen_query_error)?;
    entry_points
        .iter()
        .find(|entry_point| entry_point.name == real_name)
        .ok_or(device::ShaderError::MissingEntryPoint(source.entry.into()))
        .and_then(|entry_point| {
            let stage = conv::map_execution_model(entry_point.execution_model);
            let shader = compile_hlsl_shader(
                stage,
                shader_model,
                &entry_point.name,
                shader_code.as_bytes(),
            )?;
            Ok(Some(unsafe { ComPtr::from_raw(shader) }))
        })
}

pub(crate) fn compile_hlsl_shader(
    stage: ShaderStage,
    shader_model: hlsl::ShaderModel,
    entry: &str,
    code: &[u8],
) -> Result<*mut d3dcommon::ID3DBlob, device::ShaderError> {
    let stage_str = {
        let stage = match stage {
            ShaderStage::Vertex => "vs",
            ShaderStage::Fragment => "ps",
            ShaderStage::Compute => "cs",
            _ => unimplemented!(),
        };

        let model = match shader_model {
            hlsl::ShaderModel::V5_0 => "5_0",
            // TODO: >= 11.3
            hlsl::ShaderModel::V5_1 => "5_1",
            // TODO: >= 12?, no mention of 11 on msdn
            hlsl::ShaderModel::V6_0 => "6_0",
            _ => unimplemented!(),
        };

        format!("{}_{}\0", stage, model)
    };

    let mut blob = ptr::null_mut();
    let mut error = ptr::null_mut();
    let entry = ffi::CString::new(entry).unwrap();
    let hr = unsafe {
        d3dcompiler::D3DCompile(
            code.as_ptr() as *const _,
            code.len(),
            ptr::null(),
            ptr::null(),
            ptr::null_mut(),
            entry.as_ptr() as *const _,
            stage_str.as_ptr() as *const i8,
            1,
            0,
            &mut blob as *mut *mut _,
            &mut error as *mut *mut _,
        )
    };

    if !winerror::SUCCEEDED(hr) {
        let error = unsafe { ComPtr::<d3dcommon::ID3DBlob>::from_raw(error) };
        let message = unsafe {
            let pointer = error.GetBufferPointer();
            let size = error.GetBufferSize();
            let slice = slice::from_raw_parts(pointer as *const u8, size as usize);
            String::from_utf8_lossy(slice).into_owned()
        };

        Err(device::ShaderError::CompilationFailed(message))
    } else {
        Ok(blob)
    }
}

fn parse_spirv(raw_data: &[u32]) -> Result<spirv::Ast<hlsl::Target>, device::ShaderError> {
    let module = spirv::Module::from_words(raw_data);

    spirv::Ast::parse(&module).map_err(|err| {
        let msg = match err {
            SpirvErrorCode::CompilationError(msg) => msg,
            SpirvErrorCode::Unhandled => "Unknown parsing error".into(),
        };
        device::ShaderError::CompilationFailed(msg)
    })
}

fn patch_spirv_resources(
    ast: &mut spirv::Ast<hlsl::Target>,
    stage: ShaderStage,
    layout: &PipelineLayout,
) -> Result<(), device::ShaderError> {
    // we remap all `layout(binding = n, set = n)` to a flat space which we get from our
    // `PipelineLayout` which knows of all descriptor set layouts

    let shader_resources = ast.get_shader_resources().map_err(gen_query_error)?;
    for image in &shader_resources.separate_images {
        let set = ast
            .get_decoration(image.id, spirv::Decoration::DescriptorSet)
            .map_err(gen_query_error)? as usize;
        let binding = ast
            .get_decoration(image.id, spirv::Decoration::Binding)
            .map_err(gen_query_error)?;
        let (_content, res_index) = layout.sets[set].find_register(stage, binding);

        ast.set_decoration(image.id, spirv::Decoration::Binding, res_index.t as u32)
            .map_err(gen_unexpected_error)?;
    }

    for uniform_buffer in &shader_resources.uniform_buffers {
        let set = ast
            .get_decoration(uniform_buffer.id, spirv::Decoration::DescriptorSet)
            .map_err(gen_query_error)? as usize;
        let binding = ast
            .get_decoration(uniform_buffer.id, spirv::Decoration::Binding)
            .map_err(gen_query_error)?;
        let (_content, res_index) = layout.sets[set].find_register(stage, binding);

        ast.set_decoration(
            uniform_buffer.id,
            spirv::Decoration::Binding,
            res_index.c as u32,
        )
        .map_err(gen_unexpected_error)?;
    }

    for storage_buffer in &shader_resources.storage_buffers {
        let set = ast
            .get_decoration(storage_buffer.id, spirv::Decoration::DescriptorSet)
            .map_err(gen_query_error)? as usize;
        let binding = ast
            .get_decoration(storage_buffer.id, spirv::Decoration::Binding)
            .map_err(gen_query_error)?;
        // In order to find out of the buffer is read-only, we must ask if the first member of its base type is read only.
        let read_only = ast
            .get_member_decoration(storage_buffer.base_type_id, 0, spirv::Decoration::NonWritable)
            .map_err(gen_query_error)? != 0;
        let (_content, res_index) = layout.sets[set].find_register(stage, binding);

        let index = if read_only {
            // Read only storage buffers are SRVs
            res_index.t as u32
        }
        // Compute uses bottom up stack, all other stages use top down.
        else if stage == ShaderStage::Compute {
            res_index.u as u32
        } else {
            d3d11::D3D11_PS_CS_UAV_REGISTER_COUNT - 1 - res_index.u as u32
        };

        ast.set_decoration(
            storage_buffer.id,
            spirv::Decoration::Binding,
            index,
        )
        .map_err(gen_unexpected_error)?;
    }

    for image in &shader_resources.storage_images {
        let set = ast
            .get_decoration(image.id, spirv::Decoration::DescriptorSet)
            .map_err(gen_query_error)? as usize;
        let binding = ast
            .get_decoration(image.id, spirv::Decoration::Binding)
            .map_err(gen_query_error)?;
        let (_content, res_index) = layout.sets[set].find_register(stage, binding);

        // Read only storage images are generated as UAVs by spirv-cross.
        //
        // Compute uses bottom up stack, all other stages use top down.
        let index = if stage == ShaderStage::Compute {
            res_index.u as u32
        } else {
            d3d11::D3D11_PS_CS_UAV_REGISTER_COUNT - 1 - res_index.u as u32
        };

        ast.set_decoration(
            image.id,
            spirv::Decoration::Binding,
            index,
        )
        .map_err(gen_unexpected_error)?;
    }

    for sampler in &shader_resources.separate_samplers {
        let set = ast
            .get_decoration(sampler.id, spirv::Decoration::DescriptorSet)
            .map_err(gen_query_error)? as usize;
        let binding = ast
            .get_decoration(sampler.id, spirv::Decoration::Binding)
            .map_err(gen_query_error)?;
        let (_content, res_index) = layout.sets[set].find_register(stage, binding);

        ast.set_decoration(sampler.id, spirv::Decoration::Binding, res_index.s as u32)
            .map_err(gen_unexpected_error)?;
    }

    for image in &shader_resources.sampled_images {
        let set = ast
            .get_decoration(image.id, spirv::Decoration::DescriptorSet)
            .map_err(gen_query_error)? as usize;
        let binding = ast
            .get_decoration(image.id, spirv::Decoration::Binding)
            .map_err(gen_query_error)?;
        let (_content, res_index) = layout.sets[set].find_register(stage, binding);

        ast.set_decoration(image.id, spirv::Decoration::Binding, res_index.t as u32)
            .map_err(gen_unexpected_error)?;
    }

    assert!(shader_resources.push_constant_buffers.len() <= 1, "Only 1 push constant buffer is supported");
    for push_constant_buffer in &shader_resources.push_constant_buffers {
        ast.set_decoration(
            push_constant_buffer.id,
            spirv::Decoration::DescriptorSet,
            0 // value doesn't matter, just needs a value
        ).map_err(gen_unexpected_error)?;
        ast.set_decoration(
            push_constant_buffer.id,
            spirv::Decoration::Binding,
            d3d11::D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT - 1
        ).map_err(gen_unexpected_error)?;
    }

    Ok(())
}

fn translate_spirv(
    ast: &mut spirv::Ast<hlsl::Target>,
    shader_model: hlsl::ShaderModel,
    _layout: &PipelineLayout,
    stage: ShaderStage,
    features: &hal::Features,
    entry_point: &str,
) -> Result<String, device::ShaderError> {
    let mut compile_options = hlsl::CompilerOptions::default();
    compile_options.shader_model = shader_model;
    compile_options.vertex.invert_y = !features.contains(hal::Features::NDC_Y_UP);
    compile_options.force_zero_initialized_variables = true;
    compile_options.entry_point = Some((entry_point.to_string(), conv::map_stage(stage)));

    //let stage_flag = stage.into();

    // TODO:
    /*let root_constant_layout = layout
    .root_constants
    .iter()
    .filter_map(|constant| if constant.stages.contains(stage_flag) {
        Some(hlsl::RootConstant {
            start: constant.range.start * 4,
            end: constant.range.end * 4,
            binding: constant.range.start,
            space: 0,
        })
    } else {
        None
    })
    .collect();*/
    ast.set_compiler_options(&compile_options)
        .map_err(gen_unexpected_error)?;
    //ast.set_root_constant_layout(root_constant_layout)
    //    .map_err(gen_unexpected_error)?;
    ast.compile().map_err(|err| {
        let msg = match err {
            SpirvErrorCode::CompilationError(msg) => msg,
            SpirvErrorCode::Unhandled => "Unknown compile error".into(),
        };
        device::ShaderError::CompilationFailed(msg)
    })
}
<commit_msg>[dx11] Fix read only buffers with read/write bindings<commit_after>use std::{ffi, ptr, slice};

use spirv_cross::{hlsl, spirv, ErrorCode as SpirvErrorCode};

use winapi::{
    shared::winerror,
    um::{d3d11, d3dcommon, d3dcompiler},
};
use wio::com::ComPtr;

use auxil::{spirv_cross_specialize_ast, ShaderStage};
use hal::{device, pso};

use crate::{conv, Backend, PipelineLayout};

/// Emit error during shader module creation. Used if we don't expect an error
/// but might panic due to an exception in SPIRV-Cross.
fn gen_unexpected_error(err: SpirvErrorCode) -> device::ShaderError {
    let msg = match err {
        SpirvErrorCode::CompilationError(msg) => msg,
        SpirvErrorCode::Unhandled => "Unexpected error".into(),
    };
    device::ShaderError::CompilationFailed(msg)
}

/// Emit error during shader module creation. Used if we execute an query command.
fn gen_query_error(err: SpirvErrorCode) -> device::ShaderError {
    let msg = match err {
        SpirvErrorCode::CompilationError(msg) => msg,
        SpirvErrorCode::Unhandled => "Unknown query error".into(),
    };
    device::ShaderError::CompilationFailed(msg)
}

/// Introspects the input attributes of given SPIR-V shader and returns an optional vertex semantic remapping.
///
/// The returned hashmap has attribute location as a key and an Optional remapping to a two part semantic.
///
/// eg.
/// `2 -> None` means use default semantic `TEXCOORD2`
/// `2 -> Some((0, 2))` means use two part semantic `TEXCOORD0_2`. This is how matrices are represented by spirv-cross.
///
/// This is a temporary workaround for https://github.com/KhronosGroup/SPIRV-Cross/issues/1512.
///
/// This workaround also exists under the same name in the DX12 backend.
pub(crate) fn introspect_spirv_vertex_semantic_remapping(raw_data: &[u32]) -> Result<auxil::FastHashMap<u32, Option<(u32, u32)>>, device::ShaderError> {
    // This is inefficient as we already parse it once before. This is a temporary workaround only called
    // on vertex shaders. If this becomes permanent or shows up in profiles, deduplicate these as first course of action.
    let ast = parse_spirv(raw_data)?;

    let mut map = auxil::FastHashMap::default();

    let inputs = ast.get_shader_resources().map_err(gen_query_error)?.stage_inputs;
    for input in inputs {
        let idx = ast.get_decoration(input.id, spirv::Decoration::Location).map_err(gen_query_error)?;

        let ty = ast.get_type(input.type_id).map_err(gen_query_error)?;

        match ty {
            spirv::Type::Boolean { columns, .. }
                | spirv::Type::Int { columns, .. }
                | spirv::Type::UInt { columns, .. }
                | spirv::Type::Half { columns, .. }
                | spirv::Type::Float { columns, .. }
                | spirv::Type::Double { columns, .. } if columns > 1 => {
                for col in 0..columns {
                    if let Some(_) = map.insert(idx + col, Some((idx, col))) {
                        return Err(device::ShaderError::CompilationFailed(format!("Shader has overlapping input attachments at location {}", idx)))
                    }
                }
            }
            _ => {
                if let Some(_) = map.insert(idx, None) {
                    return Err(device::ShaderError::CompilationFailed(format!("Shader has overlapping input attachments at location {}", idx)))
                }
            }
        }
    }

    Ok(map)
}

pub(crate) fn compile_spirv_entrypoint(
    raw_data: &[u32],
    stage: ShaderStage,
    source: &pso::EntryPoint<Backend>,
    layout: &PipelineLayout,
    features: &hal::Features,
) -> Result<Option<ComPtr<d3dcommon::ID3DBlob>>, device::ShaderError> {
    let mut ast = parse_spirv(raw_data)?;
    spirv_cross_specialize_ast(&mut ast, &source.specialization)?;

    patch_spirv_resources(&mut ast, stage, layout)?;
    let shader_model = hlsl::ShaderModel::V5_0;
    let shader_code = translate_spirv(&mut ast, shader_model, layout, stage, features, source.entry)?;
    log::debug!(
        "Generated {:?} shader:\n{}",
        stage,
        shader_code,
    );

    let real_name = ast
        .get_cleansed_entry_point_name(source.entry, conv::map_stage(stage))
        .map_err(gen_query_error)?;

    // TODO: opt: don't query *all* entry points.
    let entry_points = ast.get_entry_points().map_err(gen_query_error)?;
    entry_points
        .iter()
        .find(|entry_point| entry_point.name == real_name)
        .ok_or(device::ShaderError::MissingEntryPoint(source.entry.into()))
        .and_then(|entry_point| {
            let stage = conv::map_execution_model(entry_point.execution_model);
            let shader = compile_hlsl_shader(
                stage,
                shader_model,
                &entry_point.name,
                shader_code.as_bytes(),
            )?;
            Ok(Some(unsafe { ComPtr::from_raw(shader) }))
        })
}

pub(crate) fn compile_hlsl_shader(
    stage: ShaderStage,
    shader_model: hlsl::ShaderModel,
    entry: &str,
    code: &[u8],
) -> Result<*mut d3dcommon::ID3DBlob, device::ShaderError> {
    let stage_str = {
        let stage = match stage {
            ShaderStage::Vertex => "vs",
            ShaderStage::Fragment => "ps",
            ShaderStage::Compute => "cs",
            _ => unimplemented!(),
        };

        let model = match shader_model {
            hlsl::ShaderModel::V5_0 => "5_0",
            // TODO: >= 11.3
            hlsl::ShaderModel::V5_1 => "5_1",
            // TODO: >= 12?, no mention of 11 on msdn
            hlsl::ShaderModel::V6_0 => "6_0",
            _ => unimplemented!(),
        };

        format!("{}_{}\0", stage, model)
    };

    let mut blob = ptr::null_mut();
    let mut error = ptr::null_mut();
    let entry = ffi::CString::new(entry).unwrap();
    let hr = unsafe {
        d3dcompiler::D3DCompile(
            code.as_ptr() as *const _,
            code.len(),
            ptr::null(),
            ptr::null(),
            ptr::null_mut(),
            entry.as_ptr() as *const _,
            stage_str.as_ptr() as *const i8,
            1,
            0,
            &mut blob as *mut *mut _,
            &mut error as *mut *mut _,
        )
    };

    if !winerror::SUCCEEDED(hr) {
        let error = unsafe { ComPtr::<d3dcommon::ID3DBlob>::from_raw(error) };
        let message = unsafe {
            let pointer = error.GetBufferPointer();
            let size = error.GetBufferSize();
            let slice = slice::from_raw_parts(pointer as *const u8, size as usize);
            String::from_utf8_lossy(slice).into_owned()
        };

        Err(device::ShaderError::CompilationFailed(message))
    } else {
        Ok(blob)
    }
}

fn parse_spirv(raw_data: &[u32]) -> Result<spirv::Ast<hlsl::Target>, device::ShaderError> {
    let module = spirv::Module::from_words(raw_data);

    spirv::Ast::parse(&module).map_err(|err| {
        let msg = match err {
            SpirvErrorCode::CompilationError(msg) => msg,
            SpirvErrorCode::Unhandled => "Unknown parsing error".into(),
        };
        device::ShaderError::CompilationFailed(msg)
    })
}

fn patch_spirv_resources(
    ast: &mut spirv::Ast<hlsl::Target>,
    stage: ShaderStage,
    layout: &PipelineLayout,
) -> Result<(), device::ShaderError> {
    // we remap all `layout(binding = n, set = n)` to a flat space which we get from our
    // `PipelineLayout` which knows of all descriptor set layouts

    let shader_resources = ast.get_shader_resources().map_err(gen_query_error)?;
    for image in &shader_resources.separate_images {
        let set = ast
            .get_decoration(image.id, spirv::Decoration::DescriptorSet)
            .map_err(gen_query_error)? as usize;
        let binding = ast
            .get_decoration(image.id, spirv::Decoration::Binding)
            .map_err(gen_query_error)?;
        let (_content, res_index) = layout.sets[set].find_register(stage, binding);

        ast.set_decoration(image.id, spirv::Decoration::Binding, res_index.t as u32)
            .map_err(gen_unexpected_error)?;
    }

    for uniform_buffer in &shader_resources.uniform_buffers {
        let set = ast
            .get_decoration(uniform_buffer.id, spirv::Decoration::DescriptorSet)
            .map_err(gen_query_error)? as usize;
        let binding = ast
            .get_decoration(uniform_buffer.id, spirv::Decoration::Binding)
            .map_err(gen_query_error)?;
        let (_content, res_index) = layout.sets[set].find_register(stage, binding);

        ast.set_decoration(
            uniform_buffer.id,
            spirv::Decoration::Binding,
            res_index.c as u32,
        )
        .map_err(gen_unexpected_error)?;
    }

    for storage_buffer in &shader_resources.storage_buffers {
        let set = ast
            .get_decoration(storage_buffer.id, spirv::Decoration::DescriptorSet)
            .map_err(gen_query_error)? as usize;
        let binding = ast
            .get_decoration(storage_buffer.id, spirv::Decoration::Binding)
            .map_err(gen_query_error)?;
        let (_content, res_index) = layout.sets[set].find_register(stage, binding);

        let read_only = match layout.sets[set].bindings[binding as usize].ty {
            pso::DescriptorType::Buffer {
                ty: pso::BufferDescriptorType::Storage {
                    read_only
                },
                ..
            } => {
                read_only
            }
            _ => unreachable!()
        };

        // If the binding is read/write, we need to generate a UAV here.
        if !read_only {
            ast.set_member_decoration(storage_buffer.type_id, 0, spirv::Decoration::NonWritable, 0)
                .map_err(gen_unexpected_error)?;
        }

        let index = if read_only {
            res_index.t as u32
        } else if stage == ShaderStage::Compute {
            res_index.u as u32
        } else {
            d3d11::D3D11_PS_CS_UAV_REGISTER_COUNT - 1 - res_index.u as u32
        };

        ast.set_decoration(
            storage_buffer.id,
            spirv::Decoration::Binding,
            index,
        )
        .map_err(gen_unexpected_error)?;
    }

    for image in &shader_resources.storage_images {
        let set = ast
            .get_decoration(image.id, spirv::Decoration::DescriptorSet)
            .map_err(gen_query_error)? as usize;
        let binding = ast
            .get_decoration(image.id, spirv::Decoration::Binding)
            .map_err(gen_query_error)?;
        let (_content, res_index) = layout.sets[set].find_register(stage, binding);

        // Read only storage images are generated as UAVs by spirv-cross.
        //
        // Compute uses bottom up stack, all other stages use top down.
        let index = if stage == ShaderStage::Compute {
            res_index.u as u32
        } else {
            d3d11::D3D11_PS_CS_UAV_REGISTER_COUNT - 1 - res_index.u as u32
        };

        ast.set_decoration(
            image.id,
            spirv::Decoration::Binding,
            index,
        )
        .map_err(gen_unexpected_error)?;
    }

    for sampler in &shader_resources.separate_samplers {
        let set = ast
            .get_decoration(sampler.id, spirv::Decoration::DescriptorSet)
            .map_err(gen_query_error)? as usize;
        let binding = ast
            .get_decoration(sampler.id, spirv::Decoration::Binding)
            .map_err(gen_query_error)?;
        let (_content, res_index) = layout.sets[set].find_register(stage, binding);

        ast.set_decoration(sampler.id, spirv::Decoration::Binding, res_index.s as u32)
            .map_err(gen_unexpected_error)?;
    }

    for image in &shader_resources.sampled_images {
        let set = ast
            .get_decoration(image.id, spirv::Decoration::DescriptorSet)
            .map_err(gen_query_error)? as usize;
        let binding = ast
            .get_decoration(image.id, spirv::Decoration::Binding)
            .map_err(gen_query_error)?;
        let (_content, res_index) = layout.sets[set].find_register(stage, binding);

        ast.set_decoration(image.id, spirv::Decoration::Binding, res_index.t as u32)
            .map_err(gen_unexpected_error)?;
    }

    assert!(shader_resources.push_constant_buffers.len() <= 1, "Only 1 push constant buffer is supported");
    for push_constant_buffer in &shader_resources.push_constant_buffers {
        ast.set_decoration(
            push_constant_buffer.id,
            spirv::Decoration::DescriptorSet,
            0 // value doesn't matter, just needs a value
        ).map_err(gen_unexpected_error)?;
        ast.set_decoration(
            push_constant_buffer.id,
            spirv::Decoration::Binding,
            d3d11::D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT - 1
        ).map_err(gen_unexpected_error)?;
    }

    Ok(())
}

fn translate_spirv(
    ast: &mut spirv::Ast<hlsl::Target>,
    shader_model: hlsl::ShaderModel,
    _layout: &PipelineLayout,
    stage: ShaderStage,
    features: &hal::Features,
    entry_point: &str,
) -> Result<String, device::ShaderError> {
    let mut compile_options = hlsl::CompilerOptions::default();
    compile_options.shader_model = shader_model;
    compile_options.vertex.invert_y = !features.contains(hal::Features::NDC_Y_UP);
    compile_options.force_zero_initialized_variables = true;
    compile_options.entry_point = Some((entry_point.to_string(), conv::map_stage(stage)));

    //let stage_flag = stage.into();

    // TODO:
    /*let root_constant_layout = layout
    .root_constants
    .iter()
    .filter_map(|constant| if constant.stages.contains(stage_flag) {
        Some(hlsl::RootConstant {
            start: constant.range.start * 4,
            end: constant.range.end * 4,
            binding: constant.range.start,
            space: 0,
        })
    } else {
        None
    })
    .collect();*/
    ast.set_compiler_options(&compile_options)
        .map_err(gen_unexpected_error)?;
    //ast.set_root_constant_layout(root_constant_layout)
    //    .map_err(gen_unexpected_error)?;
    ast.compile().map_err(|err| {
        let msg = match err {
            SpirvErrorCode::CompilationError(msg) => msg,
            SpirvErrorCode::Unhandled => "Unknown compile error".into(),
        };
        device::ShaderError::CompilationFailed(msg)
    })
}
<|endoftext|>


<commit_before>use regex::Regex;
use parser::parse;
use normalizer::normalize;
use parser::span::parse_spans;
use parser::Block;
use parser::Block::{Header, Paragraph, Blockquote};
use parser::Span;
use parser::Span::{Break, Text, Emphasis, Strong, Code, Link, Image};

static ATX_HEADER      : Regex = regex!(r"^(?P<level>#{1,6})\s(?P<text>.*)");
static SETEXT_HEADER_1 : Regex = regex!(r"(?P<text>.+)\n===+");
static SETEXT_HEADER_2 : Regex = regex!(r"(?P<text>.+)\n---+");
static BLOCKQUOTE      : Regex = regex!(r"(?m)^> ?");

pub fn parse_block (text : &str) -> Option<Block>{
    if text.is_empty(){
        return None;
    }else if BLOCKQUOTE.is_match(text){
        // remove top-level >s
        let caps = BLOCKQUOTE.replace_all(text, "");
        return Some(
            Blockquote(parse(caps.as_slice()))
        );
    }else if ATX_HEADER.is_match(text){
        let caps = ATX_HEADER.captures(text).unwrap();
        return Some(
            Header(
                parse_spans(caps.name("text")),
                caps.name("level").len()
                )
            );
    }else if SETEXT_HEADER_1.is_match(text){
        let caps = SETEXT_HEADER_1.captures(text).unwrap();
        return Some(
            Header(
                parse_spans(caps.name("text")),
                1
                )
            );
    }else if SETEXT_HEADER_2.is_match(text){
        let caps = SETEXT_HEADER_2.captures(text).unwrap();
        return Some(
            Header(
                parse_spans(caps.name("text")),
                2
                )
            );
    }
    return Some(Paragraph(parse_spans(text)));
}

#[test]
fn parse_atx_header_test() {
    assert_eq!(
        parse_block("### Test").unwrap(),
        Header(vec![Text("Test".to_string())], 3)
    );

    assert_eq!(
        parse_block("# Test").unwrap(),
        Header(vec![Text("Test".to_string())], 1)
    );

    assert_eq!(
        parse_block("###### Test").unwrap(),
        Header(vec![Text("Test".to_string())], 6)
    );

    assert_eq!(
        parse_block("####### Test").unwrap(),
        Paragraph(vec![Text("####### Test".to_string())])
    );
}

<commit_msg>Start blockquote tests<commit_after>use regex::Regex;
use parser::parse;
use normalizer::normalize;
use parser::span::parse_spans;
use parser::Block;
use parser::Block::{Header, Paragraph, Blockquote};
use parser::Span;
use parser::Span::{Break, Text, Emphasis, Strong, Code, Link, Image};

static ATX_HEADER      : Regex = regex!(r"^(?P<level>#{1,6})\s(?P<text>.*)");
static SETEXT_HEADER_1 : Regex = regex!(r"(?P<text>.+)\n===+");
static SETEXT_HEADER_2 : Regex = regex!(r"(?P<text>.+)\n---+");
static BLOCKQUOTE      : Regex = regex!(r"(?m)^> ?");

pub fn parse_block (text : &str) -> Option<Block>{
    if text.is_empty(){
        return None;
    }else if BLOCKQUOTE.is_match(text){
        // remove top-level >s
        let caps = BLOCKQUOTE.replace_all(text, "");
        return Some(
            Blockquote(parse(caps.as_slice()))
        );
    }else if ATX_HEADER.is_match(text){
        let caps = ATX_HEADER.captures(text).unwrap();
        return Some(
            Header(
                parse_spans(caps.name("text")),
                caps.name("level").len()
                )
            );
    }else if SETEXT_HEADER_1.is_match(text){
        let caps = SETEXT_HEADER_1.captures(text).unwrap();
        return Some(
            Header(
                parse_spans(caps.name("text")),
                1
                )
            );
    }else if SETEXT_HEADER_2.is_match(text){
        let caps = SETEXT_HEADER_2.captures(text).unwrap();
        return Some(
            Header(
                parse_spans(caps.name("text")),
                2
                )
            );
    }
    return Some(Paragraph(parse_spans(text)));
}

#[test]
fn parse_atx_header_test() {
    assert_eq!(
        parse_block("### Test").unwrap(),
        Header(vec![Text("Test".to_string())], 3)
    );

    assert_eq!(
        parse_block("# Test").unwrap(),
        Header(vec![Text("Test".to_string())], 1)
    );

    assert_eq!(
        parse_block("###### Test").unwrap(),
        Header(vec![Text("Test".to_string())], 6)
    );

    assert_eq!(
        parse_block("####### Test").unwrap(),
        Paragraph(vec![Text("####### Test".to_string())])
    );
}

#[test]
fn parse_blockquote_test() {
    assert_eq!(
        parse_block("> One Paragraph\n>\n> > Another blockquote\n>\n").unwrap(),
        Blockquote(vec![Paragraph(vec![Text("One Paragraph".to_string())]),
                   Blockquote(vec![Paragraph(vec![Text("Another blockquote".to_string())])])])
    );

    assert_eq!(
        parse_block("> One Paragraph\n>\n> ## H2 \n>\n").unwrap(),
        Blockquote(vec![Paragraph(vec![Text("One Paragraph".to_string())]), Header(vec![Text("H2 ".to_string())], 2)])
    );
}

<|endoftext|>


<commit_before>// presentation.rs
// Copyright 2016 Alexander Altman
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::io;
use std::io::prelude::*;
use super::colored::*;
use super::{ExtensionPoint, Provenance};
use Provenance::*;

pub trait Presentation: ExtensionPoint {
  fn present(&self, diff: Vec<(String, Provenance)>);
}

impl<L: Presentation + ?Sized> Presentation for Box<L> {
  fn present(&self, diff: Vec<(String, Provenance)>) { self.as_ref().present(diff); }
}

#[derive(Eq,Ord,PartialEq,PartialOrd,Hash,Clone,Copy,Default,Debug)]
pub struct BasicColored;

impl ExtensionPoint for BasicColored {
  fn name(&self) -> String { "basic-colored".to_string() }

  fn description(&self) -> String { "Makes new-file-only parts red and old-file-only parts green".to_string() }
}

impl Presentation for BasicColored {
  fn present(&self, diff: Vec<(String, Provenance)>) {
    for (x, sx) in diff {
      print!("{}",
             match sx {
               Old => x.red(),
               Shared => x.as_str().into(),
               New => x.green(),
             });
      io::stdout().flush().expect("error when attempting to flush standard ouput");
    }
  }
}

#[derive(Eq,Ord,PartialEq,PartialOrd,Hash,Clone,Copy,Default,Debug)]
pub struct BasicStyled;

impl ExtensionPoint for BasicStyled {
  fn name(&self) -> String { "basic-styled".to_string() }

  fn description(&self) -> String { "Makes new-file-only parts bold and old-file-only parts italic".to_string() }
}

impl Presentation for BasicStyled {
  fn present(&self, diff: Vec<(String, Provenance)>) {
    for (x, sx) in diff {
      print!("{}",
             match sx {
               Old => x.italic(),
               Shared => x.as_str().into(),
               New => x.bold(),
             });
      io::stdout().flush().expect("error when attempting to flush standard ouput");
    }
  }
}
<commit_msg>Fixing stuff.<commit_after>// presentation.rs
// Copyright 2016 Alexander Altman
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::io;
use std::io::prelude::*;
use super::colored::*;
use super::{ExtensionPoint, Provenance};
use Provenance::*;

pub trait Presentation: ExtensionPoint {
  fn present(&self, diff: Vec<(String, Provenance)>);
}

impl<L: Presentation + ?Sized> Presentation for Box<L> {
  fn present(&self, diff: Vec<(String, Provenance)>) { self.as_ref().present(diff); }
}

#[derive(Eq,Ord,PartialEq,PartialOrd,Hash,Clone,Copy,Default,Debug)]
pub struct BasicColored;

impl ExtensionPoint for BasicColored {
  fn name(&self) -> String { "basic-colored".to_string() }

  fn description(&self) -> String { "Makes new-file-only parts red and old-file-only parts green".to_string() }
}

impl Presentation for BasicColored {
  fn present(&self, diff: Vec<(String, Provenance)>) {
    for (x, sx) in diff {
      print!("{}",
             match sx {
               Old => x.red(),
               Shared => x.normal(),
               New => x.green(),
             });
      io::stdout().flush().expect("error when attempting to flush standard ouput");
    }
  }
}

#[derive(Eq,Ord,PartialEq,PartialOrd,Hash,Clone,Copy,Default,Debug)]
pub struct BasicStyled;

impl ExtensionPoint for BasicStyled {
  fn name(&self) -> String { "basic-styled".to_string() }

  fn description(&self) -> String { "Makes new-file-only parts bold and old-file-only parts underlined".to_string() }
}

impl Presentation for BasicStyled {
  fn present(&self, diff: Vec<(String, Provenance)>) {
    for (x, sx) in diff {
      print!("{}",
             match sx {
               Old => x.underline(),
               Shared => x.normal(),
               New => x.bold(),
             });
      io::stdout().flush().expect("error when attempting to flush standard ouput");
    }
  }
}
<|endoftext|>


<commit_before>use super::*;

#[test]
// #[ignore]
fn symbol() {
    print_str("abc", "abc");
}

#[test]
// #[ignore]
fn number() {
    print_str("123", "123");
}

#[test]
// #[ignore]
fn unit() {
    print_str(";", ";");
}

#[test]
#[ignore]
fn tpiq() {
    // print_str("|: abc 123", ":< abc 123 >");
    print_str("|# abc 123", "#< abc 123 >");
}

#[test]
// #[ignore]
fn nested_tpiq() {
    print_str("|: |: cde |: abc 123 456", ":(:(cde :(abc 123)) 456)");
}

#[test]
// #[ignore]
fn list() {
    print_str("[abc 123]", ":(abc :(123 ;))");
}

#[test]
// #[ignore]
fn empty_env() {
    print_str("|% ; -1", "%(; -1)");
}

#[test]
// #[ignore]
fn resolve_piq() {
    print_str("|@ abc ;", "@(abc ;)");
}

#[test]
// #[ignore]
fn block() {
    print_str(
        r"|> ; |! |\ |% ; ; ^> -1 [|# abc 123 |@ ; abc] ;",
        r">(; !(\(%(; ;) >(-1 :(#(abc 123) :(@(; abc) ;)))) ;))"
    );
}

#[test]
// #[ignore]
fn evaled_symbol_ast() {
    print_evaled_str("abc", "abc");
}

#[test]
// #[ignore]
fn evaled_number_ast() {
    print_evaled_str("123", "123");
}

#[test]
// #[ignore]
fn evaled_define_ast() {
    // define symbol is number
    print_evaled_str("|> ; |# abc 123", ";");
}

/*
#[test]
#[ignore]
fn evaled_apply() {
    print_evaled_str(r"|> ; |! |\ |% ; ; 0 ;", r"0");
}
*/

#[test]
// #[ignore]
fn evaled_list() {
    print_evaled_str(r"|> ; ^> -1 [1 2 3]", r"3");
}

#[test]
// #[ignore]
fn evaled_defining_list() {
    // print_evaled_str(r"|> ; |# abc 123", r";");
    // print_evaled_str(r"|> ; |@ ; abc", r";");
    print_evaled_str(r"|> ; ^> -1 [|# abc 1234 |@ ; abc]", r"1234");
}

#[test]
// #[ignore]
fn access() {
    // print_str("|. a p", ".(a p)");
    // print_evaled_str("|> ; |. |: 1 3 p", "p")
    print_evaled_str("|> ; |. |: 1 3 q", "3")
}

#[test]
// #[ignore]
fn condition() {
    // print_str("|? abc 123", "?(abc 123)");
    // print_str("^T", "^T");
    // print_str("^F", "^F");
    print_evaled_str("|> ; |? ^T |: 1 0", "1");
    print_evaled_str("|> ; |? ^F |: 1 0", "0");
}

#[test]
// #[ignore]
fn exec_func() {
    // print_str(r"|% ; ;", ";a")
    // print_str(r"|> ; |! |\ |% ; ; 1 ;", ";a")
    print_evaled_str(r"|> ; |! |\ |% ; [a b c] |> ; ^> -1 [|@ ; c |@ ; b] [6667 6668 6669]", "6668")
}

#[test]
#[ignore]
fn primitive_function() {
    // print_evaled_str("|> ; |@ ; decr", "Prim(decr)");
    // print_evaled_str("|> ; |@ ; ltoreq", "Prim(ltoreq)");
    print_evaled_str("|> ; |! |> ; |@ ; decr [5]", "4");
    print_evaled_str("|> ; |! |> ; |@ ; ltoreq [5 4]", "^F"); // 5 <= 4
    // print_evaled_str("|> ; ^> -1 [|! |> ; |@ ; ltoreq [5 4] |! |> ; |@ ; decr [5]]", ";");
    // print_evaled_str(r"|> ; ^> -1 [|! |> ; |@ ; ltoreq [5 4] |! |> ; |\ |% ; [x] |> ; ^> -1 [|@ ; x][5642]]", ";");
}

#[test]
#[ignore]
fn tarai() {
    /*
    print_evaled_str(
        r"|> ; ^> -1 [
          |# abc |\ |% ; [x] ^> -1 [|@ ; x]
          |! |> ; |@ ; abc [8888]
        ]",
        "8888",
    );
    */

    /*
    print_evaled_str(
        r"|> ; ^> -1 [
          |# abc |\ |% ; [x]
                    ^> -1 [
                       |? |> ; |! |> ; |@ ; ltoreq [|> ; |@ ; x 10]
                          |: 1234
                             4321
                       ]
          |! |> ; |@ ; abc [8888]
        ]",
        "8888",
    );
    */

    // (defun tak (x y z)
    //   (if (<= x y)
    //       y
    //       (tak (tak (1- x) y z)
    //            (tak (1- y) z x)
    //            (tak (1- z) x y))))
    // (tak 12 6 0)

    /*
    print_evaled_str(
        r"|> ; ^> -1
        [
            |# recursive |\ |% ; [x]
                      ^> -1 [
                         |? |> ; |! |> ; |@ ; ltoreq [|> ; |@ ; x 0]
                            |: ^T
                               |> ; |! |> ; |@ ; recursive [0]
                      ]

            |! |> ; |@ ; recursive [1]
        ]",
        r";",
    );
    */

    print_evaled_str(
        r"|> ; ^> -1
        [
            |# tak |\ |% ; [x y z]
                      ^> -1 [
                         |? |> ; |! |> ; |@ ; ltoreq [|> ; |@ ; x |> ; |@ ; y]
                            |: |> ; |@ ; y
                               |> ; |! |> ; |@ ; tak [
                                  |> ; |! |> ; |@ ; tak [|> ; |! |> ; |@ ; decr [|> ; |@ ; x] |> ; |@ ; y |> ; |@ ; z]
                                  |> ; |! |> ; |@ ; tak [|> ; |! |> ; |@ ; decr [|> ; |@ ; y] |> ; |@ ; z |> ; |@ ; x]
                                  |> ; |! |> ; |@ ; tak [|> ; |! |> ; |@ ; decr [|> ; |@ ; z] |> ; |@ ; x |> ; |@ ; y]
                               ]
                      ]

            |! |> ; |@ ; tak [2 1 0]
        ]",
        r"2"
    );
}

#[test]
#[ignore]
fn fib() {
    print_evaled_str(
        r"|> ; ^> -1
        [
            |# fib |\ |% ; [n]
                      ^> -1 [
                         |? |> ; |! |> ; |@ ; eq [|> ; |@ ; n 0]
                            |: 0
                               |> ; |? |> ; |! |> ; |@ ; eq [|> ; |@ ; n 1]
                                       |: 1
                                       |> ; |! |> ; |@ ; plus [
                                          |> ; |! |> ; |@ ; fib [|> ; |! |> ; |@ ; minus [|> ; |@ ; n 2]]
                                          |> ; |! |> ; |@ ; fib [|> ; |! |> ; |@ ; minus [|> ; |@ ; n 1]]
                                       ]
                      ]
            |! |> ; |@ ; fib [30]
        ]",
        r"832040"
    );
}

#[test]
#[ignore]
fn prim() {
    print_evaled_str(
        r"|> ; ^> -1
        [
            |# x 1
            |# y 2
            |! |> ; |@ ; ltoreq [|> ; |@ ; x |> ; |@ ; y]
        ]",
        r"^T"
    );
}

#[test]
// #[ignore]
fn text() {
    print_str(r#""o58nkry drtse""#, r#""o58nkry drtse""#);
}

#[test]
// #[ignore]
fn evaled_text() {
    print_evaled_str(r#""o58nkry drtse"", r#""o58nkry drtse""#);
}

#[test]
// #[ignore]
fn evaled_text_in_list() {
    print_str(r#"["bbb" 0 "aaa"]"#, r#":("bbb" :(0 :("aaa" ;)))"#);
}

#[test]
fn access_in_list() {
    print_evaled_str("'> |. |: 0 1 p", "0");
}

#[test]
fn single_quote() {
    print_str("'> [a]", ">(; :(a ;))");
}

#[test]
fn resolve_syntax_sugar() {
    print_str("@abc", ">(; @(; abc))");
}

#[test]
fn apply_syntax_sugar() {
    print_syntax_sugar("abc! [1 2]", "'> |! abc [1 2]");
}

#[test]
fn resolve_and_apply_syntax_sugar() {
    print_syntax_sugar("@abc! [1 3]", "'> |! '> '@ abc [1 3]");
}

#[test]
fn lpiq() {
    print_syntax_sugar(r#"abc:"a""#, r#"|: abc "a""#);
}

#[test]
fn nested_lpiq() {
    print_syntax_sugar(r#"abc:"a":1"#, r#"|: abc |: "a" 1"#);
}

#[test]
fn accessor() {
    print_syntax_sugar("a.b", "'> |. a b");
}

#[test]
fn nested_accessor() {
    print_syntax_sugar("a.b.c.d", "'> |. a '> |. b '> |. c d");
}
<commit_msg>fix bug for Rust raw string<commit_after>use super::*;

#[test]
// #[ignore]
fn symbol() {
    print_str("abc", "abc");
}

#[test]
// #[ignore]
fn number() {
    print_str("123", "123");
}

#[test]
// #[ignore]
fn unit() {
    print_str(";", ";");
}

#[test]
#[ignore]
fn tpiq() {
    // print_str("|: abc 123", ":< abc 123 >");
    print_str("|# abc 123", "#< abc 123 >");
}

#[test]
// #[ignore]
fn nested_tpiq() {
    print_str("|: |: cde |: abc 123 456", ":(:(cde :(abc 123)) 456)");
}

#[test]
// #[ignore]
fn list() {
    print_str("[abc 123]", ":(abc :(123 ;))");
}

#[test]
// #[ignore]
fn empty_env() {
    print_str("|% ; -1", "%(; -1)");
}

#[test]
// #[ignore]
fn resolve_piq() {
    print_str("|@ abc ;", "@(abc ;)");
}

#[test]
// #[ignore]
fn block() {
    print_str(
        r"|> ; |! |\ |% ; ; ^> -1 [|# abc 123 |@ ; abc] ;",
        r">(; !(\(%(; ;) >(-1 :(#(abc 123) :(@(; abc) ;)))) ;))"
    );
}

#[test]
// #[ignore]
fn evaled_symbol_ast() {
    print_evaled_str("abc", "abc");
}

#[test]
// #[ignore]
fn evaled_number_ast() {
    print_evaled_str("123", "123");
}

#[test]
// #[ignore]
fn evaled_define_ast() {
    // define symbol is number
    print_evaled_str("|> ; |# abc 123", ";");
}

/*
#[test]
#[ignore]
fn evaled_apply() {
    print_evaled_str(r"|> ; |! |\ |% ; ; 0 ;", r"0");
}
*/

#[test]
// #[ignore]
fn evaled_list() {
    print_evaled_str(r"|> ; ^> -1 [1 2 3]", r"3");
}

#[test]
// #[ignore]
fn evaled_defining_list() {
    // print_evaled_str(r"|> ; |# abc 123", r";");
    // print_evaled_str(r"|> ; |@ ; abc", r";");
    print_evaled_str(r"|> ; ^> -1 [|# abc 1234 |@ ; abc]", r"1234");
}

#[test]
// #[ignore]
fn access() {
    // print_str("|. a p", ".(a p)");
    // print_evaled_str("|> ; |. |: 1 3 p", "p")
    print_evaled_str("|> ; |. |: 1 3 q", "3")
}

#[test]
// #[ignore]
fn condition() {
    // print_str("|? abc 123", "?(abc 123)");
    // print_str("^T", "^T");
    // print_str("^F", "^F");
    print_evaled_str("|> ; |? ^T |: 1 0", "1");
    print_evaled_str("|> ; |? ^F |: 1 0", "0");
}

#[test]
// #[ignore]
fn exec_func() {
    // print_str(r"|% ; ;", ";a")
    // print_str(r"|> ; |! |\ |% ; ; 1 ;", ";a")
    print_evaled_str(r"|> ; |! |\ |% ; [a b c] |> ; ^> -1 [|@ ; c |@ ; b] [6667 6668 6669]", "6668")
}

#[test]
#[ignore]
fn primitive_function() {
    // print_evaled_str("|> ; |@ ; decr", "Prim(decr)");
    // print_evaled_str("|> ; |@ ; ltoreq", "Prim(ltoreq)");
    print_evaled_str("|> ; |! |> ; |@ ; decr [5]", "4");
    print_evaled_str("|> ; |! |> ; |@ ; ltoreq [5 4]", "^F"); // 5 <= 4
    // print_evaled_str("|> ; ^> -1 [|! |> ; |@ ; ltoreq [5 4] |! |> ; |@ ; decr [5]]", ";");
    // print_evaled_str(r"|> ; ^> -1 [|! |> ; |@ ; ltoreq [5 4] |! |> ; |\ |% ; [x] |> ; ^> -1 [|@ ; x][5642]]", ";");
}

#[test]
#[ignore]
fn tarai() {
    /*
    print_evaled_str(
        r"|> ; ^> -1 [
          |# abc |\ |% ; [x] ^> -1 [|@ ; x]
          |! |> ; |@ ; abc [8888]
        ]",
        "8888",
    );
    */

    /*
    print_evaled_str(
        r"|> ; ^> -1 [
          |# abc |\ |% ; [x]
                    ^> -1 [
                       |? |> ; |! |> ; |@ ; ltoreq [|> ; |@ ; x 10]
                          |: 1234
                             4321
                       ]
          |! |> ; |@ ; abc [8888]
        ]",
        "8888",
    );
    */

    // (defun tak (x y z)
    //   (if (<= x y)
    //       y
    //       (tak (tak (1- x) y z)
    //            (tak (1- y) z x)
    //            (tak (1- z) x y))))
    // (tak 12 6 0)

    /*
    print_evaled_str(
        r"|> ; ^> -1
        [
            |# recursive |\ |% ; [x]
                      ^> -1 [
                         |? |> ; |! |> ; |@ ; ltoreq [|> ; |@ ; x 0]
                            |: ^T
                               |> ; |! |> ; |@ ; recursive [0]
                      ]

            |! |> ; |@ ; recursive [1]
        ]",
        r";",
    );
    */

    print_evaled_str(
        r"|> ; ^> -1
        [
            |# tak |\ |% ; [x y z]
                      ^> -1 [
                         |? |> ; |! |> ; |@ ; ltoreq [|> ; |@ ; x |> ; |@ ; y]
                            |: |> ; |@ ; y
                               |> ; |! |> ; |@ ; tak [
                                  |> ; |! |> ; |@ ; tak [|> ; |! |> ; |@ ; decr [|> ; |@ ; x] |> ; |@ ; y |> ; |@ ; z]
                                  |> ; |! |> ; |@ ; tak [|> ; |! |> ; |@ ; decr [|> ; |@ ; y] |> ; |@ ; z |> ; |@ ; x]
                                  |> ; |! |> ; |@ ; tak [|> ; |! |> ; |@ ; decr [|> ; |@ ; z] |> ; |@ ; x |> ; |@ ; y]
                               ]
                      ]

            |! |> ; |@ ; tak [2 1 0]
        ]",
        r"2"
    );
}

#[test]
#[ignore]
fn fib() {
    print_evaled_str(
        r"|> ; ^> -1
        [
            |# fib |\ |% ; [n]
                      ^> -1 [
                         |? |> ; |! |> ; |@ ; eq [|> ; |@ ; n 0]
                            |: 0
                               |> ; |? |> ; |! |> ; |@ ; eq [|> ; |@ ; n 1]
                                       |: 1
                                       |> ; |! |> ; |@ ; plus [
                                          |> ; |! |> ; |@ ; fib [|> ; |! |> ; |@ ; minus [|> ; |@ ; n 2]]
                                          |> ; |! |> ; |@ ; fib [|> ; |! |> ; |@ ; minus [|> ; |@ ; n 1]]
                                       ]
                      ]
            |! |> ; |@ ; fib [30]
        ]",
        r"832040"
    );
}

#[test]
#[ignore]
fn prim() {
    print_evaled_str(
        r"|> ; ^> -1
        [
            |# x 1
            |# y 2
            |! |> ; |@ ; ltoreq [|> ; |@ ; x |> ; |@ ; y]
        ]",
        r"^T"
    );
}

#[test]
// #[ignore]
fn text() {
    print_str(r#""o58nkry drtse""#, r#""o58nkry drtse""#);
}

#[test]
// #[ignore]
fn evaled_text() {
    print_evaled_str(r#""o58nkry drtse""#, r#""o58nkry drtse""#);
}

#[test]
// #[ignore]
fn evaled_text_in_list() {
    print_str(r#"["bbb" 0 "aaa"]"#, r#":("bbb" :(0 :("aaa" ;)))"#);
}

#[test]
fn access_in_list() {
    print_evaled_str("'> |. |: 0 1 p", "0");
}

#[test]
fn single_quote() {
    print_str("'> [a]", ">(; :(a ;))");
}

#[test]
fn resolve_syntax_sugar() {
    print_str("@abc", ">(; @(; abc))");
}

#[test]
fn apply_syntax_sugar() {
    print_syntax_sugar("abc! [1 2]", "'> |! abc [1 2]");
}

#[test]
fn resolve_and_apply_syntax_sugar() {
    print_syntax_sugar("@abc! [1 3]", "'> |! '> '@ abc [1 3]");
}

#[test]
fn lpiq() {
    print_syntax_sugar(r#"abc:"a""#, r#"|: abc "a""#);
}

#[test]
fn nested_lpiq() {
    print_syntax_sugar(r#"abc:"a":1"#, r#"|: abc |: "a" 1"#);
}

#[test]
fn accessor() {
    print_syntax_sugar("a.b", "'> |. a b");
}

#[test]
fn nested_accessor() {
    print_syntax_sugar("a.b.c.d", "'> |. a '> |. b '> |. c d");
}
<|endoftext|>