<commit_before>use crate::animations::{Anim, AnimState, Animations};
use crate::components;
use crate::event::Event;
use crate::flags::Flags;
use crate::fov::SightFov;
use crate::item::{Item, Slot, Stacking};
use crate::location::{Location, Portal};
use crate::mutate::Mutate;
use crate::query::Query;
use crate::sector::{WorldSkeleton, SECTOR_WIDTH};
use crate::spatial::{Place, Spatial};
use crate::spec::EntitySpawn;
use crate::terraform::{Terraform, TerrainQuery};
use crate::terrain::Terrain;
use crate::volume::Volume;
use crate::world_cache::WorldCache;
use crate::Distribution;
use crate::Rng;
use calx::{seeded_rng, HexFov, HexFovIter};
use calx_ecs::Entity;
use serde_derive::{Deserialize, Serialize};
use std::collections::HashSet;
use std::iter::FromIterator;
use std::slice;

pub const GAME_VERSION: &str = "0.1.0";

calx_ecs::build_ecs! {
    desc: components::Desc,
    anim: Anim,
    map_memory: components::MapMemory,
    health: components::Health,
    brain: components::Brain,
    item: Item,
    stacking: Stacking,
    stats: components::StatsComponent,
    status: components::Statuses,
}

/// Toplevel game state object.
#[derive(Serialize, Deserialize)]
pub struct World {
    /// Game version. Not mutable in the slightest, but the simplest way to
    /// get versioned save files is to just drop it here.
    version: String,
    /// Entity component system.
    ecs: Ecs,
    /// Static startup game world
    world_cache: WorldCache,
    /// Spawns from worldgen that have been generated in world.
    generated_spawns: HashSet<(Location, EntitySpawn)>,
    /// Spatial index for game entities.
    pub(crate) spatial: Spatial,
    /// Global gamestate flags.
    flags: Flags,
    /// Persistent random number generator.
    rng: Rng,
    /// Event queue
    pub(crate) events: Vec<Event>,
}

impl World {
    pub fn new(seed: u32, skeleton: WorldSkeleton) -> World {
        let mut ret = World {
            version: GAME_VERSION.to_string(),
            ecs: Ecs::new(),
            world_cache: WorldCache::new(seed, skeleton),
            generated_spawns: Default::default(),
            spatial: Spatial::new(),
            flags: Flags::new(),
            rng: seeded_rng(&seed),
            events: Vec::new(),
        };

        // TODO non-lexical borrow
        let player_entry = ret.world_cache.player_entrance();
        ret.spawn_player(player_entry);
        ret.generate_world_spawns();

        ret
    }

    pub fn events(&self) -> &Vec<Event> { &self.events }

    pub(crate) fn clear_events(&mut self) { self.events.clear() }

    fn generate_world_spawns(&mut self) {
        let mut spawns = self.world_cache.drain_spawns();
        spawns.retain(|s| !self.generated_spawns.contains(s));
        let seed = self.rng_seed();

        for (loc, s) in &spawns {
            // Create one-off RNG from just the spawn info, will always run the same for same info.
            let mut rng = calx::seeded_rng(&(seed, loc, s));
            // Construct loadout from the spawn info and generate it in world.
            self.spawn(&s.sample(&mut rng), *loc);
            self.generated_spawns.insert((*loc, s.clone()));
        }
    }
}

impl TerrainQuery for World {
    fn is_valid_location(&self, _loc: Location) -> bool {
        //Location::origin().v2_at(loc).map_or(false, ::on_screen)
        true
    }

    fn terrain(&self, loc: Location) -> Terrain {
        let mut t = self.world_cache.get_terrain(loc);

        if t == Terrain::Door && self.has_mobs(loc) {
            // Standing in the doorway opens the door.
            t = Terrain::OpenDoor;
        }

        t
    }

    fn portal(&self, loc: Location) -> Option<Location> { self.world_cache.get_portal(loc) }

    fn is_untouched(&self, _loc: Location) -> bool { unimplemented!() }
}

impl Query for World {
    fn location(&self, e: Entity) -> Option<Location> {
        match self.spatial.get(e) {
            Some(Place::At(loc)) => Some(loc),
            Some(Place::In(container, _)) => self.location(container),
            _ => None,
        }
    }

    fn player(&self) -> Option<Entity> {
        if let Some(p) = self.flags.player {
            if self.is_alive(p) {
                return Some(p);
            }
        }

        None
    }

    fn get_tick(&self) -> u64 { self.flags.tick }

    fn rng_seed(&self) -> u32 { self.world_cache.seed() }

    fn entities(&self) -> slice::Iter<'_, Entity> { self.ecs.iter() }

    fn entities_at(&self, loc: Location) -> Vec<Entity> { self.spatial.entities_at(loc) }

    fn entities_in(&self, parent: Entity) -> Vec<(Slot, Entity)> {
        self.spatial.entities_in(parent)
    }

    fn is_empty(&self, e: Entity) -> bool { self.spatial.is_empty(e) }

    fn ecs(&self) -> &Ecs { &self.ecs }

    fn entity_equipped(&self, parent: Entity, slot: Slot) -> Option<Entity> {
        self.spatial.entity_equipped(parent, slot)
    }

    fn entity_contains(&self, parent: Entity, child: Entity) -> bool {
        self.spatial.contains(parent, child)
    }

    fn entity_slot(&self, e: Entity) -> Option<Slot> {
        if let Some(Place::In(_, slot)) = self.spatial.get(e) {
            Some(slot)
        } else {
            None
        }
    }

    fn sphere_volume(&self, origin: Location, radius: u32) -> Volume {
        Volume::sphere(self, origin, radius)
    }
}

impl Mutate for World {
    fn next_tick(&mut self) {
        self.generate_world_spawns();
        self.tick_anims();

        self.ai_main();

        self.clean_dead();
        self.flags.tick += 1;

        // Expiring entities (animation effects) disappear if their time is up.
        let es: Vec<Entity> = self.ecs.anim.ent_iter().cloned().collect();
        for e in es.into_iter() {
            if let Some(anim) = self.anim(e) {
                if anim
                    .anim_done_world_tick
                    .map(|t| t <= self.get_tick())
                    .unwrap_or(false)
                {
                    self.kill_entity(e);
                }
            }
        }
    }

    fn set_entity_location(&mut self, e: Entity, loc: Location) { self.spatial.insert_at(e, loc); }

    fn equip_item(&mut self, e: Entity, parent: Entity, slot: Slot) {
        self.spatial.equip(e, parent, slot);
        self.rebuild_stats(parent);
    }

    fn set_player(&mut self, player: Option<Entity>) { self.flags.player = player; }

    fn spawn(&mut self, loadout: &Loadout, loc: Location) -> Entity {
        let e = loadout.make(&mut self.ecs);
        self.place_entity(e, loc);
        e
    }

    fn kill_entity(&mut self, e: Entity) {
        if self.count(e) > 1 {
            self.ecs_mut().stacking[e].count -= 1;
        } else {
            self.spatial.remove(e);
        }
    }

    fn remove_entity(&mut self, e: Entity) { self.ecs.remove(e); }

    fn do_fov(&mut self, e: Entity) {
        if !self.ecs.map_memory.contains(e) {
            return;
        }

        if let Some(origin) = self.location(e) {
            const DEFAULT_FOV_RANGE: u32 = 7;
            const OVERLAND_FOV_RANGE: u32 = SECTOR_WIDTH as u32;

            // Long-range sight while in overworld.
            // XXX: Presumes that overland iff z == 0, this might not be guaranteed...
            let range = if origin.z == 0 {
                OVERLAND_FOV_RANGE
            } else {
                DEFAULT_FOV_RANGE
            };

            let fov: HashSet<Location> = HashSet::from_iter(
                HexFov::new(SightFov::new(self, range, origin))
                    .add_fake_isometric_acute_corners(|pos, a| {
                        self.terrain(a.origin + pos).is_wall()
                    })
                    .map(|(pos, a)| a.origin + pos),
            );

            let memory = &mut self.ecs.map_memory[e];
            memory.seen.clear();

            for &loc in &fov {
                memory.seen.insert(loc);
                memory.remembered.insert(loc);
            }
        }
    }

    fn push_event(&mut self, event: Event) { self.events.push(event); }

    fn rng(&mut self) -> &mut Rng { &mut self.rng }

    fn ecs_mut(&mut self) -> &mut Ecs { &mut self.ecs }

    /// Spawn a transient animation effect.
    fn spawn_fx(&mut self, loc: Location, state: AnimState) -> Entity {
        let e = self.ecs.make();
        self.place_entity(e, loc);

        let mut anim = Anim::default();
        debug_assert!(state.is_transient_anim_state());
        anim.state = state;
        anim.anim_start = self.get_anim_tick();

        // Set the (world clock, not anim clock to preserve determinism) time when animation entity
        // should be cleaned up.
        // XXX: Animations stick around for a bunch of time after becoming spent and invisible,
        // simpler than trying to figure out precise durations.
        anim.anim_done_world_tick = Some(self.get_tick() + 300);

        self.ecs.anim.insert(e, anim);
        e
    }
}

impl Terraform for World {
    fn set_terrain(&mut self, _loc: Location, _terrain: Terrain) {
        unimplemented!();
    }

    fn set_portal(&mut self, _loc: Location, _portal: Portal) {
        unimplemented!();
    }

    fn remove_portal(&mut self, _loc: Location) {
        unimplemented!();
    }
}

impl Animations for World {
    fn get_anim_tick(&self) -> u64 { self.flags.anim_tick }
    fn anim(&self, e: Entity) -> Option<&Anim> { self.ecs.anim.get(e) }
    fn anim_mut(&mut self, e: Entity) -> Option<&mut Anim> { self.ecs.anim.get_mut(e) }
    fn tick_anims(&mut self) { self.flags.anim_tick += 1; }
}
<commit_msg>Simplify syntax now that it borrow checks<commit_after>use crate::animations::{Anim, AnimState, Animations};
use crate::components;
use crate::event::Event;
use crate::flags::Flags;
use crate::fov::SightFov;
use crate::item::{Item, Slot, Stacking};
use crate::location::{Location, Portal};
use crate::mutate::Mutate;
use crate::query::Query;
use crate::sector::{WorldSkeleton, SECTOR_WIDTH};
use crate::spatial::{Place, Spatial};
use crate::spec::EntitySpawn;
use crate::terraform::{Terraform, TerrainQuery};
use crate::terrain::Terrain;
use crate::volume::Volume;
use crate::world_cache::WorldCache;
use crate::Distribution;
use crate::Rng;
use calx::{seeded_rng, HexFov, HexFovIter};
use calx_ecs::Entity;
use serde_derive::{Deserialize, Serialize};
use std::collections::HashSet;
use std::iter::FromIterator;
use std::slice;

pub const GAME_VERSION: &str = "0.1.0";

calx_ecs::build_ecs! {
    desc: components::Desc,
    anim: Anim,
    map_memory: components::MapMemory,
    health: components::Health,
    brain: components::Brain,
    item: Item,
    stacking: Stacking,
    stats: components::StatsComponent,
    status: components::Statuses,
}

/// Toplevel game state object.
#[derive(Serialize, Deserialize)]
pub struct World {
    /// Game version. Not mutable in the slightest, but the simplest way to
    /// get versioned save files is to just drop it here.
    version: String,
    /// Entity component system.
    ecs: Ecs,
    /// Static startup game world
    world_cache: WorldCache,
    /// Spawns from worldgen that have been generated in world.
    generated_spawns: HashSet<(Location, EntitySpawn)>,
    /// Spatial index for game entities.
    pub(crate) spatial: Spatial,
    /// Global gamestate flags.
    flags: Flags,
    /// Persistent random number generator.
    rng: Rng,
    /// Event queue
    pub(crate) events: Vec<Event>,
}

impl World {
    pub fn new(seed: u32, skeleton: WorldSkeleton) -> World {
        let mut ret = World {
            version: GAME_VERSION.to_string(),
            ecs: Ecs::new(),
            world_cache: WorldCache::new(seed, skeleton),
            generated_spawns: Default::default(),
            spatial: Spatial::new(),
            flags: Flags::new(),
            rng: seeded_rng(&seed),
            events: Vec::new(),
        };

        ret.spawn_player(ret.world_cache.player_entrance());
        ret.generate_world_spawns();

        ret
    }

    pub fn events(&self) -> &Vec<Event> { &self.events }

    pub(crate) fn clear_events(&mut self) { self.events.clear() }

    fn generate_world_spawns(&mut self) {
        let mut spawns = self.world_cache.drain_spawns();
        spawns.retain(|s| !self.generated_spawns.contains(s));
        let seed = self.rng_seed();

        for (loc, s) in &spawns {
            // Create one-off RNG from just the spawn info, will always run the same for same info.
            let mut rng = calx::seeded_rng(&(seed, loc, s));
            // Construct loadout from the spawn info and generate it in world.
            self.spawn(&s.sample(&mut rng), *loc);
            self.generated_spawns.insert((*loc, s.clone()));
        }
    }
}

impl TerrainQuery for World {
    fn is_valid_location(&self, _loc: Location) -> bool {
        //Location::origin().v2_at(loc).map_or(false, ::on_screen)
        true
    }

    fn terrain(&self, loc: Location) -> Terrain {
        let mut t = self.world_cache.get_terrain(loc);

        if t == Terrain::Door && self.has_mobs(loc) {
            // Standing in the doorway opens the door.
            t = Terrain::OpenDoor;
        }

        t
    }

    fn portal(&self, loc: Location) -> Option<Location> { self.world_cache.get_portal(loc) }

    fn is_untouched(&self, _loc: Location) -> bool { unimplemented!() }
}

impl Query for World {
    fn location(&self, e: Entity) -> Option<Location> {
        match self.spatial.get(e) {
            Some(Place::At(loc)) => Some(loc),
            Some(Place::In(container, _)) => self.location(container),
            _ => None,
        }
    }

    fn player(&self) -> Option<Entity> {
        if let Some(p) = self.flags.player {
            if self.is_alive(p) {
                return Some(p);
            }
        }

        None
    }

    fn get_tick(&self) -> u64 { self.flags.tick }

    fn rng_seed(&self) -> u32 { self.world_cache.seed() }

    fn entities(&self) -> slice::Iter<'_, Entity> { self.ecs.iter() }

    fn entities_at(&self, loc: Location) -> Vec<Entity> { self.spatial.entities_at(loc) }

    fn entities_in(&self, parent: Entity) -> Vec<(Slot, Entity)> {
        self.spatial.entities_in(parent)
    }

    fn is_empty(&self, e: Entity) -> bool { self.spatial.is_empty(e) }

    fn ecs(&self) -> &Ecs { &self.ecs }

    fn entity_equipped(&self, parent: Entity, slot: Slot) -> Option<Entity> {
        self.spatial.entity_equipped(parent, slot)
    }

    fn entity_contains(&self, parent: Entity, child: Entity) -> bool {
        self.spatial.contains(parent, child)
    }

    fn entity_slot(&self, e: Entity) -> Option<Slot> {
        if let Some(Place::In(_, slot)) = self.spatial.get(e) {
            Some(slot)
        } else {
            None
        }
    }

    fn sphere_volume(&self, origin: Location, radius: u32) -> Volume {
        Volume::sphere(self, origin, radius)
    }
}

impl Mutate for World {
    fn next_tick(&mut self) {
        self.generate_world_spawns();
        self.tick_anims();

        self.ai_main();

        self.clean_dead();
        self.flags.tick += 1;

        // Expiring entities (animation effects) disappear if their time is up.
        let es: Vec<Entity> = self.ecs.anim.ent_iter().cloned().collect();
        for e in es.into_iter() {
            if let Some(anim) = self.anim(e) {
                if anim
                    .anim_done_world_tick
                    .map(|t| t <= self.get_tick())
                    .unwrap_or(false)
                {
                    self.kill_entity(e);
                }
            }
        }
    }

    fn set_entity_location(&mut self, e: Entity, loc: Location) { self.spatial.insert_at(e, loc); }

    fn equip_item(&mut self, e: Entity, parent: Entity, slot: Slot) {
        self.spatial.equip(e, parent, slot);
        self.rebuild_stats(parent);
    }

    fn set_player(&mut self, player: Option<Entity>) { self.flags.player = player; }

    fn spawn(&mut self, loadout: &Loadout, loc: Location) -> Entity {
        let e = loadout.make(&mut self.ecs);
        self.place_entity(e, loc);
        e
    }

    fn kill_entity(&mut self, e: Entity) {
        if self.count(e) > 1 {
            self.ecs_mut().stacking[e].count -= 1;
        } else {
            self.spatial.remove(e);
        }
    }

    fn remove_entity(&mut self, e: Entity) { self.ecs.remove(e); }

    fn do_fov(&mut self, e: Entity) {
        if !self.ecs.map_memory.contains(e) {
            return;
        }

        if let Some(origin) = self.location(e) {
            const DEFAULT_FOV_RANGE: u32 = 7;
            const OVERLAND_FOV_RANGE: u32 = SECTOR_WIDTH as u32;

            // Long-range sight while in overworld.
            // XXX: Presumes that overland iff z == 0, this might not be guaranteed...
            let range = if origin.z == 0 {
                OVERLAND_FOV_RANGE
            } else {
                DEFAULT_FOV_RANGE
            };

            let fov: HashSet<Location> = HashSet::from_iter(
                HexFov::new(SightFov::new(self, range, origin))
                    .add_fake_isometric_acute_corners(|pos, a| {
                        self.terrain(a.origin + pos).is_wall()
                    })
                    .map(|(pos, a)| a.origin + pos),
            );

            let memory = &mut self.ecs.map_memory[e];
            memory.seen.clear();

            for &loc in &fov {
                memory.seen.insert(loc);
                memory.remembered.insert(loc);
            }
        }
    }

    fn push_event(&mut self, event: Event) { self.events.push(event); }

    fn rng(&mut self) -> &mut Rng { &mut self.rng }

    fn ecs_mut(&mut self) -> &mut Ecs { &mut self.ecs }

    /// Spawn a transient animation effect.
    fn spawn_fx(&mut self, loc: Location, state: AnimState) -> Entity {
        let e = self.ecs.make();
        self.place_entity(e, loc);

        let mut anim = Anim::default();
        debug_assert!(state.is_transient_anim_state());
        anim.state = state;
        anim.anim_start = self.get_anim_tick();

        // Set the (world clock, not anim clock to preserve determinism) time when animation entity
        // should be cleaned up.
        // XXX: Animations stick around for a bunch of time after becoming spent and invisible,
        // simpler than trying to figure out precise durations.
        anim.anim_done_world_tick = Some(self.get_tick() + 300);

        self.ecs.anim.insert(e, anim);
        e
    }
}

impl Terraform for World {
    fn set_terrain(&mut self, _loc: Location, _terrain: Terrain) {
        unimplemented!();
    }

    fn set_portal(&mut self, _loc: Location, _portal: Portal) {
        unimplemented!();
    }

    fn remove_portal(&mut self, _loc: Location) {
        unimplemented!();
    }
}

impl Animations for World {
    fn get_anim_tick(&self) -> u64 { self.flags.anim_tick }
    fn anim(&self, e: Entity) -> Option<&Anim> { self.ecs.anim.get(e) }
    fn anim_mut(&mut self, e: Entity) -> Option<&mut Anim> { self.ecs.anim.get_mut(e) }
    fn tick_anims(&mut self) { self.flags.anim_tick += 1; }
}
<|endoftext|>


<commit_before>use crate::atlas_cache::AtlasCache;
use crate::backend::Backend;
use crate::keycode::Keycode;
use crate::{Canvas, FontData, ImageData, SubImageSpec, UiState};
use crate::{Flick, FLICKS_PER_SECOND};
use euclid::default::Size2D;
use euclid::size2;
use image::RgbaImage;
use std::error::Error;
use std::sync::Mutex;

pub type ImageKey = SubImageSpec<String>;

/// Toplevel type for current program GUI state.
///
/// Parametrized on a shared application state type T.
pub trait Scene<T> {
    /// Update the logic for this scene.
    ///
    /// May be called several times for one `render` call. Return value tells if this update should
    /// be followed by a change of scene.
    fn update(&mut self, _ctx: &mut T) -> Option<SceneSwitch<T>> { None }

    /// Draw this scene to a Vitral canvas.
    ///
    /// Render is separate from update for frame rate regulation reasons. If drawing is slow,
    /// multiple updates will be run for one render call.
    ///
    /// The render method can also introduce scene transitions in case there is immediate mode GUI
    /// logic written in the render code.
    fn render(&mut self, _ctx: &mut T, _canvas: &mut Canvas) -> Option<SceneSwitch<T>> { None }

    /// Process an input event.
    fn input(
        &mut self,
        _ctx: &mut T,
        _event: &InputEvent,
        _canvas: &mut Canvas,
    ) -> Option<SceneSwitch<T>> {
        None
    }

    /// Return true if the scene below this one in the scene stack should be visible.
    ///
    /// Is true for scenes that implement a pop-up element instead of a full-screen scene.
    fn draw_previous(&self) -> bool { false }
}

/// Scene transition description.
pub enum SceneSwitch<T> {
    /// Exit from current scene and return to the previous one on the scene stack.
    Pop,
    /// Push a new scene on top of this one.
    Push(Box<dyn Scene<T>>),
    /// Replace this one with a different scene on the top of the stack.
    Replace(Box<dyn Scene<T>>),
}

#[derive(Clone, Debug)]
pub enum InputEvent {
    Typed(char),
    KeyEvent {
        is_down: bool,
        key: Option<Keycode>,
        hardware_key: Option<Keycode>,
    },
}

pub struct AppConfig {
    pub frame_duration: Flick,
    pub resolution: Size2D<u32>,
    pub window_title: String,
}

impl AppConfig {
    pub fn new(title: impl Into<String>) -> AppConfig {
        AppConfig {
            frame_duration: Flick(FLICKS_PER_SECOND / 30),
            resolution: size2(640, 360),
            window_title: title.into(),
        }
    }

    pub fn frame_duration(mut self, frame_duration: Flick) -> AppConfig {
        self.frame_duration = frame_duration;
        self
    }
}

#[derive(Default)]
struct EngineState {
    atlas_cache: AtlasCache<String>,
}

lazy_static! {
    /// Global game engine state.
    static ref ENGINE_STATE: Mutex<EngineState> = { Mutex::new(EngineState::default()) };
}

/// Start running a Scene state machine app with the given configuration.
pub fn run_app<T>(
    config: AppConfig,
    world: T,
    scenes: Vec<Box<dyn Scene<T>>>,
) -> Result<(), Box<dyn Error>> {
    // TODO: Maybe want world to come in as &mut T instead of T in the future so that the caller
    // can access it after run_app finishes? Or just return the world if exit is successful?
    let backend = Backend::start(
        config.resolution.width,
        config.resolution.height,
        config.window_title,
    )?;

    let mut gameloop = GameLoop::new(backend, world, scenes).frame_duration(config.frame_duration);

    gameloop.run();

    Ok(())
}

/// Return the average frame duration for recent frames.
///
/// Panics if called when an app isn't running via `run_app`.
pub fn get_frame_duration() -> Flick {
    unimplemented!();
}

/// Add a named image into the engine image atlas.
pub fn add_sheet(id: impl Into<String>, sheet: impl Into<RgbaImage>) -> ImageKey {
    ENGINE_STATE
        .lock()
        .unwrap()
        .atlas_cache
        .add_sheet(id, sheet)
}

/// Add a tilesheet image that gets automatically split to subimages based on image structure.
///
/// Tiles are bounding boxes of non-background pixel groups surrounded by only background pixels or
/// image edges. Background color is the color of the bottom right corner pixel of the image. The
/// bounding boxes are returned lexically sorted by the coordinates of their bottom right corners,
/// first along the y-axis then along the x-axis. This produces a natural left-to-right,
/// bottom-to-top ordering for a cleanly laid out tile sheet.
///
/// Note that the background color is a solid color, not transparent pixels. The inner tiles may
/// have transparent parts, so a solid color is needed to separate them.
pub fn add_tilesheet(
    id: impl Into<String>,
    sheet: impl Into<RgbaImage>,
    _span: impl IntoIterator<Item = char>,
) -> Vec<ImageKey> {
    ENGINE_STATE
        .lock()
        .unwrap()
        .atlas_cache
        .add_tilesheet(id, sheet)
}

/// Add a bitmap font read from a tilesheet image.
pub fn add_tilesheet_font(
    id: impl Into<String>,
    sheet: impl Into<RgbaImage>,
    span: impl IntoIterator<Item = char>,
) -> FontData {
    ENGINE_STATE
        .lock()
        .unwrap()
        .atlas_cache
        .add_tilesheet_font(id, sheet, span)
}

/// Get a drawable (sub)image from the cache corresponding to the given `ImageKey`.
///
/// If the `ImageKey` specifies a sheet not found in the cache or invalid dimensions, will return
/// `None`.
pub fn get_image(key: &ImageKey) -> Option<ImageData> {
    // TODO: Convert AtlasCache::get to match this API instead of panicing at failure
    // (Best done when conversion to new gameloop is done and AtlasCache API isn't used publicly
    // anymore.)
    Some(ENGINE_STATE.lock().unwrap().atlas_cache.get(key).clone())
}

/// `Scene` stack based game loop runner.
pub struct GameLoop<T> {
    frame_duration: Flick,
    scene_stack: Vec<Box<dyn Scene<T>>>,
    world: T,
    backend: Backend,
    ui: UiState,
}

impl<T> GameLoop<T> {
    pub fn new(backend: Backend, world: T, scenes: Vec<Box<dyn Scene<T>>>) -> GameLoop<T> {
        GameLoop {
            frame_duration: Flick(FLICKS_PER_SECOND / 30),
            scene_stack: scenes,
            world,
            backend,
            ui: UiState::default(),
        }
    }

    pub fn frame_duration(mut self, frame_duration: Flick) -> Self {
        self.frame_duration = frame_duration;
        self
    }

    fn update(&mut self) {
        let top = self.scene_stack.len() - 1;
        let ret = self.scene_stack[top].update(&mut self.world);
        self.process(ret);
    }

    fn process(&mut self, switch: Option<SceneSwitch<T>>) {
        let top = self.scene_stack.len() - 1;
        match switch {
            Some(SceneSwitch::Pop) => {
                self.scene_stack.pop();
            }
            Some(SceneSwitch::Push(scene)) => {
                self.scene_stack.push(scene);
            }
            Some(SceneSwitch::Replace(scene)) => {
                self.scene_stack[top] = scene;
            }
            None => {}
        }
    }

    fn render(&mut self) {
        // Find the lowest scene from top with only transparent scenes on top of it.
        let end = self.scene_stack.len();
        let mut begin = end - 1;
        while begin > 0 && self.scene_stack[begin].draw_previous() {
            begin -= 1;
        }

        let mut switch = None;
        let draw_list = {
            let mut canvas =
                Canvas::new(self.backend.canvas_size(), &mut self.ui, &mut self.backend);

            for i in begin..end {
                // Only the switch result from the topmost state counts here.
                switch = self.scene_stack[i].render(&mut self.world, &mut canvas);
            }
            canvas.end_frame()
        };
        self.process(switch);
        self.backend.render(&draw_list);
    }

    pub fn run(&mut self) {
        // Inspired by https://gafferongames.com/post/fix_your_timestep/
        let mut t = Flick::now();
        let mut accum = Flick(0);
        'gameloop: loop {
            let new_t = Flick::now();
            let frame_duration = new_t - t;
            t = new_t;

            accum += frame_duration;

            while accum >= self.frame_duration {
                self.update();
                accum -= self.frame_duration;
                if self.scene_stack.is_empty() {
                    break 'gameloop;
                }
            }

            self.backend
                .sync_with_atlas_cache(&mut ENGINE_STATE.lock().unwrap().atlas_cache);

            self.render();

            if self.scene_stack.is_empty() {
                break 'gameloop;
            }

            if let Ok(events) = self.backend.process_events(&mut self.ui) {
                for event in &events {
                    let idx = self.scene_stack.len() - 1;
                    let ret = {
                        let mut canvas = Canvas::new(
                            self.backend.canvas_size(),
                            &mut self.ui,
                            &mut self.backend,
                        );
                        self.scene_stack[idx].input(&mut self.world, event, &mut canvas)
                    };
                    self.process(ret);

                    if self.scene_stack.is_empty() {
                        break 'gameloop;
                    }
                }
            } else {
                // Window closed
                break 'gameloop;
            }
        }
    }
}
<commit_msg>Track how long scene updates and renders take<commit_after>use crate::atlas_cache::AtlasCache;
use crate::backend::Backend;
use crate::keycode::Keycode;
use crate::{Canvas, FontData, ImageData, SubImageSpec, UiState};
use crate::{Flick, FLICKS_PER_SECOND};
use euclid::default::Size2D;
use euclid::size2;
use image::RgbaImage;
use log::{debug, warn};
use std::error::Error;
use std::sync::Mutex;

pub type ImageKey = SubImageSpec<String>;

/// Toplevel type for current program GUI state.
///
/// Parametrized on a shared application state type T.
pub trait Scene<T> {
    /// Update the logic for this scene.
    ///
    /// May be called several times for one `render` call. Return value tells if this update should
    /// be followed by a change of scene.
    fn update(&mut self, _ctx: &mut T) -> Option<SceneSwitch<T>> { None }

    /// Draw this scene to a Vitral canvas.
    ///
    /// Render is separate from update for frame rate regulation reasons. If drawing is slow,
    /// multiple updates will be run for one render call.
    ///
    /// The render method can also introduce scene transitions in case there is immediate mode GUI
    /// logic written in the render code.
    fn render(&mut self, _ctx: &mut T, _canvas: &mut Canvas) -> Option<SceneSwitch<T>> { None }

    /// Process an input event.
    fn input(
        &mut self,
        _ctx: &mut T,
        _event: &InputEvent,
        _canvas: &mut Canvas,
    ) -> Option<SceneSwitch<T>> {
        None
    }

    /// Return true if the scene below this one in the scene stack should be visible.
    ///
    /// Is true for scenes that implement a pop-up element instead of a full-screen scene.
    fn draw_previous(&self) -> bool { false }
}

/// Scene transition description.
pub enum SceneSwitch<T> {
    /// Exit from current scene and return to the previous one on the scene stack.
    Pop,
    /// Push a new scene on top of this one.
    Push(Box<dyn Scene<T>>),
    /// Replace this one with a different scene on the top of the stack.
    Replace(Box<dyn Scene<T>>),
}

#[derive(Clone, Debug)]
pub enum InputEvent {
    Typed(char),
    KeyEvent {
        is_down: bool,
        key: Option<Keycode>,
        hardware_key: Option<Keycode>,
    },
}

pub struct AppConfig {
    pub frame_duration: Flick,
    pub resolution: Size2D<u32>,
    pub window_title: String,
}

impl AppConfig {
    pub fn new(title: impl Into<String>) -> AppConfig {
        AppConfig {
            frame_duration: Flick(FLICKS_PER_SECOND / 30),
            resolution: size2(640, 360),
            window_title: title.into(),
        }
    }

    pub fn frame_duration(mut self, frame_duration: Flick) -> AppConfig {
        self.frame_duration = frame_duration;
        self
    }
}

#[derive(Default)]
struct EngineState {
    atlas_cache: AtlasCache<String>,
    average_frame_duration: Flick,
}

lazy_static! {
    /// Global game engine state.
    static ref ENGINE_STATE: Mutex<EngineState> = { Mutex::new(EngineState::default()) };
}

/// Start running a Scene state machine app with the given configuration.
pub fn run_app<T>(
    config: AppConfig,
    world: T,
    scenes: Vec<Box<dyn Scene<T>>>,
) -> Result<(), Box<dyn Error>> {
    // TODO: Maybe want world to come in as &mut T instead of T in the future so that the caller
    // can access it after run_app finishes? Or just return the world if exit is successful?
    let backend = Backend::start(
        config.resolution.width,
        config.resolution.height,
        config.window_title,
    )?;

    let mut gameloop = GameLoop::new(backend, world, scenes).frame_duration(config.frame_duration);

    gameloop.run();

    Ok(())
}

/// Return the average frame duration for recent frames.
///
/// Panics if called when an app isn't running via `run_app`.
pub fn get_frame_duration() -> Flick { ENGINE_STATE.lock().unwrap().average_frame_duration }

/// Add a named image into the engine image atlas.
pub fn add_sheet(id: impl Into<String>, sheet: impl Into<RgbaImage>) -> ImageKey {
    ENGINE_STATE
        .lock()
        .unwrap()
        .atlas_cache
        .add_sheet(id, sheet)
}

/// Add a tilesheet image that gets automatically split to subimages based on image structure.
///
/// Tiles are bounding boxes of non-background pixel groups surrounded by only background pixels or
/// image edges. Background color is the color of the bottom right corner pixel of the image. The
/// bounding boxes are returned lexically sorted by the coordinates of their bottom right corners,
/// first along the y-axis then along the x-axis. This produces a natural left-to-right,
/// bottom-to-top ordering for a cleanly laid out tile sheet.
///
/// Note that the background color is a solid color, not transparent pixels. The inner tiles may
/// have transparent parts, so a solid color is needed to separate them.
pub fn add_tilesheet(
    id: impl Into<String>,
    sheet: impl Into<RgbaImage>,
    _span: impl IntoIterator<Item = char>,
) -> Vec<ImageKey> {
    ENGINE_STATE
        .lock()
        .unwrap()
        .atlas_cache
        .add_tilesheet(id, sheet)
}

/// Add a bitmap font read from a tilesheet image.
pub fn add_tilesheet_font(
    id: impl Into<String>,
    sheet: impl Into<RgbaImage>,
    span: impl IntoIterator<Item = char>,
) -> FontData {
    ENGINE_STATE
        .lock()
        .unwrap()
        .atlas_cache
        .add_tilesheet_font(id, sheet, span)
}

/// Get a drawable (sub)image from the cache corresponding to the given `ImageKey`.
///
/// If the `ImageKey` specifies a sheet not found in the cache or invalid dimensions, will return
/// `None`.
pub fn get_image(key: &ImageKey) -> Option<ImageData> {
    // TODO: Convert AtlasCache::get to match this API instead of panicing at failure
    // (Best done when conversion to new gameloop is done and AtlasCache API isn't used publicly
    // anymore.)
    Some(ENGINE_STATE.lock().unwrap().atlas_cache.get(key).clone())
}

/// `Scene` stack based game loop runner.
pub struct GameLoop<T> {
    frame_duration: Flick,
    scene_stack: Vec<Box<dyn Scene<T>>>,
    world: T,
    backend: Backend,
    ui: UiState,
}

impl<T> GameLoop<T> {
    pub fn new(backend: Backend, world: T, scenes: Vec<Box<dyn Scene<T>>>) -> GameLoop<T> {
        GameLoop {
            frame_duration: Flick(FLICKS_PER_SECOND / 30),
            scene_stack: scenes,
            world,
            backend,
            ui: UiState::default(),
        }
    }

    pub fn frame_duration(mut self, frame_duration: Flick) -> Self {
        self.frame_duration = frame_duration;
        self
    }

    fn update(&mut self) {
        let top = self.scene_stack.len() - 1;
        let ret = self.scene_stack[top].update(&mut self.world);
        self.process(ret);
    }

    fn process(&mut self, switch: Option<SceneSwitch<T>>) {
        let top = self.scene_stack.len() - 1;
        match switch {
            Some(SceneSwitch::Pop) => {
                self.scene_stack.pop();
            }
            Some(SceneSwitch::Push(scene)) => {
                self.scene_stack.push(scene);
            }
            Some(SceneSwitch::Replace(scene)) => {
                self.scene_stack[top] = scene;
            }
            None => {}
        }
    }

    fn render(&mut self) {
        // Find the lowest scene from top with only transparent scenes on top of it.
        let end = self.scene_stack.len();
        let mut begin = end - 1;
        while begin > 0 && self.scene_stack[begin].draw_previous() {
            begin -= 1;
        }

        let mut switch = None;
        let draw_list = {
            let mut canvas =
                Canvas::new(self.backend.canvas_size(), &mut self.ui, &mut self.backend);

            for i in begin..end {
                // Only the switch result from the topmost state counts here.
                switch = self.scene_stack[i].render(&mut self.world, &mut canvas);
            }
            canvas.end_frame()
        };
        self.process(switch);
        self.backend.render(&draw_list);
    }

    pub fn run(&mut self) {
        // Inspired by https://gafferongames.com/post/fix_your_timestep/
        let mut t = Flick::now();
        let mut accum = Flick(0);
        let mut average_duration = self.frame_duration;

        'gameloop: loop {
            let new_t = Flick::now();
            let frame_duration = new_t - t;

            average_duration =
                Flick((0.95 * average_duration.0 as f64 + 0.05 * frame_duration.0 as f64) as u64);
            ENGINE_STATE.lock().unwrap().average_frame_duration = average_duration;
            debug!(
                "FPS {:.1}",
                FLICKS_PER_SECOND as f64 / average_duration.0 as f64
            );

            t = new_t;

            accum += frame_duration;

            while accum >= self.frame_duration {
                let update_time = Flick::now();
                self.update();
                let update_time = Flick::now() - update_time;
                let update_ratio = update_time.0 as f64 / self.frame_duration.0 as f64;

                // If a single update takes most of the frame time, things are bad. Game loop may
                // enter a death spiral where every cycle needs more updates and makes the lag
                // worse. If an over-long update is detected, cut things short and flush the
                // accumulator.
                //
                // XXX: This has not been tested.
                if update_ratio > 0.9 {
                    warn!(
                        "Scene update took {} % of alotted frame time. Skipping further updates",
                        (update_ratio * 100.0) as i32
                    );

                    accum = Flick(0);
                    break;
                }

                accum -= self.frame_duration;
                if self.scene_stack.is_empty() {
                    break 'gameloop;
                }
            }

            self.backend
                .sync_with_atlas_cache(&mut ENGINE_STATE.lock().unwrap().atlas_cache);

            let render_time = Flick::now();
            self.render();
            let render_time = Flick::now() - render_time;
            let render_ratio = render_time.0 as f64 / self.frame_duration.0 as f64;

            // Renders can be expected to go over budget occasionally, but it's still a problem if
            // you want to keep a steady FPS, so make some log noise.
            if render_ratio > 1.0 {
                debug!(
                    "Scene render too {} % of alotted frame time",
                    (render_ratio * 100.0) as i32
                );
            }

            if self.scene_stack.is_empty() {
                break 'gameloop;
            }

            if let Ok(events) = self.backend.process_events(&mut self.ui) {
                for event in &events {
                    let idx = self.scene_stack.len() - 1;
                    let ret = {
                        let mut canvas = Canvas::new(
                            self.backend.canvas_size(),
                            &mut self.ui,
                            &mut self.backend,
                        );
                        self.scene_stack[idx].input(&mut self.world, event, &mut canvas)
                    };
                    self.process(ret);

                    if self.scene_stack.is_empty() {
                        break 'gameloop;
                    }
                }
            } else {
                // Window closed
                break 'gameloop;
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate combine;
extern crate combine_language;
extern crate mnemosyne as core;

use combine::*;
use combine_language::{ LanguageEnv
                      , LanguageDef
                      , Identifier
                      };
use combine::primitives::{ Stream
                         , Positioner
                         , SourcePosition
                         };

use core::semantic::*;
use core::semantic::annotations::{ Annotated
                                 , UnscopedState
                                 , Unscoped
                                 };
use core::semantic::types::*;
use core::semantic::ast::*;
use core::position::*;

use std::rc::Rc;

type ParseFn<'a, I, T> = fn (&MnEnv<'a, I>, State<I>) -> ParseResult<T, I>;

type U = UnscopedState;

/// Unicode code point for the lambda character
const LAMBDA: &'static str = "\u{03bb}";

mod tests;

/// Wraps a parsing function with a language definition environment.
///
/// TODO: this could probably push identifiers to the symbol table here?
#[derive(Copy)]
struct MnParser<'a: 'b, 'b, I, T>
where I: Stream<Item=char>
    , I::Range: 'b
    , I: 'b {
        env: &'b MnEnv<'a, I>
      , parser: ParseFn<'a, I, T>
}

impl<'a, 'b, I, T> Clone for MnParser<'a, 'b, I, T>
where I: Stream<Item=char>
    , I::Range: 'b
    , I: 'b
    , 'a: 'b {

    fn clone(&self) -> Self {
        MnParser { env: self.env , parser: self.parser }
    }
}

impl<'a, 'b, I, T> Parser for MnParser<'a, 'b, I, T>
where I: Stream<Item=char>
    , I::Range: 'b
    , I: 'b
    , 'a: 'b {

    type Input = I;
    type Output = T;

    fn parse_state(&mut self, input: State<I>) -> ParseResult<T, I> {
        (self.parser)(self.env, input)
    }

}

struct MnEnv<'a, I>
where I: Stream<Item = char>
    , I::Item: Positioner<Position = SourcePosition> {
    env: LanguageEnv<'a, I>
}

impl <'a, I> std::ops::Deref for MnEnv<'a, I>
where I: Stream<Item=char> {
    type Target = LanguageEnv<'a, I>;
    fn deref(&self) -> &LanguageEnv<'a, I> { &self.env }
}

impl<'a, 'b, I> MnEnv<'a, I>
where I: Stream<Item=char>
    , I::Item: Positioner<Position = SourcePosition>
    , I::Range: 'b {

    /// Wrap a function into a MnParser with this environment
    fn parser<T>(&'b self, parser: ParseFn<'a, I, T>)
                -> MnParser<'a, 'b, I, T> {
        MnParser { env: self, parser: parser }
    }

    #[allow(dead_code)]
    fn parse_def(&self, input: State<I>) -> ParseResult<Form<'a, U>, I> {
        let function_form
            = self.name()
                  .and(self.function())
                  .map(|(name, fun)| DefForm::Function { name: name
                                                       , fun: fun });
        let top_level
            = self.name()
                  .and(self.type_annotation())
                  .and(self.expr())
                  .map(|((name, ty), body)|
                    DefForm::TopLevel { name: name
                                      , annot: ty
                                      , value: Rc::new(body) });

        self.reserved("defn").or(self.reserved("define"))
            .with(function_form.or(top_level))
            .map(Form::Define)
            .parse_state(input)
    }

    #[allow(dead_code)]
    fn parse_if(&self, input: State<I>) -> ParseResult<Form<'a, U>, I> {
        self.reserved("if")
            .with(self.expr())
            .and(self.expr())
            .and(optional(self.expr()))
            .map(|((cond, if_clause), else_clause)|
                Form::If { condition: Rc::new(cond)
                         , if_clause: Rc::new(if_clause)
                         , else_clause: else_clause.map(Rc::new)
                         })
            .parse_state(input)
    }

    #[allow(dead_code)]
    fn parse_lambda(&self, input: State<I>) -> ParseResult<Form<'a, U>, I> {
        self.reserved("lambda")
            .or(self.reserved(LAMBDA))
            .with(self.function())
            .map(Form::Lambda)
            .parse_state(input)
    }

    #[allow(dead_code)]
    fn parse_function(&self, input: State<I>) -> ParseResult<Function<'a, U>, I> {
        unimplemented!()
    }

    #[allow(dead_code)]
    fn parse_primitive_ty(&self, input: State<I>) -> ParseResult<Type, I> {
        choice([ self.reserved("int")
                     .with(value(Primitive::IntSize))
               , self.reserved("uint")
                     .with(value(Primitive::IntSize))
               , self.reserved("float")
                     .with(value(Primitive::Float))
               , self.reserved("double")
                     .with(value(Primitive::Double))
               , self.reserved("bool")
                     .with(value(Primitive::Bool))
               , self.reserved("i8")
                     .with(value(Primitive::Int(Int::Int8)))
               , self.reserved("i16")
                     .with(value(Primitive::Int(Int::Int16)))
               , self.reserved("i32")
                    .with(value(Primitive::Int(Int::Int32)))
               , self.reserved("i64")
                     .with(value(Primitive::Int(Int::Int64)))
              , self.reserved("u8")
                    .with(value(Primitive::Uint(Int::Int8)))
              , self.reserved("u16")
                    .with(value(Primitive::Uint(Int::Int16)))
              , self.reserved("u32")
                    .with(value(Primitive::Uint(Int::Int32)))
              , self.reserved("u64")
                    .with(value(Primitive::Uint(Int::Int64)))
               ])
               .map(|primitive| Type::Prim(primitive))
               .parse_state(input)
    }

    fn raw_ptr_ty(&self, input: State<I>) -> ParseResult<Type, I> {
        char('*').with(self.type_annotation())
                 .map(|t| Type::Ref(Reference::Raw(Rc::new(t))))
                 .parse_state(input)
    }

    fn unique_ptr_ty(&self, input: State<I>) -> ParseResult<Type, I> {
        char('@').with(self.type_annotation())
                 .map(|t| Type::Ref(Reference::Unique(Rc::new(t))))
                 .parse_state(input)
    }

    fn borrow_ptr_ty(&self, input: State<I>) -> ParseResult<Type, I> {
        char('&').with(self.type_annotation())
                 .map(|t| Type::Ref(Reference::Borrowed(Rc::new(t))))
                 .parse_state(input)
    }
    fn parse_type(&self, input: State<I>) -> ParseResult<Type, I> {
        choice([ self.parser(MnEnv::parse_primitive_ty)
               , self.parser(MnEnv::raw_ptr_ty)
               , self.parser(MnEnv::unique_ptr_ty)
               , self.parser(MnEnv::borrow_ptr_ty)
               ])
            .parse_state(input)
    }

    fn parse_binding(&self, input: State<I>)
                    -> ParseResult<Unscoped<'a, Binding<'a, U>>, I> {
        let pos = input.position.clone();
        self.parser(MnEnv::parse_name)
            .and(self.type_annotation())
            .and(self.expr())
            .map(|((name, typ), value)|
                Annotated::new( Binding { name: name
                                        , typ: typ
                                        , value: Rc::new(value)
                                        }
                               , Position::from(pos)
                           ))
            .parse_state(input)
    }

    #[allow(dead_code)]
    fn parse_logical(&self, input: State<I>)
                    -> ParseResult<Logical<'a, U>, I> {
        let and = self.reserved("and")
                      .with(self.expr())
                      .and(self.expr())
                      .map(|(a, b)| Logical::And { a: Rc::new(a)
                                                 , b: Rc::new(b)
                                                 });

         let or = self.reserved("or")
                      .with(self.expr())
                      .and(self.expr())
                      .map(|(a, b)| Logical::And { a: Rc::new(a)
                                                 , b: Rc::new(b)
                                                 });

        and.or(or)
           .parse_state(input)
    }

    fn int_const(&'b self) -> MnParser<'a, 'b, I, Const> {
        self.parser(MnEnv::parse_int_const)
    }

    #[allow(dead_code)]
    fn parse_int_const(&self, input: State<I>) -> ParseResult<Const, I> {
        self.integer()
            .map(Const::IntConst)
            .parse_state(input)
    }

    fn parse_let(&self, input: State<I>) -> ParseResult<Form<'a, U>, I> {

        let binding_form =
            self.reserved("let")
                .with(many(self.parens(self.binding())))
                .and(many(self.expr()))
                .map(|(bindings, body)| LetForm::Let { bindings: bindings
                                                     , body: body });

        choice([ binding_form ])
            .map(Form::Let)
            .parse_state(input)
    }

    fn parse_name (&self, input: State<I>) -> ParseResult<Ident, I> {
        let position = input.position.clone();
        self.env.identifier::<'b>()
            .map(|name| Positional { pos: Position::from(position)
                                   , value: name })
            .parse_state(input)
    }

    fn parse_call(&self, input: State<I>) -> ParseResult<Form<'a, U>, I> {
        self.name()
            .and(many(self.expr()))
            .map(|(name, args)| Form::Call { fun: name, body: args })
            .parse_state(input)
    }

    fn parse_expr(&self, input: State<I>) -> ParseResult<Expr<'a, U>, I> {
        let pos = Position::from(input.position.clone());
        self.env
            .parens(choice([ self.def()
                           , self.if_form()
                           , self.lambda()
                           , self.let_form()
                           , self.call()
                           ]))
            .or(self.int_const()
                    .map(Form::Constant))
            .map(|f| Annotated::new(f, pos) )
            .parse_state(input)
    }

    fn expr(&'b self) -> MnParser<'a, 'b, I, Expr<'a, U>> {
        self.parser(MnEnv::parse_expr)
    }

    fn def(&'b self) -> MnParser<'a, 'b, I, Form<'a, U>> {
        self.parser(MnEnv::parse_def)
    }

    fn if_form(&'b self) -> MnParser<'a, 'b, I, Form<'a, U>> {
        self.parser(MnEnv::parse_if)
    }

    fn let_form(&'b self) -> MnParser<'a, 'b, I, Form<'a, U>> {
        self.parser(MnEnv::parse_let)
    }

    fn lambda(&'b self)-> MnParser<'a, 'b, I, Form<'a, U>> {
        self.parser(MnEnv::parse_lambda)
    }

    fn call(&'b self) -> MnParser<'a, 'b, I, Form<'a, U>> {
        self.parser(MnEnv::parse_call)
    }

    fn name(&'b self) -> MnParser<'a, 'b, I, Ident> {
        self.parser(MnEnv::parse_name)
    }

    fn binding(&'b self)
              -> MnParser< 'a, 'b, I, Unscoped<'a, Binding<'a, U>>> {
        self.parser(MnEnv::parse_binding)
    }

    fn type_annotation(&'b self) -> MnParser<'a, 'b, I, types::Type> {
        self.parser(MnEnv::parse_type)
    }

    fn function(&'b self) -> MnParser<'a, 'b, I, Function<'a, U>> {
        self.parser(MnEnv::parse_function)
    }

}
pub fn parse_module<'a>(code: &'a str)
                        -> Result< Vec<Expr<'a, UnscopedState>>
                                 , ParseError<&str>>
 {
    let alpha_ext = "+-*/<=>!?:$%_~^";
    let ops = "+-*/|=<>";
    let env = LanguageEnv::new(LanguageDef {
        ident: Identifier {
            start: letter().or(satisfy(move |c| alpha_ext.contains(c)))
          , rest: alpha_num().or(satisfy(move |c| alpha_ext.contains(c)))
          , reserved: [ "and"               , "begin"
                      , "case"              , "cond"
                      , "data"              , "define"
                      , "defn"              , "delay"
                      , "do"                , "else"
                      , "if"                , "lambda"      , LAMBDA
                      , "let"               , "let*"        , "letrec"
                      , "or"
                      , "quasiquote"        , "quote"       , "unquote"
                      , "set!"              , "unquote-splicing"
                      , "i8"                , "u8"
                      , "i16"               , "u16"
                      , "i32"               , "u32"         , "f32"
                      , "i64"               , "u64"         , "f64"
                      , "int"               , "uint"        , "float"
                      , "bool"                              , "double"
                      ].iter().map(|x| (*x).into())
                       .collect()
        }
      , op: Identifier {
            start: satisfy(move |c| ops.contains(c))
          , rest:  satisfy(move |c| ops.contains(c))
          , reserved: [ "=>", "->", "\\", "|"].iter().map(|x| (*x).into())
                                                     .collect()
        }
      , comment_line: string(";").map(|_| ())
      , comment_start: string("#|").map(|_| ())
      , comment_end: string("|#").map(|_| ())
    });
    let env = MnEnv { env: env };

    env.white_space()
       .with(many1::<Vec<Expr<'a, U>>, _>(env.expr()))
       .parse(code)
       .map(|(e, _)| e)
}
<commit_msg>Reserve some more words for potential future use<commit_after>extern crate combine;
extern crate combine_language;
extern crate mnemosyne as core;

use combine::*;
use combine_language::{ LanguageEnv
                      , LanguageDef
                      , Identifier
                      };
use combine::primitives::{ Stream
                         , Positioner
                         , SourcePosition
                         };

use core::semantic::*;
use core::semantic::annotations::{ Annotated
                                 , UnscopedState
                                 , Unscoped
                                 };
use core::semantic::types::*;
use core::semantic::ast::*;
use core::position::*;

use std::rc::Rc;

type ParseFn<'a, I, T> = fn (&MnEnv<'a, I>, State<I>) -> ParseResult<T, I>;

type U = UnscopedState;

/// Unicode code point for the lambda character
const LAMBDA: &'static str = "\u{03bb}";

mod tests;

/// Wraps a parsing function with a language definition environment.
///
/// TODO: this could probably push identifiers to the symbol table here?
#[derive(Copy)]
struct MnParser<'a: 'b, 'b, I, T>
where I: Stream<Item=char>
    , I::Range: 'b
    , I: 'b {
        env: &'b MnEnv<'a, I>
      , parser: ParseFn<'a, I, T>
}

impl<'a, 'b, I, T> Clone for MnParser<'a, 'b, I, T>
where I: Stream<Item=char>
    , I::Range: 'b
    , I: 'b
    , 'a: 'b {

    fn clone(&self) -> Self {
        MnParser { env: self.env , parser: self.parser }
    }
}

impl<'a, 'b, I, T> Parser for MnParser<'a, 'b, I, T>
where I: Stream<Item=char>
    , I::Range: 'b
    , I: 'b
    , 'a: 'b {

    type Input = I;
    type Output = T;

    fn parse_state(&mut self, input: State<I>) -> ParseResult<T, I> {
        (self.parser)(self.env, input)
    }

}

struct MnEnv<'a, I>
where I: Stream<Item = char>
    , I::Item: Positioner<Position = SourcePosition> {
    env: LanguageEnv<'a, I>
}

impl <'a, I> std::ops::Deref for MnEnv<'a, I>
where I: Stream<Item=char> {
    type Target = LanguageEnv<'a, I>;
    fn deref(&self) -> &LanguageEnv<'a, I> { &self.env }
}

impl<'a, 'b, I> MnEnv<'a, I>
where I: Stream<Item=char>
    , I::Item: Positioner<Position = SourcePosition>
    , I::Range: 'b {

    /// Wrap a function into a MnParser with this environment
    fn parser<T>(&'b self, parser: ParseFn<'a, I, T>)
                -> MnParser<'a, 'b, I, T> {
        MnParser { env: self, parser: parser }
    }

    #[allow(dead_code)]
    fn parse_def(&self, input: State<I>) -> ParseResult<Form<'a, U>, I> {
        let function_form
            = self.name()
                  .and(self.function())
                  .map(|(name, fun)| DefForm::Function { name: name
                                                       , fun: fun });
        let top_level
            = self.name()
                  .and(self.type_annotation())
                  .and(self.expr())
                  .map(|((name, ty), body)|
                    DefForm::TopLevel { name: name
                                      , annot: ty
                                      , value: Rc::new(body) });

        self.reserved("defn").or(self.reserved("define"))
            .with(function_form.or(top_level))
            .map(Form::Define)
            .parse_state(input)
    }

    #[allow(dead_code)]
    fn parse_if(&self, input: State<I>) -> ParseResult<Form<'a, U>, I> {
        self.reserved("if")
            .with(self.expr())
            .and(self.expr())
            .and(optional(self.expr()))
            .map(|((cond, if_clause), else_clause)|
                Form::If { condition: Rc::new(cond)
                         , if_clause: Rc::new(if_clause)
                         , else_clause: else_clause.map(Rc::new)
                         })
            .parse_state(input)
    }

    #[allow(dead_code)]
    fn parse_lambda(&self, input: State<I>) -> ParseResult<Form<'a, U>, I> {
        self.reserved("lambda")
            .or(self.reserved(LAMBDA))
            .with(self.function())
            .map(Form::Lambda)
            .parse_state(input)
    }

    #[allow(dead_code)]
    fn parse_function(&self, input: State<I>) -> ParseResult<Function<'a, U>, I> {
        unimplemented!()
    }

    #[allow(dead_code)]
    fn parse_primitive_ty(&self, input: State<I>) -> ParseResult<Type, I> {
        choice([ self.reserved("int")
                     .with(value(Primitive::IntSize))
               , self.reserved("uint")
                     .with(value(Primitive::IntSize))
               , self.reserved("float")
                     .with(value(Primitive::Float))
               , self.reserved("double")
                     .with(value(Primitive::Double))
               , self.reserved("bool")
                     .with(value(Primitive::Bool))
               , self.reserved("i8")
                     .with(value(Primitive::Int(Int::Int8)))
               , self.reserved("i16")
                     .with(value(Primitive::Int(Int::Int16)))
               , self.reserved("i32")
                    .with(value(Primitive::Int(Int::Int32)))
               , self.reserved("i64")
                     .with(value(Primitive::Int(Int::Int64)))
              , self.reserved("u8")
                    .with(value(Primitive::Uint(Int::Int8)))
              , self.reserved("u16")
                    .with(value(Primitive::Uint(Int::Int16)))
              , self.reserved("u32")
                    .with(value(Primitive::Uint(Int::Int32)))
              , self.reserved("u64")
                    .with(value(Primitive::Uint(Int::Int64)))
               ])
               .map(|primitive| Type::Prim(primitive))
               .parse_state(input)
    }

    fn raw_ptr_ty(&self, input: State<I>) -> ParseResult<Type, I> {
        char('*').with(self.type_annotation())
                 .map(|t| Type::Ref(Reference::Raw(Rc::new(t))))
                 .parse_state(input)
    }

    fn unique_ptr_ty(&self, input: State<I>) -> ParseResult<Type, I> {
        char('@').with(self.type_annotation())
                 .map(|t| Type::Ref(Reference::Unique(Rc::new(t))))
                 .parse_state(input)
    }

    fn borrow_ptr_ty(&self, input: State<I>) -> ParseResult<Type, I> {
        char('&').with(self.type_annotation())
                 .map(|t| Type::Ref(Reference::Borrowed(Rc::new(t))))
                 .parse_state(input)
    }
    fn parse_type(&self, input: State<I>) -> ParseResult<Type, I> {
        choice([ self.parser(MnEnv::parse_primitive_ty)
               , self.parser(MnEnv::raw_ptr_ty)
               , self.parser(MnEnv::unique_ptr_ty)
               , self.parser(MnEnv::borrow_ptr_ty)
               ])
            .parse_state(input)
    }

    fn parse_binding(&self, input: State<I>)
                    -> ParseResult<Unscoped<'a, Binding<'a, U>>, I> {
        let pos = input.position.clone();
        self.parser(MnEnv::parse_name)
            .and(self.type_annotation())
            .and(self.expr())
            .map(|((name, typ), value)|
                Annotated::new( Binding { name: name
                                        , typ: typ
                                        , value: Rc::new(value)
                                        }
                               , Position::from(pos)
                           ))
            .parse_state(input)
    }

    #[allow(dead_code)]
    fn parse_logical(&self, input: State<I>)
                    -> ParseResult<Logical<'a, U>, I> {
        let and = self.reserved("and")
                      .with(self.expr())
                      .and(self.expr())
                      .map(|(a, b)| Logical::And { a: Rc::new(a)
                                                 , b: Rc::new(b)
                                                 });

         let or = self.reserved("or")
                      .with(self.expr())
                      .and(self.expr())
                      .map(|(a, b)| Logical::And { a: Rc::new(a)
                                                 , b: Rc::new(b)
                                                 });

        and.or(or)
           .parse_state(input)
    }

    fn int_const(&'b self) -> MnParser<'a, 'b, I, Const> {
        self.parser(MnEnv::parse_int_const)
    }

    #[allow(dead_code)]
    fn parse_int_const(&self, input: State<I>) -> ParseResult<Const, I> {
        self.integer()
            .map(Const::IntConst)
            .parse_state(input)
    }

    fn parse_let(&self, input: State<I>) -> ParseResult<Form<'a, U>, I> {

        let binding_form =
            self.reserved("let")
                .with(many(self.parens(self.binding())))
                .and(many(self.expr()))
                .map(|(bindings, body)| LetForm::Let { bindings: bindings
                                                     , body: body });

        choice([ binding_form ])
            .map(Form::Let)
            .parse_state(input)
    }

    fn parse_name (&self, input: State<I>) -> ParseResult<Ident, I> {
        let position = input.position.clone();
        self.env.identifier::<'b>()
            .map(|name| Positional { pos: Position::from(position)
                                   , value: name })
            .parse_state(input)
    }

    fn parse_call(&self, input: State<I>) -> ParseResult<Form<'a, U>, I> {
        self.name()
            .and(many(self.expr()))
            .map(|(name, args)| Form::Call { fun: name, body: args })
            .parse_state(input)
    }

    fn parse_expr(&self, input: State<I>) -> ParseResult<Expr<'a, U>, I> {
        let pos = Position::from(input.position.clone());
        self.env
            .parens(choice([ self.def()
                           , self.if_form()
                           , self.lambda()
                           , self.let_form()
                           , self.call()
                           ]))
            .or(self.int_const()
                    .map(Form::Constant))
            .map(|f| Annotated::new(f, pos) )
            .parse_state(input)
    }

    fn expr(&'b self) -> MnParser<'a, 'b, I, Expr<'a, U>> {
        self.parser(MnEnv::parse_expr)
    }

    fn def(&'b self) -> MnParser<'a, 'b, I, Form<'a, U>> {
        self.parser(MnEnv::parse_def)
    }

    fn if_form(&'b self) -> MnParser<'a, 'b, I, Form<'a, U>> {
        self.parser(MnEnv::parse_if)
    }

    fn let_form(&'b self) -> MnParser<'a, 'b, I, Form<'a, U>> {
        self.parser(MnEnv::parse_let)
    }

    fn lambda(&'b self)-> MnParser<'a, 'b, I, Form<'a, U>> {
        self.parser(MnEnv::parse_lambda)
    }

    fn call(&'b self) -> MnParser<'a, 'b, I, Form<'a, U>> {
        self.parser(MnEnv::parse_call)
    }

    fn name(&'b self) -> MnParser<'a, 'b, I, Ident> {
        self.parser(MnEnv::parse_name)
    }

    fn binding(&'b self)
              -> MnParser< 'a, 'b, I, Unscoped<'a, Binding<'a, U>>> {
        self.parser(MnEnv::parse_binding)
    }

    fn type_annotation(&'b self) -> MnParser<'a, 'b, I, types::Type> {
        self.parser(MnEnv::parse_type)
    }

    fn function(&'b self) -> MnParser<'a, 'b, I, Function<'a, U>> {
        self.parser(MnEnv::parse_function)
    }

}
pub fn parse_module<'a>(code: &'a str)
                        -> Result< Vec<Expr<'a, UnscopedState>>
                                 , ParseError<&str>>
 {
    let alpha_ext = "+-*/<=>!?:$%_~^";
    let ops = "+-*/|=<>";
    let env = LanguageEnv::new(LanguageDef {
        ident: Identifier {
            start: letter().or(satisfy(move |c| alpha_ext.contains(c)))
          , rest: alpha_num().or(satisfy(move |c| alpha_ext.contains(c)))
          , reserved: [ // a number of these reserved words have no meaning yet
                        "and"               , "begin"
                      , "case"              , "cond"
                      , "data"
                      , "define"            , "defn"        , "def"
                      , "delay"             , "fn"
                      , "do"                , "else"
                      , "if"                , "lambda"      , LAMBDA
                      , "let"               , "let*"        , "letrec"
                      , "or"
                      , "quasiquote"        , "quote"       , "unquote"
                      , "set!"              , "unquote-splicing"
                      , "i8"                , "u8"
                      , "i16"               , "u16"
                      , "i32"               , "u32"         , "f32"
                      , "i64"               , "u64"         , "f64"
                      , "int"               , "uint"        , "float"
                      , "bool"                              , "double"
                      , "ref"               , "move"        , "borrow"
                      ].iter().map(|x| (*x).into())
                       .collect()
        }
      , op: Identifier {
            start: satisfy(move |c| ops.contains(c))
          , rest:  satisfy(move |c| ops.contains(c))
          , reserved: [ "=>", "->", "\\", "|"].iter().map(|x| (*x).into())
                                                     .collect()
        }
      , comment_line: string(";").map(|_| ())
      , comment_start: string("#|").map(|_| ())
      , comment_end: string("|#").map(|_| ())
    });
    let env = MnEnv { env: env };

    env.white_space()
       .with(many1::<Vec<Expr<'a, U>>, _>(env.expr()))
       .parse(code)
       .map(|(e, _)| e)
}
<|endoftext|>


<commit_before>// pest. The Elegant Parser
// Copyright (c) 2018 Dragoș Tiselice
//
// 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. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.

use std::error;
use std::fmt;

use RuleType;
use position::Position;
use span::Span;

/// An `enum` which defines possible errors.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum Error<'i, R> {
    /// Generated parsing error with expected and unexpected `Rule`s and a position
    ParsingError {
        /// Positive attempts
        positives: Vec<R>,
        /// Negative attempts
        negatives: Vec<R>,
        /// Deepest position of attempts
        pos: Position<'i>
    },
    /// Custom error with a message and a position
    CustomErrorPos {
        /// Short explanation
        message: String,
        /// Error `Position` for formatting
        pos: Position<'i>
    },
    /// Custom error with a message and a span defined by a start and end position
    CustomErrorSpan {
        /// Short explanation
        message: String,
        /// Error `Span` for formatting
        span: Span<'i>
    }
}

impl<'i, R: RuleType> Error<'i, R> {
    /// Renames all `Rule`s from a `ParsingError` variant returning a `CustomErrorPos`. It does
    /// nothing when called on `CustomErrorPos` and `CustomErrorSpan` variants.
    ///
    /// Useful in order to rename verbose rules or have detailed per-`Rule` formatting.
    ///
    /// # Examples
    ///
    /// ```
    /// # use pest::Error;
    /// # use pest::Position;
    /// # #[allow(non_camel_case_types)]
    /// # #[allow(dead_code)]
    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
    /// # enum Rule {
    /// #     open_paren,
    /// #     closed_paren
    /// # }
    /// # let input = "";
    /// # let pos = Position::from_start(input);
    /// Error::ParsingError {
    ///     positives: vec![Rule::open_paren],
    ///     negatives: vec![Rule::closed_paren],
    ///     pos: pos
    /// }.renamed_rules(|rule| {
    ///     match *rule {
    ///         Rule::open_paren => "(".to_owned(),
    ///         Rule::closed_paren => "closed paren".to_owned()
    ///     }
    /// });
    /// ```
    pub fn renamed_rules<F>(self, f: F) -> Error<'i, R>
    where
        F: FnMut(&R) -> String
    {
        match self {
            Error::ParsingError {
                positives,
                negatives,
                pos
            } => {
                let message = parsing_error_message(&positives, &negatives, f);
                Error::CustomErrorPos { message, pos }
            }
            error => error
        }
    }
}

fn message<'i, R: fmt::Debug>(error: &Error<'i, R>) -> String {
    match *error {
        Error::ParsingError {
            ref positives,
            ref negatives,
            ..
        } => parsing_error_message(positives, negatives, |r| format!("{:?}", r)),
        Error::CustomErrorPos { ref message, .. } | Error::CustomErrorSpan { ref message, .. } => {
            message.to_owned()
        }
    }
}

fn parsing_error_message<R: fmt::Debug, F>(positives: &[R], negatives: &[R], mut f: F) -> String
where
    F: FnMut(&R) -> String
{
    match (negatives.is_empty(), positives.is_empty()) {
        (false, false) => format!(
            "unexpected {}; expected {}",
            enumerate(negatives, &mut f),
            enumerate(positives, &mut f)
        ),
        (false, true) => format!("unexpected {}", enumerate(negatives, &mut f)),
        (true, false) => format!("expected {}", enumerate(positives, &mut f)),
        (true, true) => "unknown parsing error".to_owned()
    }
}

fn enumerate<R: fmt::Debug, F>(rules: &[R], f: &mut F) -> String
where
    F: FnMut(&R) -> String
{
    match rules.len() {
        1 => f(&rules[0]),
        2 => format!("{} or {}", f(&rules[0]), f(&rules[1])),
        l => {
            let separated = rules
                .iter()
                .take(l - 1)
                .map(|r| f(r))
                .collect::<Vec<_>>()
                .join(", ");
            format!("{}, or {}", separated, f(&rules[l - 1]))
        }
    }
}

fn underline<'i, R: fmt::Debug>(error: &Error<'i, R>, offset: usize) -> String {
    let mut underline = String::new();

    for _ in 0..offset {
        underline.push(' ');
    }

    match *error {
        Error::CustomErrorSpan { ref span, .. } => {
            if span.end() - span.start() > 1 {
                underline.push('^');
                for _ in 2..(span.end() - span.start()) {
                    underline.push('-');
                }
                underline.push('^');
            } else {
                underline.push('^');
            }
        }
        _ => underline.push_str("^---")
    };

    underline
}

fn format<'i, R: fmt::Debug>(error: &Error<'i, R>) -> String {
    let pos = match *error {
        Error::ParsingError { ref pos, .. } | Error::CustomErrorPos { ref pos, .. } => pos.clone(),
        Error::CustomErrorSpan { ref span, .. } => span.clone().split().0.clone()
    };
    let (line, col) = pos.line_col();
    let line_str_len = format!("{}", line).len();

    let mut spacing = String::new();
    for _ in 0..line_str_len {
        spacing.push(' ');
    }

    let mut result = format!("{}--> {}:{}\n", spacing, line, col);
    result.push_str(&format!("{} |\n", spacing));
    result.push_str(&format!("{} | ", line));

    let line = pos.line_of();
    result.push_str(&format!("{}\n", line));
    result.push_str(&format!("{} | {}\n", spacing, underline(error, col - 1)));
    result.push_str(&format!("{} |\n", spacing));
    result.push_str(&format!("{} = {}", spacing, message(error)));

    result
}

impl<'i, R: fmt::Debug> fmt::Display for Error<'i, R> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", format(self))
    }
}

impl<'i, R: fmt::Debug> error::Error for Error<'i, R> {
    fn description(&self) -> &str {
        match *self {
            Error::ParsingError { .. } => "parsing error",
            Error::CustomErrorPos { ref message, .. }
            | Error::CustomErrorSpan { ref message, .. } => message
        }
    }
}

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

    #[test]
    fn display_parsing_error_mixed() {
        let input = "ab\ncd\nef";
        let pos = unsafe { position::new(input, 4) };
        let error: Error<u32> = Error::ParsingError {
            positives: vec![1, 2, 3],
            negatives: vec![4, 5, 6],
            pos: pos
        };

        assert_eq!(
            format!("{}", error),
            vec![
                " --> 2:2",
                "  |",
                "2 | cd",
                "  |  ^---",
                "  |",
                "  = unexpected 4, 5, or 6; expected 1, 2, or 3",
            ].join("\n")
        );
    }

    #[test]
    fn display_parsing_error_positives() {
        let input = "ab\ncd\nef";
        let pos = unsafe { position::new(input, 4) };
        let error: Error<u32> = Error::ParsingError {
            positives: vec![1, 2],
            negatives: vec![],
            pos: pos
        };

        assert_eq!(
            format!("{}", error),
            vec![
                " --> 2:2",
                "  |",
                "2 | cd",
                "  |  ^---",
                "  |",
                "  = expected 1 or 2",
            ].join("\n")
        );
    }

    #[test]
    fn display_parsing_error_negatives() {
        let input = "ab\ncd\nef";
        let pos = unsafe { position::new(input, 4) };
        let error: Error<u32> = Error::ParsingError {
            positives: vec![],
            negatives: vec![4, 5, 6],
            pos: pos
        };

        assert_eq!(
            format!("{}", error),
            vec![
                " --> 2:2",
                "  |",
                "2 | cd",
                "  |  ^---",
                "  |",
                "  = unexpected 4, 5, or 6",
            ].join("\n")
        );
    }

    #[test]
    fn display_parsing_error_unknown() {
        let input = "ab\ncd\nef";
        let pos = unsafe { position::new(input, 4) };
        let error: Error<u32> = Error::ParsingError {
            positives: vec![],
            negatives: vec![],
            pos: pos
        };

        assert_eq!(
            format!("{}", error),
            vec![
                " --> 2:2",
                "  |",
                "2 | cd",
                "  |  ^---",
                "  |",
                "  = unknown parsing error",
            ].join("\n")
        );
    }

    #[test]
    fn display_custom() {
        let input = "ab\ncd\nef";
        let pos = unsafe { position::new(input, 4) };
        let error: Error<&str> = Error::CustomErrorPos {
            message: "error: big one".to_owned(),
            pos: pos
        };

        assert_eq!(
            format!("{}", error),
            vec![
                " --> 2:2",
                "  |",
                "2 | cd",
                "  |  ^---",
                "  |",
                "  = error: big one",
            ].join("\n")
        );
    }

    #[test]
    fn mapped_parsing_error() {
        let input = "ab\ncd\nef";
        let pos = unsafe { position::new(input, 4) };
        let error: Error<u32> = Error::ParsingError {
            positives: vec![1, 2, 3],
            negatives: vec![4, 5, 6],
            pos: pos
        }.renamed_rules(|n| format!("{}", n + 1));

        assert_eq!(
            format!("{}", error),
            vec![
                " --> 2:2",
                "  |",
                "2 | cd",
                "  |  ^---",
                "  |",
                "  = unexpected 5, 6, or 7; expected 2, 3, or 4",
            ].join("\n")
        );
    }
}
<commit_msg>Fixed errors to handle multi-line spans.<commit_after>// pest. The Elegant Parser
// Copyright (c) 2018 Dragoș Tiselice
//
// 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. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.

use std::error;
use std::fmt;
use std::mem;

use RuleType;
use position::Position;
use span::Span;

/// An `enum` which defines possible errors.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum Error<'i, R> {
    /// Generated parsing error with expected and unexpected `Rule`s and a position
    ParsingError {
        /// Positive attempts
        positives: Vec<R>,
        /// Negative attempts
        negatives: Vec<R>,
        /// Deepest position of attempts
        pos: Position<'i>
    },
    /// Custom error with a message and a position
    CustomErrorPos {
        /// Short explanation
        message: String,
        /// Error `Position` for formatting
        pos: Position<'i>
    },
    /// Custom error with a message and a span defined by a start and end position
    CustomErrorSpan {
        /// Short explanation
        message: String,
        /// Error `Span` for formatting
        span: Span<'i>
    }
}

impl<'i, R: RuleType> Error<'i, R> {
    /// Renames all `Rule`s from a `ParsingError` variant returning a `CustomErrorPos`. It does
    /// nothing when called on `CustomErrorPos` and `CustomErrorSpan` variants.
    ///
    /// Useful in order to rename verbose rules or have detailed per-`Rule` formatting.
    ///
    /// # Examples
    ///
    /// ```
    /// # use pest::Error;
    /// # use pest::Position;
    /// # #[allow(non_camel_case_types)]
    /// # #[allow(dead_code)]
    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
    /// # enum Rule {
    /// #     open_paren,
    /// #     closed_paren
    /// # }
    /// # let input = "";
    /// # let pos = Position::from_start(input);
    /// Error::ParsingError {
    ///     positives: vec![Rule::open_paren],
    ///     negatives: vec![Rule::closed_paren],
    ///     pos: pos
    /// }.renamed_rules(|rule| {
    ///     match *rule {
    ///         Rule::open_paren => "(".to_owned(),
    ///         Rule::closed_paren => "closed paren".to_owned()
    ///     }
    /// });
    /// ```
    pub fn renamed_rules<F>(self, f: F) -> Error<'i, R>
    where
        F: FnMut(&R) -> String
    {
        match self {
            Error::ParsingError {
                positives,
                negatives,
                pos
            } => {
                let message = parsing_error_message(&positives, &negatives, f);
                Error::CustomErrorPos { message, pos }
            }
            error => error
        }
    }
}

fn message<'i, R: fmt::Debug>(error: &Error<'i, R>) -> String {
    match *error {
        Error::ParsingError {
            ref positives,
            ref negatives,
            ..
        } => parsing_error_message(positives, negatives, |r| format!("{:?}", r)),
        Error::CustomErrorPos { ref message, .. } | Error::CustomErrorSpan { ref message, .. } => {
            message.to_owned()
        }
    }
}

fn parsing_error_message<R: fmt::Debug, F>(positives: &[R], negatives: &[R], mut f: F) -> String
where
    F: FnMut(&R) -> String
{
    match (negatives.is_empty(), positives.is_empty()) {
        (false, false) => format!(
            "unexpected {}; expected {}",
            enumerate(negatives, &mut f),
            enumerate(positives, &mut f)
        ),
        (false, true) => format!("unexpected {}", enumerate(negatives, &mut f)),
        (true, false) => format!("expected {}", enumerate(positives, &mut f)),
        (true, true) => "unknown parsing error".to_owned()
    }
}

fn enumerate<R: fmt::Debug, F>(rules: &[R], f: &mut F) -> String
where
    F: FnMut(&R) -> String
{
    match rules.len() {
        1 => f(&rules[0]),
        2 => format!("{} or {}", f(&rules[0]), f(&rules[1])),
        l => {
            let separated = rules
                .iter()
                .take(l - 1)
                .map(|r| f(r))
                .collect::<Vec<_>>()
                .join(", ");
            format!("{}, or {}", separated, f(&rules[l - 1]))
        }
    }
}

fn underline(start: usize, end: Option<usize>, offset: usize) -> String {
    let mut underline = String::new();

    for _ in 0..offset {
        underline.push(' ');
    }

    if let Some(end) = end {
        if end - start > 1 {
            underline.push('^');
            for _ in 2..(end - start) {
                underline.push('-');
            }
            underline.push('^');
        } else {
            underline.push('^');
        }
    } else {
        underline.push_str("^---")
    }

    underline
}

fn format<'i, R: fmt::Debug>(error: &Error<'i, R>) -> String {
    let pos = match *error {
        Error::ParsingError { ref pos, .. } | Error::CustomErrorPos { ref pos, .. } => pos.clone(),
        Error::CustomErrorSpan { ref span, .. } => span.clone().split().0.clone()
    };
    let (line, col) = pos.line_col();
    let line_str_len = format!("{}", line).len();

    let mut spacing = String::new();
    for _ in 0..line_str_len {
        spacing.push(' ');
    }

    let mut result = format!("{}--> {}:{}\n", spacing, line, col);
    result.push_str(&format!("{} |\n", spacing));

    let underlined;

    if let Error::CustomErrorSpan { ref span, .. } = *error {
        let mut start = span.start_pos().line_col();
        let mut end = span.end_pos().line_col();

        if start.1 > end.1 {
            mem::swap(&mut start.1, &mut end.1);
            start.1 -= 1;
            end.1 += 1;
        }

        underlined = underline(start.1, Some(end.1), start.1 - 1);

        if start.0 != end.0 {
            result.push_str(&format!("{} | ", start.0));

            let pos = span.start_pos();
            let line = pos.line_of();
            result.push_str(&format!("{}\n", line));

            if end.0 - start.0 > 1 {
                result.push_str(&format!("{} | ...\n", spacing));
            }

            result.push_str(&format!("{} | ", end.0));

            let pos = span.end_pos();
            let line = pos.line_of();
            result.push_str(&format!("{}\n", line));
            result.push_str(&format!("{} | {}\n", spacing, underlined));

            result.push_str(&format!("{} |\n", spacing));
            result.push_str(&format!("{} = {}", spacing, message(error)));

            return result;
        }
    } else {
        underlined = underline(pos.pos(), None, col - 1);
    }

    result.push_str(&format!("{} | ", line));

    let line = pos.line_of();
    result.push_str(&format!("{}\n", line));
    result.push_str(&format!("{} | {}\n", spacing, underlined));

    result.push_str(&format!("{} |\n", spacing));
    result.push_str(&format!("{} = {}", spacing, message(error)));

    result
}

impl<'i, R: fmt::Debug> fmt::Display for Error<'i, R> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", format(self))
    }
}

impl<'i, R: fmt::Debug> error::Error for Error<'i, R> {
    fn description(&self) -> &str {
        match *self {
            Error::ParsingError { .. } => "parsing error",
            Error::CustomErrorPos { ref message, .. }
            | Error::CustomErrorSpan { ref message, .. } => message
        }
    }
}

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

    #[test]
    fn display_parsing_error_mixed() {
        let input = "ab\ncd\nef";
        let pos = unsafe { position::new(input, 4) };
        let error: Error<u32> = Error::ParsingError {
            positives: vec![1, 2, 3],
            negatives: vec![4, 5, 6],
            pos
        };

        assert_eq!(
            format!("{}", error),
            vec![
                " --> 2:2",
                "  |",
                "2 | cd",
                "  |  ^---",
                "  |",
                "  = unexpected 4, 5, or 6; expected 1, 2, or 3",
            ].join("\n")
        );
    }

    #[test]
    fn display_parsing_error_positives() {
        let input = "ab\ncd\nef";
        let pos = unsafe { position::new(input, 4) };
        let error: Error<u32> = Error::ParsingError {
            positives: vec![1, 2],
            negatives: vec![],
            pos
        };

        assert_eq!(
            format!("{}", error),
            vec![
                " --> 2:2",
                "  |",
                "2 | cd",
                "  |  ^---",
                "  |",
                "  = expected 1 or 2",
            ].join("\n")
        );
    }

    #[test]
    fn display_parsing_error_negatives() {
        let input = "ab\ncd\nef";
        let pos = unsafe { position::new(input, 4) };
        let error: Error<u32> = Error::ParsingError {
            positives: vec![],
            negatives: vec![4, 5, 6],
            pos
        };

        assert_eq!(
            format!("{}", error),
            vec![
                " --> 2:2",
                "  |",
                "2 | cd",
                "  |  ^---",
                "  |",
                "  = unexpected 4, 5, or 6",
            ].join("\n")
        );
    }

    #[test]
    fn display_parsing_error_unknown() {
        let input = "ab\ncd\nef";
        let pos = unsafe { position::new(input, 4) };
        let error: Error<u32> = Error::ParsingError {
            positives: vec![],
            negatives: vec![],
            pos
        };

        assert_eq!(
            format!("{}", error),
            vec![
                " --> 2:2",
                "  |",
                "2 | cd",
                "  |  ^---",
                "  |",
                "  = unknown parsing error",
            ].join("\n")
        );
    }

    #[test]
    fn display_custom_pos() {
        let input = "ab\ncd\nef";
        let pos = unsafe { position::new(input, 4) };
        let error: Error<&str> = Error::CustomErrorPos {
            message: "error: big one".to_owned(),
            pos
        };

        assert_eq!(
            format!("{}", error),
            vec![
                " --> 2:2",
                "  |",
                "2 | cd",
                "  |  ^---",
                "  |",
                "  = error: big one",
            ].join("\n")
        );
    }

    #[test]
    fn display_custom_span_two_lines() {
        let input = "ab\ncd\nefgh";
        let start = unsafe { position::new(input, 4) };
        let end = unsafe { position::new(input, 9) };
        let error: Error<&str> = Error::CustomErrorSpan {
            message: "error: big one".to_owned(),
            span: start.span(&end)
        };

        assert_eq!(
            format!("{}", error),
            vec![
                " --> 2:2",
                "  |",
                "2 | cd",
                "3 | efgh",
                "  |  ^^",
                "  |",
                "  = error: big one",
            ].join("\n")
        );
    }

    #[test]
    fn display_custom_span_three_lines() {
        let input = "ab\ncd\nefgh";
        let start = unsafe { position::new(input, 1) };
        let end = unsafe { position::new(input, 9) };
        let error: Error<&str> = Error::CustomErrorSpan {
            message: "error: big one".to_owned(),
            span: start.span(&end)
        };

        assert_eq!(
            format!("{}", error),
            vec![
                " --> 1:2",
                "  |",
                "1 | ab",
                "  | ...",
                "3 | efgh",
                "  |  ^^",
                "  |",
                "  = error: big one",
            ].join("\n")
        );
    }

    #[test]
    fn display_custom_span_two_lines_inverted() {
        let input = "abcdef\ngh";
        let start = unsafe { position::new(input, 5) };
        let end = unsafe { position::new(input, 8) };
        let error: Error<&str> = Error::CustomErrorSpan {
            message: "error: big one".to_owned(),
            span: start.span(&end)
        };

        assert_eq!(
            format!("{}", error),
            vec![
                " --> 1:6",
                "  |",
                "1 | abcdef",
                "2 | gh",
                "  | ^----^",
                "  |",
                "  = error: big one",
            ].join("\n")
        );
    }

    #[test]
    fn mapped_parsing_error() {
        let input = "ab\ncd\nef";
        let pos = unsafe { position::new(input, 4) };
        let error: Error<u32> = Error::ParsingError {
            positives: vec![1, 2, 3],
            negatives: vec![4, 5, 6],
            pos
        }.renamed_rules(|n| format!("{}", n + 1));

        assert_eq!(
            format!("{}", error),
            vec![
                " --> 2:2",
                "  |",
                "2 | cd",
                "  |  ^---",
                "  |",
                "  = unexpected 5, 6, or 7; expected 2, 3, or 4",
            ].join("\n")
        );
    }
}
<|endoftext|>