<commit_before>use crate::homectl_core::device::{Device, DeviceState, Light};
use byteorder::{ByteOrder, LittleEndian};
use palette::Hsv;
use std::{error::Error, net::SocketAddr};

#[derive(Clone)]
pub struct LifxState {
    pub hue: u16,
    pub sat: u16,
    pub bri: u16,
    pub power: u16,
    pub label: String,
    pub addr: SocketAddr,
    pub transition: Option<u16>,
}

#[derive(Clone)]
pub enum LifxMsg {
    Get(SocketAddr),
    SetColor(LifxState),
    State(LifxState),
    SetPower(LifxState),
    Unknown,
}

pub fn lifx_msg_type_to_u16(msg_type: LifxMsg) -> u16 {
    match msg_type {
        LifxMsg::Get(_) => 101,
        LifxMsg::SetColor(_) => 102,
        LifxMsg::State(_) => 107,
        LifxMsg::SetPower(_) => 117,
        LifxMsg::Unknown => panic!("Cannot convert LifxMsg::Unknown to u16"),
    }
}

fn mk_lifx_msg_payload(lifx_msg: LifxMsg) -> Option<Vec<u8>> {
    match lifx_msg {
        LifxMsg::SetColor(state) => {
            let mut buf: [u8; 16 + 32] = [0; 16 + 32];

            LittleEndian::write_u16(&mut buf, state.power);

            state
                .transition
                .map(|t| LittleEndian::write_u16(&mut buf[2..], t));

            Some(buf.to_vec())
        }
        LifxMsg::SetPower(state) => {
            let mut buf: [u8; 8 + 16 * 4 + 32] = [0; 8 + 16 * 4 + 32];

            LittleEndian::write_u16(&mut buf[1..], state.hue);
            LittleEndian::write_u16(&mut buf[3..], state.sat);
            LittleEndian::write_u16(&mut buf[5..], state.bri);
            LittleEndian::write_u16(&mut buf[7..], 6500); // lifx requires this weird color temperature parameter?

            state
                .transition
                .map(|t| LittleEndian::write_u16(&mut buf[9..], t));

            Some(buf.to_vec())
        }
        _ => None,
    }
}

pub fn mk_lifx_udp_msg(lifx_msg: LifxMsg) -> Vec<u8> {
    // frame
    // https://lan.developer.lifx.com/docs/header-description#frame
    let mut frame: [u8; 8] = [0; 8];
    let protocol = 1024;
    let origin = 0;
    let tagged = 1;
    let addressable = 1;

    LittleEndian::write_u16(&mut frame, 0); // size to be filled in later
    LittleEndian::write_u16(
        &mut frame[2..],
        protocol | (origin << 14) | (tagged << 13) | (addressable << 12),
    );
    LittleEndian::write_u16(&mut frame[1..], 4);

    // frame address
    // https://lan.developer.lifx.com/docs/header-description#frame-address
    let mut frame_address: [u8; 16] = [0; 16];
    let ack_required = 0;
    let res_required = match lifx_msg {
        LifxMsg::Get(_) => 1,
        _ => 0,
    };

    frame_address[14] = (ack_required << 1) | res_required;

    // protocol header
    // https://lan.developer.lifx.com/docs/header-description#protocol-header
    let mut protocol_header: [u8; 12] = [0; 12];
    LittleEndian::write_u16(
        &mut protocol_header[8..],
        lifx_msg_type_to_u16(lifx_msg.clone()),
    );

    let payload = mk_lifx_msg_payload(lifx_msg.clone());
    let payload_size = payload.clone().map(|p| p.len()).unwrap_or(0);
    let msg_size = frame.len() + frame_address.len() + protocol_header.len() + payload_size;

    // we now know the total size - write it into the beginning of the frame header
    LittleEndian::write_u16(&mut frame, msg_size as u16);

    let mut msg: Vec<u8> = vec![];
    msg.append(&mut frame.to_vec());
    msg.append(&mut frame_address.to_vec());
    msg.append(&mut protocol_header.to_vec());

    match payload {
        Some(payload) => {
            msg.append(&mut payload.to_vec());
        }
        None => {}
    };

    msg
}

pub fn read_lifx_msg(buf: &[u8], addr: SocketAddr) -> LifxMsg {
    let msg_type = LittleEndian::read_u16(&buf[32..]);
    let payload = &buf[36..];

    match msg_type {
        107 => {
            // State (107) message, response to Get (101)
            // https://lan.developer.lifx.com/docs/light-messages#section-state-107

            let hue = LittleEndian::read_u16(&payload);
            let sat = LittleEndian::read_u16(&payload[2..]);
            let bri = LittleEndian::read_u16(&payload[4..]);

            let power = LittleEndian::read_u16(&payload[10..]);

            let label = std::str::from_utf8(&payload[12..(12 + 32)])
                .unwrap_or("Unknown")
                .to_owned()
                .replace("\0", "");

            let state = LifxState {
                hue,
                sat,
                bri,
                power,
                label,
                addr,
                transition: None,
            };

            LifxMsg::State(state)
        }
        _ => LifxMsg::Unknown,
    }
}

pub fn from_lifx_state(lifx_state: LifxState, integration_id: String) -> Device {
    let hue = (f32::from(lifx_state.hue) / 65535.0) * 360.0;
    let sat = f32::from(lifx_state.sat) / 65535.0;
    let bri = f32::from(lifx_state.bri) / 65535.0;

    let power = lifx_state.power == 65535;

    let color = Hsv::new(hue, sat, bri);

    let state = DeviceState::Light(Light {
        power,
        brightness: None,
        color: Some(color.into()),
    });

    let device = Device {
        id: lifx_state.addr.ip().to_string(),
        name: lifx_state.label.clone(),
        integration_id: integration_id.clone(),
        scene: None,
        state,
    };

    device
}

pub fn to_lifx_state(device: Device) -> Result<LifxState, Box<dyn Error>> {
    let light_state = match device.state {
        DeviceState::Light(Light {
            brightness,
            color,
            power,
        }) => Ok(Light {
            power,
            brightness,
            color,
        }),
        _ => Err("Unsupported device state"),
    }?;

    let color = light_state
        .color
        .map(|color| color.into())
        .unwrap_or(Hsv::new(0.0, 255.0, 255.0));

    let hue = ((color.hue.to_positive_degrees() / 360.0) * 65535.0).floor() as u16;
    let sat = (color.saturation * 65535.0).floor() as u16;
    let bri = (color.value * 65535.0).floor() as u16;

    let power = if light_state.power { 65535 } else { 0 };

    Ok(LifxState {
        hue,
        sat,
        bri,
        power,
        label: device.name,
        addr: device.id.parse()?,
        transition: Some(500),
    })
}
<commit_msg>lifx device id needs to contain port<commit_after>use crate::homectl_core::device::{Device, DeviceState, Light};
use byteorder::{ByteOrder, LittleEndian};
use palette::Hsv;
use std::{error::Error, net::SocketAddr};

#[derive(Clone)]
pub struct LifxState {
    pub hue: u16,
    pub sat: u16,
    pub bri: u16,
    pub power: u16,
    pub label: String,
    pub addr: SocketAddr,
    pub transition: Option<u16>,
}

#[derive(Clone)]
pub enum LifxMsg {
    Get(SocketAddr),
    SetColor(LifxState),
    State(LifxState),
    SetPower(LifxState),
    Unknown,
}

pub fn lifx_msg_type_to_u16(msg_type: LifxMsg) -> u16 {
    match msg_type {
        LifxMsg::Get(_) => 101,
        LifxMsg::SetColor(_) => 102,
        LifxMsg::State(_) => 107,
        LifxMsg::SetPower(_) => 117,
        LifxMsg::Unknown => panic!("Cannot convert LifxMsg::Unknown to u16"),
    }
}

fn mk_lifx_msg_payload(lifx_msg: LifxMsg) -> Option<Vec<u8>> {
    match lifx_msg {
        LifxMsg::SetColor(state) => {
            let mut buf: [u8; 16 + 32] = [0; 16 + 32];

            LittleEndian::write_u16(&mut buf, state.power);

            state
                .transition
                .map(|t| LittleEndian::write_u16(&mut buf[2..], t));

            Some(buf.to_vec())
        }
        LifxMsg::SetPower(state) => {
            let mut buf: [u8; 8 + 16 * 4 + 32] = [0; 8 + 16 * 4 + 32];

            LittleEndian::write_u16(&mut buf[1..], state.hue);
            LittleEndian::write_u16(&mut buf[3..], state.sat);
            LittleEndian::write_u16(&mut buf[5..], state.bri);
            LittleEndian::write_u16(&mut buf[7..], 6500); // lifx requires this weird color temperature parameter?

            state
                .transition
                .map(|t| LittleEndian::write_u16(&mut buf[9..], t));

            Some(buf.to_vec())
        }
        _ => None,
    }
}

pub fn mk_lifx_udp_msg(lifx_msg: LifxMsg) -> Vec<u8> {
    // frame
    // https://lan.developer.lifx.com/docs/header-description#frame
    let mut frame: [u8; 8] = [0; 8];
    let protocol = 1024;
    let origin = 0;
    let tagged = 1;
    let addressable = 1;

    LittleEndian::write_u16(&mut frame, 0); // size to be filled in later
    LittleEndian::write_u16(
        &mut frame[2..],
        protocol | (origin << 14) | (tagged << 13) | (addressable << 12),
    );
    LittleEndian::write_u16(&mut frame[1..], 4);

    // frame address
    // https://lan.developer.lifx.com/docs/header-description#frame-address
    let mut frame_address: [u8; 16] = [0; 16];
    let ack_required = 0;
    let res_required = match lifx_msg {
        LifxMsg::Get(_) => 1,
        _ => 0,
    };

    frame_address[14] = (ack_required << 1) | res_required;

    // protocol header
    // https://lan.developer.lifx.com/docs/header-description#protocol-header
    let mut protocol_header: [u8; 12] = [0; 12];
    LittleEndian::write_u16(
        &mut protocol_header[8..],
        lifx_msg_type_to_u16(lifx_msg.clone()),
    );

    let payload = mk_lifx_msg_payload(lifx_msg.clone());
    let payload_size = payload.clone().map(|p| p.len()).unwrap_or(0);
    let msg_size = frame.len() + frame_address.len() + protocol_header.len() + payload_size;

    // we now know the total size - write it into the beginning of the frame header
    LittleEndian::write_u16(&mut frame, msg_size as u16);

    let mut msg: Vec<u8> = vec![];
    msg.append(&mut frame.to_vec());
    msg.append(&mut frame_address.to_vec());
    msg.append(&mut protocol_header.to_vec());

    match payload {
        Some(payload) => {
            msg.append(&mut payload.to_vec());
        }
        None => {}
    };

    msg
}

pub fn read_lifx_msg(buf: &[u8], addr: SocketAddr) -> LifxMsg {
    let msg_type = LittleEndian::read_u16(&buf[32..]);
    let payload = &buf[36..];

    match msg_type {
        107 => {
            // State (107) message, response to Get (101)
            // https://lan.developer.lifx.com/docs/light-messages#section-state-107

            let hue = LittleEndian::read_u16(&payload);
            let sat = LittleEndian::read_u16(&payload[2..]);
            let bri = LittleEndian::read_u16(&payload[4..]);

            let power = LittleEndian::read_u16(&payload[10..]);

            let label = std::str::from_utf8(&payload[12..(12 + 32)])
                .unwrap_or("Unknown")
                .to_owned()
                .replace("\0", "");

            let state = LifxState {
                hue,
                sat,
                bri,
                power,
                label,
                addr,
                transition: None,
            };

            LifxMsg::State(state)
        }
        _ => LifxMsg::Unknown,
    }
}

pub fn from_lifx_state(lifx_state: LifxState, integration_id: String) -> Device {
    let hue = (f32::from(lifx_state.hue) / 65535.0) * 360.0;
    let sat = f32::from(lifx_state.sat) / 65535.0;
    let bri = f32::from(lifx_state.bri) / 65535.0;

    let power = lifx_state.power == 65535;

    let color = Hsv::new(hue, sat, bri);

    let state = DeviceState::Light(Light {
        power,
        brightness: None,
        color: Some(color.into()),
    });

    let device = Device {
        id: lifx_state.addr.to_string(),
        name: lifx_state.label.clone(),
        integration_id: integration_id.clone(),
        scene: None,
        state,
    };

    device
}

pub fn to_lifx_state(device: Device) -> Result<LifxState, Box<dyn Error>> {
    let light_state = match device.state {
        DeviceState::Light(Light {
            brightness,
            color,
            power,
        }) => Ok(Light {
            power,
            brightness,
            color,
        }),
        _ => Err("Unsupported device state"),
    }?;

    let color = light_state
        .color
        .map(|color| color.into())
        .unwrap_or(Hsv::new(0.0, 255.0, 255.0));

    let hue = ((color.hue.to_positive_degrees() / 360.0) * 65535.0).floor() as u16;
    let sat = (color.saturation * 65535.0).floor() as u16;
    let bri = (color.value * 65535.0).floor() as u16;

    let power = if light_state.power { 65535 } else { 0 };

    Ok(LifxState {
        hue,
        sat,
        bri,
        power,
        label: device.name,
        addr: device.id.parse()?,
        transition: Some(500),
    })
}
<|endoftext|>


<commit_before>#![feature(async_closure)]
#![feature(box_patterns)]
#![feature(label_break_value)]
#![feature(never_type)]
#![feature(try_trait_v2)]
#![allow(clippy::module_inception)]

mod builtin_functions;
mod compiler;
mod database;
mod fuzzer;
mod language_server;
mod module;
mod vm;

use crate::{
    compiler::{
        ast_to_hir::AstToHir,
        cst_to_ast::CstToAst,
        error::CompilerError,
        hir::{self, CollectErrors},
        hir_to_lir::HirToLir,
        rcst_to_cst::RcstToCst,
        string_to_rcst::StringToRcst,
    },
    database::Database,
    language_server::utils::LspPositionConversion,
    module::{Module, ModuleKind},
    vm::{use_provider::DbUseProvider, Closure, TearDownResult, Vm},
};
use compiler::lir::Lir;
use itertools::Itertools;
use language_server::CandyLanguageServer;
use notify::{watcher, RecursiveMode, Watcher};
use std::{
    env::current_dir,
    path::PathBuf,
    sync::{mpsc::channel, Arc},
    time::Duration,
};
use structopt::StructOpt;
use tower_lsp::{LspService, Server};
use tracing::{debug, error, info, warn, Level, Metadata};
use tracing_subscriber::{filter, fmt::format::FmtSpan, layer::Filter, prelude::*, FmtSubscriber};

#[derive(StructOpt, Debug)]
#[structopt(name = "candy", about = "The 🍭 Candy CLI.")]
enum CandyOptions {
    Build(CandyBuildOptions),
    Run(CandyRunOptions),
    Fuzz(CandyFuzzOptions),
    Lsp,
}

#[derive(StructOpt, Debug)]
struct CandyBuildOptions {
    #[structopt(long)]
    debug: bool,

    #[structopt(long)]
    watch: bool,

    #[structopt(parse(from_os_str))]
    file: PathBuf,
}

#[derive(StructOpt, Debug)]
struct CandyRunOptions {
    #[structopt(long)]
    debug: bool,

    #[structopt(parse(from_os_str))]
    file: PathBuf,
}

#[derive(StructOpt, Debug)]
struct CandyFuzzOptions {
    #[structopt(parse(from_os_str))]
    file: PathBuf,
}

#[tokio::main]
async fn main() {
    init_logger();
    match CandyOptions::from_args() {
        CandyOptions::Build(options) => build(options),
        CandyOptions::Run(options) => run(options),
        CandyOptions::Fuzz(options) => fuzz(options).await,
        CandyOptions::Lsp => lsp().await,
    }
}

fn build(options: CandyBuildOptions) {
    let module = Module::from_package_root_and_file(
        current_dir().unwrap(),
        options.file.clone(),
        ModuleKind::Code,
    );
    raw_build(module.clone(), options.debug);

    if options.watch {
        let (tx, rx) = channel();
        let mut watcher = watcher(tx, Duration::from_secs(1)).unwrap();
        watcher
            .watch(&options.file, RecursiveMode::Recursive)
            .unwrap();
        loop {
            match rx.recv() {
                Ok(_) => {
                    raw_build(module.clone(), options.debug);
                }
                Err(e) => error!("watch error: {e:#?}"),
            }
        }
    }
}
fn raw_build(module: Module, debug: bool) -> Option<Arc<Lir>> {
    let db = Database::default();

    tracing::span!(Level::DEBUG, "Parsing string to RCST").in_scope(|| {
        let rcst = db
            .rcst(module.clone())
            .unwrap_or_else(|err| panic!("Error parsing file `{}`: {:?}", module, err));
        if debug {
            module.dump_associated_debug_file("rcst", &format!("{:#?}\n", rcst));
        }
    });

    tracing::span!(Level::DEBUG, "Turning RCST to CST").in_scope(|| {
        let cst = db.cst(module.clone()).unwrap();
        if debug {
            module.dump_associated_debug_file("cst", &format!("{:#?}\n", cst));
        }
    });

    tracing::span!(Level::DEBUG, "Abstracting CST to AST").in_scope(|| {
        let (asts, ast_cst_id_map) = db.ast(module.clone()).unwrap();
        if debug {
            module.dump_associated_debug_file(
                "ast",
                &format!("{}\n", asts.iter().map(|ast| format!("{}", ast)).join("\n")),
            );
            module.dump_associated_debug_file(
                "ast_to_cst_ids",
                &ast_cst_id_map
                    .keys()
                    .into_iter()
                    .sorted_by_key(|it| it.local)
                    .map(|key| format!("{key} -> {}\n", ast_cst_id_map[key].0))
                    .join(""),
            );
        }
    });

    tracing::span!(Level::DEBUG, "Turning AST to HIR").in_scope(|| {
        let (hir, hir_ast_id_map) = db.hir(module.clone()).unwrap();
        if debug {
            module.dump_associated_debug_file("hir", &format!("{}", hir));
            module.dump_associated_debug_file(
                "hir_to_ast_ids",
                &hir_ast_id_map
                    .keys()
                    .into_iter()
                    .map(|key| format!("{key} -> {}\n", hir_ast_id_map[key]))
                    .join(""),
            );
        }
        let mut errors = vec![];
        hir.collect_errors(&mut errors);
        for CompilerError { span, payload, .. } in errors {
            let (start_line, start_col) = db.offset_to_lsp(module.clone(), span.start);
            let (end_line, end_col) = db.offset_to_lsp(module.clone(), span.end);
            warn!("{start_line}:{start_col} – {end_line}:{end_col}: {payload:?}");
        }
    });

    let lir = tracing::span!(Level::DEBUG, "Lowering HIR to LIR").in_scope(|| {
        let lir = db.lir(module.clone()).unwrap();
        if debug {
            module.dump_associated_debug_file("lir", &format!("{lir}"));
        }
        lir
    });

    Some(lir)
}

fn run(options: CandyRunOptions) {
    let module = Module::from_package_root_and_file(
        current_dir().unwrap(),
        options.file.clone(),
        ModuleKind::Code,
    );
    let db = Database::default();

    if raw_build(module.clone(), false).is_none() {
        warn!("Build failed.");
        return;
    };
    let module_closure = Closure::of_module(&db, module.clone()).unwrap();

    let path_string = options.file.to_string_lossy();
    info!("Running `{path_string}`.");

    let use_provider = DbUseProvider { db: &db };
    let vm = Vm::new_for_running_module_closure(&use_provider, module_closure);
    let TearDownResult {
        tracer,
        result,
        heap,
        ..
    } = vm.run_synchronously_until_completion(&db);

    match result {
        Ok(return_value) => info!(
            "The module exports these definitions: {}",
            return_value.format(&heap)
        ),
        Err(reason) => {
            error!("The module panicked because {reason}.");
            error!("This is the stack trace:");
            tracer.dump_stack_trace(&db, &heap);
        }
    }

    if options.debug {
        module.dump_associated_debug_file("trace", &tracer.format_call_tree(&heap));
    }
}

async fn fuzz(options: CandyFuzzOptions) {
    let module = Module::from_package_root_and_file(
        current_dir().unwrap(),
        options.file.clone(),
        ModuleKind::Code,
    );

    if raw_build(module.clone(), false).is_none() {
        info!("Build failed.");
        return;
    }

    debug!("Fuzzing `{module}`.");
    let db = Database::default();
    fuzzer::fuzz(&db, module).await;
}

async fn lsp() {
    info!("Starting language server…");
    let (service, socket) = LspService::new(CandyLanguageServer::from_client);
    Server::new(tokio::io::stdin(), tokio::io::stdout(), socket)
        .serve(service)
        .await;
}

fn init_logger() {
    let console_log = tracing_subscriber::fmt::layer()
        .compact()
        .with_span_events(FmtSpan::FULL)
        .with_filter(filter::filter_fn(|metadata| {
            // For external packages, show only the error logs.
            metadata.level() <= &Level::ERROR
                || metadata
                    .module_path()
                    .unwrap_or_default()
                    .starts_with("candy")
        }))
        .with_filter(filter::filter_fn(level_for("candy::compiler", Level::WARN)))
        .with_filter(filter::filter_fn(level_for(
            "candy::language_server",
            Level::DEBUG,
        )))
        .with_filter(filter::filter_fn(level_for("candy::vm", Level::DEBUG)))
        .with_filter(filter::filter_fn(level_for(
            "candy::vm::heap",
            Level::DEBUG,
        )));
    tracing_subscriber::registry().with(console_log).init();
}
fn level_for(module: &'static str, level: Level) -> impl Fn(&Metadata) -> bool {
    move |metadata| {
        if metadata.module_path().unwrap_or("").starts_with(module) {
            metadata.level() <= &level
        } else {
            true
        }
    }
}
<commit_msg>Make logs cleaner<commit_after>#![feature(async_closure)]
#![feature(box_patterns)]
#![feature(label_break_value)]
#![feature(never_type)]
#![feature(try_trait_v2)]
#![allow(clippy::module_inception)]

mod builtin_functions;
mod compiler;
mod database;
mod fuzzer;
mod language_server;
mod module;
mod vm;

use crate::{
    compiler::{
        ast_to_hir::AstToHir,
        cst_to_ast::CstToAst,
        error::CompilerError,
        hir::{self, CollectErrors},
        hir_to_lir::HirToLir,
        rcst_to_cst::RcstToCst,
        string_to_rcst::StringToRcst,
    },
    database::Database,
    language_server::utils::LspPositionConversion,
    module::{Module, ModuleKind},
    vm::{use_provider::DbUseProvider, Closure, TearDownResult, Vm},
};
use compiler::lir::Lir;
use itertools::Itertools;
use language_server::CandyLanguageServer;
use notify::{watcher, RecursiveMode, Watcher};
use std::{
    env::current_dir,
    path::PathBuf,
    sync::{mpsc::channel, Arc},
    time::Duration,
};
use structopt::StructOpt;
use tower_lsp::{LspService, Server};
use tracing::{debug, error, info, warn, Level, Metadata};
use tracing_subscriber::{filter, fmt::format::FmtSpan, prelude::*};

#[derive(StructOpt, Debug)]
#[structopt(name = "candy", about = "The 🍭 Candy CLI.")]
enum CandyOptions {
    Build(CandyBuildOptions),
    Run(CandyRunOptions),
    Fuzz(CandyFuzzOptions),
    Lsp,
}

#[derive(StructOpt, Debug)]
struct CandyBuildOptions {
    #[structopt(long)]
    debug: bool,

    #[structopt(long)]
    watch: bool,

    #[structopt(parse(from_os_str))]
    file: PathBuf,
}

#[derive(StructOpt, Debug)]
struct CandyRunOptions {
    #[structopt(long)]
    debug: bool,

    #[structopt(parse(from_os_str))]
    file: PathBuf,
}

#[derive(StructOpt, Debug)]
struct CandyFuzzOptions {
    #[structopt(parse(from_os_str))]
    file: PathBuf,
}

#[tokio::main]
async fn main() {
    init_logger();
    match CandyOptions::from_args() {
        CandyOptions::Build(options) => build(options),
        CandyOptions::Run(options) => run(options),
        CandyOptions::Fuzz(options) => fuzz(options).await,
        CandyOptions::Lsp => lsp().await,
    }
}

fn build(options: CandyBuildOptions) {
    let module = Module::from_package_root_and_file(
        current_dir().unwrap(),
        options.file.clone(),
        ModuleKind::Code,
    );
    raw_build(module.clone(), options.debug);

    if options.watch {
        let (tx, rx) = channel();
        let mut watcher = watcher(tx, Duration::from_secs(1)).unwrap();
        watcher
            .watch(&options.file, RecursiveMode::Recursive)
            .unwrap();
        loop {
            match rx.recv() {
                Ok(_) => {
                    raw_build(module.clone(), options.debug);
                }
                Err(e) => error!("watch error: {e:#?}"),
            }
        }
    }
}
fn raw_build(module: Module, debug: bool) -> Option<Arc<Lir>> {
    let db = Database::default();

    tracing::span!(Level::DEBUG, "Parsing string to RCST").in_scope(|| {
        let rcst = db
            .rcst(module.clone())
            .unwrap_or_else(|err| panic!("Error parsing file `{}`: {:?}", module, err));
        if debug {
            module.dump_associated_debug_file("rcst", &format!("{:#?}\n", rcst));
        }
    });

    tracing::span!(Level::DEBUG, "Turning RCST to CST").in_scope(|| {
        let cst = db.cst(module.clone()).unwrap();
        if debug {
            module.dump_associated_debug_file("cst", &format!("{:#?}\n", cst));
        }
    });

    tracing::span!(Level::DEBUG, "Abstracting CST to AST").in_scope(|| {
        let (asts, ast_cst_id_map) = db.ast(module.clone()).unwrap();
        if debug {
            module.dump_associated_debug_file(
                "ast",
                &format!("{}\n", asts.iter().map(|ast| format!("{}", ast)).join("\n")),
            );
            module.dump_associated_debug_file(
                "ast_to_cst_ids",
                &ast_cst_id_map
                    .keys()
                    .into_iter()
                    .sorted_by_key(|it| it.local)
                    .map(|key| format!("{key} -> {}\n", ast_cst_id_map[key].0))
                    .join(""),
            );
        }
    });

    tracing::span!(Level::DEBUG, "Turning AST to HIR").in_scope(|| {
        let (hir, hir_ast_id_map) = db.hir(module.clone()).unwrap();
        if debug {
            module.dump_associated_debug_file("hir", &format!("{}", hir));
            module.dump_associated_debug_file(
                "hir_to_ast_ids",
                &hir_ast_id_map
                    .keys()
                    .into_iter()
                    .map(|key| format!("{key} -> {}\n", hir_ast_id_map[key]))
                    .join(""),
            );
        }
        let mut errors = vec![];
        hir.collect_errors(&mut errors);
        for CompilerError { span, payload, .. } in errors {
            let (start_line, start_col) = db.offset_to_lsp(module.clone(), span.start);
            let (end_line, end_col) = db.offset_to_lsp(module.clone(), span.end);
            warn!("{start_line}:{start_col} – {end_line}:{end_col}: {payload:?}");
        }
    });

    let lir = tracing::span!(Level::DEBUG, "Lowering HIR to LIR").in_scope(|| {
        let lir = db.lir(module.clone()).unwrap();
        if debug {
            module.dump_associated_debug_file("lir", &format!("{lir}"));
        }
        lir
    });

    Some(lir)
}

fn run(options: CandyRunOptions) {
    let module = Module::from_package_root_and_file(
        current_dir().unwrap(),
        options.file.clone(),
        ModuleKind::Code,
    );
    let db = Database::default();

    if raw_build(module.clone(), false).is_none() {
        warn!("Build failed.");
        return;
    };
    let module_closure = Closure::of_module(&db, module.clone()).unwrap();

    let path_string = options.file.to_string_lossy();
    info!("Running `{path_string}`.");

    let use_provider = DbUseProvider { db: &db };
    let vm = Vm::new_for_running_module_closure(&use_provider, module_closure);
    let TearDownResult {
        tracer,
        result,
        heap,
        ..
    } = vm.run_synchronously_until_completion(&db);

    match result {
        Ok(return_value) => info!(
            "The module exports these definitions: {}",
            return_value.format(&heap)
        ),
        Err(reason) => {
            error!("The module panicked because {reason}.");
            error!("This is the stack trace:");
            tracer.dump_stack_trace(&db, &heap);
        }
    }

    if options.debug {
        module.dump_associated_debug_file("trace", &tracer.format_call_tree(&heap));
    }
}

async fn fuzz(options: CandyFuzzOptions) {
    let module = Module::from_package_root_and_file(
        current_dir().unwrap(),
        options.file.clone(),
        ModuleKind::Code,
    );

    if raw_build(module.clone(), false).is_none() {
        info!("Build failed.");
        return;
    }

    debug!("Fuzzing `{module}`.");
    let db = Database::default();
    fuzzer::fuzz(&db, module).await;
}

async fn lsp() {
    info!("Starting language server…");
    let (service, socket) = LspService::new(CandyLanguageServer::from_client);
    Server::new(tokio::io::stdin(), tokio::io::stdout(), socket)
        .serve(service)
        .await;
}

fn init_logger() {
    let console_log = tracing_subscriber::fmt::layer()
        .compact()
        .with_span_events(FmtSpan::ENTER)
        .with_filter(filter::filter_fn(|metadata| {
            // For external packages, show only the error logs.
            metadata.level() <= &Level::ERROR
                || metadata
                    .module_path()
                    .unwrap_or_default()
                    .starts_with("candy")
        }))
        .with_filter(filter::filter_fn(level_for("candy::compiler", Level::WARN)))
        .with_filter(filter::filter_fn(level_for(
            "candy::language_server",
            Level::DEBUG,
        )))
        .with_filter(filter::filter_fn(level_for("candy::vm", Level::DEBUG)))
        .with_filter(filter::filter_fn(level_for(
            "candy::vm::heap",
            Level::DEBUG,
        )));
    tracing_subscriber::registry().with(console_log).init();
}
fn level_for(module: &'static str, level: Level) -> impl Fn(&Metadata) -> bool {
    move |metadata| {
        if metadata.module_path().unwrap_or("").starts_with(module) {
            metadata.level() <= &level
        } else {
            true
        }
    }
}
<|endoftext|>


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

//! A structure for holding a set of enum variants.
//!
//! This module defines a container which uses an efficient bit mask
//! representation to hold C-like enum variants.

use core::prelude::*;
use core::fmt;

// FIXME(conventions): implement BitXor
// FIXME(contentions): implement union family of methods? (general design may be wrong here)
// FIXME(conventions): implement len

#[deriving(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
/// A specialized `Set` implementation to use enum types.
pub struct EnumSet<E> {
    // We must maintain the invariant that no bits are set
    // for which no variant exists
    bits: uint
}

impl<E:CLike+fmt::Show> fmt::Show for EnumSet<E> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        try!(write!(fmt, "{{"));
        let mut first = true;
        for e in self.iter() {
            if !first {
                try!(write!(fmt, ", "));
            }
            try!(write!(fmt, "{}", e));
            first = false;
        }
        write!(fmt, "}}")
    }
}

/**
An interface for casting C-like enum to uint and back.
A typically implementation is as below.

```{rust,ignore}
#[repr(uint)]
enum Foo {
    A, B, C
}

impl CLike for Foo {
    fn to_uint(&self) -> uint {
        *self as uint
    }

    fn from_uint(v: uint) -> Foo {
        unsafe { mem::transmute(v) }
    }
}
```
*/
pub trait CLike {
    /// Converts a C-like enum to a `uint`.
    fn to_uint(&self) -> uint;
    /// Converts a `uint` to a C-like enum.
    fn from_uint(uint) -> Self;
}

fn bit<E:CLike>(e: &E) -> uint {
    use core::uint;
    let value = e.to_uint();
    assert!(value < uint::BITS,
            "EnumSet only supports up to {} variants.", uint::BITS - 1);
    1 << value
}

impl<E:CLike> EnumSet<E> {
    /// Deprecated: Renamed to `new`.
    #[deprecated = "Renamed to `new`"]
    pub fn empty() -> EnumSet<E> {
        EnumSet::new()
    }

    /// Returns an empty `EnumSet`.
    #[unstable = "matches collection reform specification, waiting for dust to settle"]
    pub fn new() -> EnumSet<E> {
        EnumSet {bits: 0}
    }

    /// Returns true if the `EnumSet` is empty.
    #[unstable = "matches collection reform specification, waiting for dust to settle"]
    pub fn is_empty(&self) -> bool {
        self.bits == 0
    }

    pub fn clear(&mut self) {
        self.bits = 0;
    }

    /// Returns `true` if the `EnumSet` contains any enum of the given `EnumSet`.
    /// Deprecated: Use `is_disjoint`.
    #[deprecated = "Use `is_disjoint`"]
    pub fn intersects(&self, e: EnumSet<E>) -> bool {
        !self.is_disjoint(&e)
    }

    /// Returns `false` if the `EnumSet` contains any enum of the given `EnumSet`.
    #[unstable = "matches collection reform specification, waiting for dust to settle"]
    pub fn is_disjoint(&self, other: &EnumSet<E>) -> bool {
        (self.bits & other.bits) == 0
    }

    /// Returns `true` if a given `EnumSet` is included in this `EnumSet`.
    #[unstable = "matches collection reform specification, waiting for dust to settle"]
    pub fn is_superset(&self, other: &EnumSet<E>) -> bool {
        (self.bits & other.bits) == other.bits
    }

    /// Returns `true` if this `EnumSet` is included in the given `EnumSet`.
    #[unstable = "matches collection reform specification, waiting for dust to settle"]
    pub fn is_subset(&self, other: &EnumSet<E>) -> bool {
        other.is_superset(self)
    }

    /// Returns the union of both `EnumSets`.
    pub fn union(&self, e: EnumSet<E>) -> EnumSet<E> {
        EnumSet {bits: self.bits | e.bits}
    }

    /// Returns the intersection of both `EnumSets`.
    pub fn intersection(&self, e: EnumSet<E>) -> EnumSet<E> {
        EnumSet {bits: self.bits & e.bits}
    }

    /// Deprecated: Use `insert`.
    #[deprecated = "Use `insert`"]
    pub fn add(&mut self, e: E) {
        self.insert(e);
    }

    /// Adds an enum to the `EnumSet`, and returns `true` if it wasn't there before
    #[unstable = "matches collection reform specification, waiting for dust to settle"]
    pub fn insert(&mut self, e: E) -> bool {
        let result = !self.contains(&e);
        self.bits |= bit(&e);
        result
    }

    /// Removes an enum from the EnumSet
    #[unstable = "matches collection reform specification, waiting for dust to settle"]
    pub fn remove(&mut self, e: &E) -> bool {
        let result = self.contains(e);
        self.bits &= !bit(e);
        result
    }

    /// Deprecated: use `contains`.
    #[deprecated = "use `contains"]
    pub fn contains_elem(&self, e: E) -> bool {
        self.contains(&e)
    }

    /// Returns `true` if an `EnumSet` contains a given enum.
    #[unstable = "matches collection reform specification, waiting for dust to settle"]
    pub fn contains(&self, e: &E) -> bool {
        (self.bits & bit(e)) != 0
    }

    /// Returns an iterator over an `EnumSet`.
    #[unstable = "matches collection reform specification, waiting for dust to settle"]
    pub fn iter(&self) -> Items<E> {
        Items::new(self.bits)
    }
}

impl<E:CLike> Sub<EnumSet<E>, EnumSet<E>> for EnumSet<E> {
    fn sub(&self, e: &EnumSet<E>) -> EnumSet<E> {
        EnumSet {bits: self.bits & !e.bits}
    }
}

impl<E:CLike> BitOr<EnumSet<E>, EnumSet<E>> for EnumSet<E> {
    fn bitor(&self, e: &EnumSet<E>) -> EnumSet<E> {
        EnumSet {bits: self.bits | e.bits}
    }
}

impl<E:CLike> BitAnd<EnumSet<E>, EnumSet<E>> for EnumSet<E> {
    fn bitand(&self, e: &EnumSet<E>) -> EnumSet<E> {
        EnumSet {bits: self.bits & e.bits}
    }
}

/// An iterator over an EnumSet
pub struct Items<E> {
    index: uint,
    bits: uint,
}

impl<E:CLike> Items<E> {
    fn new(bits: uint) -> Items<E> {
        Items { index: 0, bits: bits }
    }
}

impl<E:CLike> Iterator<E> for Items<E> {
    fn next(&mut self) -> Option<E> {
        if self.bits == 0 {
            return None;
        }

        while (self.bits & 1) == 0 {
            self.index += 1;
            self.bits >>= 1;
        }
        let elem = CLike::from_uint(self.index);
        self.index += 1;
        self.bits >>= 1;
        Some(elem)
    }

    fn size_hint(&self) -> (uint, Option<uint>) {
        let exact = self.bits.count_ones();
        (exact, Some(exact))
    }
}

#[cfg(test)]
mod test {
    use std::prelude::*;
    use std::mem;

    use super::{EnumSet, CLike};

    #[deriving(PartialEq, Show)]
    #[repr(uint)]
    enum Foo {
        A, B, C
    }

    impl CLike for Foo {
        fn to_uint(&self) -> uint {
            *self as uint
        }

        fn from_uint(v: uint) -> Foo {
            unsafe { mem::transmute(v) }
        }
    }

    #[test]
    fn test_new() {
        let e: EnumSet<Foo> = EnumSet::new();
        assert!(e.is_empty());
    }

    #[test]
    fn test_show() {
        let mut e = EnumSet::new();
        assert_eq!("{}", e.to_string().as_slice());
        e.insert(A);
        assert_eq!("{A}", e.to_string().as_slice());
        e.insert(C);
        assert_eq!("{A, C}", e.to_string().as_slice());
    }

    ///////////////////////////////////////////////////////////////////////////
    // intersect

    #[test]
    fn test_two_empties_do_not_intersect() {
        let e1: EnumSet<Foo> = EnumSet::new();
        let e2: EnumSet<Foo> = EnumSet::new();
        assert!(e1.is_disjoint(&e2));
    }

    #[test]
    fn test_empty_does_not_intersect_with_full() {
        let e1: EnumSet<Foo> = EnumSet::new();

        let mut e2: EnumSet<Foo> = EnumSet::new();
        e2.insert(A);
        e2.insert(B);
        e2.insert(C);

        assert!(e1.is_disjoint(&e2));
    }

    #[test]
    fn test_disjoint_intersects() {
        let mut e1: EnumSet<Foo> = EnumSet::new();
        e1.insert(A);

        let mut e2: EnumSet<Foo> = EnumSet::new();
        e2.insert(B);

        assert!(e1.is_disjoint(&e2));
    }

    #[test]
    fn test_overlapping_intersects() {
        let mut e1: EnumSet<Foo> = EnumSet::new();
        e1.insert(A);

        let mut e2: EnumSet<Foo> = EnumSet::new();
        e2.insert(A);
        e2.insert(B);

        assert!(!e1.is_disjoint(&e2));
    }

    ///////////////////////////////////////////////////////////////////////////
    // contains and contains_elem

    #[test]
    fn test_superset() {
        let mut e1: EnumSet<Foo> = EnumSet::new();
        e1.insert(A);

        let mut e2: EnumSet<Foo> = EnumSet::new();
        e2.insert(A);
        e2.insert(B);

        let mut e3: EnumSet<Foo> = EnumSet::new();
        e3.insert(C);

        assert!(e1.is_subset(&e2));
        assert!(e2.is_superset(&e1));
        assert!(!e3.is_superset(&e2))
        assert!(!e2.is_superset(&e3))
    }

    #[test]
    fn test_contains() {
        let mut e1: EnumSet<Foo> = EnumSet::new();
        e1.insert(A);
        assert!(e1.contains(&A));
        assert!(!e1.contains(&B));
        assert!(!e1.contains(&C));

        e1.insert(A);
        e1.insert(B);
        assert!(e1.contains(&A));
        assert!(e1.contains(&B));
        assert!(!e1.contains(&C));
    }

    ///////////////////////////////////////////////////////////////////////////
    // iter

    #[test]
    fn test_iterator() {
        let mut e1: EnumSet<Foo> = EnumSet::new();

        let elems: Vec<Foo> = e1.iter().collect();
        assert!(elems.is_empty())

        e1.insert(A);
        let elems = e1.iter().collect();
        assert_eq!(vec![A], elems)

        e1.insert(C);
        let elems = e1.iter().collect();
        assert_eq!(vec![A,C], elems)

        e1.insert(C);
        let elems = e1.iter().collect();
        assert_eq!(vec![A,C], elems)

        e1.insert(B);
        let elems = e1.iter().collect();
        assert_eq!(vec![A,B,C], elems)
    }

    ///////////////////////////////////////////////////////////////////////////
    // operators

    #[test]
    fn test_operators() {
        let mut e1: EnumSet<Foo> = EnumSet::new();
        e1.insert(A);
        e1.insert(C);

        let mut e2: EnumSet<Foo> = EnumSet::new();
        e2.insert(B);
        e2.insert(C);

        let e_union = e1 | e2;
        let elems = e_union.iter().collect();
        assert_eq!(vec![A,B,C], elems)

        let e_intersection = e1 & e2;
        let elems = e_intersection.iter().collect();
        assert_eq!(vec![C], elems)

        let e_subtract = e1 - e2;
        let elems = e_subtract.iter().collect();
        assert_eq!(vec![A], elems)
    }

    #[test]
    #[should_fail]
    fn test_overflow() {
        #[allow(dead_code)]
        #[repr(uint)]
        enum Bar {
            V00, V01, V02, V03, V04, V05, V06, V07, V08, V09,
            V10, V11, V12, V13, V14, V15, V16, V17, V18, V19,
            V20, V21, V22, V23, V24, V25, V26, V27, V28, V29,
            V30, V31, V32, V33, V34, V35, V36, V37, V38, V39,
            V40, V41, V42, V43, V44, V45, V46, V47, V48, V49,
            V50, V51, V52, V53, V54, V55, V56, V57, V58, V59,
            V60, V61, V62, V63, V64, V65, V66, V67, V68, V69,
        }
        impl CLike for Bar {
            fn to_uint(&self) -> uint {
                *self as uint
            }

            fn from_uint(v: uint) -> Bar {
                unsafe { mem::transmute(v) }
            }
        }
        let mut set = EnumSet::empty();
        set.add(V64);
    }
}
<commit_msg>Add BitXor to EnumSet.<commit_after>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! A structure for holding a set of enum variants.
//!
//! This module defines a container which uses an efficient bit mask
//! representation to hold C-like enum variants.

use core::prelude::*;
use core::fmt;

// FIXME(contentions): implement union family of methods? (general design may be wrong here)
// FIXME(conventions): implement len

#[deriving(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
/// A specialized `Set` implementation to use enum types.
pub struct EnumSet<E> {
    // We must maintain the invariant that no bits are set
    // for which no variant exists
    bits: uint
}

impl<E:CLike+fmt::Show> fmt::Show for EnumSet<E> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        try!(write!(fmt, "{{"));
        let mut first = true;
        for e in self.iter() {
            if !first {
                try!(write!(fmt, ", "));
            }
            try!(write!(fmt, "{}", e));
            first = false;
        }
        write!(fmt, "}}")
    }
}

/**
An interface for casting C-like enum to uint and back.
A typically implementation is as below.

```{rust,ignore}
#[repr(uint)]
enum Foo {
    A, B, C
}

impl CLike for Foo {
    fn to_uint(&self) -> uint {
        *self as uint
    }

    fn from_uint(v: uint) -> Foo {
        unsafe { mem::transmute(v) }
    }
}
```
*/
pub trait CLike {
    /// Converts a C-like enum to a `uint`.
    fn to_uint(&self) -> uint;
    /// Converts a `uint` to a C-like enum.
    fn from_uint(uint) -> Self;
}

fn bit<E:CLike>(e: &E) -> uint {
    use core::uint;
    let value = e.to_uint();
    assert!(value < uint::BITS,
            "EnumSet only supports up to {} variants.", uint::BITS - 1);
    1 << value
}

impl<E:CLike> EnumSet<E> {
    /// Deprecated: Renamed to `new`.
    #[deprecated = "Renamed to `new`"]
    pub fn empty() -> EnumSet<E> {
        EnumSet::new()
    }

    /// Returns an empty `EnumSet`.
    #[unstable = "matches collection reform specification, waiting for dust to settle"]
    pub fn new() -> EnumSet<E> {
        EnumSet {bits: 0}
    }

    /// Returns true if the `EnumSet` is empty.
    #[unstable = "matches collection reform specification, waiting for dust to settle"]
    pub fn is_empty(&self) -> bool {
        self.bits == 0
    }

    pub fn clear(&mut self) {
        self.bits = 0;
    }

    /// Returns `true` if the `EnumSet` contains any enum of the given `EnumSet`.
    /// Deprecated: Use `is_disjoint`.
    #[deprecated = "Use `is_disjoint`"]
    pub fn intersects(&self, e: EnumSet<E>) -> bool {
        !self.is_disjoint(&e)
    }

    /// Returns `false` if the `EnumSet` contains any enum of the given `EnumSet`.
    #[unstable = "matches collection reform specification, waiting for dust to settle"]
    pub fn is_disjoint(&self, other: &EnumSet<E>) -> bool {
        (self.bits & other.bits) == 0
    }

    /// Returns `true` if a given `EnumSet` is included in this `EnumSet`.
    #[unstable = "matches collection reform specification, waiting for dust to settle"]
    pub fn is_superset(&self, other: &EnumSet<E>) -> bool {
        (self.bits & other.bits) == other.bits
    }

    /// Returns `true` if this `EnumSet` is included in the given `EnumSet`.
    #[unstable = "matches collection reform specification, waiting for dust to settle"]
    pub fn is_subset(&self, other: &EnumSet<E>) -> bool {
        other.is_superset(self)
    }

    /// Returns the union of both `EnumSets`.
    pub fn union(&self, e: EnumSet<E>) -> EnumSet<E> {
        EnumSet {bits: self.bits | e.bits}
    }

    /// Returns the intersection of both `EnumSets`.
    pub fn intersection(&self, e: EnumSet<E>) -> EnumSet<E> {
        EnumSet {bits: self.bits & e.bits}
    }

    /// Deprecated: Use `insert`.
    #[deprecated = "Use `insert`"]
    pub fn add(&mut self, e: E) {
        self.insert(e);
    }

    /// Adds an enum to the `EnumSet`, and returns `true` if it wasn't there before
    #[unstable = "matches collection reform specification, waiting for dust to settle"]
    pub fn insert(&mut self, e: E) -> bool {
        let result = !self.contains(&e);
        self.bits |= bit(&e);
        result
    }

    /// Removes an enum from the EnumSet
    #[unstable = "matches collection reform specification, waiting for dust to settle"]
    pub fn remove(&mut self, e: &E) -> bool {
        let result = self.contains(e);
        self.bits &= !bit(e);
        result
    }

    /// Deprecated: use `contains`.
    #[deprecated = "use `contains"]
    pub fn contains_elem(&self, e: E) -> bool {
        self.contains(&e)
    }

    /// Returns `true` if an `EnumSet` contains a given enum.
    #[unstable = "matches collection reform specification, waiting for dust to settle"]
    pub fn contains(&self, e: &E) -> bool {
        (self.bits & bit(e)) != 0
    }

    /// Returns an iterator over an `EnumSet`.
    #[unstable = "matches collection reform specification, waiting for dust to settle"]
    pub fn iter(&self) -> Items<E> {
        Items::new(self.bits)
    }
}

impl<E:CLike> Sub<EnumSet<E>, EnumSet<E>> for EnumSet<E> {
    fn sub(&self, e: &EnumSet<E>) -> EnumSet<E> {
        EnumSet {bits: self.bits & !e.bits}
    }
}

impl<E:CLike> BitOr<EnumSet<E>, EnumSet<E>> for EnumSet<E> {
    fn bitor(&self, e: &EnumSet<E>) -> EnumSet<E> {
        EnumSet {bits: self.bits | e.bits}
    }
}

impl<E:CLike> BitAnd<EnumSet<E>, EnumSet<E>> for EnumSet<E> {
    fn bitand(&self, e: &EnumSet<E>) -> EnumSet<E> {
        EnumSet {bits: self.bits & e.bits}
    }
}

impl<E:CLike> BitXor<EnumSet<E>, EnumSet<E>> for EnumSet<E> {
    fn bitxor(&self, e: &EnumSet<E>) -> EnumSet<E> {
        EnumSet {bits: self.bits ^ e.bits}
    }
}

/// An iterator over an EnumSet
pub struct Items<E> {
    index: uint,
    bits: uint,
}

impl<E:CLike> Items<E> {
    fn new(bits: uint) -> Items<E> {
        Items { index: 0, bits: bits }
    }
}

impl<E:CLike> Iterator<E> for Items<E> {
    fn next(&mut self) -> Option<E> {
        if self.bits == 0 {
            return None;
        }

        while (self.bits & 1) == 0 {
            self.index += 1;
            self.bits >>= 1;
        }
        let elem = CLike::from_uint(self.index);
        self.index += 1;
        self.bits >>= 1;
        Some(elem)
    }

    fn size_hint(&self) -> (uint, Option<uint>) {
        let exact = self.bits.count_ones();
        (exact, Some(exact))
    }
}

#[cfg(test)]
mod test {
    use std::prelude::*;
    use std::mem;

    use super::{EnumSet, CLike};

    #[deriving(PartialEq, Show)]
    #[repr(uint)]
    enum Foo {
        A, B, C
    }

    impl CLike for Foo {
        fn to_uint(&self) -> uint {
            *self as uint
        }

        fn from_uint(v: uint) -> Foo {
            unsafe { mem::transmute(v) }
        }
    }

    #[test]
    fn test_new() {
        let e: EnumSet<Foo> = EnumSet::new();
        assert!(e.is_empty());
    }

    #[test]
    fn test_show() {
        let mut e = EnumSet::new();
        assert_eq!("{}", e.to_string().as_slice());
        e.insert(A);
        assert_eq!("{A}", e.to_string().as_slice());
        e.insert(C);
        assert_eq!("{A, C}", e.to_string().as_slice());
    }

    ///////////////////////////////////////////////////////////////////////////
    // intersect

    #[test]
    fn test_two_empties_do_not_intersect() {
        let e1: EnumSet<Foo> = EnumSet::new();
        let e2: EnumSet<Foo> = EnumSet::new();
        assert!(e1.is_disjoint(&e2));
    }

    #[test]
    fn test_empty_does_not_intersect_with_full() {
        let e1: EnumSet<Foo> = EnumSet::new();

        let mut e2: EnumSet<Foo> = EnumSet::new();
        e2.insert(A);
        e2.insert(B);
        e2.insert(C);

        assert!(e1.is_disjoint(&e2));
    }

    #[test]
    fn test_disjoint_intersects() {
        let mut e1: EnumSet<Foo> = EnumSet::new();
        e1.insert(A);

        let mut e2: EnumSet<Foo> = EnumSet::new();
        e2.insert(B);

        assert!(e1.is_disjoint(&e2));
    }

    #[test]
    fn test_overlapping_intersects() {
        let mut e1: EnumSet<Foo> = EnumSet::new();
        e1.insert(A);

        let mut e2: EnumSet<Foo> = EnumSet::new();
        e2.insert(A);
        e2.insert(B);

        assert!(!e1.is_disjoint(&e2));
    }

    ///////////////////////////////////////////////////////////////////////////
    // contains and contains_elem

    #[test]
    fn test_superset() {
        let mut e1: EnumSet<Foo> = EnumSet::new();
        e1.insert(A);

        let mut e2: EnumSet<Foo> = EnumSet::new();
        e2.insert(A);
        e2.insert(B);

        let mut e3: EnumSet<Foo> = EnumSet::new();
        e3.insert(C);

        assert!(e1.is_subset(&e2));
        assert!(e2.is_superset(&e1));
        assert!(!e3.is_superset(&e2))
        assert!(!e2.is_superset(&e3))
    }

    #[test]
    fn test_contains() {
        let mut e1: EnumSet<Foo> = EnumSet::new();
        e1.insert(A);
        assert!(e1.contains(&A));
        assert!(!e1.contains(&B));
        assert!(!e1.contains(&C));

        e1.insert(A);
        e1.insert(B);
        assert!(e1.contains(&A));
        assert!(e1.contains(&B));
        assert!(!e1.contains(&C));
    }

    ///////////////////////////////////////////////////////////////////////////
    // iter

    #[test]
    fn test_iterator() {
        let mut e1: EnumSet<Foo> = EnumSet::new();

        let elems: Vec<Foo> = e1.iter().collect();
        assert!(elems.is_empty())

        e1.insert(A);
        let elems = e1.iter().collect();
        assert_eq!(vec![A], elems)

        e1.insert(C);
        let elems = e1.iter().collect();
        assert_eq!(vec![A,C], elems)

        e1.insert(C);
        let elems = e1.iter().collect();
        assert_eq!(vec![A,C], elems)

        e1.insert(B);
        let elems = e1.iter().collect();
        assert_eq!(vec![A,B,C], elems)
    }

    ///////////////////////////////////////////////////////////////////////////
    // operators

    #[test]
    fn test_operators() {
        let mut e1: EnumSet<Foo> = EnumSet::new();
        e1.insert(A);
        e1.insert(C);

        let mut e2: EnumSet<Foo> = EnumSet::new();
        e2.insert(B);
        e2.insert(C);

        let e_union = e1 | e2;
        let elems = e_union.iter().collect();
        assert_eq!(vec![A,B,C], elems)

        let e_intersection = e1 & e2;
        let elems = e_intersection.iter().collect();
        assert_eq!(vec![C], elems)

        let e_subtract = e1 - e2;
        let elems = e_subtract.iter().collect();
        assert_eq!(vec![A], elems)
    }

    #[test]
    #[should_fail]
    fn test_overflow() {
        #[allow(dead_code)]
        #[repr(uint)]
        enum Bar {
            V00, V01, V02, V03, V04, V05, V06, V07, V08, V09,
            V10, V11, V12, V13, V14, V15, V16, V17, V18, V19,
            V20, V21, V22, V23, V24, V25, V26, V27, V28, V29,
            V30, V31, V32, V33, V34, V35, V36, V37, V38, V39,
            V40, V41, V42, V43, V44, V45, V46, V47, V48, V49,
            V50, V51, V52, V53, V54, V55, V56, V57, V58, V59,
            V60, V61, V62, V63, V64, V65, V66, V67, V68, V69,
        }
        impl CLike for Bar {
            fn to_uint(&self) -> uint {
                *self as uint
            }

            fn from_uint(v: uint) -> Bar {
                unsafe { mem::transmute(v) }
            }
        }
        let mut set = EnumSet::empty();
        set.add(V64);
    }
}
<|endoftext|>


<commit_before>#[feature(macro_rules)];

use std::rt::io::{Writer, CreateOrTruncate};
use std::rt::io::file::FileInfo;
use std::os;

pub mod branchify;
pub mod status;
pub mod read_method;

fn main() {
    let args = os::args();
    match args.len() {
        0 => {
            println("usage: codegen [read_method|status].rs");
            os::set_exit_status(1);
        },
        3 => {
            let output_dir = from_str(args[2]).unwrap();
            // TODO: maybe not 0777?
            os::make_dir(&output_dir, 0b111_111_111);

            match args[1] {
                ~"read_method.rs" => read_method::generate(&output_dir),
                ~"status.rs" => status::generate(&output_dir),
                s => {
                    println!("unknown thing-to-generate '{}'", s);
                    os::set_exit_status(1);
                }
            }
        },
        _ => {
            println!("usage: {} [read_method|status].rs", args[0]);
            os::set_exit_status(1);
        }
    }
}

pub fn get_writer(output_dir: &Path, filename: &str) -> ~Writer {
    let mut output_file = output_dir.clone();
    output_file.push(filename);
    match output_file.open_writer(CreateOrTruncate) {
        Some(writer) => ~writer as ~Writer,
        None => fail!("Unable to write file"),
    }
}
<commit_msg>Update to rust-master<commit_after>#[feature(macro_rules)];

use std::rt::io;
use std::rt::io::{File, Truncate, Write, fs};
use std::os;

pub mod branchify;
pub mod status;
pub mod read_method;

fn main() {
    let args = os::args();
    match args.len() {
        0 => {
            println("usage: codegen [read_method|status].rs");
            os::set_exit_status(1);
        },
        3 => {
            let output_dir = Path::new(args[2].as_slice());
            // TODO: maybe not 0777?
            if !output_dir.exists() {
                fs::mkdir(&output_dir, 0b111_111_111);
            }

            match args[1] {
                ~"read_method.rs" => read_method::generate(&output_dir),
                ~"status.rs" => status::generate(&output_dir),
                s => {
                    println!("unknown thing-to-generate '{}'", s);
                    os::set_exit_status(1);
                }
            }
        },
        _ => {
            println!("usage: {} [read_method|status].rs", args[0]);
            os::set_exit_status(1);
        }
    }
}

pub fn get_writer(output_dir: &Path, filename: &str) -> ~Writer {
    let mut output_file = output_dir.clone();
    output_file.push(filename);
    match io::result(|| File::open_mode(&output_file, Truncate, Write)) {
        Ok(writer) => ~writer as ~Writer,
        Err(e) => fail!("Unable to write file: {}", e.desc),
    }
}
<|endoftext|>


<commit_before>// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
// Andrew Hobden (@hoverbear) <andrew@hoverbear.org>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
// MIT/Apache 2.0 license.

//! This crate is custom derive for clap. It should not be used
//! directly. See [clap documentation](https://docs.rs/clap)
//! for the usage of `#[derive(Clap)]`.

extern crate proc_macro;

use proc_macro::TokenStream;
use proc_macro_error::proc_macro_error;
use syn::{parse_macro_input, DeriveInput};

mod attrs;
mod derives;
mod dummies;
mod parse;
mod utils;

/// Generates the `ArgEnum` impl.
#[proc_macro_derive(ArgEnum, attributes(clap))]
#[proc_macro_error]
pub fn arg_enum(input: TokenStream) -> TokenStream {
    let input: DeriveInput = parse_macro_input!(input);
    derives::derive_arg_enum(&input).into()
}

/// Generates the `Clap` impl.
#[proc_macro_derive(Clap, attributes(clap))]
#[proc_macro_error]
pub fn clap(input: TokenStream) -> TokenStream {
    let input: DeriveInput = parse_macro_input!(input);
    derives::derive_clap(&input).into()
}

// /// Generates the `FromArgMatches` impl.
// #[proc_macro_derive(FromArgMatches, attributes(clap))]
// #[proc_macro_error]
// pub fn from_arg_matches(input: TokenStream) -> TokenStream {
//     let input: DeriveInput = parse_macro_input!(input);
//     derives::derive_from_arg_matches(&input).into()
// }

/// Generates the `IntoApp` impl.
#[proc_macro_derive(IntoApp, attributes(clap))]
#[proc_macro_error]
pub fn into_app(input: TokenStream) -> TokenStream {
    let input: DeriveInput = parse_macro_input!(input);
    derives::derive_into_app(&input).into()
}

/// Generates the `Subcommand` impl.
#[proc_macro_derive(Subcommand, attributes(clap))]
#[proc_macro_error]
pub fn subcommand(input: TokenStream) -> TokenStream {
    let input: DeriveInput = parse_macro_input!(input);
    derives::derive_subcommand(&input).into()
}
<commit_msg>wip: clap_derive docs<commit_after>// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
// Andrew Hobden (@hoverbear) <andrew@hoverbear.org>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
// MIT/Apache 2.0 license.

//! This crate is custom derive for clap. It should not be used
//! directly. See [clap documentation](https://docs.rs/clap)
//! for the usage of `#[derive(Clap)]`.

extern crate proc_macro;

use proc_macro::TokenStream;
use proc_macro_error::proc_macro_error;
use syn::{parse_macro_input, DeriveInput};

mod attrs;
mod derives;
mod dummies;
mod parse;
mod utils;

/// Generates the `ArgEnum` impl.
#[proc_macro_derive(ArgEnum, attributes(clap))]
#[proc_macro_error]
pub fn arg_enum(input: TokenStream) -> TokenStream {
    let input: DeriveInput = parse_macro_input!(input);
    derives::derive_arg_enum(&input).into()
}

/// Generates the `Clap` implementation.
///
/// This is far less verbose than defining the [`App`] struct manually,
/// receiving an instance of [`ArgMatches`] from conducting parsing, and then
/// implementing a conversion code (usually `impl From<ArgMatches> for
/// MyStruct`) to instantiate an instance of the user context struct.
#[proc_macro_derive(Clap, attributes(clap))]
#[proc_macro_error]
pub fn clap(input: TokenStream) -> TokenStream {
    let input: DeriveInput = parse_macro_input!(input);
    derives::derive_clap(&input).into()
}

// /// Generates the `FromArgMatches` impl.
// #[proc_macro_derive(FromArgMatches, attributes(clap))]
// #[proc_macro_error]
// pub fn from_arg_matches(input: TokenStream) -> TokenStream {
//     let input: DeriveInput = parse_macro_input!(input);
//     derives::derive_from_arg_matches(&input).into()
// }

/// Generates the `IntoApp` impl.
#[proc_macro_derive(IntoApp, attributes(clap))]
#[proc_macro_error]
pub fn into_app(input: TokenStream) -> TokenStream {
    let input: DeriveInput = parse_macro_input!(input);
    derives::derive_into_app(&input).into()
}

/// Generates the `Subcommand` impl.
#[proc_macro_derive(Subcommand, attributes(clap))]
#[proc_macro_error]
pub fn subcommand(input: TokenStream) -> TokenStream {
    let input: DeriveInput = parse_macro_input!(input);
    derives::derive_subcommand(&input).into()
}
<|endoftext|>


<commit_before>// Zinc, the bare metal stack for rust.
// Copyright 2014 Vladimir "farcaller" Pouzanov <farcaller@gmail.com>
//
// 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::cell::{Cell, RefCell};
use std::collections::hashmap::HashMap;
use std::fmt;
use std::rc::{Rc, Weak};
use syntax::codemap::{Span, DUMMY_SP};
use syntax::ext::base::ExtCtxt;

use builder::Builder;

/// Holds a value for an attribute.
///
/// The value can be an unsigned integer, string or reference.
#[deriving(Clone)]
pub enum AttributeValue {
  IntValue(uint),
  StrValue(String),
  RefValue(String),
}

/// Expected attribute type.
///
/// Used in Node::expect_attributes to provide the expected type of the
/// attribute.
pub enum AttributeType {
  IntAttribute,
  StrAttribute,
  RefAttribute,
}

/// Attribute value and metadata.
///
/// Stored inside of a HashMap, the key to HashMap is the attribute name.
/// Provides spans for both key and value.
#[deriving(Clone)]
pub struct Attribute {
  pub value: AttributeValue,
  pub key_span: Span,
  pub value_span: Span,
}

impl Attribute {
  pub fn new(value: AttributeValue, key_span: Span, value_span: Span)
      -> Attribute {
    Attribute {
      value: value,
      key_span: key_span,
      value_span: value_span,
    }
  }

  pub fn new_nosp(value: AttributeValue) -> Attribute {
    Attribute {
      value: value,
      key_span: DUMMY_SP,
      value_span: DUMMY_SP,
    }
  }
}

pub type NodeBuilderFn = fn(&mut Builder, &mut ExtCtxt, Rc<Node>);

pub struct Subnodes {
  by_index: Vec<Rc<Node>>,
  by_path: HashMap<String, Weak<Node>>,
}

impl Subnodes {
  pub fn new() -> Subnodes {
    Subnodes {
      by_index: vec!(),
      by_path: HashMap::new(),
    }
  }

  pub fn push(&mut self, node: Rc<Node>) {
    let weak = node.downgrade();
    self.by_path.insert(node.path.clone(), weak);
    self.by_index.push(node);
  }

  pub fn as_vec<'a>(&'a self) -> &'a Vec<Rc<Node>> {
    &self.by_index
  }

  pub fn as_map<'a>(&'a self) -> &'a HashMap<String, Weak<Node>> {
    &self.by_path
  }

  pub fn clone_from(&mut self, other: Subnodes) {
    self.by_index = other.by_index;
    self.by_path = other.by_path;
  }
}

/// PlatformTree node.
///
/// Might have an optional name, is the name is missing, name_span is equal to
/// path_span. Attributes are stored by name, subnodes are stored by path.
/// Type_name, if present, must specify the type path for the node's
/// materialized object.
pub struct Node {
  pub name: Option<String>,
  pub name_span: Span,

  pub path: String,
  pub path_span: Span,

  pub attributes: RefCell<HashMap<String, Rc<Attribute>>>,
  subnodes: RefCell<Subnodes>,
  pub parent: Option<Weak<Node>>,

  type_name: RefCell<Option<String>>,
  type_params: RefCell<Vec<String>>,

  /// A function that materializes this node.
  pub materializer: Cell<Option<NodeBuilderFn>>,

  /// Present iff this node will modify state of any other nodes.
  pub mutator: Cell<Option<NodeBuilderFn>>,

  /// List of nodes that must be materialized before this node.
  pub depends_on: RefCell<Vec<Weak<Node>>>,

  /// List of nodes that may be materialized before this node.
  pub rev_depends_on: RefCell<Vec<Weak<Node>>>,
}

impl Node {
  pub fn new(name: Option<String>, name_span: Span, path: String,
      path_span: Span, parent: Option<Weak<Node>>) -> Node {
    Node {
      name: name,
      name_span: name_span,
      path: path,
      path_span: path_span,
      attributes: RefCell::new(HashMap::new()),
      subnodes: RefCell::new(Subnodes::new()),
      parent: parent,
      type_name: RefCell::new(None),
      type_params: RefCell::new(vec!()),
      materializer: Cell::new(None),
      mutator: Cell::new(None),
      depends_on: RefCell::new(Vec::new()),
      rev_depends_on: RefCell::new(Vec::new()),
    }
  }

  pub fn set_type_name(&self, tn: String) {
    let mut borrow = self.type_name.borrow_mut();
    borrow.deref_mut().clone_from(&Some(tn));
  }

  pub fn type_name(&self) -> Option<String> {
    self.type_name.borrow().clone()
  }

  pub fn type_params(&self) -> Vec<String> {
    self.type_params.borrow().clone()
  }

  pub fn set_type_params(&self, params: Vec<String>) {
    let mut borrow = self.type_params.borrow_mut();
    borrow.deref_mut().clone_from(&params);
  }

  pub fn subnodes(&self) -> Vec<Rc<Node>> {
    self.subnodes.borrow().as_vec().clone()
  }

  pub fn with_subnodes_map(&self, f: |&HashMap<String, Weak<Node>>|) {
    let borrow = self.subnodes.borrow();
    f(borrow.as_map());
  }

  pub fn set_subnodes(&self, new: Subnodes) {
    self.subnodes.borrow_mut().clone_from(new);
  }

  pub fn path(&self) -> String {
    self.path.clone()
  }

  pub fn full_path(&self) -> String {
    let pp = match self.parent {
      Some(ref parent) => parent.clone().upgrade().unwrap().full_path() + "::",
      None => "".to_string(),
    };
    pp + self.path
  }

  /// Returns attribute by name or fail!()s.
  pub fn get_attr(&self, key: &str) -> Rc<Attribute> {
    self.attributes.borrow().get(&key.to_string()).clone()
  }

  /// Returns a string attribute by name or None, if it's not present or not of
  /// a StrAttribute type.
  pub fn get_string_attr(&self, key: &str) -> Option<String> {
    self.attributes.borrow().find(&key.to_string()).and_then(|av| match av.value {
      StrValue(ref s) => Some(s.clone()),
      _ => None,
    })
  }

  /// Returns an integer attribute by name or None, if it's not present or not
  /// of an IntAttribute type.
  pub fn get_int_attr(&self, key: &str) -> Option<uint> {
    self.attributes.borrow().find(&key.to_string()).and_then(|av| match av.value {
      IntValue(ref u) => Some(*u),
      _ => None,
    })
  }

  /// Returns a reference attribute by name or None, if it's not present or not
  /// of a RefAttribute type.
  pub fn get_ref_attr(&self, key: &str) -> Option<String> {
    self.attributes.borrow().find(&key.to_string()).and_then(|av| match av.value {
      RefValue(ref s) => Some(s.clone()),
      _ => None,
    })
  }

  /// Returns a string attribute by name or None, if it's not present or not of
  /// a StrAttribute type. Reports a parser error if an attribute is
  /// missing.
  pub fn get_required_string_attr(&self, cx: &ExtCtxt, key: &str)
      -> Option<String> {
    match self.get_string_attr(key) {
      Some(val) => Some(val),
      None => {
        cx.parse_sess().span_diagnostic.span_err(self.name_span,
            format!("required string attribute `{}` is missing", key)
            .as_slice());
        None
      }
    }
  }

  /// Returns an integer attribute by name or None, if it's not present or not
  /// of an IntAttribute type. Reports a parser error if an attribute is
  /// missing.
  pub fn get_required_int_attr(&self, cx: &ExtCtxt, key: &str)
      -> Option<uint> {
    match self.get_int_attr(key) {
      Some(val) => Some(val),
      None => {
        cx.parse_sess().span_diagnostic.span_err(self.name_span,
            format!("required integer attribute `{}` is missing", key)
            .as_slice());
        None
      }
    }
  }

  /// Returns a reference attribute by name or None, if it's not present or not
  /// of a RefAttribute type. Reports a parser error if an attribute is
  /// missing.
  pub fn get_required_ref_attr(&self, cx: &ExtCtxt, key: &str)
      -> Option<String> {
    match self.get_ref_attr(key) {
      Some(val) => Some(val),
      None => {
        cx.parse_sess().span_diagnostic.span_err(self.name_span,
            format!("required ref attribute `{}` is missing", key)
            .as_slice());
        None
      }
    }
  }

  /// Returns true if node has no attributes. Returs false and reports a parser
  /// error for each found attribute otherwise.
  pub fn expect_no_attributes(&self, cx: &ExtCtxt) -> bool {
    let mut ok = true;
    for (_, v) in self.attributes.borrow().iter() {
      ok = false;
      cx.parse_sess().span_diagnostic.span_err(v.key_span,
          "no attributes expected");
    }
    ok
  }

  /// Returns true if node has no subnodes. Returs false and reports a parser
  /// error for each found subnode otherwise.
  pub fn expect_no_subnodes(&self, cx: &ExtCtxt) -> bool {
    let mut ok = true;
    for sub in self.subnodes().iter() {
      ok = false;
      cx.parse_sess().span_diagnostic.span_err(sub.name_span,
          "no subnodes expected");
    }
    ok
  }

  /// Returns true if node has all of the requested attributes and their types
  /// match. Reports parser errors and returns false otherwise.
  pub fn expect_attributes(&self, cx: &ExtCtxt,
      expectations: &[(&str, AttributeType)]) -> bool {
    let mut ok = true;
    for &(n, ref t) in expectations.iter() {
      match t {
        &StrAttribute => {
          if self.get_required_string_attr(cx, n).is_none() {ok = false}
        },
        &IntAttribute => {
          if self.get_required_int_attr(cx, n).is_none() {ok = false}
        },
        &RefAttribute => {
          if self.get_required_ref_attr(cx, n).is_none() {ok = false}
        },
      }
    }
    ok
  }

  /// Returns true if node has all of the requested subnodes matched by path.
  /// Reports parser errors and returns false otherwise.
  pub fn expect_subnodes(&self, cx: &ExtCtxt, expectations: &[&str]) -> bool {
    let mut ok = true;
    for sub in self.subnodes().iter() {
      if !expectations.contains(&sub.path.as_slice()) {
        ok = false;
        cx.parse_sess().span_diagnostic.span_err(sub.path_span,
            format!("unknown subnode `{}` in node `{}`",
                sub.path, self.path).as_slice());
      }
    }
    ok
  }

  /// Returns a subnode by path or None, if not found.
  pub fn get_by_path(&self, path: &str) -> Option<Rc<Node>> {
    self.subnodes.borrow().as_map().find(&path.to_string()).and_then(|node| {
      Some(node.clone().upgrade().unwrap())
    })
  }
}

impl PartialEq for Node {
  fn eq(&self, other: &Node) -> bool {
    self.full_path() == other.full_path()
  }

  fn ne(&self, other: &Node) -> bool {
    self.full_path() != other.full_path()
  }
}

impl fmt::Show for Node {
  fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::FormatError> {
    fmt.write_str(format!("<Node {}>", self.full_path()).as_slice())
        .or_else(|_| { fail!() })
  }
}

/// PlatformTree root object.
///
/// Root nodes are stored by path in `nodes`, All the nmaed nodes are also
/// stored by name in `named`.
pub struct PlatformTree {
  nodes: HashMap<String, Rc<Node>>,
  named: HashMap<String, Weak<Node>>,
}

impl PlatformTree {
  pub fn new(nodes: HashMap<String, Rc<Node>>,
      named: HashMap<String, Weak<Node>>) -> PlatformTree {
    PlatformTree {
      nodes: nodes,
      named: named,
    }
  }

  pub fn nodes(&self) -> Vec<Rc<Node>> {
    let mut v = vec!();
    for (_, sub) in self.nodes.iter() {
      v.push(sub.clone())
    }
    v
  }

  /// Returns a node by name or None, if not found.
  pub fn get_by_name(&self, name: &str) -> Option<Rc<Node>> {
    self.named.find(&name.to_string()).and_then(|node| { Some(node.upgrade().unwrap().clone()) })
  }

  /// Returns a root node by path or None, if not found.
  pub fn get_by_path(&self, name: &str) -> Option<Rc<Node>> {
    self.nodes.find(&name.to_string()).and_then(|node| { Some(node.clone()) })
  }

  /// Returns true if PT has all of the requested root odes matched by path.
  /// Reports parser errors and returns false otherwise.
  pub fn expect_subnodes(&self, cx: &ExtCtxt, expectations: &[&str]) -> bool {
    let mut ok = true;
    for (path, sub) in self.nodes.iter() {
      if !expectations.contains(&path.as_slice()) {
        ok = false;
        cx.parse_sess().span_diagnostic.span_err(sub.path_span,
            format!("unknown root node `{}`", path).as_slice());
      }
    }
    ok
  }
}
<commit_msg>Improved documentation for PT::Node<commit_after>// Zinc, the bare metal stack for rust.
// Copyright 2014 Vladimir "farcaller" Pouzanov <farcaller@gmail.com>
//
// 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::cell::{Cell, RefCell};
use std::collections::hashmap::HashMap;
use std::fmt;
use std::rc::{Rc, Weak};
use syntax::codemap::{Span, DUMMY_SP};
use syntax::ext::base::ExtCtxt;

use builder::Builder;

/// Holds a value for an attribute.
///
/// The value can be an unsigned integer, string or reference.
#[deriving(Clone)]
pub enum AttributeValue {
  IntValue(uint),
  StrValue(String),
  RefValue(String),
}

/// Expected attribute type.
///
/// Used in Node::expect_attributes to provide the expected type of the
/// attribute.
pub enum AttributeType {
  IntAttribute,
  StrAttribute,
  RefAttribute,
}

/// Attribute value and metadata.
///
/// Stored inside of a HashMap, the key to HashMap is the attribute name.
/// Provides spans for both key and value.
#[deriving(Clone)]
pub struct Attribute {
  pub value: AttributeValue,
  pub key_span: Span,
  pub value_span: Span,
}

impl Attribute {
  /// Creates a new attribute with given span.
  pub fn new(value: AttributeValue, key_span: Span, value_span: Span)
      -> Attribute {
    Attribute {
      value: value,
      key_span: key_span,
      value_span: value_span,
    }
  }

  /// Creates a new attribute with DUMMY_SP for key and value.
  pub fn new_nosp(value: AttributeValue) -> Attribute {
    Attribute {
      value: value,
      key_span: DUMMY_SP,
      value_span: DUMMY_SP,
    }
  }
}

/// Node builder is a function that generates code based on the node content or
/// mutates other nodes.
pub type NodeBuilderFn = fn(&mut Builder, &mut ExtCtxt, Rc<Node>);

/// Subnodes is effectively an ordered map.
///
/// We still address nodes by path for most of use cases, but we need to know
/// the original order of appearance (it makes things deterministic).
pub struct Subnodes {
  by_index: Vec<Rc<Node>>,
  by_path: HashMap<String, Weak<Node>>,
}

impl Subnodes {
  pub fn new() -> Subnodes {
    Subnodes {
      by_index: vec!(),
      by_path: HashMap::new(),
    }
  }

  /// Adds a node to subnodes.
  ///
  /// The node must not be present in the subnodes.
  pub fn push(&mut self, node: Rc<Node>) {
    let weak = node.downgrade();
    self.by_path.insert(node.path.clone(), weak);
    self.by_index.push(node);
  }

  /// Returns a vector representation of subnodes.
  pub fn as_vec<'a>(&'a self) -> &'a Vec<Rc<Node>> {
    &self.by_index
  }

  /// Returns a map representation of subnodes.
  pub fn as_map<'a>(&'a self) -> &'a HashMap<String, Weak<Node>> {
    &self.by_path
  }

  /// A helper method to move data from other subnodes into current instance.
  ///
  /// Used as a helper for wrapping in RefCell.
  pub fn clone_from(&mut self, other: Subnodes) {
    self.by_index = other.by_index;
    self.by_path = other.by_path;
  }
}

/// PlatformTree node.
///
/// Might have an optional name, is the name is missing, name_span is equal to
/// path_span. Attributes are stored by name, subnodes are stored by path.
/// Type_name, if present, must specify the type path for the node's
/// materialized object.
///
/// Two nodes are equal if their full paths are equal.
pub struct Node {
  /// Node name, might be optional.
  pub name: Option<String>,

  /// Name span if name is present or path span otherwise.
  pub name_span: Span,

  /// Node path, which is unique among all sibling nodes.
  pub path: String,

  /// Path span.
  pub path_span: Span,

  /// a map of node's attributes.
  pub attributes: RefCell<HashMap<String, Rc<Attribute>>>,

  /// A weak reference to parent node, None for root nodes.
  pub parent: Option<Weak<Node>>,

  /// A function that materializes this node, i.e. generates some actionable
  /// code.
  ///
  /// Materializers are exectuted in order of dependencies resolution, so having
  /// a fully built tree of dependencies is a must.
  pub materializer: Cell<Option<NodeBuilderFn>>,

  /// Present iff this node will modify state of any other nodes.
  ///
  /// Mutators are executed before materializers in no specific order.
  pub mutator: Cell<Option<NodeBuilderFn>>,

  /// List of nodes that must be materialized before this node.
  ///
  /// Generally, a node must depend on something to be materialized. The root
  /// node that all other nodes must depend on implicitly or explicitly is
  /// mcu::clock, which must always be present in PT.
  pub depends_on: RefCell<Vec<Weak<Node>>>,

  /// List of nodes that may be materialized before this node.
  pub rev_depends_on: RefCell<Vec<Weak<Node>>>,

  subnodes: RefCell<Subnodes>,
  type_name: RefCell<Option<String>>,
  type_params: RefCell<Vec<String>>,
}

impl Node {
  pub fn new(name: Option<String>, name_span: Span, path: String,
      path_span: Span, parent: Option<Weak<Node>>) -> Node {
    Node {
      name: name,
      name_span: name_span,
      path: path,
      path_span: path_span,
      attributes: RefCell::new(HashMap::new()),
      subnodes: RefCell::new(Subnodes::new()),
      parent: parent,
      type_name: RefCell::new(None),
      type_params: RefCell::new(vec!()),
      materializer: Cell::new(None),
      mutator: Cell::new(None),
      depends_on: RefCell::new(Vec::new()),
      rev_depends_on: RefCell::new(Vec::new()),
    }
  }

  /// Set type name for the generated struct.
  ///
  /// If this node generates an object in main(), type_name references the type
  /// of that object, e.g. for DHT22 driver that would be
  /// `zinc::drivers::dht22::DHT22`.
  pub fn set_type_name(&self, tn: String) {
    let mut borrow = self.type_name.borrow_mut();
    borrow.deref_mut().clone_from(&Some(tn));
  }

  /// Get type name for the generated struct.
  pub fn type_name(&self) -> Option<String> {
    self.type_name.borrow().clone()
  }

  /// Get type parameters for the generated object.
  pub fn type_params(&self) -> Vec<String> {
    self.type_params.borrow().clone()
  }

  /// Set type parameters for the generated object, including lifetimes.
  ///
  /// A default lifetime if this is going to end as a task argument is 'a. Other
  /// lifetimes or fully-qualified types may be used as well. DHT22 driver uses
  /// this to provide `zinc::hal::timer::Timer` and `zinc::hal::pin::GPIO` for
  /// its public struct of `pub struct DHT22<'a, T, P>`.
  pub fn set_type_params(&self, params: Vec<String>) {
    let mut borrow = self.type_params.borrow_mut();
    borrow.deref_mut().clone_from(&params);
  }

  /// Returns a cloned vec of node's subnodes.
  pub fn subnodes(&self) -> Vec<Rc<Node>> {
    self.subnodes.borrow().as_vec().clone()
  }

  /// Invokes the closure for each node from node's subnodes passing a path and
  /// weak node reference.
  pub fn with_subnodes_map(&self, f: |&HashMap<String, Weak<Node>>|) {
    let borrow = self.subnodes.borrow();
    f(borrow.as_map());
  }

  /// Sets the node's subnodes from a passed object.
  pub fn set_subnodes(&self, new: Subnodes) {
    self.subnodes.borrow_mut().clone_from(new);
  }

  /// Returns a clones string of current node's path.
  pub fn path(&self) -> String {
    self.path.clone()
  }

  /// Returns a fully-qualified path of the current node.
  pub fn full_path(&self) -> String {
    let pp = match self.parent {
      Some(ref parent) => parent.clone().upgrade().unwrap().full_path() + "::",
      None => "".to_string(),
    };
    pp + self.path
  }

  /// Returns attribute by name or fail!()s.
  pub fn get_attr(&self, key: &str) -> Rc<Attribute> {
    self.attributes.borrow().get(&key.to_string()).clone()
  }

  /// Returns a string attribute by name or None, if it's not present or not of
  /// a StrAttribute type.
  pub fn get_string_attr(&self, key: &str) -> Option<String> {
    self.attributes.borrow().find(&key.to_string()).and_then(|av| match av.value {
      StrValue(ref s) => Some(s.clone()),
      _ => None,
    })
  }

  /// Returns an integer attribute by name or None, if it's not present or not
  /// of an IntAttribute type.
  pub fn get_int_attr(&self, key: &str) -> Option<uint> {
    self.attributes.borrow().find(&key.to_string()).and_then(|av| match av.value {
      IntValue(ref u) => Some(*u),
      _ => None,
    })
  }

  /// Returns a reference attribute by name or None, if it's not present or not
  /// of a RefAttribute type.
  pub fn get_ref_attr(&self, key: &str) -> Option<String> {
    self.attributes.borrow().find(&key.to_string()).and_then(|av| match av.value {
      RefValue(ref s) => Some(s.clone()),
      _ => None,
    })
  }

  /// Returns a string attribute by name or None, if it's not present or not of
  /// a StrAttribute type. Reports a parser error if an attribute is
  /// missing.
  pub fn get_required_string_attr(&self, cx: &ExtCtxt, key: &str)
      -> Option<String> {
    match self.get_string_attr(key) {
      Some(val) => Some(val),
      None => {
        cx.parse_sess().span_diagnostic.span_err(self.name_span,
            format!("required string attribute `{}` is missing", key)
            .as_slice());
        None
      }
    }
  }

  /// Returns an integer attribute by name or None, if it's not present or not
  /// of an IntAttribute type. Reports a parser error if an attribute is
  /// missing.
  pub fn get_required_int_attr(&self, cx: &ExtCtxt, key: &str)
      -> Option<uint> {
    match self.get_int_attr(key) {
      Some(val) => Some(val),
      None => {
        cx.parse_sess().span_diagnostic.span_err(self.name_span,
            format!("required integer attribute `{}` is missing", key)
            .as_slice());
        None
      }
    }
  }

  /// Returns a reference attribute by name or None, if it's not present or not
  /// of a RefAttribute type. Reports a parser error if an attribute is
  /// missing.
  pub fn get_required_ref_attr(&self, cx: &ExtCtxt, key: &str)
      -> Option<String> {
    match self.get_ref_attr(key) {
      Some(val) => Some(val),
      None => {
        cx.parse_sess().span_diagnostic.span_err(self.name_span,
            format!("required ref attribute `{}` is missing", key)
            .as_slice());
        None
      }
    }
  }

  /// Returns true if node has no attributes. Returs false and reports a parser
  /// error for each found attribute otherwise.
  pub fn expect_no_attributes(&self, cx: &ExtCtxt) -> bool {
    let mut ok = true;
    for (_, v) in self.attributes.borrow().iter() {
      ok = false;
      cx.parse_sess().span_diagnostic.span_err(v.key_span,
          "no attributes expected");
    }
    ok
  }

  /// Returns true if node has no subnodes. Returs false and reports a parser
  /// error for each found subnode otherwise.
  pub fn expect_no_subnodes(&self, cx: &ExtCtxt) -> bool {
    let mut ok = true;
    for sub in self.subnodes().iter() {
      ok = false;
      cx.parse_sess().span_diagnostic.span_err(sub.name_span,
          "no subnodes expected");
    }
    ok
  }

  /// Returns true if node has all of the requested attributes and their types
  /// match. Reports parser errors and returns false otherwise.
  pub fn expect_attributes(&self, cx: &ExtCtxt,
      expectations: &[(&str, AttributeType)]) -> bool {
    let mut ok = true;
    for &(n, ref t) in expectations.iter() {
      match t {
        &StrAttribute => {
          if self.get_required_string_attr(cx, n).is_none() {ok = false}
        },
        &IntAttribute => {
          if self.get_required_int_attr(cx, n).is_none() {ok = false}
        },
        &RefAttribute => {
          if self.get_required_ref_attr(cx, n).is_none() {ok = false}
        },
      }
    }
    ok
  }

  /// Returns true if node has all of the requested subnodes matched by path.
  /// Reports parser errors and returns false otherwise.
  pub fn expect_subnodes(&self, cx: &ExtCtxt, expectations: &[&str]) -> bool {
    let mut ok = true;
    for sub in self.subnodes().iter() {
      if !expectations.contains(&sub.path.as_slice()) {
        ok = false;
        cx.parse_sess().span_diagnostic.span_err(sub.path_span,
            format!("unknown subnode `{}` in node `{}`",
                sub.path, self.path).as_slice());
      }
    }
    ok
  }

  /// Returns a subnode by path or None, if not found.
  pub fn get_by_path(&self, path: &str) -> Option<Rc<Node>> {
    self.subnodes.borrow().as_map().find(&path.to_string()).and_then(|node| {
      Some(node.clone().upgrade().unwrap())
    })
  }
}

impl PartialEq for Node {
  fn eq(&self, other: &Node) -> bool {
    self.full_path() == other.full_path()
  }

  fn ne(&self, other: &Node) -> bool {
    self.full_path() != other.full_path()
  }
}

impl fmt::Show for Node {
  fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::FormatError> {
    fmt.write_str(format!("<Node {}>", self.full_path()).as_slice())
        .or_else(|_| { fail!() })
  }
}

/// PlatformTree root object.
///
/// Root nodes are stored by path in `nodes`, All the nmaed nodes are also
/// stored by name in `named`.
///
/// TODO(farcaller): this could be really refactored into transient root node
/// object that can depend on mcu::clock.
pub struct PlatformTree {
  nodes: HashMap<String, Rc<Node>>,
  named: HashMap<String, Weak<Node>>,
}

impl PlatformTree {
  pub fn new(nodes: HashMap<String, Rc<Node>>,
      named: HashMap<String, Weak<Node>>) -> PlatformTree {
    PlatformTree {
      nodes: nodes,
      named: named,
    }
  }

  pub fn nodes(&self) -> Vec<Rc<Node>> {
    let mut v = vec!();
    for (_, sub) in self.nodes.iter() {
      v.push(sub.clone())
    }
    v
  }

  /// Returns a node by name or None, if not found.
  pub fn get_by_name(&self, name: &str) -> Option<Rc<Node>> {
    self.named.find(&name.to_string()).and_then(|node| { Some(node.upgrade().unwrap().clone()) })
  }

  /// Returns a root node by path or None, if not found.
  pub fn get_by_path(&self, name: &str) -> Option<Rc<Node>> {
    self.nodes.find(&name.to_string()).and_then(|node| { Some(node.clone()) })
  }

  /// Returns true if PT has all of the requested root odes matched by path.
  /// Reports parser errors and returns false otherwise.
  pub fn expect_subnodes(&self, cx: &ExtCtxt, expectations: &[&str]) -> bool {
    let mut ok = true;
    for (path, sub) in self.nodes.iter() {
      if !expectations.contains(&path.as_slice()) {
        ok = false;
        cx.parse_sess().span_diagnostic.span_err(sub.path_span,
            format!("unknown root node `{}`", path).as_slice());
      }
    }
    ok
  }
}
<|endoftext|>